@indiegems/gem-web-sdk 9.1.3 → 9.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  # The server surface — API reference
2
2
 
3
- Generated from `@indiegems/gem-web-sdk/sdk` v9.1.3. Every symbol below links to its page on the Gem Arcade developer site.
3
+ Generated from `@indiegems/gem-web-sdk/sdk` v9.1.4, the version of the package this file ships in. Every symbol below carries an explicit anchor, its `anchor` in `reference.json` beside this file.
4
4
 
5
5
  `@indiegems/gem-web-sdk/sdk` is the one import a game writes, wherever its code runs. `@indiegems/gem-web-sdk/server` exports exactly the symbols on this page and nothing else, for a build that wants only this half.
6
6
 
@@ -8,11 +8,11 @@ Generated from `@indiegems/gem-web-sdk/sdk` v9.1.3. Every symbol below links to
8
8
  import { defineServer } from '@indiegems/gem-web-sdk/sdk';
9
9
  ```
10
10
 
11
- One dependency covers both halves of a game, and it is written out in [getting-started](https://gemarcade.com/developer/docs/getting-started#3-add-the-sdk) — the only place it is written out, so there is one copy to keep current. It is a public download needing no account and no credential, it goes in `dependencies`, and nothing is built on install. If you followed that page, the server entry is already installed.
11
+ One dependency covers both halves of a game, and it is written out in [getting-started](../getting-started.md#3-add-the-sdk) — the only place it is written out, so there is one copy to keep current. It is a public download needing no account and no credential, it goes in `dependencies`, and nothing is built on install. If you followed that page, the server entry is already installed.
12
12
 
13
- ## AnalyticsValue
13
+ <a id="analyticsvalue"></a>
14
14
 
15
- [AnalyticsValue](https://gemarcade.com/developer/api/server#analyticsvalue)
15
+ ## AnalyticsValue
16
16
 
17
17
  ```ts
18
18
  type AnalyticsValue = string | number
@@ -26,9 +26,9 @@ declared ahead of time — that is the whole reason there is no free-text
26
26
  property — and measures are the numbers the platform sums, averages and
27
27
  takes the min and max of.
28
28
 
29
- ## AnalyticsRejection
29
+ <a id="analyticsrejection"></a>
30
30
 
31
- [AnalyticsRejection](https://gemarcade.com/developer/api/server#analyticsrejection)
31
+ ## AnalyticsRejection
32
32
 
33
33
  ```ts
34
34
  interface AnalyticsRejection
@@ -36,9 +36,9 @@ interface AnalyticsRejection
36
36
 
37
37
  One event the platform refused, inside an otherwise fine batch.
38
38
 
39
- ## AnalyticsRejection.index
39
+ <a id="analyticsrejection-index"></a>
40
40
 
41
- [AnalyticsRejection.index](https://gemarcade.com/developer/api/server#analyticsrejection-index)
41
+ ## AnalyticsRejection.index
42
42
 
43
43
  ```ts
44
44
  readonly index: number
@@ -50,9 +50,9 @@ Which event was refused, as an index into the batch that was sent.
50
50
  schedule, so this is only meaningful beside the code — which is the part
51
51
  worth acting on.
52
52
 
53
- ## AnalyticsRejection.code
53
+ <a id="analyticsrejection-code"></a>
54
54
 
55
- [AnalyticsRejection.code](https://gemarcade.com/developer/api/server#analyticsrejection-code)
55
+ ## AnalyticsRejection.code
56
56
 
57
57
  ```ts
58
58
  readonly code: string
@@ -66,9 +66,9 @@ channel declares a set and this name is not in it), `unknown_dimension`,
66
66
  `missing_dimension`, `dimension_value_not_declared`, `unknown_measure`,
67
67
  `measure_wrong_type`, `source_not_permitted`.
68
68
 
69
- ## AnalyticsFlushResult
69
+ <a id="analyticsflushresult"></a>
70
70
 
71
- [AnalyticsFlushResult](https://gemarcade.com/developer/api/server#analyticsflushresult)
71
+ ## AnalyticsFlushResult
72
72
 
73
73
  ```ts
74
74
  interface AnalyticsFlushResult
@@ -76,9 +76,9 @@ interface AnalyticsFlushResult
76
76
 
77
77
  What `AnalyticsApi.flush` resolves with.
78
78
 
79
- ## AnalyticsFlushResult.accepted
79
+ <a id="analyticsflushresult-accepted"></a>
80
80
 
81
- [AnalyticsFlushResult.accepted](https://gemarcade.com/developer/api/server#analyticsflushresult-accepted)
81
+ ## AnalyticsFlushResult.accepted
82
82
 
83
83
  ```ts
84
84
  readonly accepted: number
@@ -86,9 +86,9 @@ readonly accepted: number
86
86
 
87
87
  How many events the platform accepted and wrote.
88
88
 
89
- ## AnalyticsFlushResult.rejected
89
+ <a id="analyticsflushresult-rejected"></a>
90
90
 
91
- [AnalyticsFlushResult.rejected](https://gemarcade.com/developer/api/server#analyticsflushresult-rejected)
91
+ ## AnalyticsFlushResult.rejected
92
92
 
93
93
  ```ts
94
94
  readonly rejected: readonly AnalyticsRejection[]
@@ -100,9 +100,9 @@ Every event the platform refused, by index into the batch that was sent.
100
100
  accepted events in the same batch were still written. An empty array is
101
101
  the ordinary case.
102
102
 
103
- ## AnalyticsFlushResult.dropped
103
+ <a id="analyticsflushresult-dropped"></a>
104
104
 
105
- [AnalyticsFlushResult.dropped](https://gemarcade.com/developer/api/server#analyticsflushresult-dropped)
105
+ ## AnalyticsFlushResult.dropped
106
106
 
107
107
  ```ts
108
108
  readonly dropped: number
@@ -114,9 +114,9 @@ flush, because it was full. The oldest go first.
114
114
  A number worth alarming on: it is nonzero only when the game is producing
115
115
  events faster than the platform accepts them.
116
116
 
117
- ## AnalyticsFlushResult.duplicate
117
+ <a id="analyticsflushresult-duplicate"></a>
118
118
 
119
- [AnalyticsFlushResult.duplicate](https://gemarcade.com/developer/api/server#analyticsflushresult-duplicate)
119
+ ## AnalyticsFlushResult.duplicate
120
120
 
121
121
  ```ts
122
122
  readonly duplicate: boolean
@@ -128,9 +128,9 @@ A retry that the first attempt had in fact delivered. Not an error — it is
128
128
  what stops a flush racing a `pagehide` from being counted twice — and
129
129
  `accepted` is 0 on one.
130
130
 
131
- ## Base64String
131
+ <a id="base64string"></a>
132
132
 
133
- [Base64String](https://gemarcade.com/developer/api/server#base64string)
133
+ ## Base64String
134
134
 
135
135
  ```ts
136
136
  type Base64String = string & { __contentEncoding: "base64" }
@@ -148,12 +148,12 @@ This type moves that failure to your build. `{ value: JSON.stringify(state) }`
148
148
  type-checks perfectly against `string` and is exactly the mistake being
149
149
  stopped.
150
150
 
151
- MAKE ONE WITH [toBase64](https://gemarcade.com/developer/api/server#tobase64). Writing `as Base64String` instead silences the
151
+ MAKE ONE WITH [toBase64](#tobase64). Writing `as Base64String` instead silences the
152
152
  error and puts the trap back.
153
153
 
154
- ## toBase64
154
+ <a id="tobase64"></a>
155
155
 
156
- [toBase64](https://gemarcade.com/developer/api/server#tobase64)
156
+ ## toBase64
157
157
 
158
158
  ```ts
159
159
  function toBase64(value: string | Uint8Array<ArrayBufferLike>): Base64String
@@ -161,7 +161,7 @@ function toBase64(value: string | Uint8Array<ArrayBufferLike>): Base64String
161
161
 
162
162
  Encode text or bytes for a field that wants base64.
163
163
 
164
- The only way to produce a [Base64String](https://gemarcade.com/developer/api/server#base64string), and the reason not to reach
164
+ The only way to produce a [Base64String](#base64string), and the reason not to reach
165
165
  for `btoa` yourself: `btoa` takes a *binary* string and throws on any
166
166
  character above U+00FF, so `btoa(name)` works all through your testing and
167
167
  then fails on the first player whose display name is not Latin-1. This
@@ -179,9 +179,9 @@ await gem.api.putEntityGameSetting('loadout', {
179
179
  });
180
180
  ```
181
181
 
182
- ## GemErrorReason
182
+ <a id="gemerrorreason"></a>
183
183
 
184
- [GemErrorReason](https://gemarcade.com/developer/api/server#gemerrorreason)
184
+ ## GemErrorReason
185
185
 
186
186
  ```ts
187
187
  type GemErrorReason = "invalid" | "forbidden" | "not_found" | "conflict" | "precondition_failed" | "not_modified" | "insufficient_funds" | "too_large" | "rate_limited" | "unavailable"
@@ -191,11 +191,11 @@ What went wrong, in terms you can act on.
191
191
 
192
192
  BRANCH ON THIS, not on the HTTP status — one status covers several distinct
193
193
  refusals, and the reason is what tells them apart. It is carried by
194
- [GemApiError.reason](https://gemarcade.com/developer/api/server#gemapierror-reason).
194
+ [GemApiError.reason](#gemapierror-reason).
195
195
 
196
- ## GemApiError
196
+ <a id="gemapierror"></a>
197
197
 
198
- [GemApiError](https://gemarcade.com/developer/api/server#gemapierror)
198
+ ## GemApiError
199
199
 
200
200
  ```ts
201
201
  class GemApiError
@@ -204,9 +204,9 @@ class GemApiError
204
204
  A refusal from the Gem Arcade backend. Any call that reaches the platform can
205
205
  throw one.
206
206
 
207
- You catch this; you never construct it. Branch on [GemApiError.reason](https://gemarcade.com/developer/api/server#gemapierror-reason)
208
- for what went wrong and [GemApiError.retryable](https://gemarcade.com/developer/api/server#gemapierror-retryable) for whether another
209
- attempt could help — not on [GemApiError.status](https://gemarcade.com/developer/api/server#gemapierror-status), because one status
207
+ You catch this; you never construct it. Branch on [GemApiError.reason](#gemapierror-reason)
208
+ for what went wrong and [GemApiError.retryable](#gemapierror-retryable) for whether another
209
+ attempt could help — not on [GemApiError.status](#gemapierror-status), because one status
210
210
  covers several distinct refusals.
211
211
 
212
212
  ```js
@@ -222,9 +222,9 @@ try {
222
222
  }
223
223
  ```
224
224
 
225
- ## GemApiError.reason
225
+ <a id="gemapierror-reason"></a>
226
226
 
227
- [GemApiError.reason](https://gemarcade.com/developer/api/server#gemapierror-reason)
227
+ ## GemApiError.reason
228
228
 
229
229
  ```ts
230
230
  readonly reason: GemErrorReason
@@ -237,9 +237,9 @@ What went wrong, in terms you can act on — `invalid`, `forbidden`,
237
237
  This is the field to branch on. Two refusals can share a status and mean
238
238
  different things; they do not share a reason.
239
239
 
240
- ## GemApiError.status
240
+ <a id="gemapierror-status"></a>
241
241
 
242
- [GemApiError.status](https://gemarcade.com/developer/api/server#gemapierror-status)
242
+ ## GemApiError.status
243
243
 
244
244
  ```ts
245
245
  readonly status: number
@@ -247,13 +247,13 @@ readonly status: number
247
247
 
248
248
  The HTTP status the server answered with.
249
249
 
250
- Carried for logs and bug reports. Prefer [GemApiError.reason](https://gemarcade.com/developer/api/server#gemapierror-reason) for
250
+ Carried for logs and bug reports. Prefer [GemApiError.reason](#gemapierror-reason) for
251
251
  decisions: several distinct refusals map onto one status, so branching
252
252
  here treats different problems as the same one.
253
253
 
254
- ## GemApiError.detail
254
+ <a id="gemapierror-detail"></a>
255
255
 
256
- [GemApiError.detail](https://gemarcade.com/developer/api/server#gemapierror-detail)
256
+ ## GemApiError.detail
257
257
 
258
258
  ```ts
259
259
  readonly detail: string
@@ -265,9 +265,9 @@ The server's own sentence, without the `reason (status):` prefix that
265
265
  Use this when you want to quote the platform — in a log line, or a bug
266
266
  report. It is not written for players, so do not put it on screen.
267
267
 
268
- ## GemApiError.retryAfterSeconds
268
+ <a id="gemapierror-retryafterseconds"></a>
269
269
 
270
- [GemApiError.retryAfterSeconds](https://gemarcade.com/developer/api/server#gemapierror-retryafterseconds)
270
+ ## GemApiError.retryAfterSeconds
271
271
 
272
272
  ```ts
273
273
  readonly retryAfterSeconds?: number
@@ -276,15 +276,15 @@ readonly retryAfterSeconds?: number
276
276
  How long the server asked you to wait, in whole seconds.
277
277
 
278
278
  ABSENT MEANS "NO ESTIMATE GIVEN", NEVER "DO NOT RETRY" —
279
- [GemApiError.retryable](https://gemarcade.com/developer/api/server#gemapierror-retryable) is what answers that. A refusal can be
279
+ [GemApiError.retryable](#gemapierror-retryable) is what answers that. A refusal can be
280
280
  worth repeating and still name no wait, so treat absence as "pick your
281
281
  own backoff" rather than as a stop signal. The platform estimates a wait
282
282
  for capacity and for rate limits, and sends none for the refusals it
283
283
  cannot put a number on.
284
284
 
285
- ## GemApiError.code
285
+ <a id="gemapierror-code"></a>
286
286
 
287
- [GemApiError.code](https://gemarcade.com/developer/api/server#gemapierror-code)
287
+ ## GemApiError.code
288
288
 
289
289
  ```ts
290
290
  readonly code?: string
@@ -294,7 +294,7 @@ The platform's own error code, when it sent one —
294
294
  `too_many_concurrent_matches`, `cluster_target_required`,
295
295
  `no_compatible_server`.
296
296
 
297
- Finer-grained than [GemApiError.reason](https://gemarcade.com/developer/api/server#gemapierror-reason), and the only way to tell
297
+ Finer-grained than [GemApiError.reason](#gemapierror-reason), and the only way to tell
298
298
  apart two refusals that share both a reason and a status. Absent on most
299
299
  refusals, so treat it as extra information rather than something to
300
300
  depend on.
@@ -305,9 +305,9 @@ refusals carry the platform's purchase codes — `insufficient_funds`,
305
305
  strings `gem.purchases.request` answers as its `reason`, so one `switch`
306
306
  on `err.code` covers both purchase paths.
307
307
 
308
- ## GemApiError.retryable
308
+ <a id="gemapierror-retryable"></a>
309
309
 
310
- [GemApiError.retryable](https://gemarcade.com/developer/api/server#gemapierror-retryable)
310
+ ## GemApiError.retryable
311
311
 
312
312
  ```ts
313
313
  get retryable(): boolean
@@ -318,45 +318,45 @@ Whether another attempt could plausibly succeed.
318
318
  True for `unavailable` and `rate_limited` and false for everything else —
319
319
  a `forbidden` or `not_found` will refuse identically however many times you
320
320
  send it. Check this before retrying, and wait
321
- [GemApiError.retryAfterSeconds](https://gemarcade.com/developer/api/server#gemapierror-retryafterseconds) if the server named one.
321
+ [GemApiError.retryAfterSeconds](#gemapierror-retryafterseconds) if the server named one.
322
322
 
323
- ## ClientOptions
323
+ <a id="clientoptions"></a>
324
324
 
325
- [ClientOptions](https://gemarcade.com/developer/api/server#clientoptions)
325
+ ## ClientOptions
326
326
 
327
327
  ```ts
328
328
  interface ClientOptions
329
329
  ```
330
330
 
331
- How a [GemClient](https://gemarcade.com/developer/api/server#gemclient) is built.
331
+ How a [GemClient](#gemclient) is built.
332
332
 
333
- You rarely construct one yourself — [createGemServer](https://gemarcade.com/developer/api/server#creategemserver) does it — but the
333
+ You rarely construct one yourself — [createGemServer](#creategemserver) does it — but the
334
334
  type has to be nameable to write a helper that takes one.
335
335
 
336
- ## ClientOptions.transport
336
+ <a id="clientoptions-transport"></a>
337
337
 
338
- [ClientOptions.transport](https://gemarcade.com/developer/api/server#clientoptions-transport)
338
+ ## ClientOptions.transport
339
339
 
340
340
  ```ts
341
341
  readonly transport: Transport
342
342
  ```
343
343
 
344
344
  How requests actually leave. Substitute one to test without a network — see
345
- [Transport](https://gemarcade.com/developer/api/server#transport).
345
+ [Transport](#transport).
346
346
 
347
- ## ClientOptions.clock
347
+ <a id="clientoptions-clock"></a>
348
348
 
349
- [ClientOptions.clock](https://gemarcade.com/developer/api/server#clientoptions-clock)
349
+ ## ClientOptions.clock
350
350
 
351
351
  ```ts
352
352
  readonly clock: Clock
353
353
  ```
354
354
 
355
- Where time comes from, for retry backoff. See [Clock](https://gemarcade.com/developer/api/server#clock).
355
+ Where time comes from, for retry backoff. See [Clock](#clock).
356
356
 
357
- ## ClientOptions.maxRetries
357
+ <a id="clientoptions-maxretries"></a>
358
358
 
359
- [ClientOptions.maxRetries](https://gemarcade.com/developer/api/server#clientoptions-maxretries)
359
+ ## ClientOptions.maxRetries
360
360
 
361
361
  ```ts
362
362
  readonly maxRetries?: number
@@ -366,15 +366,15 @@ How many times to retry a read that failed. Defaults to 2 — three attempts
366
366
  in all.
367
367
 
368
368
  READS SPEND THIS BUDGET, and so does a write the caller has declared
369
- replayable with [RequestOptions.replaySafe](https://gemarcade.com/developer/api/server#requestoptions-replaysafe). Every other write gets
369
+ replayable with [RequestOptions.replaySafe](#requestoptions-replaysafe). Every other write gets
370
370
  one attempt whatever you set here: a write that timed out may already have
371
371
  been applied, and repeating it is how a client charges someone twice.
372
372
  Whether a given write is safe to repeat is something only its caller knows,
373
373
  so the SDK does not guess.
374
374
 
375
- ## RequestOptions
375
+ <a id="requestoptions"></a>
376
376
 
377
- [RequestOptions](https://gemarcade.com/developer/api/server#requestoptions)
377
+ ## RequestOptions
378
378
 
379
379
  ```ts
380
380
  interface RequestOptions
@@ -382,9 +382,9 @@ interface RequestOptions
382
382
 
383
383
  Per-call options, for the occasional call that needs one.
384
384
 
385
- ## RequestOptions.keepalive
385
+ <a id="requestoptions-keepalive"></a>
386
386
 
387
- [RequestOptions.keepalive](https://gemarcade.com/developer/api/server#requestoptions-keepalive)
387
+ ## RequestOptions.keepalive
388
388
 
389
389
  ```ts
390
390
  readonly keepalive?: boolean
@@ -396,9 +396,9 @@ For the last write before a player closes the tab. Browsers cap how much
396
396
  can be in flight this way, so use it for the write that matters, not for
397
397
  every write.
398
398
 
399
- ## RequestOptions.ifMatch
399
+ <a id="requestoptions-ifmatch"></a>
400
400
 
401
- [RequestOptions.ifMatch](https://gemarcade.com/developer/api/server#requestoptions-ifmatch)
401
+ ## RequestOptions.ifMatch
402
402
 
403
403
  ```ts
404
404
  readonly ifMatch?: string
@@ -411,9 +411,9 @@ someone else wrote in between, the call fails with `precondition_failed`
411
411
  instead of overwriting them — re-read, decide again against the new state,
412
412
  and write again.
413
413
 
414
- ## RequestOptions.replaySafe
414
+ <a id="requestoptions-replaysafe"></a>
415
415
 
416
- [RequestOptions.replaySafe](https://gemarcade.com/developer/api/server#requestoptions-replaysafe)
416
+ ## RequestOptions.replaySafe
417
417
 
418
418
  ```ts
419
419
  readonly replaySafe?: boolean
@@ -435,14 +435,14 @@ opposite: a retry the caller cannot see is how one purchase becomes two.
435
435
  If you are unsure, leave it alone and decide for yourself whether to
436
436
  repeat the call.
437
437
 
438
- It buys the same budget a read gets — see [ClientOptions.maxRetries](https://gemarcade.com/developer/api/server#clientoptions-maxretries)
438
+ It buys the same budget a read gets — see [ClientOptions.maxRetries](#clientoptions-maxretries)
439
439
  — and nothing else. A refusal the server made deliberately is still
440
- final: only the failures [GemApiError.retryable](https://gemarcade.com/developer/api/server#gemapierror-retryable) describes, and a
440
+ final: only the failures [GemApiError.retryable](#gemapierror-retryable) describes, and a
441
441
  connection that died with no answer, are attempted again.
442
442
 
443
- ## VersionedGetOptions
443
+ <a id="versionedgetoptions"></a>
444
444
 
445
- [VersionedGetOptions](https://gemarcade.com/developer/api/server#versionedgetoptions)
445
+ ## VersionedGetOptions
446
446
 
447
447
  ```ts
448
448
  interface VersionedGetOptions
@@ -450,9 +450,9 @@ interface VersionedGetOptions
450
450
 
451
451
  Options for a read that also wants the value's version back.
452
452
 
453
- ## VersionedGetOptions.signal
453
+ <a id="versionedgetoptions-signal"></a>
454
454
 
455
- [VersionedGetOptions.signal](https://gemarcade.com/developer/api/server#versionedgetoptions-signal)
455
+ ## VersionedGetOptions.signal
456
456
 
457
457
  ```ts
458
458
  readonly signal?: AbortSignal
@@ -460,9 +460,9 @@ readonly signal?: AbortSignal
460
460
 
461
461
  Cancel the read. An aborted call rejects rather than resolving.
462
462
 
463
- ## VersionedGetOptions.ifNoneMatch
463
+ <a id="versionedgetoptions-ifnonematch"></a>
464
464
 
465
- [VersionedGetOptions.ifNoneMatch](https://gemarcade.com/developer/api/server#versionedgetoptions-ifnonematch)
465
+ ## VersionedGetOptions.ifNoneMatch
466
466
 
467
467
  ```ts
468
468
  readonly ifNoneMatch?: string
@@ -473,9 +473,9 @@ Ask "has this changed?" instead of "give me this".
473
473
  Pass the version from your last read. If nothing has changed you get back
474
474
  `{ modified: false }` and no body — cheaper for you and for the platform.
475
475
 
476
- ## VersionedResponse
476
+ <a id="versionedresponse"></a>
477
477
 
478
- [VersionedResponse](https://gemarcade.com/developer/api/server#versionedresponse)
478
+ ## VersionedResponse
479
479
 
480
480
  ```ts
481
481
  type VersionedResponse<T> = { modified: true; body: T; etag?: string } | { modified: false; etag?: string }
@@ -497,20 +497,20 @@ if (result.modified) {
497
497
  }
498
498
  ```
499
499
 
500
- ## BytesResponse
500
+ <a id="bytesresponse"></a>
501
501
 
502
- [BytesResponse](https://gemarcade.com/developer/api/server#bytesresponse)
502
+ ## BytesResponse
503
503
 
504
504
  ```ts
505
505
  interface BytesResponse
506
506
  ```
507
507
 
508
- What [GemClient.postBytes](https://gemarcade.com/developer/api/server#gemclient-postbytes) resolves: the answer's raw bytes, and the id
508
+ What [GemClient.postBytes](#gemclient-postbytes) resolves: the answer's raw bytes, and the id
509
509
  the platform gave the invocation when there was one.
510
510
 
511
- ## BytesResponse.bytes
511
+ <a id="bytesresponse-bytes"></a>
512
512
 
513
- [BytesResponse.bytes](https://gemarcade.com/developer/api/server#bytesresponse-bytes)
513
+ ## BytesResponse.bytes
514
514
 
515
515
  ```ts
516
516
  readonly bytes: Uint8Array
@@ -518,20 +518,20 @@ readonly bytes: Uint8Array
518
518
 
519
519
  Exactly the bytes that arrived — empty when the answer had no body.
520
520
 
521
- ## BytesResponse.invocationId
521
+ <a id="bytesresponse-invocationid"></a>
522
522
 
523
- [BytesResponse.invocationId](https://gemarcade.com/developer/api/server#bytesresponse-invocationid)
523
+ ## BytesResponse.invocationId
524
524
 
525
525
  ```ts
526
526
  readonly invocationId?: string
527
527
  ```
528
528
 
529
529
  The platform's id for this invocation, when the answer named one and the
530
- runtime could read it. See [TransportResponse.invocationId](https://gemarcade.com/developer/api/server#transportresponse-invocationid).
530
+ runtime could read it. See [TransportResponse.invocationId](#transportresponse-invocationid).
531
531
 
532
- ## GemClient
532
+ <a id="gemclient"></a>
533
533
 
534
- [GemClient](https://gemarcade.com/developer/api/server#gemclient)
534
+ ## GemClient
535
535
 
536
536
  ```ts
537
537
  class GemClient
@@ -540,25 +540,25 @@ class GemClient
540
540
  The HTTP client underneath the generated operations — for calls those do not
541
541
  cover.
542
542
 
543
- PREFER [ServerApi](https://gemarcade.com/developer/api/server#serverapi). Its methods are typed against the platform's own
543
+ PREFER [ServerApi](#serverapi). Its methods are typed against the platform's own
544
544
  description, so a shape that changes becomes a build error in your bundle.
545
545
  This one takes a path and checks nothing you give it.
546
546
 
547
547
  What it does give you is everything around the call: the credential, retries
548
548
  on the requests that are safe to retry, and failures already classified into
549
- [GemApiError](https://gemarcade.com/developer/api/server#gemapierror) so you can branch on a reason instead of a status.
549
+ [GemApiError](#gemapierror) so you can branch on a reason instead of a status.
550
550
 
551
551
  READS ARE RETRIED; A WRITE IS NOT, unless its caller declares it replayable
552
- with [RequestOptions.replaySafe](https://gemarcade.com/developer/api/server#requestoptions-replaysafe). A write that timed out may well have
552
+ with [RequestOptions.replaySafe](#requestoptions-replaysafe). A write that timed out may well have
553
553
  been applied, and retrying it silently is how a client duplicates a purchase
554
554
  it cannot see — so the default is one attempt, and widening it is a decision
555
555
  made one call at a time by whoever knows what the call does.
556
556
 
557
557
  You reach one as `gem.client`; there is normally no reason to construct it.
558
558
 
559
- ## GemClient.constructor
559
+ <a id="gemclient-constructor"></a>
560
560
 
561
- [GemClient.constructor](https://gemarcade.com/developer/api/server#gemclient-constructor)
561
+ ## GemClient.constructor
562
562
 
563
563
  ```ts
564
564
  constructor(options: ClientOptions): GemClient
@@ -569,11 +569,11 @@ it to you as `gem.client`.
569
569
 
570
570
  | Parameter | Type | Description |
571
571
  | --- | --- | --- |
572
- | `options` | `ClientOptions` | See [ClientOptions](https://gemarcade.com/developer/api/server#clientoptions). |
572
+ | `options` | `ClientOptions` | See [ClientOptions](#clientoptions). |
573
573
 
574
- ## GemClient.get
574
+ <a id="gemclient-get"></a>
575
575
 
576
- [GemClient.get](https://gemarcade.com/developer/api/server#gemclient-get)
576
+ ## GemClient.get
577
577
 
578
578
  ```ts
579
579
  get<T>(path: string, signal?: AbortSignal): Promise<T>
@@ -586,11 +586,11 @@ Read something.
586
586
  | `path` | `string` | From the API root, e.g. `/v1/players/me/settings`. |
587
587
  | `signal` | `AbortSignal` | Cancels the read. |
588
588
 
589
- **Throws.** [GemApiError](https://gemarcade.com/developer/api/server#gemapierror) on a refusal — branch on `reason`.
589
+ **Throws.** [GemApiError](#gemapierror) on a refusal — branch on `reason`.
590
590
 
591
- ## GemClient.getWithEtag
591
+ <a id="gemclient-getwithetag"></a>
592
592
 
593
- [GemClient.getWithEtag](https://gemarcade.com/developer/api/server#gemclient-getwithetag)
593
+ ## GemClient.getWithEtag
594
594
 
595
595
  ```ts
596
596
  getWithEtag<T>(path: string, options?: VersionedGetOptions): Promise<VersionedResponse<T>>
@@ -603,21 +603,21 @@ it as `ifMatch` on your write, and the write is refused rather than
603
603
  overwriting somebody. Pass it as `ifNoneMatch` here and an unchanged
604
604
  resource comes back with no body at all.
605
605
 
606
- SEPARATE FROM [GemClient.get](https://gemarcade.com/developer/api/server#gemclient-get) on purpose — `get` answers with the value
606
+ SEPARATE FROM [GemClient.get](#gemclient-get) on purpose — `get` answers with the value
607
607
  and nothing else, which is what most calls want.
608
608
 
609
609
  | Parameter | Type | Description |
610
610
  | --- | --- | --- |
611
611
  | `path` | `string` | From the API root. |
612
- | `options` | `VersionedGetOptions` | See [VersionedGetOptions](https://gemarcade.com/developer/api/server#versionedgetoptions). |
612
+ | `options` | `VersionedGetOptions` | See [VersionedGetOptions](#versionedgetoptions). |
613
613
 
614
- **Returns.** See [VersionedResponse](https://gemarcade.com/developer/api/server#versionedresponse) — check `modified` before reading a body.
614
+ **Returns.** See [VersionedResponse](#versionedresponse) — check `modified` before reading a body.
615
615
 
616
- **Throws.** [GemApiError](https://gemarcade.com/developer/api/server#gemapierror) on a refusal.
616
+ **Throws.** [GemApiError](#gemapierror) on a refusal.
617
617
 
618
- ## GemClient.put
618
+ <a id="gemclient-put"></a>
619
619
 
620
- [GemClient.put](https://gemarcade.com/developer/api/server#gemclient-put)
620
+ ## GemClient.put
621
621
 
622
622
  ```ts
623
623
  put<T>(path: string, body: unknown, signal?: AbortSignal, options?: RequestOptions): Promise<T>
@@ -626,7 +626,7 @@ put<T>(path: string, body: unknown, signal?: AbortSignal, options?: RequestOptio
626
626
  Replace something.
627
627
 
628
628
  NOT RETRIED. A write that timed out may already have been applied — set
629
- [RequestOptions.replaySafe](https://gemarcade.com/developer/api/server#requestoptions-replaysafe) if repeating this one cannot change the
629
+ [RequestOptions.replaySafe](#requestoptions-replaysafe) if repeating this one cannot change the
630
630
  outcome.
631
631
 
632
632
  | Parameter | Type | Description |
@@ -634,14 +634,14 @@ outcome.
634
634
  | `path` | `string` | From the API root. |
635
635
  | `body` | `unknown` | What to send. Encoded as JSON. |
636
636
  | `signal` | `AbortSignal` | |
637
- | `options` | `RequestOptions` | See [RequestOptions](https://gemarcade.com/developer/api/server#requestoptions) — `ifMatch` is how you avoid
637
+ | `options` | `RequestOptions` | See [RequestOptions](#requestoptions) — `ifMatch` is how you avoid
638
638
  overwriting someone else's write. |
639
639
 
640
- **Throws.** [GemApiError](https://gemarcade.com/developer/api/server#gemapierror) on a refusal.
640
+ **Throws.** [GemApiError](#gemapierror) on a refusal.
641
641
 
642
- ## GemClient.post
642
+ <a id="gemclient-post"></a>
643
643
 
644
- [GemClient.post](https://gemarcade.com/developer/api/server#gemclient-post)
644
+ ## GemClient.post
645
645
 
646
646
  ```ts
647
647
  post<T>(path: string, body: unknown, signal?: AbortSignal, options?: RequestOptions): Promise<T>
@@ -649,7 +649,7 @@ post<T>(path: string, body: unknown, signal?: AbortSignal, options?: RequestOpti
649
649
 
650
650
  Create something, or perform an action.
651
651
 
652
- NOT RETRIED, for the same reason as [GemClient.put](https://gemarcade.com/developer/api/server#gemclient-put), and widened the
652
+ NOT RETRIED, for the same reason as [GemClient.put](#gemclient-put), and widened the
653
653
  same way.
654
654
 
655
655
  | Parameter | Type | Description |
@@ -657,13 +657,13 @@ same way.
657
657
  | `path` | `string` | From the API root. |
658
658
  | `body` | `unknown` | What to send. Encoded as JSON. |
659
659
  | `signal` | `AbortSignal` | |
660
- | `options` | `RequestOptions` | See [RequestOptions](https://gemarcade.com/developer/api/server#requestoptions). |
660
+ | `options` | `RequestOptions` | See [RequestOptions](#requestoptions). |
661
661
 
662
- **Throws.** [GemApiError](https://gemarcade.com/developer/api/server#gemapierror) on a refusal.
662
+ **Throws.** [GemApiError](#gemapierror) on a refusal.
663
663
 
664
- ## GemClient.postBytes
664
+ <a id="gemclient-postbytes"></a>
665
665
 
666
- [GemClient.postBytes](https://gemarcade.com/developer/api/server#gemclient-postbytes)
666
+ ## GemClient.postBytes
667
667
 
668
668
  ```ts
669
669
  postBytes(path: string, bytes: Uint8Array, signal?: AbortSignal, options?: RequestOptions): Promise<BytesResponse>
@@ -673,9 +673,9 @@ Send raw bytes and get raw bytes back.
673
673
 
674
674
  For a route whose input and output are your own bytes rather than JSON.
675
675
  The body goes out as it is, labelled `application/octet-stream`, and a
676
- success comes back undecoded. A refusal is still a [GemApiError](https://gemarcade.com/developer/api/server#gemapierror).
676
+ success comes back undecoded. A refusal is still a [GemApiError](#gemapierror).
677
677
 
678
- NOT RETRIED, for the same reason as [GemClient.put](https://gemarcade.com/developer/api/server#gemclient-put), and widened the
678
+ NOT RETRIED, for the same reason as [GemClient.put](#gemclient-put), and widened the
679
679
  same way.
680
680
 
681
681
  | Parameter | Type | Description |
@@ -683,13 +683,13 @@ same way.
683
683
  | `path` | `string` | From the API root. |
684
684
  | `bytes` | `Uint8Array` | What to send, byte for byte. |
685
685
  | `signal` | `AbortSignal` | |
686
- | `options` | `RequestOptions` | See [RequestOptions](https://gemarcade.com/developer/api/server#requestoptions). |
686
+ | `options` | `RequestOptions` | See [RequestOptions](#requestoptions). |
687
687
 
688
- **Throws.** [GemApiError](https://gemarcade.com/developer/api/server#gemapierror) on a refusal.
688
+ **Throws.** [GemApiError](#gemapierror) on a refusal.
689
689
 
690
- ## GemClient.patch
690
+ <a id="gemclient-patch"></a>
691
691
 
692
- [GemClient.patch](https://gemarcade.com/developer/api/server#gemclient-patch)
692
+ ## GemClient.patch
693
693
 
694
694
  ```ts
695
695
  patch<T>(path: string, body: unknown, signal?: AbortSignal, options?: RequestOptions): Promise<T>
@@ -697,7 +697,7 @@ patch<T>(path: string, body: unknown, signal?: AbortSignal, options?: RequestOpt
697
697
 
698
698
  Change part of something.
699
699
 
700
- NOT RETRIED, for the same reason as [GemClient.put](https://gemarcade.com/developer/api/server#gemclient-put), and widened the
700
+ NOT RETRIED, for the same reason as [GemClient.put](#gemclient-put), and widened the
701
701
  same way.
702
702
 
703
703
  | Parameter | Type | Description |
@@ -705,13 +705,13 @@ same way.
705
705
  | `path` | `string` | From the API root. |
706
706
  | `body` | `unknown` | The change. Encoded as JSON. |
707
707
  | `signal` | `AbortSignal` | |
708
- | `options` | `RequestOptions` | See [RequestOptions](https://gemarcade.com/developer/api/server#requestoptions). |
708
+ | `options` | `RequestOptions` | See [RequestOptions](#requestoptions). |
709
709
 
710
- **Throws.** [GemApiError](https://gemarcade.com/developer/api/server#gemapierror) on a refusal.
710
+ **Throws.** [GemApiError](#gemapierror) on a refusal.
711
711
 
712
- ## GemClient.delete
712
+ <a id="gemclient-delete"></a>
713
713
 
714
- [GemClient.delete](https://gemarcade.com/developer/api/server#gemclient-delete)
714
+ ## GemClient.delete
715
715
 
716
716
  ```ts
717
717
  delete<T>(path: string, signal?: AbortSignal, options?: RequestOptions): Promise<T>
@@ -719,20 +719,20 @@ delete<T>(path: string, signal?: AbortSignal, options?: RequestOptions): Promise
719
719
 
720
720
  Remove something.
721
721
 
722
- NOT RETRIED, for the same reason as [GemClient.put](https://gemarcade.com/developer/api/server#gemclient-put), and widened the
722
+ NOT RETRIED, for the same reason as [GemClient.put](#gemclient-put), and widened the
723
723
  same way.
724
724
 
725
725
  | Parameter | Type | Description |
726
726
  | --- | --- | --- |
727
727
  | `path` | `string` | From the API root. |
728
728
  | `signal` | `AbortSignal` | Cancels the call. |
729
- | `options` | `RequestOptions` | See [RequestOptions](https://gemarcade.com/developer/api/server#requestoptions). |
729
+ | `options` | `RequestOptions` | See [RequestOptions](#requestoptions). |
730
730
 
731
- **Throws.** [GemApiError](https://gemarcade.com/developer/api/server#gemapierror) on a refusal.
731
+ **Throws.** [GemApiError](#gemapierror) on a refusal.
732
732
 
733
- ## AuthFailure
733
+ <a id="authfailure"></a>
734
734
 
735
- [AuthFailure](https://gemarcade.com/developer/api/server#authfailure)
735
+ ## AuthFailure
736
736
 
737
737
  ```ts
738
738
  type AuthFailure = "chain_exhausted" | "unavailable" | "timeout" | "internal"
@@ -742,7 +742,7 @@ Why the session could not produce a usable token.
742
742
 
743
743
  THIS IS THE CAUSE, NOT A VERDICT. It deliberately never distinguishes a
744
744
  sign-out from a ban from a revocation, and it does not tell you whether to
745
- try again — [AuthState](https://gemarcade.com/developer/api/server#authstate) does that. A game that branches on the reason
745
+ try again — [AuthState](#authstate) does that. A game that branches on the reason
746
746
  is guessing.
747
747
 
748
748
  - `chain_exhausted` — the token chain cannot be renewed any further.
@@ -750,9 +750,9 @@ is guessing.
750
750
  - `timeout` — the arcade did not answer in time.
751
751
  - `internal` — something went wrong on this side.
752
752
 
753
- ## AuthState
753
+ <a id="authstate"></a>
754
754
 
755
- [AuthState](https://gemarcade.com/developer/api/server#authstate)
755
+ ## AuthState
756
756
 
757
757
  ```ts
758
758
  type AuthState = "ready" | "pending" | "degraded" | "lost"
@@ -771,9 +771,9 @@ the callback instead.
771
771
  - `lost` — terminal for this session. Signing back in is the only way
772
772
  forward, and retrying will not produce one.
773
773
 
774
- ## TokenGrant
774
+ <a id="tokengrant"></a>
775
775
 
776
- [TokenGrant](https://gemarcade.com/developer/api/server#tokengrant)
776
+ ## TokenGrant
777
777
 
778
778
  ```ts
779
779
  interface TokenGrant
@@ -791,9 +791,9 @@ credential stops working and a fresh one is fetched — routine, invisible.
791
791
  `chainEndsAt` is when fetching a fresh one stops being possible at all: past
792
792
  it the session is over and only signing in again helps.
793
793
 
794
- ## TokenGrant.token
794
+ <a id="tokengrant-token"></a>
795
795
 
796
- [TokenGrant.token](https://gemarcade.com/developer/api/server#tokengrant-token)
796
+ ## TokenGrant.token
797
797
 
798
798
  ```ts
799
799
  readonly token: string
@@ -804,9 +804,9 @@ The credential itself.
804
804
  NEVER LOG IT AND NEVER FORWARD IT. Anything holding it can act as this
805
805
  session until it expires.
806
806
 
807
- ## TokenGrant.expiresAt
807
+ <a id="tokengrant-expiresat"></a>
808
808
 
809
- [TokenGrant.expiresAt](https://gemarcade.com/developer/api/server#tokengrant-expiresat)
809
+ ## TokenGrant.expiresAt
810
810
 
811
811
  ```ts
812
812
  readonly expiresAt: number
@@ -816,9 +816,9 @@ When this credential stops working, in seconds since the Unix epoch.
816
816
 
817
817
  Routine — the SDK renews before this, and you do not see it happen.
818
818
 
819
- ## TokenGrant.chainEndsAt
819
+ <a id="tokengrant-chainendsat"></a>
820
820
 
821
- [TokenGrant.chainEndsAt](https://gemarcade.com/developer/api/server#tokengrant-chainendsat)
821
+ ## TokenGrant.chainEndsAt
822
822
 
823
823
  ```ts
824
824
  readonly chainEndsAt: number
@@ -827,11 +827,11 @@ readonly chainEndsAt: number
827
827
  When renewal stops being possible, in seconds since the Unix epoch.
828
828
 
829
829
  Past this the session cannot be extended by anything. Watch
830
- [AuthState](https://gemarcade.com/developer/api/server#authstate) rather than this number.
830
+ [AuthState](#authstate) rather than this number.
831
831
 
832
- ## AuthError
832
+ <a id="autherror"></a>
833
833
 
834
- [AuthError](https://gemarcade.com/developer/api/server#autherror)
834
+ ## AuthError
835
835
 
836
836
  ```ts
837
837
  class AuthError
@@ -843,25 +843,25 @@ Thrown by any call that needs the player's session. You catch this; you never
843
843
  construct it.
844
844
 
845
845
  IT CARRIES NO `retryable` FIELD, and that is deliberate rather than an
846
- oversight — [AuthError.reason](https://gemarcade.com/developer/api/server#autherror-reason) is the cause, and the retry verdict
846
+ oversight — [AuthError.reason](#autherror-reason) is the cause, and the retry verdict
847
847
  lives on the session's state. Watch `GemOptions.onAuthStateChange` and treat
848
- [AuthState](https://gemarcade.com/developer/api/server#authstate) `degraded` as worth retrying and `lost` as terminal. A game
848
+ [AuthState](#authstate) `degraded` as worth retrying and `lost` as terminal. A game
849
849
  that decides from the reason will retry a session that can never come back.
850
850
 
851
- ## AuthError.reason
851
+ <a id="autherror-reason"></a>
852
852
 
853
- [AuthError.reason](https://gemarcade.com/developer/api/server#autherror-reason)
853
+ ## AuthError.reason
854
854
 
855
855
  ```ts
856
856
  readonly reason: AuthFailure
857
857
  ```
858
858
 
859
- Why the token could not be produced. See [AuthFailure](https://gemarcade.com/developer/api/server#authfailure) — it is the
859
+ Why the token could not be produced. See [AuthFailure](#authfailure) — it is the
860
860
  cause, and not a signal about whether to try again.
861
861
 
862
- ## AuthError.isAuthError
862
+ <a id="autherror-isautherror"></a>
863
863
 
864
- [AuthError.isAuthError](https://gemarcade.com/developer/api/server#autherror-isautherror)
864
+ ## AuthError.isAuthError
865
865
 
866
866
  ```ts
867
867
  isAuthError(error: unknown): error is AuthError
@@ -891,9 +891,9 @@ bundle alike, and the two have no call in common.
891
891
  | --- | --- |
892
892
  | `error` | `unknown` |
893
893
 
894
- ## TransportRefusal
894
+ <a id="transportrefusal"></a>
895
895
 
896
- [TransportRefusal](https://gemarcade.com/developer/api/server#transportrefusal)
896
+ ## TransportRefusal
897
897
 
898
898
  ```ts
899
899
  class TransportRefusal
@@ -912,9 +912,9 @@ conditional-header value the wire cannot carry, and a retry count outside
912
912
  Nothing reached the platform, so there is no status and no response to read.
913
913
  Fix the call.
914
914
 
915
- ## TransportRefusal.retryable
915
+ <a id="transportrefusal-retryable"></a>
916
916
 
917
- [TransportRefusal.retryable](https://gemarcade.com/developer/api/server#transportrefusal-retryable)
917
+ ## TransportRefusal.retryable
918
918
 
919
919
  ```ts
920
920
  readonly retryable: false
@@ -926,9 +926,9 @@ Present so that code branching on `retryable` across the errors this SDK
926
926
  throws reads a real `false` here rather than `undefined`. A refusal is
927
927
  deterministic; retrying cannot change it.
928
928
 
929
- ## TransportRefusal.isRefusal
929
+ <a id="transportrefusal-isrefusal"></a>
930
930
 
931
- [TransportRefusal.isRefusal](https://gemarcade.com/developer/api/server#transportrefusal-isrefusal)
931
+ ## TransportRefusal.isRefusal
932
932
 
933
933
  ```ts
934
934
  isRefusal(error: unknown): error is TransportRefusal
@@ -944,9 +944,9 @@ one copy fails `instanceof` in the other.
944
944
  | --- | --- |
945
945
  | `error` | `unknown` |
946
946
 
947
- ## CONDITIONAL_HEADERS
947
+ <a id="conditional_headers"></a>
948
948
 
949
- [CONDITIONAL_HEADERS](https://gemarcade.com/developer/api/server#conditional_headers)
949
+ ## CONDITIONAL_HEADERS
950
950
 
951
951
  ```ts
952
952
  CONDITIONAL_HEADERS: readonly ["if-match", "if-none-match"]
@@ -958,33 +958,33 @@ A CLOSED SET, ON PURPOSE. Everything else — the credential, the content type,
958
958
  the client's identity — is the SDK's to send, and a call that could add its
959
959
  own headers could send headers the platform trusts.
960
960
 
961
- ## ConditionalRequestHeaders
961
+ <a id="conditionalrequestheaders"></a>
962
962
 
963
- [ConditionalRequestHeaders](https://gemarcade.com/developer/api/server#conditionalrequestheaders)
963
+ ## ConditionalRequestHeaders
964
964
 
965
965
  ```ts
966
966
  type ConditionalRequestHeaders = Partial<Record<unknown[number], string>>
967
967
  ```
968
968
 
969
- The type of [TransportRequest.headers](https://gemarcade.com/developer/api/server#transportrequest-headers) — derived from
970
- [CONDITIONAL_HEADERS](https://gemarcade.com/developer/api/server#conditional_headers), so the two cannot disagree.
969
+ The type of [TransportRequest.headers](#transportrequest-headers) — derived from
970
+ [CONDITIONAL_HEADERS](#conditional_headers), so the two cannot disagree.
971
971
 
972
- ## TransportRequest
972
+ <a id="transportrequest"></a>
973
973
 
974
- [TransportRequest](https://gemarcade.com/developer/api/server#transportrequest)
974
+ ## TransportRequest
975
975
 
976
976
  ```ts
977
977
  interface TransportRequest
978
978
  ```
979
979
 
980
- One outgoing call, as a [Transport](https://gemarcade.com/developer/api/server#transport) receives it.
980
+ One outgoing call, as a [Transport](#transport) receives it.
981
981
 
982
982
  You meet this when you write a transport — usually in a test, to assert what
983
983
  your code sends without a network.
984
984
 
985
- ## TransportRequest.method
985
+ <a id="transportrequest-method"></a>
986
986
 
987
- [TransportRequest.method](https://gemarcade.com/developer/api/server#transportrequest-method)
987
+ ## TransportRequest.method
988
988
 
989
989
  ```ts
990
990
  readonly method: "GET" | "PUT" | "POST" | "PATCH" | "DELETE"
@@ -992,9 +992,9 @@ readonly method: "GET" | "PUT" | "POST" | "PATCH" | "DELETE"
992
992
 
993
993
  The HTTP method.
994
994
 
995
- ## TransportRequest.path
995
+ <a id="transportrequest-path"></a>
996
996
 
997
- [TransportRequest.path](https://gemarcade.com/developer/api/server#transportrequest-path)
997
+ ## TransportRequest.path
998
998
 
999
999
  ```ts
1000
1000
  readonly path: string
@@ -1003,9 +1003,9 @@ readonly path: string
1003
1003
  The path, from the API root — `/v1/players/me/settings`. Never a full URL:
1004
1004
  where it is sent is not a call's decision.
1005
1005
 
1006
- ## TransportRequest.body
1006
+ <a id="transportrequest-body"></a>
1007
1007
 
1008
- [TransportRequest.body](https://gemarcade.com/developer/api/server#transportrequest-body)
1008
+ ## TransportRequest.body
1009
1009
 
1010
1010
  ```ts
1011
1011
  readonly body?: unknown
@@ -1013,9 +1013,9 @@ readonly body?: unknown
1013
1013
 
1014
1014
  The request body, if there is one. Encoded as JSON.
1015
1015
 
1016
- ## TransportRequest.bytes
1016
+ <a id="transportrequest-bytes"></a>
1017
1017
 
1018
- [TransportRequest.bytes](https://gemarcade.com/developer/api/server#transportrequest-bytes)
1018
+ ## TransportRequest.bytes
1019
1019
 
1020
1020
  ```ts
1021
1021
  readonly bytes?: Uint8Array<ArrayBufferLike>
@@ -1024,12 +1024,12 @@ readonly bytes?: Uint8Array<ArrayBufferLike>
1024
1024
  A raw request body, sent as it is and labelled `application/octet-stream`.
1025
1025
 
1026
1026
  For a route whose input is your own bytes rather than JSON. Set this or
1027
- [TransportRequest.body](https://gemarcade.com/developer/api/server#transportrequest-body), never both: a request carrying both is
1027
+ [TransportRequest.body](#transportrequest-body), never both: a request carrying both is
1028
1028
  refused rather than sent with one of them dropped.
1029
1029
 
1030
- ## TransportRequest.responseType
1030
+ <a id="transportrequest-responsetype"></a>
1031
1031
 
1032
- [TransportRequest.responseType](https://gemarcade.com/developer/api/server#transportrequest-responsetype)
1032
+ ## TransportRequest.responseType
1033
1033
 
1034
1034
  ```ts
1035
1035
  readonly responseType?: "json" | "bytes"
@@ -1042,19 +1042,19 @@ Only a success is read as bytes. A refusal is the platform's own JSON
1042
1042
  envelope whichever you ask for, so its code and message still reach the
1043
1043
  error you catch.
1044
1044
 
1045
- ## TransportRequest.headers
1045
+ <a id="transportrequest-headers"></a>
1046
1046
 
1047
- [TransportRequest.headers](https://gemarcade.com/developer/api/server#transportrequest-headers)
1047
+ ## TransportRequest.headers
1048
1048
 
1049
1049
  ```ts
1050
1050
  readonly headers?: Partial<Record<"if-match" | "if-none-match", string>>
1051
1051
  ```
1052
1052
 
1053
- See [ConditionalRequestHeaders](https://gemarcade.com/developer/api/server#conditionalrequestheaders) — nothing else may be set.
1053
+ See [ConditionalRequestHeaders](#conditionalrequestheaders) — nothing else may be set.
1054
1054
 
1055
- ## TransportRequest.signal
1055
+ <a id="transportrequest-signal"></a>
1056
1056
 
1057
- [TransportRequest.signal](https://gemarcade.com/developer/api/server#transportrequest-signal)
1057
+ ## TransportRequest.signal
1058
1058
 
1059
1059
  ```ts
1060
1060
  readonly signal?: AbortSignal
@@ -1062,9 +1062,9 @@ readonly signal?: AbortSignal
1062
1062
 
1063
1063
  Cancels the request. Timeouts are the caller's policy, not the SDK's.
1064
1064
 
1065
- ## TransportRequest.keepalive
1065
+ <a id="transportrequest-keepalive"></a>
1066
1066
 
1067
- [TransportRequest.keepalive](https://gemarcade.com/developer/api/server#transportrequest-keepalive)
1067
+ ## TransportRequest.keepalive
1068
1068
 
1069
1069
  ```ts
1070
1070
  readonly keepalive?: boolean
@@ -1079,23 +1079,23 @@ and nothing arrives.
1079
1079
  NOT ON EVERYTHING, because browsers cap how much can be in flight this way
1080
1080
  (64 KB across all such requests). Use it for the write that matters.
1081
1081
 
1082
- ## TransportResponse
1082
+ <a id="transportresponse"></a>
1083
1083
 
1084
- [TransportResponse](https://gemarcade.com/developer/api/server#transportresponse)
1084
+ ## TransportResponse
1085
1085
 
1086
1086
  ```ts
1087
1087
  interface TransportResponse
1088
1088
  ```
1089
1089
 
1090
- What a [Transport](https://gemarcade.com/developer/api/server#transport) answers with.
1090
+ What a [Transport](#transport) answers with.
1091
1091
 
1092
1092
  THREE HEADERS ARE PARSED OUT and there is no header bag, because these are the
1093
1093
  only three anything above this point reads — which also keeps a stub in a test
1094
1094
  to a few fields.
1095
1095
 
1096
- ## TransportResponse.status
1096
+ <a id="transportresponse-status"></a>
1097
1097
 
1098
- [TransportResponse.status](https://gemarcade.com/developer/api/server#transportresponse-status)
1098
+ ## TransportResponse.status
1099
1099
 
1100
1100
  ```ts
1101
1101
  readonly status: number
@@ -1103,9 +1103,9 @@ readonly status: number
1103
1103
 
1104
1104
  The HTTP status.
1105
1105
 
1106
- ## TransportResponse.body
1106
+ <a id="transportresponse-body"></a>
1107
1107
 
1108
- [TransportResponse.body](https://gemarcade.com/developer/api/server#transportresponse-body)
1108
+ ## TransportResponse.body
1109
1109
 
1110
1110
  ```ts
1111
1111
  readonly body: unknown
@@ -1114,9 +1114,9 @@ readonly body: unknown
1114
1114
  The decoded response body — or, for a success when the request asked for
1115
1115
  `responseType: 'bytes'`, a `Uint8Array` of exactly what arrived.
1116
1116
 
1117
- ## TransportResponse.etag
1117
+ <a id="transportresponse-etag"></a>
1118
1118
 
1119
- [TransportResponse.etag](https://gemarcade.com/developer/api/server#transportresponse-etag)
1119
+ ## TransportResponse.etag
1120
1120
 
1121
1121
  ```ts
1122
1122
  readonly etag?: string
@@ -1128,9 +1128,9 @@ Absent when the server sent none — not every resource is versioned. If you
1128
1128
  are writing a transport for a test, set it: leaving it out makes every
1129
1129
  conditional read look unversioned.
1130
1130
 
1131
- ## TransportResponse.retryAfterSeconds
1131
+ <a id="transportresponse-retryafterseconds"></a>
1132
1132
 
1133
- [TransportResponse.retryAfterSeconds](https://gemarcade.com/developer/api/server#transportresponse-retryafterseconds)
1133
+ ## TransportResponse.retryAfterSeconds
1134
1134
 
1135
1135
  ```ts
1136
1136
  readonly retryAfterSeconds?: number
@@ -1141,11 +1141,11 @@ How long the server asked you to wait, in WHOLE SECONDS.
1141
1141
  Seconds, not milliseconds — do not divide it by anything.
1142
1142
 
1143
1143
  ABSENT MEANS "NO ESTIMATE", NEVER "DO NOT RETRY". Whether a refusal is
1144
- worth repeating is [GemApiError.retryable](https://gemarcade.com/developer/api/server#gemapierror-retryable)'s answer, not this field's.
1144
+ worth repeating is [GemApiError.retryable](#gemapierror-retryable)'s answer, not this field's.
1145
1145
 
1146
- ## TransportResponse.invocationId
1146
+ <a id="transportresponse-invocationid"></a>
1147
1147
 
1148
- [TransportResponse.invocationId](https://gemarcade.com/developer/api/server#transportresponse-invocationid)
1148
+ ## TransportResponse.invocationId
1149
1149
 
1150
1150
  ```ts
1151
1151
  readonly invocationId?: string
@@ -1158,9 +1158,9 @@ Absent when the answer named none, and also when the runtime could not read
1158
1158
  the header: a browser only reads the response headers the platform exposes
1159
1159
  to it.
1160
1160
 
1161
- ## Transport
1161
+ <a id="transport"></a>
1162
1162
 
1163
- [Transport](https://gemarcade.com/developer/api/server#transport)
1163
+ ## Transport
1164
1164
 
1165
1165
  ```ts
1166
1166
  interface Transport
@@ -1181,9 +1181,9 @@ const transport = {
1181
1181
  };
1182
1182
  ```
1183
1183
 
1184
- ## Transport.request
1184
+ <a id="transport-request"></a>
1185
1185
 
1186
- [Transport.request](https://gemarcade.com/developer/api/server#transport-request)
1186
+ ## Transport.request
1187
1187
 
1188
1188
  ```ts
1189
1189
  request(request: TransportRequest): Promise<TransportResponse>
@@ -1195,9 +1195,9 @@ Send one request and resolve with what came back.
1195
1195
  | --- | --- |
1196
1196
  | `request` | `TransportRequest` |
1197
1197
 
1198
- ## ChallengeWindow
1198
+ <a id="challengewindow"></a>
1199
1199
 
1200
- [ChallengeWindow](https://gemarcade.com/developer/api/server#challengewindow)
1200
+ ## ChallengeWindow
1201
1201
 
1202
1202
  ```ts
1203
1203
  type ChallengeWindow = "daily" | "weekly" | "monthly" | "cron" | string & object
@@ -1209,9 +1209,9 @@ Open on purpose: the platform may add a window, and a game that pattern-
1209
1209
  matches on the three it knows should fall through rather than fail to
1210
1210
  compile against a newer SDK.
1211
1211
 
1212
- ## Challenge
1212
+ <a id="challenge"></a>
1213
1213
 
1214
- [Challenge](https://gemarcade.com/developer/api/server#challenge)
1214
+ ## Challenge
1215
1215
 
1216
1216
  ```ts
1217
1217
  interface Challenge
@@ -1222,14 +1222,14 @@ One challenge, in one period.
1222
1222
  Everything here is a function of your game config, the calendar and the
1223
1223
  player's time zone. It says nothing about the player's progress or streak.
1224
1224
 
1225
- ⚠️ **Key a period on [Challenge.periodLocalDate](https://gemarcade.com/developer/api/server#challenge-periodlocaldate), or on
1226
- [Challenge.seed](https://gemarcade.com/developer/api/server#challenge-seed) for a `cron` challenge — never on
1227
- [Challenge.periodStartAt](https://gemarcade.com/developer/api/server#challenge-periodstartat).** Two players on the same date, and on the
1225
+ ⚠️ **Key a period on [Challenge.periodLocalDate](#challenge-periodlocaldate), or on
1226
+ [Challenge.seed](#challenge-seed) for a `cron` challenge — never on
1227
+ [Challenge.periodStartAt](#challenge-periodstartat).** Two players on the same date, and on the
1228
1228
  same board, see different start instants when their time zones differ.
1229
1229
 
1230
- ## Challenge.id
1230
+ <a id="challenge-id"></a>
1231
1231
 
1232
- [Challenge.id](https://gemarcade.com/developer/api/server#challenge-id)
1232
+ ## Challenge.id
1233
1233
 
1234
1234
  ```ts
1235
1235
  readonly id: string
@@ -1242,9 +1242,9 @@ A challenge's id means one thing forever — reusing it for a different
1242
1242
  challenge merges two histories into one; the daily-challenges guide, §7,
1243
1243
  is the page.
1244
1244
 
1245
- ## Challenge.slug
1245
+ <a id="challenge-slug"></a>
1246
1246
 
1247
- [Challenge.slug](https://gemarcade.com/developer/api/server#challenge-slug)
1247
+ ## Challenge.slug
1248
1248
 
1249
1249
  ```ts
1250
1250
  readonly slug?: string
@@ -1254,9 +1254,9 @@ The slug from your game config, when you declared one.
1254
1254
 
1255
1255
  Absent means absent — the field is omitted, never `null` or `''`.
1256
1256
 
1257
- ## Challenge.window
1257
+ <a id="challenge-window"></a>
1258
1258
 
1259
- [Challenge.window](https://gemarcade.com/developer/api/server#challenge-window)
1259
+ ## Challenge.window
1260
1260
 
1261
1261
  ```ts
1262
1262
  readonly window: ChallengeWindow
@@ -1264,9 +1264,9 @@ readonly window: ChallengeWindow
1264
1264
 
1265
1265
  How often this challenge resets.
1266
1266
 
1267
- ## Challenge.periodStartAt
1267
+ <a id="challenge-periodstartat"></a>
1268
1268
 
1269
- [Challenge.periodStartAt](https://gemarcade.com/developer/api/server#challenge-periodstartat)
1269
+ ## Challenge.periodStartAt
1270
1270
 
1271
1271
  ```ts
1272
1272
  readonly periodStartAt: number
@@ -1278,12 +1278,12 @@ When this period began for this player, in Unix seconds.
1278
1278
  not a result you send your server. A `local` period begins at the player's
1279
1279
  own reset time, so the same date starts at a different instant in each
1280
1280
  time zone, and an earlier period (`periodsBack`) is computed in the zone
1281
- the player has now. Key on [Challenge.periodLocalDate](https://gemarcade.com/developer/api/server#challenge-periodlocaldate), or on
1282
- [Challenge.seed](https://gemarcade.com/developer/api/server#challenge-seed) for a `cron` challenge.
1281
+ the player has now. Key on [Challenge.periodLocalDate](#challenge-periodlocaldate), or on
1282
+ [Challenge.seed](#challenge-seed) for a `cron` challenge.
1283
1283
 
1284
- ## Challenge.periodEndsAt
1284
+ <a id="challenge-periodendsat"></a>
1285
1285
 
1286
- [Challenge.periodEndsAt](https://gemarcade.com/developer/api/server#challenge-periodendsat)
1286
+ ## Challenge.periodEndsAt
1287
1287
 
1288
1288
  ```ts
1289
1289
  readonly periodEndsAt: number
@@ -1292,7 +1292,7 @@ readonly periodEndsAt: number
1292
1292
  When this period stops accepting progress, in Unix seconds.
1293
1293
 
1294
1294
  **This is the seed's expiry.** After this instant the challenge is in a new
1295
- period with a different [Challenge.seed](https://gemarcade.com/developer/api/server#challenge-seed); read `gem.challenges.list`
1295
+ period with a different [Challenge.seed](#challenge-seed); read `gem.challenges.list`
1296
1296
  again rather than computing the next boundary.
1297
1297
 
1298
1298
  ⚠️ **Comparing it against the device clock is the one place that clock
@@ -1307,9 +1307,9 @@ sent something this SDK could not read (see below), and
1307
1307
  fires immediately, every time — a tight poll with no backoff. A
1308
1308
  `Math.max(60_000, ...)` floor turns that into a slow retry.
1309
1309
 
1310
- ## Challenge.periodLocalDate
1310
+ <a id="challenge-periodlocaldate"></a>
1311
1311
 
1312
- [Challenge.periodLocalDate](https://gemarcade.com/developer/api/server#challenge-periodlocaldate)
1312
+ ## Challenge.periodLocalDate
1313
1313
 
1314
1314
  ```ts
1315
1315
  readonly periodLocalDate?: string
@@ -1319,17 +1319,17 @@ The period's calendar date, `YYYY-MM-DD`: the day for a daily, the Monday
1319
1319
  for a weekly, the 1st for a monthly, in the calendar the challenge runs on.
1320
1320
 
1321
1321
  **This is the period's identity, and what to key on** — together with
1322
- [Challenge.id](https://gemarcade.com/developer/api/server#challenge-id), for content, a cache, or a result your server checks.
1323
- Every player on the same date shares the same [Challenge.seed](https://gemarcade.com/developer/api/server#challenge-seed) and
1322
+ [Challenge.id](#challenge-id), for content, a cache, or a result your server checks.
1323
+ Every player on the same date shares the same [Challenge.seed](#challenge-seed) and
1324
1324
  the same stored content, whatever their time zone.
1325
1325
 
1326
1326
  Absent — never `''` or `null` — for a `cron` challenge, which has no
1327
1327
  calendar date, and when the platform sent no date this SDK would pass on;
1328
- key on [Challenge.seed](https://gemarcade.com/developer/api/server#challenge-seed) then: `c.periodLocalDate ?? c.seed`.
1328
+ key on [Challenge.seed](#challenge-seed) then: `c.periodLocalDate ?? c.seed`.
1329
1329
 
1330
- ## Challenge.boundary
1330
+ <a id="challenge-boundary"></a>
1331
1331
 
1332
- [Challenge.boundary](https://gemarcade.com/developer/api/server#challenge-boundary)
1332
+ ## Challenge.boundary
1333
1333
 
1334
1334
  ```ts
1335
1335
  readonly boundary: string
@@ -1344,24 +1344,24 @@ challenge is on UTC for them.
1344
1344
 
1345
1345
  `''` when the platform sent something this SDK would not pass on.
1346
1346
 
1347
- ## Challenge.resetTime
1347
+ <a id="challenge-resettime"></a>
1348
1348
 
1349
- [Challenge.resetTime](https://gemarcade.com/developer/api/server#challenge-resettime)
1349
+ ## Challenge.resetTime
1350
1350
 
1351
1351
  ```ts
1352
1352
  readonly resetTime: string
1353
1353
  ```
1354
1354
 
1355
- The clock time, `HH:MM` in [Challenge.boundary](https://gemarcade.com/developer/api/server#challenge-boundary)'s zone, at which a
1355
+ The clock time, `HH:MM` in [Challenge.boundary](#challenge-boundary)'s zone, at which a
1356
1356
  new period begins. `'00:00'` unless your game config set one, and always
1357
1357
  `'00:00'` for a `cron` challenge, whose periods begin when its expression
1358
1358
  fires.
1359
1359
 
1360
1360
  `''` when the platform sent something this SDK would not pass on.
1361
1361
 
1362
- ## Challenge.seed
1362
+ <a id="challenge-seed"></a>
1363
1363
 
1364
- [Challenge.seed](https://gemarcade.com/developer/api/server#challenge-seed)
1364
+ ## Challenge.seed
1365
1365
 
1366
1366
  ```ts
1367
1367
  readonly seed: string
@@ -1372,7 +1372,7 @@ The subject selector for this challenge in this period.
1372
1372
  Identical for every player in this game and period — **in every channel**,
1373
1373
  because the channel is not part of what the platform derives it from — and
1374
1374
  different in the next period. For a daily, weekly or monthly challenge the
1375
- period is the [Challenge.periodLocalDate](https://gemarcade.com/developer/api/server#challenge-periodlocaldate), so players in different
1375
+ period is the [Challenge.periodLocalDate](#challenge-periodlocaldate), so players in different
1376
1376
  time zones share each board, each reaching it when their own period begins.
1377
1377
 
1378
1378
  Lowercase hex, and **a string on purpose**: JavaScript numbers are doubles,
@@ -1393,9 +1393,9 @@ agreement, not secrecy.
1393
1393
  a value outside the lowercase-hex contract above. It is falsy, so
1394
1394
  `if (today.seed)` is the guard; do not start a seeded board without one.
1395
1395
 
1396
- ## Challenge.data
1396
+ <a id="challenge-data"></a>
1397
1397
 
1398
- [Challenge.data](https://gemarcade.com/developer/api/server#challenge-data)
1398
+ ## Challenge.data
1399
1399
 
1400
1400
  ```ts
1401
1401
  readonly data?: string
@@ -1406,7 +1406,7 @@ usually JSON describing today's board — when it stored some.
1406
1406
 
1407
1407
  ⚠️ **It may be absent, and your game must keep working when it is.** The
1408
1408
  field is omitted — never `''` — when nothing is stored for this period, and
1409
- then you fall back to [Challenge.seed](https://gemarcade.com/developer/api/server#challenge-seed) or a pool bundled with your
1409
+ then you fall back to [Challenge.seed](#challenge-seed) or a pool bundled with your
1410
1410
  game. Content is written ahead of time by your studio's producer (a CI job,
1411
1411
  a cron job, an agent, or a developer with `gemctl challenges data push`),
1412
1412
  and a producer's runway can run out.
@@ -1418,7 +1418,7 @@ treat a value you cannot parse the way you treat an absent one.
1418
1418
  period and served on every channel whose config displays that challenge —
1419
1419
  the same bytes on `dev` as on `release`. Content pushed to try out on `dev`
1420
1420
  reaches `release` players as soon as the `release` config displays the same
1421
- challenge [Challenge.id](https://gemarcade.com/developer/api/server#challenge-id); to trial different content, give the trial
1421
+ challenge [Challenge.id](#challenge-id); to trial different content, give the trial
1422
1422
  challenge a different id. The player's progress on it stays per channel.
1423
1423
 
1424
1424
  ⚠️ **It is not authority.** Every player in the period receives the same
@@ -1428,38 +1428,38 @@ data.
1428
1428
 
1429
1429
  A period's content becomes readable only once that period has begun.
1430
1430
 
1431
- ## Challenge.dataHash
1431
+ <a id="challenge-datahash"></a>
1432
1432
 
1433
- [Challenge.dataHash](https://gemarcade.com/developer/api/server#challenge-datahash)
1433
+ ## Challenge.dataHash
1434
1434
 
1435
1435
  ```ts
1436
1436
  readonly dataHash?: string
1437
1437
  ```
1438
1438
 
1439
- 16 lowercase hex identifying [Challenge.data](https://gemarcade.com/developer/api/server#challenge-data) — present exactly when
1439
+ 16 lowercase hex identifying [Challenge.data](#challenge-data) — present exactly when
1440
1440
  `data` is.
1441
1441
 
1442
- **Send it with a result, together with [Challenge.periodLocalDate](https://gemarcade.com/developer/api/server#challenge-periodlocaldate)**
1443
- — or [Challenge.seed](https://gemarcade.com/developer/api/server#challenge-seed) for a `cron` challenge, which has no date.
1442
+ **Send it with a result, together with [Challenge.periodLocalDate](#challenge-periodlocaldate)**
1443
+ — or [Challenge.seed](#challenge-seed) for a `cron` challenge, which has no date.
1444
1444
  A verifier reads the same period and compares hashes, which is how it
1445
1445
  proves the player played the content it is checking against. It changes
1446
1446
  only when your studio corrects a running period; clients may hold the old
1447
1447
  content for up to an hour afterwards, and the hash is how a verifier tells
1448
1448
  them apart.
1449
1449
 
1450
- ## FunctionInvokeOptions
1450
+ <a id="functioninvokeoptions"></a>
1451
1451
 
1452
- [FunctionInvokeOptions](https://gemarcade.com/developer/api/server#functioninvokeoptions)
1452
+ ## FunctionInvokeOptions
1453
1453
 
1454
1454
  ```ts
1455
1455
  interface FunctionInvokeOptions
1456
1456
  ```
1457
1457
 
1458
- What [FunctionsApi.invoke](https://gemarcade.com/developer/api/server#functionsapi-invoke) takes besides the input.
1458
+ What [FunctionsApi.invoke](#functionsapi-invoke) takes besides the input.
1459
1459
 
1460
- ## FunctionInvokeOptions.signal
1460
+ <a id="functioninvokeoptions-signal"></a>
1461
1461
 
1462
- [FunctionInvokeOptions.signal](https://gemarcade.com/developer/api/server#functioninvokeoptions-signal)
1462
+ ## FunctionInvokeOptions.signal
1463
1463
 
1464
1464
  ```ts
1465
1465
  readonly signal?: AbortSignal
@@ -1467,9 +1467,9 @@ readonly signal?: AbortSignal
1467
1467
 
1468
1468
  Cancel the call. A call already cancelled sends nothing.
1469
1469
 
1470
- ## FunctionResult
1470
+ <a id="functionresult"></a>
1471
1471
 
1472
- [FunctionResult](https://gemarcade.com/developer/api/server#functionresult)
1472
+ ## FunctionResult
1473
1473
 
1474
1474
  ```ts
1475
1475
  interface FunctionResult
@@ -1482,9 +1482,9 @@ const result = await gem.functions.invoke({ v: 2, runId, finishMs });
1482
1482
  const verdict = result.json();
1483
1483
  ```
1484
1484
 
1485
- ## FunctionResult.bytes
1485
+ <a id="functionresult-bytes"></a>
1486
1486
 
1487
- [FunctionResult.bytes](https://gemarcade.com/developer/api/server#functionresult-bytes)
1487
+ ## FunctionResult.bytes
1488
1488
 
1489
1489
  ```ts
1490
1490
  readonly bytes: Uint8Array
@@ -1492,9 +1492,9 @@ readonly bytes: Uint8Array
1492
1492
 
1493
1493
  Exactly the bytes your function returned in `Ok`. Empty when it returned none.
1494
1494
 
1495
- ## FunctionResult.invocationId
1495
+ <a id="functionresult-invocationid"></a>
1496
1496
 
1497
- [FunctionResult.invocationId](https://gemarcade.com/developer/api/server#functionresult-invocationid)
1497
+ ## FunctionResult.invocationId
1498
1498
 
1499
1499
  ```ts
1500
1500
  readonly invocationId?: string
@@ -1505,9 +1505,9 @@ The platform's id for this call — what to quote when you ask about one call.
1505
1505
  Absent when your code cannot read it: a server bundle can, and a game page
1506
1506
  can only once the platform lets a page read that response header.
1507
1507
 
1508
- ## FunctionResult.text
1508
+ <a id="functionresult-text"></a>
1509
1509
 
1510
- [FunctionResult.text](https://gemarcade.com/developer/api/server#functionresult-text)
1510
+ ## FunctionResult.text
1511
1511
 
1512
1512
  ```ts
1513
1513
  text(): string
@@ -1518,9 +1518,9 @@ The answer as UTF-8 text.
1518
1518
  **Throws.** `TypeError` when the bytes are not valid UTF-8, rather than
1519
1519
  replacing what it cannot decode.
1520
1520
 
1521
- ## FunctionResult.json
1521
+ <a id="functionresult-json"></a>
1522
1522
 
1523
- [FunctionResult.json](https://gemarcade.com/developer/api/server#functionresult-json)
1523
+ ## FunctionResult.json
1524
1524
 
1525
1525
  ```ts
1526
1526
  json<T>(): T
@@ -1530,16 +1530,16 @@ The answer parsed as JSON.
1530
1530
 
1531
1531
  **Throws.** `SyntaxError` when the answer is not JSON — including an empty one.
1532
1532
 
1533
- ## FunctionFailure
1533
+ <a id="functionfailure"></a>
1534
1534
 
1535
- [FunctionFailure](https://gemarcade.com/developer/api/server#functionfailure)
1535
+ ## FunctionFailure
1536
1536
 
1537
1537
  ```ts
1538
1538
  type FunctionFailure = "trap" | "memory_exceeded" | "cpu_budget_exceeded" | "host_call_limit" | "output_too_large" | "argument_too_large"
1539
1539
  ```
1540
1540
 
1541
1541
  Why a call that reached your function did not finish — carried on
1542
- [GemFunctionError.failure](https://gemarcade.com/developer/api/server#gemfunctionerror-failure).
1542
+ [GemFunctionError.failure](#gemfunctionerror-failure).
1543
1543
 
1544
1544
  - `trap` — a panic, an `unreachable`, an out-of-bounds access or a stack
1545
1545
  overflow. No stack trace comes back.
@@ -1551,9 +1551,9 @@ Why a call that reached your function did not finish — carried on
1551
1551
  (`log`, `call-api`, `download-link` or `upload-link`) was over the 16 MiB
1552
1552
  per-call limit.
1553
1553
 
1554
- ## GemFunctionError
1554
+ <a id="gemfunctionerror"></a>
1555
1555
 
1556
- [GemFunctionError](https://gemarcade.com/developer/api/server#gemfunctionerror)
1556
+ ## GemFunctionError
1557
1557
 
1558
1558
  ```ts
1559
1559
  class GemFunctionError
@@ -1562,8 +1562,8 @@ class GemFunctionError
1562
1562
  A call to your serverless function was refused, or your function did not
1563
1563
  answer.
1564
1564
 
1565
- Thrown by [FunctionsApi.invoke](https://gemarcade.com/developer/api/server#functionsapi-invoke) for every refusal the platform sends.
1566
- It IS a [GemApiError](https://gemarcade.com/developer/api/server#gemapierror), so `reason`, `status`, `code`, `detail` and
1565
+ Thrown by [FunctionsApi.invoke](#functionsapi-invoke) for every refusal the platform sends.
1566
+ It IS a [GemApiError](#gemapierror), so `reason`, `status`, `code`, `detail` and
1567
1567
  `retryAfterSeconds` mean what they mean there. Branch on `code` for what
1568
1568
  happened; `retryable` is decided by the code. You catch this; you never
1569
1569
  construct it.
@@ -1579,9 +1579,9 @@ try {
1579
1579
  }
1580
1580
  ```
1581
1581
 
1582
- ## GemFunctionError.reason
1582
+ <a id="gemfunctionerror-reason"></a>
1583
1583
 
1584
- [GemFunctionError.reason](https://gemarcade.com/developer/api/server#gemfunctionerror-reason)
1584
+ ## GemFunctionError.reason
1585
1585
 
1586
1586
  ```ts
1587
1587
  readonly reason: GemErrorReason
@@ -1594,9 +1594,9 @@ What went wrong, in terms you can act on — `invalid`, `forbidden`,
1594
1594
  This is the field to branch on. Two refusals can share a status and mean
1595
1595
  different things; they do not share a reason.
1596
1596
 
1597
- ## GemFunctionError.status
1597
+ <a id="gemfunctionerror-status"></a>
1598
1598
 
1599
- [GemFunctionError.status](https://gemarcade.com/developer/api/server#gemfunctionerror-status)
1599
+ ## GemFunctionError.status
1600
1600
 
1601
1601
  ```ts
1602
1602
  readonly status: number
@@ -1604,13 +1604,13 @@ readonly status: number
1604
1604
 
1605
1605
  The HTTP status the server answered with.
1606
1606
 
1607
- Carried for logs and bug reports. Prefer [GemApiError.reason](https://gemarcade.com/developer/api/server#gemapierror-reason) for
1607
+ Carried for logs and bug reports. Prefer [GemApiError.reason](#gemapierror-reason) for
1608
1608
  decisions: several distinct refusals map onto one status, so branching
1609
1609
  here treats different problems as the same one.
1610
1610
 
1611
- ## GemFunctionError.detail
1611
+ <a id="gemfunctionerror-detail"></a>
1612
1612
 
1613
- [GemFunctionError.detail](https://gemarcade.com/developer/api/server#gemfunctionerror-detail)
1613
+ ## GemFunctionError.detail
1614
1614
 
1615
1615
  ```ts
1616
1616
  readonly detail: string
@@ -1622,9 +1622,9 @@ The server's own sentence, without the `reason (status):` prefix that
1622
1622
  Use this when you want to quote the platform — in a log line, or a bug
1623
1623
  report. It is not written for players, so do not put it on screen.
1624
1624
 
1625
- ## GemFunctionError.retryAfterSeconds
1625
+ <a id="gemfunctionerror-retryafterseconds"></a>
1626
1626
 
1627
- [GemFunctionError.retryAfterSeconds](https://gemarcade.com/developer/api/server#gemfunctionerror-retryafterseconds)
1627
+ ## GemFunctionError.retryAfterSeconds
1628
1628
 
1629
1629
  ```ts
1630
1630
  readonly retryAfterSeconds?: number
@@ -1633,15 +1633,15 @@ readonly retryAfterSeconds?: number
1633
1633
  How long the server asked you to wait, in whole seconds.
1634
1634
 
1635
1635
  ABSENT MEANS "NO ESTIMATE GIVEN", NEVER "DO NOT RETRY" —
1636
- [GemApiError.retryable](https://gemarcade.com/developer/api/server#gemapierror-retryable) is what answers that. A refusal can be
1636
+ [GemApiError.retryable](#gemapierror-retryable) is what answers that. A refusal can be
1637
1637
  worth repeating and still name no wait, so treat absence as "pick your
1638
1638
  own backoff" rather than as a stop signal. The platform estimates a wait
1639
1639
  for capacity and for rate limits, and sends none for the refusals it
1640
1640
  cannot put a number on.
1641
1641
 
1642
- ## GemFunctionError.code
1642
+ <a id="gemfunctionerror-code"></a>
1643
1643
 
1644
- [GemFunctionError.code](https://gemarcade.com/developer/api/server#gemfunctionerror-code)
1644
+ ## GemFunctionError.code
1645
1645
 
1646
1646
  ```ts
1647
1647
  readonly code?: string
@@ -1651,7 +1651,7 @@ The platform's own error code, when it sent one —
1651
1651
  `too_many_concurrent_matches`, `cluster_target_required`,
1652
1652
  `no_compatible_server`.
1653
1653
 
1654
- Finer-grained than [GemApiError.reason](https://gemarcade.com/developer/api/server#gemapierror-reason), and the only way to tell
1654
+ Finer-grained than [GemApiError.reason](#gemapierror-reason), and the only way to tell
1655
1655
  apart two refusals that share both a reason and a status. Absent on most
1656
1656
  refusals, so treat it as extra information rather than something to
1657
1657
  depend on.
@@ -1662,20 +1662,20 @@ refusals carry the platform's purchase codes — `insufficient_funds`,
1662
1662
  strings `gem.purchases.request` answers as its `reason`, so one `switch`
1663
1663
  on `err.code` covers both purchase paths.
1664
1664
 
1665
- ## GemFunctionError.invocationId
1665
+ <a id="gemfunctionerror-invocationid"></a>
1666
1666
 
1667
- [GemFunctionError.invocationId](https://gemarcade.com/developer/api/server#gemfunctionerror-invocationid)
1667
+ ## GemFunctionError.invocationId
1668
1668
 
1669
1669
  ```ts
1670
1670
  readonly invocationId?: string
1671
1671
  ```
1672
1672
 
1673
1673
  The platform's id for the invocation, when the call became one and your
1674
- code can read the id. See [FunctionResult.invocationId](https://gemarcade.com/developer/api/server#functionresult-invocationid).
1674
+ code can read the id. See [FunctionResult.invocationId](#functionresult-invocationid).
1675
1675
 
1676
- ## GemFunctionError.functionMessage
1676
+ <a id="gemfunctionerror-functionmessage"></a>
1677
1677
 
1678
- [GemFunctionError.functionMessage](https://gemarcade.com/developer/api/server#gemfunctionerror-functionmessage)
1678
+ ## GemFunctionError.functionMessage
1679
1679
 
1680
1680
  ```ts
1681
1681
  readonly functionMessage?: string
@@ -1686,21 +1686,21 @@ The text your function returned in `Err`, on `code` `function_error`.
1686
1686
  Written by your function, which may have repeated what the caller sent, so
1687
1687
  treat it as untrusted: log it, never render it as markup.
1688
1688
 
1689
- ## GemFunctionError.failure
1689
+ <a id="gemfunctionerror-failure"></a>
1690
1690
 
1691
- [GemFunctionError.failure](https://gemarcade.com/developer/api/server#gemfunctionerror-failure)
1691
+ ## GemFunctionError.failure
1692
1692
 
1693
1693
  ```ts
1694
1694
  readonly failure?: string & object | FunctionFailure
1695
1695
  ```
1696
1696
 
1697
1697
  On `code` `function_failed`, which limit or fault ended the call. See
1698
- [FunctionFailure](https://gemarcade.com/developer/api/server#functionfailure). A value this SDK does not know yet arrives as the
1698
+ [FunctionFailure](#functionfailure). A value this SDK does not know yet arrives as the
1699
1699
  platform sent it.
1700
1700
 
1701
- ## GemFunctionError.retryable
1701
+ <a id="gemfunctionerror-retryable"></a>
1702
1702
 
1703
- [GemFunctionError.retryable](https://gemarcade.com/developer/api/server#gemfunctionerror-retryable)
1703
+ ## GemFunctionError.retryable
1704
1704
 
1705
1705
  ```ts
1706
1706
  get retryable(): boolean
@@ -1715,9 +1715,9 @@ when the function ran — `function_error`, `function_failed`,
1715
1715
  nothing published, input too large, functions switched off. A lost
1716
1716
  connection is `retryable` too, but your function may have run.
1717
1717
 
1718
- ## FunctionsApi
1718
+ <a id="functionsapi"></a>
1719
1719
 
1720
- [FunctionsApi](https://gemarcade.com/developer/api/server#functionsapi)
1720
+ ## FunctionsApi
1721
1721
 
1722
1722
  ```ts
1723
1723
  class FunctionsApi
@@ -1726,9 +1726,9 @@ class FunctionsApi
1726
1726
  Your game's serverless function. Reached as `gem.functions`, in the browser
1727
1727
  and in a server bundle.
1728
1728
 
1729
- ## FunctionsApi.invoke
1729
+ <a id="functionsapi-invoke"></a>
1730
1730
 
1731
- [FunctionsApi.invoke](https://gemarcade.com/developer/api/server#functionsapi-invoke)
1731
+ ## FunctionsApi.invoke
1732
1732
 
1733
1733
  ```ts
1734
1734
  invoke(input: unknown, options?: FunctionInvokeOptions): Promise<FunctionResult>
@@ -1742,7 +1742,7 @@ are. Pass anything else and it is sent as its JSON text — a string too, so
1742
1742
  `'ping'` arrives as `"ping"` with its quotes.
1743
1743
 
1744
1744
  The call is made once. It is never repeated for you, because your function
1745
- may have granted or written something: see [GemFunctionError.retryable](https://gemarcade.com/developer/api/server#gemfunctionerror-retryable)
1745
+ may have granted or written something: see [GemFunctionError.retryable](#gemfunctionerror-retryable)
1746
1746
  for whether repeating it could help.
1747
1747
 
1748
1748
  ```js
@@ -1755,13 +1755,13 @@ const { accepted, why } = result.json();
1755
1755
  | `input` | `unknown` | Bytes, or a value to send as JSON. |
1756
1756
  | `options` | `FunctionInvokeOptions` | |
1757
1757
 
1758
- **Throws.** [GemFunctionError](https://gemarcade.com/developer/api/server#gemfunctionerror) for a refusal, including your function's
1758
+ **Throws.** [GemFunctionError](#gemfunctionerror) for a refusal, including your function's
1759
1759
  own `Err`. `TransportRefusal` for an input that is neither bytes nor
1760
1760
  JSON, before anything is sent. `AuthError` if the token cannot be renewed.
1761
1761
 
1762
- ## Clock
1762
+ <a id="clock"></a>
1763
1763
 
1764
- [Clock](https://gemarcade.com/developer/api/server#clock)
1764
+ ## Clock
1765
1765
 
1766
1766
  ```ts
1767
1767
  interface Clock
@@ -1773,7 +1773,7 @@ Credential refresh, retry backoff and timeouts are all wall-clock behaviour.
1773
1773
  A test that has to wait ten real seconds to prove a timeout fires is a test
1774
1774
  nobody runs, so every SDK option that involves time takes one of these.
1775
1775
 
1776
- Pass [systemClock](https://gemarcade.com/developer/api/server#systemclock) — or nothing, which means the same — to use real
1776
+ Pass [systemClock](#systemclock) — or nothing, which means the same — to use real
1777
1777
  time. Pass your own to control it.
1778
1778
 
1779
1779
  ```js
@@ -1787,9 +1787,9 @@ const clock = {
1787
1787
  // advance time yourself, and run whatever is due
1788
1788
  ```
1789
1789
 
1790
- ## Clock.now
1790
+ <a id="clock-now"></a>
1791
1791
 
1792
- [Clock.now](https://gemarcade.com/developer/api/server#clock-now)
1792
+ ## Clock.now
1793
1793
 
1794
1794
  ```ts
1795
1795
  now(): number
@@ -1797,52 +1797,52 @@ now(): number
1797
1797
 
1798
1798
  The current time, in milliseconds — the same scale as `Date.now()`.
1799
1799
 
1800
- ## Clock.setTimeout
1800
+ <a id="clock-settimeout"></a>
1801
1801
 
1802
- [Clock.setTimeout](https://gemarcade.com/developer/api/server#clock-settimeout)
1802
+ ## Clock.setTimeout
1803
1803
 
1804
1804
  ```ts
1805
1805
  setTimeout(fn: () => void, ms: number): unknown
1806
1806
  ```
1807
1807
 
1808
1808
  Run `fn` after `ms` milliseconds. Return whatever identifies it; the SDK
1809
- only ever hands that value back to [Clock.clearTimeout](https://gemarcade.com/developer/api/server#clock-cleartimeout).
1809
+ only ever hands that value back to [Clock.clearTimeout](#clock-cleartimeout).
1810
1810
 
1811
1811
  | Parameter | Type |
1812
1812
  | --- | --- |
1813
1813
  | `fn` | `() => void` |
1814
1814
  | `ms` | `number` |
1815
1815
 
1816
- ## Clock.clearTimeout
1816
+ <a id="clock-cleartimeout"></a>
1817
1817
 
1818
- [Clock.clearTimeout](https://gemarcade.com/developer/api/server#clock-cleartimeout)
1818
+ ## Clock.clearTimeout
1819
1819
 
1820
1820
  ```ts
1821
1821
  clearTimeout(handle: unknown): void
1822
1822
  ```
1823
1823
 
1824
- Cancel a timer created by [Clock.setTimeout](https://gemarcade.com/developer/api/server#clock-settimeout).
1824
+ Cancel a timer created by [Clock.setTimeout](#clock-settimeout).
1825
1825
 
1826
1826
  | Parameter | Type |
1827
1827
  | --- | --- |
1828
1828
  | `handle` | `unknown` |
1829
1829
 
1830
- ## systemClock
1830
+ <a id="systemclock"></a>
1831
1831
 
1832
- [systemClock](https://gemarcade.com/developer/api/server#systemclock)
1832
+ ## systemClock
1833
1833
 
1834
1834
  ```ts
1835
1835
  systemClock: Clock
1836
1836
  ```
1837
1837
 
1838
- Real time — the [Clock](https://gemarcade.com/developer/api/server#clock) every SDK option defaults to.
1838
+ Real time — the [Clock](#clock) every SDK option defaults to.
1839
1839
 
1840
1840
  Name it when you want to be explicit, or wrap it when you want to watch what
1841
1841
  the SDK schedules without replacing the clock entirely.
1842
1842
 
1843
- ## defineServer
1843
+ <a id="defineserver"></a>
1844
1844
 
1845
- [defineServer](https://gemarcade.com/developer/api/server#defineserver)
1845
+ ## defineServer
1846
1846
 
1847
1847
  ```ts
1848
1848
  function defineServer(setup: (gem: GemMatchServer) => GemServerBundle, options?: GemServerOptions): GemServerFactory
@@ -1850,7 +1850,7 @@ function defineServer(setup: (gem: GemMatchServer) => GemServerBundle, options?:
1850
1850
 
1851
1851
  Declare your dedicated server. Default-export the result.
1852
1852
 
1853
- You pass a setup function; it receives a [GemMatchServer](https://gemarcade.com/developer/api/server#gemmatchserver) — the platform
1853
+ You pass a setup function; it receives a [GemMatchServer](#gemmatchserver) — the platform
1854
1854
  operations, your room, your players — and returns the handlers you want. The
1855
1855
  credential and its refresh are handled for you, and the SDK is disposed when
1856
1856
  the match ends.
@@ -1866,8 +1866,8 @@ a broken handler quietly serving nobody is worse than one that stops.
1866
1866
  | Parameter | Type | Description |
1867
1867
  | --- | --- | --- |
1868
1868
  | `setup` | `(gem: GemMatchServer) => GemServerBundle` | Called once. Return the handlers you want from
1869
- [GemServerBundle](https://gemarcade.com/developer/api/server#gemserverbundle); omit the rest. |
1870
- | `options` | `GemServerOptions` | Passed through to [createGemServer](https://gemarcade.com/developer/api/server#creategemserver). Normally omit it. |
1869
+ [GemServerBundle](#gemserverbundle); omit the rest. |
1870
+ | `options` | `GemServerOptions` | Passed through to [createGemServer](#creategemserver). Normally omit it. |
1871
1871
 
1872
1872
  **Returns.** The value to default-export from your bundle's entry file.
1873
1873
 
@@ -1891,11 +1891,11 @@ export default defineServer((gem) => {
1891
1891
  });
1892
1892
  ```
1893
1893
 
1894
- **Guides.** [server-bundles](https://gemarcade.com/developer/docs/server-bundles)
1894
+ **Guides.** [server-bundles](../server-bundles.md)
1895
1895
 
1896
- ## createMatchServer
1896
+ <a id="creatematchserver"></a>
1897
1897
 
1898
- [createMatchServer](https://gemarcade.com/developer/api/server#creatematchserver)
1898
+ ## createMatchServer
1899
1899
 
1900
1900
  ```ts
1901
1901
  function createMatchServer(host: GemServerHost, options?: GemServerOptions): GemMatchServer
@@ -1903,7 +1903,7 @@ function createMatchServer(host: GemServerHost, options?: GemServerOptions): Gem
1903
1903
 
1904
1904
  Build the `gem` object yourself, when you are driving the lifecycle by hand.
1905
1905
 
1906
- MOST BUNDLES WANT [defineServer](https://gemarcade.com/developer/api/server#defineserver), which calls this for you and wires up
1906
+ MOST BUNDLES WANT [defineServer](#defineserver), which calls this for you and wires up
1907
1907
  your handlers. Use this one if you are writing the lifecycle object directly.
1908
1908
 
1909
1909
  CALL IT FROM `init`, not at module scope — it needs the host, and it builds
@@ -1914,7 +1914,7 @@ is too old" into a startup failure rather than a surprise mid-match. Call
1914
1914
  | Parameter | Type | Description |
1915
1915
  | --- | --- | --- |
1916
1916
  | `host` | `GemServerHost` | The object the runtime passed to `init`. |
1917
- | `options` | `GemServerOptions` | Passed through to [createGemServer](https://gemarcade.com/developer/api/server#creategemserver). Normally omit it. |
1917
+ | `options` | `GemServerOptions` | Passed through to [createGemServer](#creategemserver). Normally omit it. |
1918
1918
 
1919
1919
  **Throws.** When the runtime named no API host, so no call could succeed.
1920
1920
 
@@ -1931,9 +1931,9 @@ export default () => {
1931
1931
  };
1932
1932
  ```
1933
1933
 
1934
- ## createGemServer
1934
+ <a id="creategemserver"></a>
1935
1935
 
1936
- [createGemServer](https://gemarcade.com/developer/api/server#creategemserver)
1936
+ ## createGemServer
1937
1937
 
1938
1938
  ```ts
1939
1939
  function createGemServer(host: ServerBundleHost, options?: GemServerOptions): GemServer
@@ -1942,11 +1942,11 @@ function createGemServer(host: ServerBundleHost, options?: GemServerOptions): Ge
1942
1942
  Build your server's connection to the platform. Call it once, in `init(host)`.
1943
1943
 
1944
1944
  Pass the `host` object the runtime handed you and you get back a
1945
- [GemServer](https://gemarcade.com/developer/api/server#gemserver): the platform operations, the room reads for your own room,
1945
+ [GemServer](#gemserver): the platform operations, the room reads for your own room,
1946
1946
  and a way to end the match. The credential is taken from the host and kept
1947
1947
  fresh for you.
1948
1948
 
1949
- MOST BUNDLES DO NOT CALL THIS. [defineServer](https://gemarcade.com/developer/api/server#defineserver) does it for you and hands
1949
+ MOST BUNDLES DO NOT CALL THIS. [defineServer](#defineserver) does it for you and hands
1950
1950
  you the result. Reach for it directly when you are managing the lifecycle
1951
1951
  yourself.
1952
1952
 
@@ -1957,7 +1957,7 @@ platform is worse than one that refuses to start.
1957
1957
  | Parameter | Type | Description |
1958
1958
  | --- | --- | --- |
1959
1959
  | `host` | `ServerBundleHost` | The object the runtime passes to `init`. |
1960
- | `options` | `GemServerOptions` | See [GemServerOptions](https://gemarcade.com/developer/api/server#gemserveroptions) — normally omit it. |
1960
+ | `options` | `GemServerOptions` | See [GemServerOptions](#gemserveroptions) — normally omit it. |
1961
1961
 
1962
1962
  **Throws.** When the runtime named no API host, so no call could succeed.
1963
1963
 
@@ -1971,11 +1971,11 @@ export async function init(host) {
1971
1971
  }
1972
1972
  ```
1973
1973
 
1974
- **Guides.** [server-bundles](https://gemarcade.com/developer/docs/server-bundles)
1974
+ **Guides.** [server-bundles](../server-bundles.md)
1975
1975
 
1976
- ## createHostTokenSource
1976
+ <a id="createhosttokensource"></a>
1977
1977
 
1978
- [createHostTokenSource](https://gemarcade.com/developer/api/server#createhosttokensource)
1978
+ ## createHostTokenSource
1979
1979
 
1980
1980
  ```ts
1981
1981
  function createHostTokenSource(host: ServerBundleHost, options: HostTokenSourceOptions): HostTokenSource
@@ -1983,7 +1983,7 @@ function createHostTokenSource(host: ServerBundleHost, options: HostTokenSourceO
1983
1983
 
1984
1984
  Keep the platform credential fresh, without building the rest of the SDK.
1985
1985
 
1986
- MOST BUNDLES NEVER CALL THIS. [createGemServer](https://gemarcade.com/developer/api/server#creategemserver) does it, and hands you a
1986
+ MOST BUNDLES NEVER CALL THIS. [createGemServer](#creategemserver) does it, and hands you a
1987
1987
  client that is already authenticated. Reach for it when you are assembling
1988
1988
  the pieces yourself.
1989
1989
 
@@ -1994,13 +1994,13 @@ after the match is over.
1994
1994
  | Parameter | Type | Description |
1995
1995
  | --- | --- | --- |
1996
1996
  | `host` | `ServerBundleHost` | The object the runtime passed to `init`. |
1997
- | `options` | `HostTokenSourceOptions` | See [HostTokenSourceOptions](https://gemarcade.com/developer/api/server#hosttokensourceoptions) — `clock` is required. |
1997
+ | `options` | `HostTokenSourceOptions` | See [HostTokenSourceOptions](#hosttokensourceoptions) — `clock` is required. |
1998
1998
 
1999
- **Returns.** Something you can stop. See [HostTokenSource](https://gemarcade.com/developer/api/server#hosttokensource).
1999
+ **Returns.** Something you can stop. See [HostTokenSource](#hosttokensource).
2000
2000
 
2001
- ## achievementReceipts
2001
+ <a id="achievementreceipts"></a>
2002
2002
 
2003
- [achievementReceipts](https://gemarcade.com/developer/api/server#achievementreceipts)
2003
+ ## achievementReceipts
2004
2004
 
2005
2005
  ```ts
2006
2006
  function achievementReceipts(order: unknown): AchievementReceipt[]
@@ -2035,11 +2035,11 @@ for (const r of achievementReceipts(order)) {
2035
2035
  }
2036
2036
  ```
2037
2037
 
2038
- **Guides.** [achievements](https://gemarcade.com/developer/docs/achievements)
2038
+ **Guides.** [achievements](../achievements.md)
2039
2039
 
2040
- ## AchievementReceipt
2040
+ <a id="achievementreceipt"></a>
2041
2041
 
2042
- [AchievementReceipt](https://gemarcade.com/developer/api/server#achievementreceipt)
2042
+ ## AchievementReceipt
2043
2043
 
2044
2044
  ```ts
2045
2045
  interface AchievementReceipt
@@ -2047,11 +2047,11 @@ interface AchievementReceipt
2047
2047
 
2048
2048
  What became of one achievement an order reached.
2049
2049
 
2050
- **Guides.** [achievements](https://gemarcade.com/developer/docs/achievements)
2050
+ **Guides.** [achievements](../achievements.md)
2051
2051
 
2052
- ## GemMatchServer
2052
+ <a id="gemmatchserver"></a>
2053
2053
 
2054
- [GemMatchServer](https://gemarcade.com/developer/api/server#gemmatchserver)
2054
+ ## GemMatchServer
2055
2055
 
2056
2056
  ```ts
2057
2057
  interface GemMatchServer
@@ -2060,8 +2060,8 @@ interface GemMatchServer
2060
2060
  The `gem` your bundle holds — everything a dedicated server can do, on one
2061
2061
  object.
2062
2062
 
2063
- This is what [defineServer](https://gemarcade.com/developer/api/server#defineserver) hands your setup function. It is a
2064
- [GemServer](https://gemarcade.com/developer/api/server#gemserver) — the platform operations, your room, ending the match —
2063
+ This is what [defineServer](#defineserver) hands your setup function. It is a
2064
+ [GemServer](#gemserver) — the platform operations, your room, ending the match —
2065
2065
  with the match surface added: the players connected right now, and how to talk
2066
2066
  to them.
2067
2067
 
@@ -2078,9 +2078,9 @@ export default defineServer((gem) => ({
2078
2078
  }));
2079
2079
  ```
2080
2080
 
2081
- ## GemServerBundle
2081
+ <a id="gemserverbundle"></a>
2082
2082
 
2083
- [GemServerBundle](https://gemarcade.com/developer/api/server#gemserverbundle)
2083
+ ## GemServerBundle
2084
2084
 
2085
2085
  ```ts
2086
2086
  interface GemServerBundle
@@ -2089,7 +2089,7 @@ interface GemServerBundle
2089
2089
  The five things that can happen to your server. Define the ones you care
2090
2090
  about; each may be `async` and is awaited before the next event arrives.
2091
2091
 
2092
- This is what your setup function returns — see [defineServer](https://gemarcade.com/developer/api/server#defineserver). Events
2092
+ This is what your setup function returns — see [defineServer](#defineserver). Events
2093
2093
  are delivered one at a time, in order, so you never handle two at once and do
2094
2094
  not need locking around your own state.
2095
2095
 
@@ -2107,30 +2107,30 @@ export default defineServer((gem) => {
2107
2107
  });
2108
2108
  ```
2109
2109
 
2110
- ## GemServerFactory
2110
+ <a id="gemserverfactory"></a>
2111
2111
 
2112
- [GemServerFactory](https://gemarcade.com/developer/api/server#gemserverfactory)
2112
+ ## GemServerFactory
2113
2113
 
2114
2114
  ```ts
2115
2115
  type GemServerFactory = () => GemServerLifecycle
2116
2116
  ```
2117
2117
 
2118
- The type of your bundle's default export — what [defineServer](https://gemarcade.com/developer/api/server#defineserver) returns.
2118
+ The type of your bundle's default export — what [defineServer](#defineserver) returns.
2119
2119
 
2120
2120
  The platform calls it once, at the start of a match.
2121
2121
 
2122
- ## GemServerLifecycle
2122
+ <a id="gemserverlifecycle"></a>
2123
2123
 
2124
- [GemServerLifecycle](https://gemarcade.com/developer/api/server#gemserverlifecycle)
2124
+ ## GemServerLifecycle
2125
2125
 
2126
2126
  ```ts
2127
2127
  interface GemServerLifecycle
2128
2128
  ```
2129
2129
 
2130
- What the platform actually calls — a [GemServerBundle](https://gemarcade.com/developer/api/server#gemserverbundle) after the SDK has
2130
+ What the platform actually calls — a [GemServerBundle](#gemserverbundle) after the SDK has
2131
2131
  wrapped it.
2132
2132
 
2133
- YOU DO NOT WRITE ONE. [defineServer](https://gemarcade.com/developer/api/server#defineserver) produces it from your bundle. It is
2133
+ YOU DO NOT WRITE ONE. [defineServer](#defineserver) produces it from your bundle. It is
2134
2134
  named here so you can type a variable holding the result, and so the shape the
2135
2135
  platform drives is written down somewhere a reader can find it.
2136
2136
 
@@ -2138,9 +2138,9 @@ platform drives is written down somewhere a reader can find it.
2138
2138
  bundle: the SDK always defines them, to receive the host and to release the
2139
2139
  credential subscription when the match is over.
2140
2140
 
2141
- ## RuntimeShutdownPayload
2141
+ <a id="runtimeshutdownpayload"></a>
2142
2142
 
2143
- [RuntimeShutdownPayload](https://gemarcade.com/developer/api/server#runtimeshutdownpayload)
2143
+ ## RuntimeShutdownPayload
2144
2144
 
2145
2145
  ```ts
2146
2146
  interface RuntimeShutdownPayload
@@ -2151,9 +2151,9 @@ What the platform runtime hands `shutdown`, on images that say why.
2151
2151
  Either spelling of the platform reason is accepted: the runtime's wire
2152
2152
  name (`platform_reason`) and the SDK's (`endedReason`).
2153
2153
 
2154
- ## ServerShutdownInfo
2154
+ <a id="servershutdowninfo"></a>
2155
2155
 
2156
- [ServerShutdownInfo](https://gemarcade.com/developer/api/server#servershutdowninfo)
2156
+ ## ServerShutdownInfo
2157
2157
 
2158
2158
  ```ts
2159
2159
  interface ServerShutdownInfo
@@ -2181,9 +2181,9 @@ nothing).
2181
2181
  OPEN: a later runtime may add a value. Treat one you do not recognise as
2182
2182
  "the match is over", which is the safe reading.
2183
2183
 
2184
- ## GemServerProfile
2184
+ <a id="gemserverprofile"></a>
2185
2185
 
2186
- [GemServerProfile](https://gemarcade.com/developer/api/server#gemserverprofile)
2186
+ ## GemServerProfile
2187
2187
 
2188
2188
  ```ts
2189
2189
  interface GemServerProfile
@@ -2191,22 +2191,22 @@ interface GemServerProfile
2191
2191
 
2192
2192
  Who a player is, for display. Safe to hold; think before you relay it.
2193
2193
 
2194
- Returned by [GemMatchServer.profile](https://gemarcade.com/developer/api/server#gemmatchserver-profile).
2194
+ Returned by [GemMatchServer.profile](#gemmatchserver-profile).
2195
2195
 
2196
2196
  RELAY THE ID, NOT THE NAME. Sending player ids to your clients and letting
2197
2197
  each one render the name it already has is better than broadcasting names
2198
2198
  through your server: it is less to send, and it keeps one player's text from
2199
2199
  reaching every other player's screen through you.
2200
2200
 
2201
- ## GemServer
2201
+ <a id="gemserver"></a>
2202
2202
 
2203
- [GemServer](https://gemarcade.com/developer/api/server#gemserver)
2203
+ ## GemServer
2204
2204
 
2205
2205
  ```ts
2206
2206
  interface GemServer
2207
2207
  ```
2208
2208
 
2209
- Your server's connection to the platform — what [createGemServer](https://gemarcade.com/developer/api/server#creategemserver)
2209
+ Your server's connection to the platform — what [createGemServer](#creategemserver)
2210
2210
  returns.
2211
2211
 
2212
2212
  Build one in `init(host)`, keep it, and call through it for the life of the
@@ -2227,9 +2227,9 @@ export default defineServer((gem) => ({
2227
2227
  }));
2228
2228
  ```
2229
2229
 
2230
- ## GemServerIdentity
2230
+ <a id="gemserveridentity"></a>
2231
2231
 
2232
- [GemServerIdentity](https://gemarcade.com/developer/api/server#gemserveridentity)
2232
+ ## GemServerIdentity
2233
2233
 
2234
2234
  ```ts
2235
2235
  interface GemServerIdentity
@@ -2238,29 +2238,29 @@ interface GemServerIdentity
2238
2238
  Which game, channel and match this server was started for.
2239
2239
 
2240
2240
  You never construct one. The platform decides all of it when it allocates the
2241
- server, and [GemServer.identity](https://gemarcade.com/developer/api/server#gemserver-identity) is it restated in one place so you do
2241
+ server, and [GemServer.identity](#gemserver-identity) is it restated in one place so you do
2242
2242
  not have to keep reaching back to the host object.
2243
2243
 
2244
2244
  These are also the ids bound into every call `api` makes, which is why a
2245
2245
  bundle never types a game id and cannot name another game's.
2246
2246
 
2247
- ## GemServerOptions
2247
+ <a id="gemserveroptions"></a>
2248
2248
 
2249
- [GemServerOptions](https://gemarcade.com/developer/api/server#gemserveroptions)
2249
+ ## GemServerOptions
2250
2250
 
2251
2251
  ```ts
2252
2252
  interface GemServerOptions
2253
2253
  ```
2254
2254
 
2255
- Options for [createGemServer](https://gemarcade.com/developer/api/server#creategemserver). Every one is optional, and a bundle
2255
+ Options for [createGemServer](#creategemserver). Every one is optional, and a bundle
2256
2256
  running on the platform normally passes none.
2257
2257
 
2258
2258
  They exist for the two cases the platform does not cover: pointing a bundle at
2259
2259
  something other than the API the platform named, and running it in a test.
2260
2260
 
2261
- ## ServerApi
2261
+ <a id="serverapi"></a>
2262
2262
 
2263
- [ServerApi](https://gemarcade.com/developer/api/server#serverapi)
2263
+ ## ServerApi
2264
2264
 
2265
2265
  ```ts
2266
2266
  type ServerApi = unknown
@@ -2281,11 +2281,11 @@ The operation list moves with the platform. An operation added to your game's
2281
2281
  API appears here without this package changing, and one that is withdrawn
2282
2282
  stops type-checking rather than failing at runtime.
2283
2283
 
2284
- **Guides.** [server-bundles](https://gemarcade.com/developer/docs/server-bundles)
2284
+ **Guides.** [server-bundles](../server-bundles.md)
2285
2285
 
2286
- ## ServerBundleHost
2286
+ <a id="serverbundlehost"></a>
2287
2287
 
2288
- [ServerBundleHost](https://gemarcade.com/developer/api/server#serverbundlehost)
2288
+ ## ServerBundleHost
2289
2289
 
2290
2290
  ```ts
2291
2291
  interface ServerBundleHost
@@ -2294,13 +2294,13 @@ interface ServerBundleHost
2294
2294
  The part of the host object that identifies the match and carries the
2295
2295
  credential.
2296
2296
 
2297
- This is what [createGemServer](https://gemarcade.com/developer/api/server#creategemserver) needs, and nothing more — which is also
2297
+ This is what [createGemServer](#creategemserver) needs, and nothing more — which is also
2298
2298
  what makes it easy to stand up in a test: four members instead of the whole
2299
- host. [GemServerHost](https://gemarcade.com/developer/api/server#gemserverhost) is the same object at full width.
2299
+ host. [GemServerHost](#gemserverhost) is the same object at full width.
2300
2300
 
2301
- ## ServerTokenGrant
2301
+ <a id="servertokengrant"></a>
2302
2302
 
2303
- [ServerTokenGrant](https://gemarcade.com/developer/api/server#servertokengrant)
2303
+ ## ServerTokenGrant
2304
2304
 
2305
2305
  ```ts
2306
2306
  interface ServerTokenGrant
@@ -2308,26 +2308,26 @@ interface ServerTokenGrant
2308
2308
 
2309
2309
  A credential the platform handed your server, and when it stops working.
2310
2310
 
2311
- You do not normally touch one. [createGemServer](https://gemarcade.com/developer/api/server#creategemserver) takes the credential
2311
+ You do not normally touch one. [createGemServer](#creategemserver) takes the credential
2312
2312
  from the host and refreshes it for you; this type is here for a bundle wiring
2313
2313
  the pieces up itself.
2314
2314
 
2315
- ## HostTokenSource
2315
+ <a id="hosttokensource"></a>
2316
2316
 
2317
- [HostTokenSource](https://gemarcade.com/developer/api/server#hosttokensource)
2317
+ ## HostTokenSource
2318
2318
 
2319
2319
  ```ts
2320
2320
  interface HostTokenSource
2321
2321
  ```
2322
2322
 
2323
- A credential subscription — what [createHostTokenSource](https://gemarcade.com/developer/api/server#createhosttokensource) gives back.
2323
+ A credential subscription — what [createHostTokenSource](#createhosttokensource) gives back.
2324
2324
 
2325
2325
  Deliberately narrow: the only thing you can do with it is stop it. The
2326
2326
  credential itself is not reachable, because nothing you can call takes one.
2327
2327
 
2328
- ## GemServerHost
2328
+ <a id="gemserverhost"></a>
2329
2329
 
2330
- [GemServerHost](https://gemarcade.com/developer/api/server#gemserverhost)
2330
+ ## GemServerHost
2331
2331
 
2332
2332
  ```ts
2333
2333
  interface GemServerHost
@@ -2335,7 +2335,7 @@ interface GemServerHost
2335
2335
 
2336
2336
  The whole `host` object, as the runtime passes it to your bundle.
2337
2337
 
2338
- [ServerBundleHost](https://gemarcade.com/developer/api/server#serverbundlehost) is its credential half; everything added here is the
2338
+ [ServerBundleHost](#serverbundlehost) is its credential half; everything added here is the
2339
2339
  match — who is connected, what they sent, and how to answer.
2340
2340
 
2341
2341
  SEVERAL MEMBERS ARE OPTIONAL BECAUSE THE PLATFORM IMAGE MAY BE OLDER than the
@@ -2343,12 +2343,12 @@ member. There is no version to ask for; presence is the signal. Check before
2343
2343
  calling, and your bundle keeps running on every image rather than only the
2344
2344
  newest.
2345
2345
 
2346
- A bundle written with [defineServer](https://gemarcade.com/developer/api/server#defineserver) usually reads [GemMatchServer](https://gemarcade.com/developer/api/server#gemmatchserver)
2346
+ A bundle written with [defineServer](#defineserver) usually reads [GemMatchServer](#gemmatchserver)
2347
2347
  instead, which wraps this.
2348
2348
 
2349
- ## GemServerHostProfile
2349
+ <a id="gemserverhostprofile"></a>
2350
2350
 
2351
- [GemServerHostProfile](https://gemarcade.com/developer/api/server#gemserverhostprofile)
2351
+ ## GemServerHostProfile
2352
2352
 
2353
2353
  ```ts
2354
2354
  interface GemServerHostProfile
@@ -2359,12 +2359,12 @@ A player's display identity, as the platform resolved it for your server.
2359
2359
  EVERY FIELD IS OPTIONAL. The platform may not have all of them for a given
2360
2360
  player, and that is not an error — fall back to something of your own.
2361
2361
 
2362
- [GemMatchServer.profile](https://gemarcade.com/developer/api/server#gemmatchserver-profile) is the version you usually want: same
2363
- information, with the name typed as [PlayerText](https://gemarcade.com/developer/api/server#playertext).
2362
+ [GemMatchServer.profile](#gemmatchserver-profile) is the version you usually want: same
2363
+ information, with the name typed as [PlayerText](#playertext).
2364
2364
 
2365
- ## SendOptions
2365
+ <a id="sendoptions"></a>
2366
2366
 
2367
- [SendOptions](https://gemarcade.com/developer/api/server#sendoptions)
2367
+ ## SendOptions
2368
2368
 
2369
2369
  ```ts
2370
2370
  interface SendOptions
@@ -2377,32 +2377,32 @@ unreliable message reliably anyway, and it never does the reverse. What you
2377
2377
  asked for is carried to the receiver either way, so they can tell the
2378
2378
  difference.
2379
2379
 
2380
- ## BroadcastOptions
2380
+ <a id="broadcastoptions"></a>
2381
2381
 
2382
- [BroadcastOptions](https://gemarcade.com/developer/api/server#broadcastoptions)
2382
+ ## BroadcastOptions
2383
2383
 
2384
2384
  ```ts
2385
2385
  interface BroadcastOptions
2386
2386
  ```
2387
2387
 
2388
- The same delivery choices as [SendOptions](https://gemarcade.com/developer/api/server#sendoptions), plus who to skip.
2388
+ The same delivery choices as [SendOptions](#sendoptions), plus who to skip.
2389
2389
 
2390
- ## HostTokenSourceOptions
2390
+ <a id="hosttokensourceoptions"></a>
2391
2391
 
2392
- [HostTokenSourceOptions](https://gemarcade.com/developer/api/server#hosttokensourceoptions)
2392
+ ## HostTokenSourceOptions
2393
2393
 
2394
2394
  ```ts
2395
2395
  interface HostTokenSourceOptions
2396
2396
  ```
2397
2397
 
2398
- Options for [createHostTokenSource](https://gemarcade.com/developer/api/server#createhosttokensource).
2398
+ Options for [createHostTokenSource](#createhosttokensource).
2399
2399
 
2400
2400
  You need these only if you are wiring the credential up yourself.
2401
- [createGemServer](https://gemarcade.com/developer/api/server#creategemserver) does it for you.
2401
+ [createGemServer](#creategemserver) does it for you.
2402
2402
 
2403
- ## RoomArgValue
2403
+ <a id="roomargvalue"></a>
2404
2404
 
2405
- [RoomArgValue](https://gemarcade.com/developer/api/server#roomargvalue)
2405
+ ## RoomArgValue
2406
2406
 
2407
2407
  ```ts
2408
2408
  type RoomArgValue = string | number | boolean | readonly string[] | readonly number[] | readonly boolean[]
@@ -2417,11 +2417,11 @@ room may be created with, already type-checked by the platform before the
2417
2417
  match was allocated — so a bundle reading settings does not need to validate
2418
2418
  them again.
2419
2419
 
2420
- **Guides.** [rooms-and-matches](https://gemarcade.com/developer/docs/rooms-and-matches)
2420
+ **Guides.** [rooms-and-matches](../rooms-and-matches.md)
2421
2421
 
2422
- ## ServerAnalytics
2422
+ <a id="serveranalytics"></a>
2423
2423
 
2424
- [ServerAnalytics](https://gemarcade.com/developer/api/server#serveranalytics)
2424
+ ## ServerAnalytics
2425
2425
 
2426
2426
  ```ts
2427
2427
  interface ServerAnalytics
@@ -2430,9 +2430,9 @@ interface ServerAnalytics
2430
2430
  Your server's analytics — the events you declared in your game config,
2431
2431
  attributed to the player they are about.
2432
2432
 
2433
- Reached as `gem.analytics`. [ServerAnalytics.track](https://gemarcade.com/developer/api/server#serveranalytics-track) is synchronous and
2433
+ Reached as `gem.analytics`. [ServerAnalytics.track](#serveranalytics-track) is synchronous and
2434
2434
  never throws: it queues, and the queue goes out on a timer, at fifty events,
2435
- and whenever you call [ServerAnalytics.flush](https://gemarcade.com/developer/api/server#serveranalytics-flush).
2435
+ and whenever you call [ServerAnalytics.flush](#serveranalytics-flush).
2436
2436
 
2437
2437
  ⚠️ **Flush before your match ends.** A dedicated server is torn down when the
2438
2438
  match is over and nothing gets a last word in for it — there is no `pagehide`
@@ -2447,11 +2447,11 @@ export default defineServer((gem) => ({
2447
2447
  }));
2448
2448
  ```
2449
2449
 
2450
- **Guides.** [analytics](https://gemarcade.com/developer/docs/analytics)
2450
+ **Guides.** [analytics](../analytics.md)
2451
2451
 
2452
- ## ServerAnalytics.track
2452
+ <a id="serveranalytics-track"></a>
2453
2453
 
2454
- [ServerAnalytics.track](https://gemarcade.com/developer/api/server#serveranalytics-track)
2454
+ ## ServerAnalytics.track
2455
2455
 
2456
2456
  ```ts
2457
2457
  track(playerId: string, name: string, props?: Readonly<Record<string, AnalyticsValue>>): void
@@ -2477,9 +2477,9 @@ events are client-sourced, so one emitted here would be refused
2477
2477
  | `name` | `string` | A declared event id — an `id` from your `analytics_events`. |
2478
2478
  | `props` | `Readonly<Record<string, AnalyticsValue>>` | Dimensions (strings) and measures (numbers). |
2479
2479
 
2480
- ## ServerAnalytics.flush
2480
+ <a id="serveranalytics-flush"></a>
2481
2481
 
2482
- [ServerAnalytics.flush](https://gemarcade.com/developer/api/server#serveranalytics-flush)
2482
+ ## ServerAnalytics.flush
2483
2483
 
2484
2484
  ```ts
2485
2485
  flush(signal?: AbortSignal): Promise<AnalyticsFlushResult>
@@ -2496,9 +2496,9 @@ discards the batch, because resending it can only fail the same way.
2496
2496
  | --- | --- | --- |
2497
2497
  | `signal` | `AbortSignal` | Cancels the call — not the queue. |
2498
2498
 
2499
- ## ServerAnalytics.pending
2499
+ <a id="serveranalytics-pending"></a>
2500
2500
 
2501
- [ServerAnalytics.pending](https://gemarcade.com/developer/api/server#serveranalytics-pending)
2501
+ ## ServerAnalytics.pending
2502
2502
 
2503
2503
  ```ts
2504
2504
  readonly pending: number
@@ -2506,19 +2506,19 @@ readonly pending: number
2506
2506
 
2507
2507
  How many events are waiting to go out.
2508
2508
 
2509
- ## ServerListChallengesOptions
2509
+ <a id="serverlistchallengesoptions"></a>
2510
2510
 
2511
- [ServerListChallengesOptions](https://gemarcade.com/developer/api/server#serverlistchallengesoptions)
2511
+ ## ServerListChallengesOptions
2512
2512
 
2513
2513
  ```ts
2514
2514
  interface ServerListChallengesOptions
2515
2515
  ```
2516
2516
 
2517
- Options for [ServerChallenges.list](https://gemarcade.com/developer/api/server#serverchallenges-list).
2517
+ Options for [ServerChallenges.list](#serverchallenges-list).
2518
2518
 
2519
- ## ServerListChallengesOptions.playerId
2519
+ <a id="serverlistchallengesoptions-playerid"></a>
2520
2520
 
2521
- [ServerListChallengesOptions.playerId](https://gemarcade.com/developer/api/server#serverlistchallengesoptions-playerid)
2521
+ ## ServerListChallengesOptions.playerId
2522
2522
 
2523
2523
  ```ts
2524
2524
  readonly playerId: string
@@ -2530,9 +2530,9 @@ Take it from `join()` or `gem.players()`, never from something a client
2530
2530
  sent you. The platform answers only for a player who plays your game or is
2531
2531
  in it now, and refuses anyone else with `not_found`.
2532
2532
 
2533
- ## ServerListChallengesOptions.periodsBack
2533
+ <a id="serverlistchallengesoptions-periodsback"></a>
2534
2534
 
2535
- [ServerListChallengesOptions.periodsBack](https://gemarcade.com/developer/api/server#serverlistchallengesoptions-periodsback)
2535
+ ## ServerListChallengesOptions.periodsBack
2536
2536
 
2537
2537
  ```ts
2538
2538
  readonly periodsBack?: number
@@ -2548,9 +2548,9 @@ player finished just before their day rolled over.
2548
2548
  An integer from 0 to 400. Anything else is a `TypeError` before the
2549
2549
  request is made.
2550
2550
 
2551
- ## ServerListChallengesOptions.signal
2551
+ <a id="serverlistchallengesoptions-signal"></a>
2552
2552
 
2553
- [ServerListChallengesOptions.signal](https://gemarcade.com/developer/api/server#serverlistchallengesoptions-signal)
2553
+ ## ServerListChallengesOptions.signal
2554
2554
 
2555
2555
  ```ts
2556
2556
  readonly signal?: AbortSignal
@@ -2558,9 +2558,9 @@ readonly signal?: AbortSignal
2558
2558
 
2559
2559
  Abort the request.
2560
2560
 
2561
- ## ServerChallenges
2561
+ <a id="serverchallenges"></a>
2562
2562
 
2563
- [ServerChallenges](https://gemarcade.com/developer/api/server#serverchallenges)
2563
+ ## ServerChallenges
2564
2564
 
2565
2565
  ```ts
2566
2566
  interface ServerChallenges
@@ -2573,7 +2573,7 @@ result against.
2573
2573
  Reached as `gem.challenges`. The browser's `gem.challenges.list()` answers
2574
2574
  for the player holding the token; a server holds no player, so this one
2575
2575
  takes the player's id. Everything else is the same read, and each entry is
2576
- the same [Challenge](https://gemarcade.com/developer/api/server#challenge).
2576
+ the same [Challenge](#challenge).
2577
2577
 
2578
2578
  ```js
2579
2579
  export default defineServer((gem) => ({
@@ -2596,9 +2596,9 @@ export default defineServer((gem) => ({
2596
2596
  }));
2597
2597
  ```
2598
2598
 
2599
- ## ServerChallenges.list
2599
+ <a id="serverchallenges-list"></a>
2600
2600
 
2601
- [ServerChallenges.list](https://gemarcade.com/developer/api/server#serverchallenges-list)
2601
+ ## ServerChallenges.list
2602
2602
 
2603
2603
  ```ts
2604
2604
  list(options: ServerListChallengesOptions): Promise<Challenge[]>
@@ -2610,8 +2610,8 @@ Every challenge your config declares, for one player, for one period.
2610
2610
  player's day ends at their own midnight, so the date and the seed you hold
2611
2611
  for one player are not another player's, and "today" on the machine this
2612
2612
  runs on is neither. Ask per player, and match on
2613
- [Challenge.periodLocalDate](https://gemarcade.com/developer/api/server#challenge-periodlocaldate) — or [Challenge.seed](https://gemarcade.com/developer/api/server#challenge-seed) for a
2614
- `cron` challenge — never on [Challenge.periodStartAt](https://gemarcade.com/developer/api/server#challenge-periodstartat).
2613
+ [Challenge.periodLocalDate](#challenge-periodlocaldate) — or [Challenge.seed](#challenge-seed) for a
2614
+ `cron` challenge — never on [Challenge.periodStartAt](#challenge-periodstartat).
2615
2615
 
2616
2616
  | Parameter | Type |
2617
2617
  | --- | --- |
@@ -2623,9 +2623,9 @@ runs on is neither. Ask per player, and match on
2623
2623
  `GemApiError` with `reason: 'not_found'` for a player who neither
2624
2624
  plays your game nor is in it now, and for a channel with no config.
2625
2625
 
2626
- ## GameApi
2626
+ <a id="gameapi"></a>
2627
2627
 
2628
- [GameApi](https://gemarcade.com/developer/api/server#gameapi)
2628
+ ## GameApi
2629
2629
 
2630
2630
  ```ts
2631
2631
  interface GameApi
@@ -2644,9 +2644,9 @@ the platform has issued a credential, which happens after the client exists.
2644
2644
  It is never something a caller passes: a bundle cannot address another
2645
2645
  game's data because it never types a game id, in a path or in a query.
2646
2646
 
2647
- ## GameApi.client
2647
+ <a id="gameapi-client"></a>
2648
2648
 
2649
- [GameApi.client](https://gemarcade.com/developer/api/server#gameapi-client)
2649
+ ## GameApi.client
2650
2650
 
2651
2651
  ```ts
2652
2652
  readonly client: GemClient
@@ -2654,9 +2654,9 @@ readonly client: GemClient
2654
2654
 
2655
2655
  The client every operation sends through.
2656
2656
 
2657
- ## GameApi.gameId
2657
+ <a id="gameapi-gameid"></a>
2658
2658
 
2659
- [GameApi.gameId](https://gemarcade.com/developer/api/server#gameapi-gameid)
2659
+ ## GameApi.gameId
2660
2660
 
2661
2661
  ```ts
2662
2662
  gameId(): string
@@ -2664,9 +2664,9 @@ gameId(): string
2664
2664
 
2665
2665
  The game this server is running — taken from its credential.
2666
2666
 
2667
- ## PlayerText
2667
+ <a id="playertext"></a>
2668
2668
 
2669
- [PlayerText](https://gemarcade.com/developer/api/server#playertext)
2669
+ ## PlayerText
2670
2670
 
2671
2671
  ```ts
2672
2672
  class PlayerText
@@ -2689,12 +2689,12 @@ const name = text.toPlain(profile.displayName);
2689
2689
  label.textContent = name;
2690
2690
  ```
2691
2691
 
2692
- To sort names, use [text.compare](https://gemarcade.com/developer/api/server#text-compare) rather than converting first — it is
2692
+ To sort names, use [text.compare](#text-compare) rather than converting first — it is
2693
2693
  locale-aware, which a plain `<` comparison is not.
2694
2694
 
2695
- ## PlayerText.toString
2695
+ <a id="playertext-tostring"></a>
2696
2696
 
2697
- [PlayerText.toString](https://gemarcade.com/developer/api/server#playertext-tostring)
2697
+ ## PlayerText.toString
2698
2698
 
2699
2699
  ```ts
2700
2700
  toString(): string
@@ -2710,9 +2710,9 @@ alternative was shipping an unescaped player name.
2710
2710
  from the package — `import { text } from '@indiegems/gem-web-sdk/sdk';` —
2711
2711
  not reached from your `gem` instance. Follow that rather than the message.
2712
2712
 
2713
- ## PlayerText.toJSON
2713
+ <a id="playertext-tojson"></a>
2714
2714
 
2715
- [PlayerText.toJSON](https://gemarcade.com/developer/api/server#playertext-tojson)
2715
+ ## PlayerText.toJSON
2716
2716
 
2717
2717
  ```ts
2718
2718
  toJSON(): string
@@ -2721,18 +2721,18 @@ toJSON(): string
2721
2721
  The same sentinel, so putting a player name into a save blob or a telemetry
2722
2722
  payload cannot silently store unescaped text.
2723
2723
 
2724
- Convert with [text.toPlain](https://gemarcade.com/developer/api/server#text-toplain) before serialising anything you intend to
2724
+ Convert with [text.toPlain](#text-toplain) before serialising anything you intend to
2725
2725
  keep.
2726
2726
 
2727
- ## text
2727
+ <a id="text"></a>
2728
2728
 
2729
- [text](https://gemarcade.com/developer/api/server#text)
2729
+ ## text
2730
2730
 
2731
2731
  ```ts
2732
2732
  text: { toPlain: null; compare: null; equals: null }
2733
2733
  ```
2734
2734
 
2735
- The only ways out of [PlayerText](https://gemarcade.com/developer/api/server#playertext).
2735
+ The only ways out of [PlayerText](#playertext).
2736
2736
 
2737
2737
  Imported from the package rather than reached from `gem`:
2738
2738
  `import { text } from '@indiegems/gem-web-sdk/sdk';`
@@ -2761,9 +2761,9 @@ lands — safe in element text and quoted attributes, unsafe in URL, style and
2761
2761
  script contexts — and your framework already escapes. One unambiguous
2762
2762
  function beats two where one carries a caveat.
2763
2763
 
2764
- ## text.toPlain
2764
+ <a id="text-toplain"></a>
2765
2765
 
2766
- [text.toPlain](https://gemarcade.com/developer/api/server#text-toplain)
2766
+ ## text.toPlain
2767
2767
 
2768
2768
  ```ts
2769
2769
  toPlain(t: PlayerText): string
@@ -2787,9 +2787,9 @@ your framework escape, which is what it already does.
2787
2787
  | --- | --- |
2788
2788
  | `t` | `PlayerText` |
2789
2789
 
2790
- ## text.compare
2790
+ <a id="text-compare"></a>
2791
2791
 
2792
- [text.compare](https://gemarcade.com/developer/api/server#text-compare)
2792
+ ## text.compare
2793
2793
 
2794
2794
  ```ts
2795
2795
  compare(a: string | PlayerText, b: string | PlayerText, locale?: string): number
@@ -2816,9 +2816,9 @@ throw here would take the whole sort down with it.
2816
2816
  | `b` | `string \| PlayerText` |
2817
2817
  | `locale` | `string` |
2818
2818
 
2819
- ## text.equals
2819
+ <a id="text-equals"></a>
2820
2820
 
2821
- [text.equals](https://gemarcade.com/developer/api/server#text-equals)
2821
+ ## text.equals
2822
2822
 
2823
2823
  ```ts
2824
2824
  equals(a: string | PlayerText, b: string | PlayerText): boolean
@@ -2839,9 +2839,9 @@ theirs. Use this for display-level comparisons, not for identity.
2839
2839
  | `a` | `string \| PlayerText` |
2840
2840
  | `b` | `string \| PlayerText` |
2841
2841
 
2842
- ## AchievementReceipt.achievementId
2842
+ <a id="achievementreceipt-achievementid"></a>
2843
2843
 
2844
- [AchievementReceipt.achievementId](https://gemarcade.com/developer/api/server#achievementreceipt-achievementid)
2844
+ ## AchievementReceipt.achievementId
2845
2845
 
2846
2846
  ```ts
2847
2847
  readonly achievementId: string
@@ -2849,9 +2849,9 @@ readonly achievementId: string
2849
2849
 
2850
2850
  The achievement's id, as your game config declares it.
2851
2851
 
2852
- ## AchievementReceipt.progressionDefinitionId
2852
+ <a id="achievementreceipt-progressiondefinitionid"></a>
2853
2853
 
2854
- [AchievementReceipt.progressionDefinitionId](https://gemarcade.com/developer/api/server#achievementreceipt-progressiondefinitionid)
2854
+ ## AchievementReceipt.progressionDefinitionId
2855
2855
 
2856
2856
  ```ts
2857
2857
  readonly progressionDefinitionId: string
@@ -2859,9 +2859,9 @@ readonly progressionDefinitionId: string
2859
2859
 
2860
2860
  The progression the achievement sits on.
2861
2861
 
2862
- ## AchievementReceipt.points
2862
+ <a id="achievementreceipt-points"></a>
2863
2863
 
2864
- [AchievementReceipt.points](https://gemarcade.com/developer/api/server#achievementreceipt-points)
2864
+ ## AchievementReceipt.points
2865
2865
 
2866
2866
  ```ts
2867
2867
  readonly points: number
@@ -2871,9 +2871,9 @@ Achievement points the unlock credited. Zero unless `outcome` is
2871
2871
  `unlocked`, so summing it over receipts counts only what this order
2872
2872
  credited.
2873
2873
 
2874
- ## AchievementReceipt.outcome
2874
+ <a id="achievementreceipt-outcome"></a>
2875
2875
 
2876
- [AchievementReceipt.outcome](https://gemarcade.com/developer/api/server#achievementreceipt-outcome)
2876
+ ## AchievementReceipt.outcome
2877
2877
 
2878
2878
  ```ts
2879
2879
  readonly outcome: "unlocked" | "already_unlocked" | "suppressed"
@@ -2887,9 +2887,9 @@ The set may grow. A receipt with an outcome this SDK does not know is
2887
2887
  left out rather than reported as one of these, so a `switch` over these
2888
2888
  three needs no default for correctness — but write one anyway.
2889
2889
 
2890
- ## AchievementReceipt.reason
2890
+ <a id="achievementreceipt-reason"></a>
2891
2891
 
2892
- [AchievementReceipt.reason](https://gemarcade.com/developer/api/server#achievementreceipt-reason)
2892
+ ## AchievementReceipt.reason
2893
2893
 
2894
2894
  ```ts
2895
2895
  readonly reason?: string
@@ -2904,9 +2904,9 @@ achievement on it never unlocks), or `achievements_unavailable`
2904
2904
  (transient; the next eligible write tries again). For your logs and your
2905
2905
  developer tools; not text to show a player.
2906
2906
 
2907
- ## GemServerOptions.baseUrl
2907
+ <a id="gemserveroptions-baseurl"></a>
2908
2908
 
2909
- [GemServerOptions.baseUrl](https://gemarcade.com/developer/api/server#gemserveroptions-baseurl)
2909
+ ## GemServerOptions.baseUrl
2910
2910
 
2911
2911
  ```ts
2912
2912
  readonly baseUrl?: string
@@ -2920,9 +2920,9 @@ machine — not for choosing which environment you are in. Setting it in a
2920
2920
  shipped bundle means your server talks to somewhere the platform did not
2921
2921
  send it.
2922
2922
 
2923
- ## GemServerOptions.clock
2923
+ <a id="gemserveroptions-clock"></a>
2924
2924
 
2925
- [GemServerOptions.clock](https://gemarcade.com/developer/api/server#gemserveroptions-clock)
2925
+ ## GemServerOptions.clock
2926
2926
 
2927
2927
  ```ts
2928
2928
  readonly clock?: Clock
@@ -2930,12 +2930,12 @@ readonly clock?: Clock
2930
2930
 
2931
2931
  Where time comes from. Defaults to the real clock.
2932
2932
 
2933
- Pass a [Clock](https://gemarcade.com/developer/api/server#clock) in a test so token refresh and retry backoff happen when
2933
+ Pass a [Clock](#clock) in a test so token refresh and retry backoff happen when
2934
2934
  you say rather than when a real second passes.
2935
2935
 
2936
- ## GemServerOptions.fetch
2936
+ <a id="gemserveroptions-fetch"></a>
2937
2937
 
2938
- [GemServerOptions.fetch](https://gemarcade.com/developer/api/server#gemserveroptions-fetch)
2938
+ ## GemServerOptions.fetch
2939
2939
 
2940
2940
  ```ts
2941
2941
  readonly fetch?: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>
@@ -2946,9 +2946,9 @@ The `fetch` used for every call. Defaults to the runtime's own.
2946
2946
  For tests: a stub here is how you assert what your bundle sends without a
2947
2947
  network.
2948
2948
 
2949
- ## GemServerOptions.onAuthStateChange
2949
+ <a id="gemserveroptions-onauthstatechange"></a>
2950
2950
 
2951
- [GemServerOptions.onAuthStateChange](https://gemarcade.com/developer/api/server#gemserveroptions-onauthstatechange)
2951
+ ## GemServerOptions.onAuthStateChange
2952
2952
 
2953
2953
  ```ts
2954
2954
  readonly onAuthStateChange?: (state: AuthState, reason?: AuthFailure) => void
@@ -2958,12 +2958,12 @@ Called whenever the server's ability to make authenticated calls changes.
2958
2958
 
2959
2959
  The same signal the browser half delivers to a game, for the same reason:
2960
2960
  the token is refreshed for you, and this is how you find out when that stops
2961
- working. See [AuthState](https://gemarcade.com/developer/api/server#authstate) for what to branch on and [AuthFailure](https://gemarcade.com/developer/api/server#authfailure)
2961
+ working. See [AuthState](#authstate) for what to branch on and [AuthFailure](#authfailure)
2962
2962
  for the cause.
2963
2963
 
2964
- ## GemServerIdentity.gameId
2964
+ <a id="gemserveridentity-gameid"></a>
2965
2965
 
2966
- [GemServerIdentity.gameId](https://gemarcade.com/developer/api/server#gemserveridentity-gameid)
2966
+ ## GemServerIdentity.gameId
2967
2967
 
2968
2968
  ```ts
2969
2969
  readonly gameId: string
@@ -2971,9 +2971,9 @@ readonly gameId: string
2971
2971
 
2972
2972
  The game this server belongs to.
2973
2973
 
2974
- ## GemServerIdentity.channel
2974
+ <a id="gemserveridentity-channel"></a>
2975
2975
 
2976
- [GemServerIdentity.channel](https://gemarcade.com/developer/api/server#gemserveridentity-channel)
2976
+ ## GemServerIdentity.channel
2977
2977
 
2978
2978
  ```ts
2979
2979
  readonly channel: string
@@ -2981,9 +2981,9 @@ readonly channel: string
2981
2981
 
2982
2982
  The channel it was allocated on — `dev`, `test` or `release`.
2983
2983
 
2984
- ## GemServerIdentity.matchId
2984
+ <a id="gemserveridentity-matchid"></a>
2985
2985
 
2986
- [GemServerIdentity.matchId](https://gemarcade.com/developer/api/server#gemserveridentity-matchid)
2986
+ ## GemServerIdentity.matchId
2987
2987
 
2988
2988
  ```ts
2989
2989
  readonly matchId: string
@@ -2991,9 +2991,9 @@ readonly matchId: string
2991
2991
 
2992
2992
  The match it is running.
2993
2993
 
2994
- ## GemServerIdentity.gameRoomId
2994
+ <a id="gemserveridentity-gameroomid"></a>
2995
2995
 
2996
- [GemServerIdentity.gameRoomId](https://gemarcade.com/developer/api/server#gemserveridentity-gameroomid)
2996
+ ## GemServerIdentity.gameRoomId
2997
2997
 
2998
2998
  ```ts
2999
2999
  readonly gameRoomId?: string
@@ -3002,11 +3002,11 @@ readonly gameRoomId?: string
3002
3002
  The room this match came from, when there is one.
3003
3003
 
3004
3004
  CHECK IT BEFORE USING IT. It is absent on older platform images, which is
3005
- also why [GemServer.room](https://gemarcade.com/developer/api/server#gemserver-room) can be absent — the two go together.
3005
+ also why [GemServer.room](#gemserver-room) can be absent — the two go together.
3006
3006
 
3007
- ## GemServer.identity
3007
+ <a id="gemserver-identity"></a>
3008
3008
 
3009
- [GemServer.identity](https://gemarcade.com/developer/api/server#gemserver-identity)
3009
+ ## GemServer.identity
3010
3010
 
3011
3011
  ```ts
3012
3012
  readonly identity: GemServerIdentity
@@ -3014,15 +3014,15 @@ readonly identity: GemServerIdentity
3014
3014
 
3015
3015
  Which game, channel and match this server is running.
3016
3016
 
3017
- ## GemServer.client
3017
+ <a id="gemserver-client"></a>
3018
3018
 
3019
- [GemServer.client](https://gemarcade.com/developer/api/server#gemserver-client)
3019
+ ## GemServer.client
3020
3020
 
3021
3021
  ```ts
3022
3022
  readonly client: GemClient
3023
3023
  ```
3024
3024
 
3025
- The HTTP client underneath [GemServer.api](https://gemarcade.com/developer/api/server#gemserver-api), for calls the generated
3025
+ The HTTP client underneath [GemServer.api](#gemserver-api), for calls the generated
3026
3026
  surface does not cover.
3027
3027
 
3028
3028
  PREFER `api`. It is typed against the platform's description, so a shape
@@ -3030,9 +3030,9 @@ that changes becomes a build error in your bundle. This one takes a path and
3030
3030
  checks nothing — reach for it when you genuinely need something `api` has
3031
3031
  no method for, and treat that as worth telling us about.
3032
3032
 
3033
- ## GemServer.api
3033
+ <a id="gemserver-api"></a>
3034
3034
 
3035
- [GemServer.api](https://gemarcade.com/developer/api/server#gemserver-api)
3035
+ ## GemServer.api
3036
3036
 
3037
3037
  ```ts
3038
3038
  readonly api: ServerApi
@@ -3040,11 +3040,11 @@ readonly api: ServerApi
3040
3040
 
3041
3041
  Every platform operation your server can call. This is the one you want.
3042
3042
 
3043
- See [ServerApi](https://gemarcade.com/developer/api/server#serverapi).
3043
+ See [ServerApi](#serverapi).
3044
3044
 
3045
- ## GemServer.match
3045
+ <a id="gemserver-match"></a>
3046
3046
 
3047
- [GemServer.match](https://gemarcade.com/developer/api/server#gemserver-match)
3047
+ ## GemServer.match
3048
3048
 
3049
3049
  ```ts
3050
3050
  readonly match: { end: null }
@@ -3065,9 +3065,9 @@ that this server is the one allocated to the match, which the platform
3065
3065
  requires. On a platform image too old to offer that, `end()` throws rather
3066
3066
  than sending a request without the proof.
3067
3067
 
3068
- ## GemServer.match.end
3068
+ <a id="gemserver-match-end"></a>
3069
3069
 
3070
- [GemServer.match.end](https://gemarcade.com/developer/api/server#gemserver-match-end)
3070
+ ## GemServer.match.end
3071
3071
 
3072
3072
  ```ts
3073
3073
  end(signal?: AbortSignal): Promise<void>
@@ -3082,9 +3082,9 @@ Tell the platform this match is finished.
3082
3082
  **Throws.** When the platform image running this server predates
3083
3083
  `host.endMatch`, so the match cannot be ended from here.
3084
3084
 
3085
- ## GemServer.analytics
3085
+ <a id="gemserver-analytics"></a>
3086
3086
 
3087
- [GemServer.analytics](https://gemarcade.com/developer/api/server#gemserver-analytics)
3087
+ ## GemServer.analytics
3088
3088
 
3089
3089
  ```ts
3090
3090
  readonly analytics: ServerAnalytics
@@ -3101,12 +3101,12 @@ game may declare an event `sources: [server]` and shut the client out of it
3101
3101
  entirely.
3102
3102
 
3103
3103
  ⚠️ **Every event names its player, and `gem_*` is unavailable here** — see
3104
- [ServerAnalytics.track](https://gemarcade.com/developer/api/server#serveranalytics-track). And flush before the match ends: there is no
3104
+ [ServerAnalytics.track](#serveranalytics-track). And flush before the match ends: there is no
3105
3105
  page unload to get the tail out on.
3106
3106
 
3107
- ## GemServer.functions
3107
+ <a id="gemserver-functions"></a>
3108
3108
 
3109
- [GemServer.functions](https://gemarcade.com/developer/api/server#gemserver-functions)
3109
+ ## GemServer.functions
3110
3110
 
3111
3111
  ```ts
3112
3112
  readonly functions: FunctionsApi
@@ -3119,9 +3119,9 @@ The call arrives as this server, not as a player: your function's
3119
3119
  `get-context` says so, and a server's per-minute budget is its own. It runs
3120
3120
  the function promoted on the channel this match runs on.
3121
3121
 
3122
- ## GemServer.challenges
3122
+ <a id="gemserver-challenges"></a>
3123
3123
 
3124
- [GemServer.challenges](https://gemarcade.com/developer/api/server#gemserver-challenges)
3124
+ ## GemServer.challenges
3125
3125
 
3126
3126
  ```ts
3127
3127
  readonly challenges: ServerChallenges
@@ -3133,11 +3133,11 @@ board that player was actually given.
3133
3133
 
3134
3134
  A period is the PLAYER's, not the calendar's: it ends at their own
3135
3135
  midnight. That is why this takes a player id where the browser's
3136
- `gem.challenges` takes none. See [ServerChallenges.list](https://gemarcade.com/developer/api/server#serverchallenges-list).
3136
+ `gem.challenges` takes none. See [ServerChallenges.list](#serverchallenges-list).
3137
3137
 
3138
- ## GemServer.room
3138
+ <a id="gemserver-room"></a>
3139
3139
 
3140
- [GemServer.room](https://gemarcade.com/developer/api/server#gemserver-room)
3140
+ ## GemServer.room
3141
3141
 
3142
3142
  ```ts
3143
3143
  readonly room?: { get: (options?: { ifNoneMatch?: string; signal?: AbortSignal }) => Promise<VersionedResponse<GameRoomResponseV2>>; args: (options?: { signal?: AbortSignal }) => Promise<RoomArgsResponse> }
@@ -3151,9 +3151,9 @@ the type says it might be. `gem.room?.get()` is the shape to write. When it
3151
3151
  is absent the same reads are still available through `api` if you know the
3152
3152
  room id from somewhere else.
3153
3153
 
3154
- ## GemServer.room.get
3154
+ <a id="gemserver-room-get"></a>
3155
3155
 
3156
- [GemServer.room.get](https://gemarcade.com/developer/api/server#gemserver-room-get)
3156
+ ## GemServer.room.get
3157
3157
 
3158
3158
  ```ts
3159
3159
  get: (options?: { ifNoneMatch?: string; signal?: AbortSignal }) => Promise<VersionedResponse<GameRoomResponseV2>>
@@ -3166,9 +3166,9 @@ avatar.
3166
3166
  This is what you want at `start()`, and it is a superset of what the older
3167
3167
  roster push carried.
3168
3168
 
3169
- ## GemServer.room.args
3169
+ <a id="gemserver-room-args"></a>
3170
3170
 
3171
- [GemServer.room.args](https://gemarcade.com/developer/api/server#gemserver-room-args)
3171
+ ## GemServer.room.args
3172
3172
 
3173
3173
  ```ts
3174
3174
  args: (options?: { signal?: AbortSignal }) => Promise<RoomArgsResponse>
@@ -3181,9 +3181,9 @@ A room whose template declares no settings answers 404
3181
3181
  settings" and "this room's settings are empty" are different answers and
3182
3182
  the API keeps them apart.
3183
3183
 
3184
- ## GemServer.dispose
3184
+ <a id="gemserver-dispose"></a>
3185
3185
 
3186
- [GemServer.dispose](https://gemarcade.com/developer/api/server#gemserver-dispose)
3186
+ ## GemServer.dispose
3187
3187
 
3188
3188
  ```ts
3189
3189
  dispose(): void
@@ -3191,13 +3191,13 @@ dispose(): void
3191
3191
 
3192
3192
  Stop listening for credential updates.
3193
3193
 
3194
- You rarely need it: a bundle built with [defineServer](https://gemarcade.com/developer/api/server#defineserver) is disposed for
3194
+ You rarely need it: a bundle built with [defineServer](#defineserver) is disposed for
3195
3195
  you when the match ends. Call it in a test, or if you built the server
3196
3196
  yourself and are tearing it down.
3197
3197
 
3198
- ## ServerTokenGrant.token
3198
+ <a id="servertokengrant-token"></a>
3199
3199
 
3200
- [ServerTokenGrant.token](https://gemarcade.com/developer/api/server#servertokengrant-token)
3200
+ ## ServerTokenGrant.token
3201
3201
 
3202
3202
  ```ts
3203
3203
  readonly token: string
@@ -3205,9 +3205,9 @@ readonly token: string
3205
3205
 
3206
3206
  The credential to present. Never log it and never send it on.
3207
3207
 
3208
- ## ServerTokenGrant.expiresAt
3208
+ <a id="servertokengrant-expiresat"></a>
3209
3209
 
3210
- [ServerTokenGrant.expiresAt](https://gemarcade.com/developer/api/server#servertokengrant-expiresat)
3210
+ ## ServerTokenGrant.expiresAt
3211
3211
 
3212
3212
  ```ts
3213
3213
  readonly expiresAt: number
@@ -3215,9 +3215,9 @@ readonly expiresAt: number
3215
3215
 
3216
3216
  When it expires, in seconds since the Unix epoch.
3217
3217
 
3218
- ## ServerBundleHost.matchId
3218
+ <a id="serverbundlehost-matchid"></a>
3219
3219
 
3220
- [ServerBundleHost.matchId](https://gemarcade.com/developer/api/server#serverbundlehost-matchid)
3220
+ ## ServerBundleHost.matchId
3221
3221
 
3222
3222
  ```ts
3223
3223
  readonly matchId: string
@@ -3225,9 +3225,9 @@ readonly matchId: string
3225
3225
 
3226
3226
  The match this server was allocated for.
3227
3227
 
3228
- ## ServerBundleHost.gameId
3228
+ <a id="serverbundlehost-gameid"></a>
3229
3229
 
3230
- [ServerBundleHost.gameId](https://gemarcade.com/developer/api/server#serverbundlehost-gameid)
3230
+ ## ServerBundleHost.gameId
3231
3231
 
3232
3232
  ```ts
3233
3233
  readonly gameId: string
@@ -3235,9 +3235,9 @@ readonly gameId: string
3235
3235
 
3236
3236
  The game it belongs to.
3237
3237
 
3238
- ## ServerBundleHost.channel
3238
+ <a id="serverbundlehost-channel"></a>
3239
3239
 
3240
- [ServerBundleHost.channel](https://gemarcade.com/developer/api/server#serverbundlehost-channel)
3240
+ ## ServerBundleHost.channel
3241
3241
 
3242
3242
  ```ts
3243
3243
  readonly channel: string
@@ -3245,32 +3245,32 @@ readonly channel: string
3245
3245
 
3246
3246
  The channel it was allocated on — `dev`, `test` or `release`.
3247
3247
 
3248
- ## ServerBundleHost.gameRoomId
3248
+ <a id="serverbundlehost-gameroomid"></a>
3249
3249
 
3250
- [ServerBundleHost.gameRoomId](https://gemarcade.com/developer/api/server#serverbundlehost-gameroomid)
3250
+ ## ServerBundleHost.gameRoomId
3251
3251
 
3252
3252
  ```ts
3253
3253
  readonly gameRoomId?: string
3254
3254
  ```
3255
3255
 
3256
3256
  The room this match came from. MAY BE ABSENT on older platform images, so
3257
- check before using it — that absence is also why [GemServer.room](https://gemarcade.com/developer/api/server#gemserver-room) can
3257
+ check before using it — that absence is also why [GemServer.room](#gemserver-room) can
3258
3258
  be missing.
3259
3259
 
3260
- ## ServerBundleHost.apiBaseUrl
3260
+ <a id="serverbundlehost-apibaseurl"></a>
3261
3261
 
3262
- [ServerBundleHost.apiBaseUrl](https://gemarcade.com/developer/api/server#serverbundlehost-apibaseurl)
3262
+ ## ServerBundleHost.apiBaseUrl
3263
3263
 
3264
3264
  ```ts
3265
3265
  readonly apiBaseUrl?: string
3266
3266
  ```
3267
3267
 
3268
3268
  Where to send API calls. Absent on older platform images, which is the case
3269
- [createGemServer](https://gemarcade.com/developer/api/server#creategemserver) refuses at startup.
3269
+ [createGemServer](#creategemserver) refuses at startup.
3270
3270
 
3271
- ## ServerBundleHost.serverToken
3271
+ <a id="serverbundlehost-servertoken"></a>
3272
3272
 
3273
- [ServerBundleHost.serverToken](https://gemarcade.com/developer/api/server#serverbundlehost-servertoken)
3273
+ ## ServerBundleHost.serverToken
3274
3274
 
3275
3275
  ```ts
3276
3276
  serverToken(): ServerTokenGrant | undefined
@@ -3278,9 +3278,9 @@ serverToken(): ServerTokenGrant | undefined
3278
3278
 
3279
3279
  The credential held right now, if the platform delivered one.
3280
3280
 
3281
- ## ServerBundleHost.onServerToken
3281
+ <a id="serverbundlehost-onservertoken"></a>
3282
3282
 
3283
- [ServerBundleHost.onServerToken](https://gemarcade.com/developer/api/server#serverbundlehost-onservertoken)
3283
+ ## ServerBundleHost.onServerToken
3284
3284
 
3285
3285
  ```ts
3286
3286
  onServerToken(listener: (grant: ServerTokenGrant) => void): () => void
@@ -3291,15 +3291,15 @@ Be told when the credential is replaced.
3291
3291
  Called straight away if one is already held, and again on every refresh.
3292
3292
  Returns a function that stops the notifications.
3293
3293
 
3294
- You rarely call this. [createGemServer](https://gemarcade.com/developer/api/server#creategemserver) subscribes for you.
3294
+ You rarely call this. [createGemServer](#creategemserver) subscribes for you.
3295
3295
 
3296
3296
  | Parameter | Type |
3297
3297
  | --- | --- |
3298
3298
  | `listener` | `(grant: ServerTokenGrant) => void` |
3299
3299
 
3300
- ## SendOptions.reliable
3300
+ <a id="sendoptions-reliable"></a>
3301
3301
 
3302
- [SendOptions.reliable](https://gemarcade.com/developer/api/server#sendoptions-reliable)
3302
+ ## SendOptions.reliable
3303
3303
 
3304
3304
  ```ts
3305
3305
  readonly reliable?: boolean
@@ -3311,9 +3311,9 @@ Ask for the message to arrive. Defaults to true.
3311
3311
  that a later one supersedes. A transport is free to deliver it reliably
3312
3312
  anyway; the receiver is still told what you asked for.
3313
3313
 
3314
- ## SendOptions.ordered
3314
+ <a id="sendoptions-ordered"></a>
3315
3315
 
3316
- [SendOptions.ordered](https://gemarcade.com/developer/api/server#sendoptions-ordered)
3316
+ ## SendOptions.ordered
3317
3317
 
3318
3318
  ```ts
3319
3319
  readonly ordered?: boolean
@@ -3321,9 +3321,9 @@ readonly ordered?: boolean
3321
3321
 
3322
3322
  Ask for messages to arrive in the order you sent them. Defaults to true.
3323
3323
 
3324
- ## BroadcastOptions.reliable
3324
+ <a id="broadcastoptions-reliable"></a>
3325
3325
 
3326
- [BroadcastOptions.reliable](https://gemarcade.com/developer/api/server#broadcastoptions-reliable)
3326
+ ## BroadcastOptions.reliable
3327
3327
 
3328
3328
  ```ts
3329
3329
  readonly reliable?: boolean
@@ -3335,9 +3335,9 @@ Ask for the message to arrive. Defaults to true.
3335
3335
  that a later one supersedes. A transport is free to deliver it reliably
3336
3336
  anyway; the receiver is still told what you asked for.
3337
3337
 
3338
- ## BroadcastOptions.ordered
3338
+ <a id="broadcastoptions-ordered"></a>
3339
3339
 
3340
- [BroadcastOptions.ordered](https://gemarcade.com/developer/api/server#broadcastoptions-ordered)
3340
+ ## BroadcastOptions.ordered
3341
3341
 
3342
3342
  ```ts
3343
3343
  readonly ordered?: boolean
@@ -3345,9 +3345,9 @@ readonly ordered?: boolean
3345
3345
 
3346
3346
  Ask for messages to arrive in the order you sent them. Defaults to true.
3347
3347
 
3348
- ## BroadcastOptions.except
3348
+ <a id="broadcastoptions-except"></a>
3349
3349
 
3350
- [BroadcastOptions.except](https://gemarcade.com/developer/api/server#broadcastoptions-except)
3350
+ ## BroadcastOptions.except
3351
3351
 
3352
3352
  ```ts
3353
3353
  readonly except?: string
@@ -3355,9 +3355,9 @@ readonly except?: string
3355
3355
 
3356
3356
  A player to leave out — usually the one whose action caused the broadcast.
3357
3357
 
3358
- ## GemServerHostProfile.displayName
3358
+ <a id="gemserverhostprofile-displayname"></a>
3359
3359
 
3360
- [GemServerHostProfile.displayName](https://gemarcade.com/developer/api/server#gemserverhostprofile-displayname)
3360
+ ## GemServerHostProfile.displayName
3361
3361
 
3362
3362
  ```ts
3363
3363
  readonly displayName?: string
@@ -3365,23 +3365,23 @@ readonly displayName?: string
3365
3365
 
3366
3366
  The player's chosen name, as a plain string.
3367
3367
 
3368
- PREFER [GemMatchServer.profile](https://gemarcade.com/developer/api/server#gemmatchserver-profile), which gives you the same name as
3369
- [PlayerText](https://gemarcade.com/developer/api/server#playertext) — a type that reminds you a stranger wrote it.
3368
+ PREFER [GemMatchServer.profile](#gemmatchserver-profile), which gives you the same name as
3369
+ [PlayerText](#playertext) — a type that reminds you a stranger wrote it.
3370
3370
 
3371
- ## GemServerHostProfile.displayTag
3371
+ <a id="gemserverhostprofile-displaytag"></a>
3372
3372
 
3373
- [GemServerHostProfile.displayTag](https://gemarcade.com/developer/api/server#gemserverhostprofile-displaytag)
3373
+ ## GemServerHostProfile.displayTag
3374
3374
 
3375
3375
  ```ts
3376
3376
  readonly displayTag?: string
3377
3377
  ```
3378
3378
 
3379
3379
  The precomposed `Name#0001`, as a plain string. It contains the name, so
3380
- PREFER [GemMatchServer.profile](https://gemarcade.com/developer/api/server#gemmatchserver-profile), which gives it as [PlayerText](https://gemarcade.com/developer/api/server#playertext).
3380
+ PREFER [GemMatchServer.profile](#gemmatchserver-profile), which gives it as [PlayerText](#playertext).
3381
3381
 
3382
- ## GemServerHostProfile.discriminator
3382
+ <a id="gemserverhostprofile-discriminator"></a>
3383
3383
 
3384
- [GemServerHostProfile.discriminator](https://gemarcade.com/developer/api/server#gemserverhostprofile-discriminator)
3384
+ ## GemServerHostProfile.discriminator
3385
3385
 
3386
3386
  ```ts
3387
3387
  readonly discriminator?: string
@@ -3389,9 +3389,9 @@ readonly discriminator?: string
3389
3389
 
3390
3390
  The disambiguating suffix, when the player has one.
3391
3391
 
3392
- ## GemServerHost.matchId
3392
+ <a id="gemserverhost-matchid"></a>
3393
3393
 
3394
- [GemServerHost.matchId](https://gemarcade.com/developer/api/server#gemserverhost-matchid)
3394
+ ## GemServerHost.matchId
3395
3395
 
3396
3396
  ```ts
3397
3397
  readonly matchId: string
@@ -3399,9 +3399,9 @@ readonly matchId: string
3399
3399
 
3400
3400
  The match this server was allocated for.
3401
3401
 
3402
- ## GemServerHost.gameId
3402
+ <a id="gemserverhost-gameid"></a>
3403
3403
 
3404
- [GemServerHost.gameId](https://gemarcade.com/developer/api/server#gemserverhost-gameid)
3404
+ ## GemServerHost.gameId
3405
3405
 
3406
3406
  ```ts
3407
3407
  readonly gameId: string
@@ -3409,9 +3409,9 @@ readonly gameId: string
3409
3409
 
3410
3410
  The game it belongs to.
3411
3411
 
3412
- ## GemServerHost.channel
3412
+ <a id="gemserverhost-channel"></a>
3413
3413
 
3414
- [GemServerHost.channel](https://gemarcade.com/developer/api/server#gemserverhost-channel)
3414
+ ## GemServerHost.channel
3415
3415
 
3416
3416
  ```ts
3417
3417
  readonly channel: string
@@ -3419,32 +3419,32 @@ readonly channel: string
3419
3419
 
3420
3420
  The channel it was allocated on — `dev`, `test` or `release`.
3421
3421
 
3422
- ## GemServerHost.gameRoomId
3422
+ <a id="gemserverhost-gameroomid"></a>
3423
3423
 
3424
- [GemServerHost.gameRoomId](https://gemarcade.com/developer/api/server#gemserverhost-gameroomid)
3424
+ ## GemServerHost.gameRoomId
3425
3425
 
3426
3426
  ```ts
3427
3427
  readonly gameRoomId?: string
3428
3428
  ```
3429
3429
 
3430
3430
  The room this match came from. MAY BE ABSENT on older platform images, so
3431
- check before using it — that absence is also why [GemServer.room](https://gemarcade.com/developer/api/server#gemserver-room) can
3431
+ check before using it — that absence is also why [GemServer.room](#gemserver-room) can
3432
3432
  be missing.
3433
3433
 
3434
- ## GemServerHost.apiBaseUrl
3434
+ <a id="gemserverhost-apibaseurl"></a>
3435
3435
 
3436
- [GemServerHost.apiBaseUrl](https://gemarcade.com/developer/api/server#gemserverhost-apibaseurl)
3436
+ ## GemServerHost.apiBaseUrl
3437
3437
 
3438
3438
  ```ts
3439
3439
  readonly apiBaseUrl?: string
3440
3440
  ```
3441
3441
 
3442
3442
  Where to send API calls. Absent on older platform images, which is the case
3443
- [createGemServer](https://gemarcade.com/developer/api/server#creategemserver) refuses at startup.
3443
+ [createGemServer](#creategemserver) refuses at startup.
3444
3444
 
3445
- ## GemServerHost.serverToken
3445
+ <a id="gemserverhost-servertoken"></a>
3446
3446
 
3447
- [GemServerHost.serverToken](https://gemarcade.com/developer/api/server#gemserverhost-servertoken)
3447
+ ## GemServerHost.serverToken
3448
3448
 
3449
3449
  ```ts
3450
3450
  serverToken(): ServerTokenGrant | undefined
@@ -3452,9 +3452,9 @@ serverToken(): ServerTokenGrant | undefined
3452
3452
 
3453
3453
  The credential held right now, if the platform delivered one.
3454
3454
 
3455
- ## GemServerHost.onServerToken
3455
+ <a id="gemserverhost-onservertoken"></a>
3456
3456
 
3457
- [GemServerHost.onServerToken](https://gemarcade.com/developer/api/server#gemserverhost-onservertoken)
3457
+ ## GemServerHost.onServerToken
3458
3458
 
3459
3459
  ```ts
3460
3460
  onServerToken(listener: (grant: ServerTokenGrant) => void): () => void
@@ -3465,15 +3465,15 @@ Be told when the credential is replaced.
3465
3465
  Called straight away if one is already held, and again on every refresh.
3466
3466
  Returns a function that stops the notifications.
3467
3467
 
3468
- You rarely call this. [createGemServer](https://gemarcade.com/developer/api/server#creategemserver) subscribes for you.
3468
+ You rarely call this. [createGemServer](#creategemserver) subscribes for you.
3469
3469
 
3470
3470
  | Parameter | Type |
3471
3471
  | --- | --- |
3472
3472
  | `listener` | `(grant: ServerTokenGrant) => void` |
3473
3473
 
3474
- ## GemServerHost.sizeClass
3474
+ <a id="gemserverhost-sizeclass"></a>
3475
3475
 
3476
- [GemServerHost.sizeClass](https://gemarcade.com/developer/api/server#gemserverhost-sizeclass)
3476
+ ## GemServerHost.sizeClass
3477
3477
 
3478
3478
  ```ts
3479
3479
  readonly sizeClass: string
@@ -3481,9 +3481,9 @@ readonly sizeClass: string
3481
3481
 
3482
3482
  Which size of machine this server was allocated on.
3483
3483
 
3484
- ## GemServerHost.roomArgs
3484
+ <a id="gemserverhost-roomargs"></a>
3485
3485
 
3486
- [GemServerHost.roomArgs](https://gemarcade.com/developer/api/server#gemserverhost-roomargs)
3486
+ ## GemServerHost.roomArgs
3487
3487
 
3488
3488
  ```ts
3489
3489
  readonly roomArgs?: Readonly<Record<string, RoomArgValue>>
@@ -3498,7 +3498,7 @@ and `4.0.0` is the earliest release that may take it off the type. Whether
3498
3498
  a server keeps sending a value is a separate question, decided where the
3499
3499
  dedicated-server image is built rather than by a package release — so check
3500
3500
  for the room read and keep this as the fallback. Both halves are in
3501
- [the deprecation policy](https://gemarcade.com/developer/docs/deprecation-policy).
3501
+ [the deprecation policy](../deprecation-policy.md).
3502
3502
 
3503
3503
  The room's settings as they were at match start.
3504
3504
 
@@ -3506,9 +3506,9 @@ The room's settings as they were at match start.
3506
3506
  room type declares no settings, or the platform image is older than this
3507
3507
  member.
3508
3508
 
3509
- ## GemServerHost.roster
3509
+ <a id="gemserverhost-roster"></a>
3510
3510
 
3511
- [GemServerHost.roster](https://gemarcade.com/developer/api/server#gemserverhost-roster)
3511
+ ## GemServerHost.roster
3512
3512
 
3513
3513
  ```ts
3514
3514
  readonly roster: readonly string[]
@@ -3522,7 +3522,7 @@ and `4.0.0` is the earliest release that may take it off the type. Whether
3522
3522
  a server keeps sending a value is a separate question, decided where the
3523
3523
  dedicated-server image is built rather than by a package release — so check
3524
3524
  for the room read and keep this as the fallback. Both halves are in
3525
- [the deprecation policy](https://gemarcade.com/developer/docs/deprecation-policy).
3525
+ [the deprecation policy](../deprecation-policy.md).
3526
3526
 
3527
3527
  Who was in the room when this server was allocated.
3528
3528
 
@@ -3530,9 +3530,9 @@ A SIZING HINT, NOT A GUEST LIST. It is never revised, and it does not decide
3530
3530
  who may connect — the platform admits players by credential. A player who
3531
3531
  joined after allocation holds a valid one and is not in here.
3532
3532
 
3533
- ## GemServerHost.players
3533
+ <a id="gemserverhost-players"></a>
3534
3534
 
3535
- [GemServerHost.players](https://gemarcade.com/developer/api/server#gemserverhost-players)
3535
+ ## GemServerHost.players
3536
3536
 
3537
3537
  ```ts
3538
3538
  players(): string[]
@@ -3540,9 +3540,9 @@ players(): string[]
3540
3540
 
3541
3541
  The player ids connected right now, as a fresh array each call.
3542
3542
 
3543
- ## GemServerHost.profile
3543
+ <a id="gemserverhost-profile"></a>
3544
3544
 
3545
- [GemServerHost.profile](https://gemarcade.com/developer/api/server#gemserverhost-profile)
3545
+ ## GemServerHost.profile
3546
3546
 
3547
3547
  ```ts
3548
3548
  profile(playerId: string): GemServerHostProfile | undefined
@@ -3552,15 +3552,15 @@ A player's display identity, if the platform resolved one.
3552
3552
 
3553
3553
  OPTIONAL TWICE OVER: the method itself may be absent on an older platform
3554
3554
  image, and it may answer `undefined` for a player it has nothing for.
3555
- [GemMatchServer.profile](https://gemarcade.com/developer/api/server#gemmatchserver-profile) collapses both into one `null`.
3555
+ [GemMatchServer.profile](#gemmatchserver-profile) collapses both into one `null`.
3556
3556
 
3557
3557
  | Parameter | Type |
3558
3558
  | --- | --- |
3559
3559
  | `playerId` | `string` |
3560
3560
 
3561
- ## GemServerHost.playerNumber
3561
+ <a id="gemserverhost-playernumber"></a>
3562
3562
 
3563
- [GemServerHost.playerNumber](https://gemarcade.com/developer/api/server#gemserverhost-playernumber)
3563
+ ## GemServerHost.playerNumber
3564
3564
 
3565
3565
  ```ts
3566
3566
  playerNumber(playerId: string): number | undefined
@@ -3575,9 +3575,9 @@ May be absent as a method on an older platform image, and answers
3575
3575
  | --- | --- |
3576
3576
  | `playerId` | `string` |
3577
3577
 
3578
- ## GemServerHost.send
3578
+ <a id="gemserverhost-send"></a>
3579
3579
 
3580
- [GemServerHost.send](https://gemarcade.com/developer/api/server#gemserverhost-send)
3580
+ ## GemServerHost.send
3581
3581
 
3582
3582
  ```ts
3583
3583
  send(playerId: string, data: string | Uint8Array<ArrayBufferLike>, options?: SendOptions): void
@@ -3590,18 +3590,18 @@ receiver is told which.
3590
3590
  | --- | --- | --- |
3591
3591
  | `playerId` | `string` | |
3592
3592
  | `data` | `string \| Uint8Array<ArrayBufferLike>` | |
3593
- | `options` | `SendOptions` | What delivery you are asking for — see [SendOptions](https://gemarcade.com/developer/api/server#sendoptions).
3593
+ | `options` | `SendOptions` | What delivery you are asking for — see [SendOptions](#sendoptions).
3594
3594
  Pass them freely: an older platform image ignores them harmlessly. |
3595
3595
 
3596
- ## GemServerHost.broadcast
3596
+ <a id="gemserverhost-broadcast"></a>
3597
3597
 
3598
- [GemServerHost.broadcast](https://gemarcade.com/developer/api/server#gemserverhost-broadcast)
3598
+ ## GemServerHost.broadcast
3599
3599
 
3600
3600
  ```ts
3601
3601
  broadcast(data: string | Uint8Array<ArrayBufferLike>, options?: BroadcastOptions): void
3602
3602
  ```
3603
3603
 
3604
- Send to every connected player. See [BroadcastOptions](https://gemarcade.com/developer/api/server#broadcastoptions) for how, and
3604
+ Send to every connected player. See [BroadcastOptions](#broadcastoptions) for how, and
3605
3605
  for leaving one out.
3606
3606
 
3607
3607
  | Parameter | Type |
@@ -3609,9 +3609,9 @@ for leaving one out.
3609
3609
  | `data` | `string \| Uint8Array<ArrayBufferLike>` |
3610
3610
  | `options` | `BroadcastOptions` |
3611
3611
 
3612
- ## GemServerHost.kick
3612
+ <a id="gemserverhost-kick"></a>
3613
3613
 
3614
- [GemServerHost.kick](https://gemarcade.com/developer/api/server#gemserverhost-kick)
3614
+ ## GemServerHost.kick
3615
3615
 
3616
3616
  ```ts
3617
3617
  kick(playerId: string, reason?: string): void
@@ -3624,9 +3624,9 @@ Disconnect a player. `reason` is shown to them and recorded in the logs.
3624
3624
  | `playerId` | `string` |
3625
3625
  | `reason` | `string` |
3626
3626
 
3627
- ## GemServerHost.endMatch
3627
+ <a id="gemserverhost-endmatch"></a>
3628
3628
 
3629
- [GemServerHost.endMatch](https://gemarcade.com/developer/api/server#gemserverhost-endmatch)
3629
+ ## GemServerHost.endMatch
3630
3630
 
3631
3631
  ```ts
3632
3632
  endMatch(): Promise<"ok" | "already_ended" | "refused" | "failed" | string & object>
@@ -3643,9 +3643,9 @@ Resolves with what the platform half did: `ok`, `already_ended` (this
3643
3643
  match was already over, or you asked twice), `refused` (the allocation was
3644
3644
  not ready yet) or `failed` (the platform could not be reached).
3645
3645
 
3646
- ## GemServerHost.log
3646
+ <a id="gemserverhost-log"></a>
3647
3647
 
3648
- [GemServerHost.log](https://gemarcade.com/developer/api/server#gemserverhost-log)
3648
+ ## GemServerHost.log
3649
3649
 
3650
3650
  ```ts
3651
3651
  log(message: string): void
@@ -3661,34 +3661,34 @@ about your bundle rather than as a log.
3661
3661
  | --- | --- |
3662
3662
  | `message` | `string` |
3663
3663
 
3664
- ## GemServerHost.warn
3664
+ <a id="gemserverhost-warn"></a>
3665
3665
 
3666
- [GemServerHost.warn](https://gemarcade.com/developer/api/server#gemserverhost-warn)
3666
+ ## GemServerHost.warn
3667
3667
 
3668
3668
  ```ts
3669
3669
  warn(message: string): void
3670
3670
  ```
3671
3671
 
3672
- Like [GemServerHost.log](https://gemarcade.com/developer/api/server#gemserverhost-log), marked as a warning.
3672
+ Like [GemServerHost.log](#gemserverhost-log), marked as a warning.
3673
3673
 
3674
3674
  | Parameter | Type |
3675
3675
  | --- | --- |
3676
3676
  | `message` | `string` |
3677
3677
 
3678
- ## HostTokenSourceOptions.clock
3678
+ <a id="hosttokensourceoptions-clock"></a>
3679
3679
 
3680
- [HostTokenSourceOptions.clock](https://gemarcade.com/developer/api/server#hosttokensourceoptions-clock)
3680
+ ## HostTokenSourceOptions.clock
3681
3681
 
3682
3682
  ```ts
3683
3683
  readonly clock: Clock
3684
3684
  ```
3685
3685
 
3686
3686
  Where time comes from — used for expiry and for the wait below. Pass a
3687
- [Clock](https://gemarcade.com/developer/api/server#clock) in a test.
3687
+ [Clock](#clock) in a test.
3688
3688
 
3689
- ## HostTokenSourceOptions.timeoutMs
3689
+ <a id="hosttokensourceoptions-timeoutms"></a>
3690
3690
 
3691
- [HostTokenSourceOptions.timeoutMs](https://gemarcade.com/developer/api/server#hosttokensourceoptions-timeoutms)
3691
+ ## HostTokenSourceOptions.timeoutMs
3692
3692
 
3693
3693
  ```ts
3694
3694
  readonly timeoutMs?: number
@@ -3697,20 +3697,20 @@ readonly timeoutMs?: number
3697
3697
  How long to wait for the platform to deliver a credential before giving up
3698
3698
  on a call. Defaults to 10 seconds.
3699
3699
 
3700
- ## HostTokenSourceOptions.onStateChange
3700
+ <a id="hosttokensourceoptions-onstatechange"></a>
3701
3701
 
3702
- [HostTokenSourceOptions.onStateChange](https://gemarcade.com/developer/api/server#hosttokensourceoptions-onstatechange)
3702
+ ## HostTokenSourceOptions.onStateChange
3703
3703
 
3704
3704
  ```ts
3705
3705
  readonly onStateChange?: (state: AuthState, reason?: AuthFailure) => void
3706
3706
  ```
3707
3707
 
3708
3708
  Called when the ability to make authenticated calls changes. See
3709
- [AuthState](https://gemarcade.com/developer/api/server#authstate) for what to branch on.
3709
+ [AuthState](#authstate) for what to branch on.
3710
3710
 
3711
- ## HostTokenSource.dispose
3711
+ <a id="hosttokensource-dispose"></a>
3712
3712
 
3713
- [HostTokenSource.dispose](https://gemarcade.com/developer/api/server#hosttokensource-dispose)
3713
+ ## HostTokenSource.dispose
3714
3714
 
3715
3715
  ```ts
3716
3716
  dispose(): void
@@ -3721,9 +3721,9 @@ Stop refreshing the credential.
3721
3721
  CALL IT AT SHUTDOWN. A pending refresh timer keeps the process alive, so a
3722
3722
  bundle that skips this looks like it has hung rather than exited.
3723
3723
 
3724
- ## GemServerBundle.init
3724
+ <a id="gemserverbundle-init"></a>
3725
3725
 
3726
- [GemServerBundle.init](https://gemarcade.com/developer/api/server#gemserverbundle-init)
3726
+ ## GemServerBundle.init
3727
3727
 
3728
3728
  ```ts
3729
3729
  init(): void | Promise<void>
@@ -3734,9 +3734,9 @@ Once, before any player arrives. Set your world up here.
3734
3734
  YOU HAVE 60 SECONDS. Throwing fails the match, so do not start anything here
3735
3735
  you are not prepared to have fail loudly.
3736
3736
 
3737
- ## GemServerBundle.join
3737
+ <a id="gemserverbundle-join"></a>
3738
3738
 
3739
- [GemServerBundle.join](https://gemarcade.com/developer/api/server#gemserverbundle-join)
3739
+ ## GemServerBundle.join
3740
3740
 
3741
3741
  ```ts
3742
3742
  join(playerId: string, meta?: { playerNumber?: number; transport?: string; rejoin?: boolean; superseded?: boolean }): void | Promise<void>
@@ -3778,9 +3778,9 @@ nothing was superseded, and on platform images that predate it.
3778
3778
  back, and whether they replaced a connection that was still up. MAY BE
3779
3779
  ABSENT on older platform images, so default before use. |
3780
3780
 
3781
- ## GemServerBundle.leave
3781
+ <a id="gemserverbundle-leave"></a>
3782
3782
 
3783
- [GemServerBundle.leave](https://gemarcade.com/developer/api/server#gemserverbundle-leave)
3783
+ ## GemServerBundle.leave
3784
3784
 
3785
3785
  ```ts
3786
3786
  leave(playerId: string, meta?: { cause?: string }): void | Promise<void>
@@ -3806,9 +3806,9 @@ on the strength of the cause alone.
3806
3806
  | `playerId` | `string` | Who left. |
3807
3807
  | `meta` | `{ cause?: string }` | Why, when the platform knows. May be absent. |
3808
3808
 
3809
- ## GemServerBundle.message
3809
+ <a id="gemserverbundle-message"></a>
3810
3810
 
3811
- [GemServerBundle.message](https://gemarcade.com/developer/api/server#gemserverbundle-message)
3811
+ ## GemServerBundle.message
3812
3812
 
3813
3813
  ```ts
3814
3814
  message(playerId: string, data: string | Uint8Array<ArrayBufferLike>, isText: boolean, meta?: { reliable?: boolean; ordered?: boolean; sessionId?: number; playerNumber?: number }): void | Promise<void>
@@ -3825,9 +3825,9 @@ A player sent you something.
3825
3825
  | `meta` | `{ reliable?: boolean; ordered?: boolean; sessionId?: number; playerNumber?: number }` | What the sender ASKED FOR — `reliable` and `ordered` — carried
3826
3826
  across whatever the transport actually did. May be absent. |
3827
3827
 
3828
- ## GemServerBundle.shutdown
3828
+ <a id="gemserverbundle-shutdown"></a>
3829
3829
 
3830
- [GemServerBundle.shutdown](https://gemarcade.com/developer/api/server#gemserverbundle-shutdown)
3830
+ ## GemServerBundle.shutdown
3831
3831
 
3832
3832
  ```ts
3833
3833
  shutdown(info?: ServerShutdownInfo): void | Promise<void>
@@ -3843,7 +3843,7 @@ is reclaiming the machine: up to 2 hours, so a match with players still in it
3843
3843
  plays out rather than dying mid-game.
3844
3844
 
3845
3845
  `info.reason` says which, on platform images that report it — see
3846
- [ServerShutdownInfo](https://gemarcade.com/developer/api/server#servershutdowninfo). On a reclaim (`signal`) do NOT end the match:
3846
+ [ServerShutdownInfo](#servershutdowninfo). On a reclaim (`signal`) do NOT end the match:
3847
3847
  the players are still in it, and ending it here drops every one of them.
3848
3848
 
3849
3849
  ```js
@@ -3858,11 +3858,11 @@ async shutdown(info) {
3858
3858
  | `info` | `ServerShutdownInfo` | Why, when the platform says. ABSENT on older platform images,
3859
3859
  so default before use. |
3860
3860
 
3861
- **Guides.** [server-bundles](https://gemarcade.com/developer/docs/server-bundles#how-long-your-server-lives)
3861
+ **Guides.** [server-bundles](../server-bundles.md#how-long-your-server-lives)
3862
3862
 
3863
- ## ServerShutdownInfo.reason
3863
+ <a id="servershutdowninfo-reason"></a>
3864
3864
 
3865
- [ServerShutdownInfo.reason](https://gemarcade.com/developer/api/server#servershutdowninfo-reason)
3865
+ ## ServerShutdownInfo.reason
3866
3866
 
3867
3867
  ```ts
3868
3868
  readonly reason: string & object | "match_ended" | "idle_timeout" | "signal"
@@ -3870,9 +3870,9 @@ readonly reason: string & object | "match_ended" | "idle_timeout" | "signal"
3870
3870
 
3871
3871
  Which of the above.
3872
3872
 
3873
- ## ServerShutdownInfo.endedReason
3873
+ <a id="servershutdowninfo-endedreason"></a>
3874
3874
 
3875
- [ServerShutdownInfo.endedReason](https://gemarcade.com/developer/api/server#servershutdowninfo-endedreason)
3875
+ ## ServerShutdownInfo.endedReason
3876
3876
 
3877
3877
  ```ts
3878
3878
  readonly endedReason?: string
@@ -3882,9 +3882,9 @@ On `match_ended`: why, in the client's `matchEnded` vocabulary (`explicit`,
3882
3882
  `expired`, `all_left`, …), or `unknown` when the runtime passed a word this
3883
3883
  SDK does not know. Absent when the runtime passed nothing.
3884
3884
 
3885
- ## GemServerLifecycle.init
3885
+ <a id="gemserverlifecycle-init"></a>
3886
3886
 
3887
- [GemServerLifecycle.init](https://gemarcade.com/developer/api/server#gemserverlifecycle-init)
3887
+ ## GemServerLifecycle.init
3888
3888
 
3889
3889
  ```ts
3890
3890
  init(host: GemServerHost): Promise<void>
@@ -3897,9 +3897,9 @@ and where your own `init` runs.
3897
3897
  | --- | --- |
3898
3898
  | `host` | `GemServerHost` |
3899
3899
 
3900
- ## GemServerLifecycle.join
3900
+ <a id="gemserverlifecycle-join"></a>
3901
3901
 
3902
- [GemServerLifecycle.join](https://gemarcade.com/developer/api/server#gemserverlifecycle-join)
3902
+ ## GemServerLifecycle.join
3903
3903
 
3904
3904
  ```ts
3905
3905
  join(playerId: string, meta?: { playerNumber?: number; transport?: string; rejoin?: boolean; superseded?: boolean }): void | Promise<void>
@@ -3912,9 +3912,9 @@ Forwarded from your bundle, when you defined a `join`.
3912
3912
  | `playerId` | `string` |
3913
3913
  | `meta` | `{ playerNumber?: number; transport?: string; rejoin?: boolean; superseded?: boolean }` |
3914
3914
 
3915
- ## GemServerLifecycle.leave
3915
+ <a id="gemserverlifecycle-leave"></a>
3916
3916
 
3917
- [GemServerLifecycle.leave](https://gemarcade.com/developer/api/server#gemserverlifecycle-leave)
3917
+ ## GemServerLifecycle.leave
3918
3918
 
3919
3919
  ```ts
3920
3920
  leave(playerId: string, meta?: { cause?: string }): void | Promise<void>
@@ -3927,9 +3927,9 @@ Forwarded from your bundle, when you defined a `leave`.
3927
3927
  | `playerId` | `string` |
3928
3928
  | `meta` | `{ cause?: string }` |
3929
3929
 
3930
- ## GemServerLifecycle.message
3930
+ <a id="gemserverlifecycle-message"></a>
3931
3931
 
3932
- [GemServerLifecycle.message](https://gemarcade.com/developer/api/server#gemserverlifecycle-message)
3932
+ ## GemServerLifecycle.message
3933
3933
 
3934
3934
  ```ts
3935
3935
  message(playerId: string, data: string | Uint8Array<ArrayBufferLike>, isText: boolean, meta?: { reliable?: boolean; ordered?: boolean; sessionId?: number; playerNumber?: number }): void | Promise<void>
@@ -3944,9 +3944,9 @@ Forwarded from your bundle, when you defined a `message`.
3944
3944
  | `isText` | `boolean` |
3945
3945
  | `meta` | `{ reliable?: boolean; ordered?: boolean; sessionId?: number; playerNumber?: number }` |
3946
3946
 
3947
- ## GemServerLifecycle.shutdown
3947
+ <a id="gemserverlifecycle-shutdown"></a>
3948
3948
 
3949
- [GemServerLifecycle.shutdown](https://gemarcade.com/developer/api/server#gemserverlifecycle-shutdown)
3949
+ ## GemServerLifecycle.shutdown
3950
3950
 
3951
3951
  ```ts
3952
3952
  shutdown(info?: RuntimeShutdownPayload): Promise<void>
@@ -3959,11 +3959,11 @@ of its own to do, and it runs whether or not yours threw.
3959
3959
  | --- | --- | --- |
3960
3960
  | `info` | `RuntimeShutdownPayload` | What the runtime passed, when it passed anything: the raw
3961
3961
  `{ reason, platform_reason }` of its shutdown frame, which the SDK
3962
- narrows into [ServerShutdownInfo](https://gemarcade.com/developer/api/server#servershutdowninfo) for your bundle. |
3962
+ narrows into [ServerShutdownInfo](#servershutdowninfo) for your bundle. |
3963
3963
 
3964
- ## RuntimeShutdownPayload.reason
3964
+ <a id="runtimeshutdownpayload-reason"></a>
3965
3965
 
3966
- [RuntimeShutdownPayload.reason](https://gemarcade.com/developer/api/server#runtimeshutdownpayload-reason)
3966
+ ## RuntimeShutdownPayload.reason
3967
3967
 
3968
3968
  ```ts
3969
3969
  readonly reason?: string
@@ -3971,9 +3971,9 @@ readonly reason?: string
3971
3971
 
3972
3972
  The runtime's cause: `match_ended`, `idle_timeout`, `signal`.
3973
3973
 
3974
- ## RuntimeShutdownPayload.platform_reason
3974
+ <a id="runtimeshutdownpayload-platform_reason"></a>
3975
3975
 
3976
- [RuntimeShutdownPayload.platform_reason](https://gemarcade.com/developer/api/server#runtimeshutdownpayload-platform_reason)
3976
+ ## RuntimeShutdownPayload.platform_reason
3977
3977
 
3978
3978
  ```ts
3979
3979
  readonly platform_reason?: string
@@ -3981,9 +3981,9 @@ readonly platform_reason?: string
3981
3981
 
3982
3982
  On `match_ended`, the platform's reason as the runtime's frame spells it.
3983
3983
 
3984
- ## RuntimeShutdownPayload.endedReason
3984
+ <a id="runtimeshutdownpayload-endedreason"></a>
3985
3985
 
3986
- [RuntimeShutdownPayload.endedReason](https://gemarcade.com/developer/api/server#runtimeshutdownpayload-endedreason)
3986
+ ## RuntimeShutdownPayload.endedReason
3987
3987
 
3988
3988
  ```ts
3989
3989
  readonly endedReason?: string
@@ -3991,35 +3991,35 @@ readonly endedReason?: string
3991
3991
 
3992
3992
  The same, as the SDK spells it — for a lifecycle driven by hand.
3993
3993
 
3994
- ## GemServerProfile.displayName
3994
+ <a id="gemserverprofile-displayname"></a>
3995
3995
 
3996
- [GemServerProfile.displayName](https://gemarcade.com/developer/api/server#gemserverprofile-displayname)
3996
+ ## GemServerProfile.displayName
3997
3997
 
3998
3998
  ```ts
3999
3999
  readonly displayName: PlayerText
4000
4000
  ```
4001
4001
 
4002
- The name the player chose — so it is [PlayerText](https://gemarcade.com/developer/api/server#playertext), not a plain string.
4002
+ The name the player chose — so it is [PlayerText](#playertext), not a plain string.
4003
4003
 
4004
4004
  The type is the reminder: this is text a stranger wrote. `text.toPlain` is
4005
4005
  the one way to turn it into a string, and on a server the honest answer is
4006
4006
  usually that you never need to.
4007
4007
 
4008
- ## GemServerProfile.displayTag
4008
+ <a id="gemserverprofile-displaytag"></a>
4009
4009
 
4010
- [GemServerProfile.displayTag](https://gemarcade.com/developer/api/server#gemserverprofile-displaytag)
4010
+ ## GemServerProfile.displayTag
4011
4011
 
4012
4012
  ```ts
4013
4013
  readonly displayTag?: PlayerText
4014
4014
  ```
4015
4015
 
4016
4016
  The precomposed `Name#0001`, when the platform has one. It contains the
4017
- name, so it is [PlayerText](https://gemarcade.com/developer/api/server#playertext) too — the same type every other surface
4017
+ name, so it is [PlayerText](#playertext) too — the same type every other surface
4018
4018
  that shows a player gives it.
4019
4019
 
4020
- ## GemServerProfile.discriminator
4020
+ <a id="gemserverprofile-discriminator"></a>
4021
4021
 
4022
- [GemServerProfile.discriminator](https://gemarcade.com/developer/api/server#gemserverprofile-discriminator)
4022
+ ## GemServerProfile.discriminator
4023
4023
 
4024
4024
  ```ts
4025
4025
  readonly discriminator?: string
@@ -4027,9 +4027,9 @@ readonly discriminator?: string
4027
4027
 
4028
4028
  The disambiguating suffix, when the player has one.
4029
4029
 
4030
- ## GemMatchServer.identity
4030
+ <a id="gemmatchserver-identity"></a>
4031
4031
 
4032
- [GemMatchServer.identity](https://gemarcade.com/developer/api/server#gemmatchserver-identity)
4032
+ ## GemMatchServer.identity
4033
4033
 
4034
4034
  ```ts
4035
4035
  readonly identity: GemServerIdentity
@@ -4037,15 +4037,15 @@ readonly identity: GemServerIdentity
4037
4037
 
4038
4038
  Which game, channel and match this server is running.
4039
4039
 
4040
- ## GemMatchServer.client
4040
+ <a id="gemmatchserver-client"></a>
4041
4041
 
4042
- [GemMatchServer.client](https://gemarcade.com/developer/api/server#gemmatchserver-client)
4042
+ ## GemMatchServer.client
4043
4043
 
4044
4044
  ```ts
4045
4045
  readonly client: GemClient
4046
4046
  ```
4047
4047
 
4048
- The HTTP client underneath [GemServer.api](https://gemarcade.com/developer/api/server#gemserver-api), for calls the generated
4048
+ The HTTP client underneath [GemServer.api](#gemserver-api), for calls the generated
4049
4049
  surface does not cover.
4050
4050
 
4051
4051
  PREFER `api`. It is typed against the platform's description, so a shape
@@ -4053,9 +4053,9 @@ that changes becomes a build error in your bundle. This one takes a path and
4053
4053
  checks nothing — reach for it when you genuinely need something `api` has
4054
4054
  no method for, and treat that as worth telling us about.
4055
4055
 
4056
- ## GemMatchServer.api
4056
+ <a id="gemmatchserver-api"></a>
4057
4057
 
4058
- [GemMatchServer.api](https://gemarcade.com/developer/api/server#gemmatchserver-api)
4058
+ ## GemMatchServer.api
4059
4059
 
4060
4060
  ```ts
4061
4061
  readonly api: ServerApi
@@ -4063,11 +4063,11 @@ readonly api: ServerApi
4063
4063
 
4064
4064
  Every platform operation your server can call. This is the one you want.
4065
4065
 
4066
- See [ServerApi](https://gemarcade.com/developer/api/server#serverapi).
4066
+ See [ServerApi](#serverapi).
4067
4067
 
4068
- ## GemMatchServer.match
4068
+ <a id="gemmatchserver-match"></a>
4069
4069
 
4070
- [GemMatchServer.match](https://gemarcade.com/developer/api/server#gemmatchserver-match)
4070
+ ## GemMatchServer.match
4071
4071
 
4072
4072
  ```ts
4073
4073
  readonly match: { end: null }
@@ -4088,9 +4088,9 @@ that this server is the one allocated to the match, which the platform
4088
4088
  requires. On a platform image too old to offer that, `end()` throws rather
4089
4089
  than sending a request without the proof.
4090
4090
 
4091
- ## GemMatchServer.match.end
4091
+ <a id="gemmatchserver-match-end"></a>
4092
4092
 
4093
- [GemMatchServer.match.end](https://gemarcade.com/developer/api/server#gemmatchserver-match-end)
4093
+ ## GemMatchServer.match.end
4094
4094
 
4095
4095
  ```ts
4096
4096
  end(signal?: AbortSignal): Promise<void>
@@ -4105,9 +4105,9 @@ Tell the platform this match is finished.
4105
4105
  **Throws.** When the platform image running this server predates
4106
4106
  `host.endMatch`, so the match cannot be ended from here.
4107
4107
 
4108
- ## GemMatchServer.analytics
4108
+ <a id="gemmatchserver-analytics"></a>
4109
4109
 
4110
- [GemMatchServer.analytics](https://gemarcade.com/developer/api/server#gemmatchserver-analytics)
4110
+ ## GemMatchServer.analytics
4111
4111
 
4112
4112
  ```ts
4113
4113
  readonly analytics: ServerAnalytics
@@ -4124,12 +4124,12 @@ game may declare an event `sources: [server]` and shut the client out of it
4124
4124
  entirely.
4125
4125
 
4126
4126
  ⚠️ **Every event names its player, and `gem_*` is unavailable here** — see
4127
- [ServerAnalytics.track](https://gemarcade.com/developer/api/server#serveranalytics-track). And flush before the match ends: there is no
4127
+ [ServerAnalytics.track](#serveranalytics-track). And flush before the match ends: there is no
4128
4128
  page unload to get the tail out on.
4129
4129
 
4130
- ## GemMatchServer.functions
4130
+ <a id="gemmatchserver-functions"></a>
4131
4131
 
4132
- [GemMatchServer.functions](https://gemarcade.com/developer/api/server#gemmatchserver-functions)
4132
+ ## GemMatchServer.functions
4133
4133
 
4134
4134
  ```ts
4135
4135
  readonly functions: FunctionsApi
@@ -4142,9 +4142,9 @@ The call arrives as this server, not as a player: your function's
4142
4142
  `get-context` says so, and a server's per-minute budget is its own. It runs
4143
4143
  the function promoted on the channel this match runs on.
4144
4144
 
4145
- ## GemMatchServer.challenges
4145
+ <a id="gemmatchserver-challenges"></a>
4146
4146
 
4147
- [GemMatchServer.challenges](https://gemarcade.com/developer/api/server#gemmatchserver-challenges)
4147
+ ## GemMatchServer.challenges
4148
4148
 
4149
4149
  ```ts
4150
4150
  readonly challenges: ServerChallenges
@@ -4156,11 +4156,11 @@ board that player was actually given.
4156
4156
 
4157
4157
  A period is the PLAYER's, not the calendar's: it ends at their own
4158
4158
  midnight. That is why this takes a player id where the browser's
4159
- `gem.challenges` takes none. See [ServerChallenges.list](https://gemarcade.com/developer/api/server#serverchallenges-list).
4159
+ `gem.challenges` takes none. See [ServerChallenges.list](#serverchallenges-list).
4160
4160
 
4161
- ## GemMatchServer.room
4161
+ <a id="gemmatchserver-room"></a>
4162
4162
 
4163
- [GemMatchServer.room](https://gemarcade.com/developer/api/server#gemmatchserver-room)
4163
+ ## GemMatchServer.room
4164
4164
 
4165
4165
  ```ts
4166
4166
  readonly room?: { get: (options?: { ifNoneMatch?: string; signal?: AbortSignal }) => Promise<VersionedResponse<GameRoomResponseV2>>; args: (options?: { signal?: AbortSignal }) => Promise<RoomArgsResponse> }
@@ -4174,9 +4174,9 @@ the type says it might be. `gem.room?.get()` is the shape to write. When it
4174
4174
  is absent the same reads are still available through `api` if you know the
4175
4175
  room id from somewhere else.
4176
4176
 
4177
- ## GemMatchServer.room.get
4177
+ <a id="gemmatchserver-room-get"></a>
4178
4178
 
4179
- [GemMatchServer.room.get](https://gemarcade.com/developer/api/server#gemmatchserver-room-get)
4179
+ ## GemMatchServer.room.get
4180
4180
 
4181
4181
  ```ts
4182
4182
  get: (options?: { ifNoneMatch?: string; signal?: AbortSignal }) => Promise<VersionedResponse<GameRoomResponseV2>>
@@ -4189,9 +4189,9 @@ avatar.
4189
4189
  This is what you want at `start()`, and it is a superset of what the older
4190
4190
  roster push carried.
4191
4191
 
4192
- ## GemMatchServer.room.args
4192
+ <a id="gemmatchserver-room-args"></a>
4193
4193
 
4194
- [GemMatchServer.room.args](https://gemarcade.com/developer/api/server#gemmatchserver-room-args)
4194
+ ## GemMatchServer.room.args
4195
4195
 
4196
4196
  ```ts
4197
4197
  args: (options?: { signal?: AbortSignal }) => Promise<RoomArgsResponse>
@@ -4204,9 +4204,9 @@ A room whose template declares no settings answers 404
4204
4204
  settings" and "this room's settings are empty" are different answers and
4205
4205
  the API keeps them apart.
4206
4206
 
4207
- ## GemMatchServer.dispose
4207
+ <a id="gemmatchserver-dispose"></a>
4208
4208
 
4209
- [GemMatchServer.dispose](https://gemarcade.com/developer/api/server#gemmatchserver-dispose)
4209
+ ## GemMatchServer.dispose
4210
4210
 
4211
4211
  ```ts
4212
4212
  dispose(): void
@@ -4214,13 +4214,13 @@ dispose(): void
4214
4214
 
4215
4215
  Stop listening for credential updates.
4216
4216
 
4217
- You rarely need it: a bundle built with [defineServer](https://gemarcade.com/developer/api/server#defineserver) is disposed for
4217
+ You rarely need it: a bundle built with [defineServer](#defineserver) is disposed for
4218
4218
  you when the match ends. Call it in a test, or if you built the server
4219
4219
  yourself and are tearing it down.
4220
4220
 
4221
- ## GemMatchServer.sizeClass
4221
+ <a id="gemmatchserver-sizeclass"></a>
4222
4222
 
4223
- [GemMatchServer.sizeClass](https://gemarcade.com/developer/api/server#gemmatchserver-sizeclass)
4223
+ ## GemMatchServer.sizeClass
4224
4224
 
4225
4225
  ```ts
4226
4226
  readonly sizeClass: string
@@ -4230,9 +4230,9 @@ Which size of machine this server was allocated on.
4230
4230
 
4231
4231
  Useful if your game scales what it does to the hardware it got.
4232
4232
 
4233
- ## GemMatchServer.roomArgs
4233
+ <a id="gemmatchserver-roomargs"></a>
4234
4234
 
4235
- [GemMatchServer.roomArgs](https://gemarcade.com/developer/api/server#gemmatchserver-roomargs)
4235
+ ## GemMatchServer.roomArgs
4236
4236
 
4237
4237
  ```ts
4238
4238
  readonly roomArgs: Readonly<Record<string, RoomArgValue>> | undefined
@@ -4247,13 +4247,13 @@ and `4.0.0` is the earliest release that may take it off the type. Whether
4247
4247
  a server keeps sending a value is a separate question, decided where the
4248
4248
  dedicated-server image is built rather than by a package release — so check
4249
4249
  for the room read and keep this as the fallback. Both halves are in
4250
- [the deprecation policy](https://gemarcade.com/developer/docs/deprecation-policy).
4250
+ [the deprecation policy](../deprecation-policy.md).
4251
4251
 
4252
4252
  The room's settings as they were at match start.
4253
4253
 
4254
- ## GemMatchServer.roster
4254
+ <a id="gemmatchserver-roster"></a>
4255
4255
 
4256
- [GemMatchServer.roster](https://gemarcade.com/developer/api/server#gemmatchserver-roster)
4256
+ ## GemMatchServer.roster
4257
4257
 
4258
4258
  ```ts
4259
4259
  readonly roster: readonly string[]
@@ -4267,7 +4267,7 @@ and `4.0.0` is the earliest release that may take it off the type. Whether
4267
4267
  a server keeps sending a value is a separate question, decided where the
4268
4268
  dedicated-server image is built rather than by a package release — so check
4269
4269
  for the room read and keep this as the fallback. Both halves are in
4270
- [the deprecation policy](https://gemarcade.com/developer/docs/deprecation-policy).
4270
+ [the deprecation policy](../deprecation-policy.md).
4271
4271
 
4272
4272
  Who was in the room when the server was allocated.
4273
4273
 
@@ -4275,9 +4275,9 @@ A HINT FOR SIZING, NOT A GUEST LIST. It is never revised, and it is not what
4275
4275
  decides who may connect — the platform admits players by credential, not by
4276
4276
  this array.
4277
4277
 
4278
- ## GemMatchServer.players
4278
+ <a id="gemmatchserver-players"></a>
4279
4279
 
4280
- [GemMatchServer.players](https://gemarcade.com/developer/api/server#gemmatchserver-players)
4280
+ ## GemMatchServer.players
4281
4281
 
4282
4282
  ```ts
4283
4283
  players(): string[]
@@ -4285,9 +4285,9 @@ players(): string[]
4285
4285
 
4286
4286
  The players connected right now, as a fresh array each call.
4287
4287
 
4288
- ## GemMatchServer.profile
4288
+ <a id="gemmatchserver-profile"></a>
4289
4289
 
4290
- [GemMatchServer.profile](https://gemarcade.com/developer/api/server#gemmatchserver-profile)
4290
+ ## GemMatchServer.profile
4291
4291
 
4292
4292
  ```ts
4293
4293
  profile(playerId: string): GemServerProfile | null
@@ -4301,9 +4301,9 @@ for them.
4301
4301
  | `playerId` | `string` | Take this from `join()` or `players()`, never from something
4302
4302
  a client sent you. |
4303
4303
 
4304
- ## GemMatchServer.playerNumber
4304
+ <a id="gemmatchserver-playernumber"></a>
4305
4305
 
4306
- [GemMatchServer.playerNumber](https://gemarcade.com/developer/api/server#gemmatchserver-playernumber)
4306
+ ## GemMatchServer.playerNumber
4307
4307
 
4308
4308
  ```ts
4309
4309
  playerNumber(playerId: string): number | undefined
@@ -4319,9 +4319,9 @@ do not need to — handle the absence.
4319
4319
  | --- | --- |
4320
4320
  | `playerId` | `string` |
4321
4321
 
4322
- ## GemMatchServer.send
4322
+ <a id="gemmatchserver-send"></a>
4323
4323
 
4324
- [GemMatchServer.send](https://gemarcade.com/developer/api/server#gemmatchserver-send)
4324
+ ## GemMatchServer.send
4325
4325
 
4326
4326
  ```ts
4327
4327
  send(playerId: string, data: string | Uint8Array<ArrayBufferLike>, options?: SendOptions): void
@@ -4333,13 +4333,13 @@ Send to one player.
4333
4333
  | --- | --- | --- |
4334
4334
  | `playerId` | `string` | Who to send to. |
4335
4335
  | `data` | `string \| Uint8Array<ArrayBufferLike>` | A string or bytes. The receiver is told which. |
4336
- | `options` | `SendOptions` | What delivery you are ASKING FOR — see [SendOptions](https://gemarcade.com/developer/api/server#sendoptions).
4336
+ | `options` | `SendOptions` | What delivery you are ASKING FOR — see [SendOptions](#sendoptions).
4337
4337
  Both default to true. Pass them freely: an older platform image ignores
4338
4338
  them harmlessly. |
4339
4339
 
4340
- ## GemMatchServer.broadcast
4340
+ <a id="gemmatchserver-broadcast"></a>
4341
4341
 
4342
- [GemMatchServer.broadcast](https://gemarcade.com/developer/api/server#gemmatchserver-broadcast)
4342
+ ## GemMatchServer.broadcast
4343
4343
 
4344
4344
  ```ts
4345
4345
  broadcast(data: string | Uint8Array<ArrayBufferLike>, options?: BroadcastOptions): void
@@ -4350,12 +4350,12 @@ Send to every connected player.
4350
4350
  | Parameter | Type | Description |
4351
4351
  | --- | --- | --- |
4352
4352
  | `data` | `string \| Uint8Array<ArrayBufferLike>` | A string or bytes. |
4353
- | `options` | `BroadcastOptions` | See [BroadcastOptions](https://gemarcade.com/developer/api/server#broadcastoptions) — the same delivery choices as
4354
- [GemMatchServer.send](https://gemarcade.com/developer/api/server#gemmatchserver-send), plus who to leave out. |
4353
+ | `options` | `BroadcastOptions` | See [BroadcastOptions](#broadcastoptions) — the same delivery choices as
4354
+ [GemMatchServer.send](#gemmatchserver-send), plus who to leave out. |
4355
4355
 
4356
- ## GemMatchServer.kick
4356
+ <a id="gemmatchserver-kick"></a>
4357
4357
 
4358
- [GemMatchServer.kick](https://gemarcade.com/developer/api/server#gemmatchserver-kick)
4358
+ ## GemMatchServer.kick
4359
4359
 
4360
4360
  ```ts
4361
4361
  kick(playerId: string, reason?: string): void
@@ -4368,9 +4368,9 @@ Disconnect a player.
4368
4368
  | `playerId` | `string` | Who to remove. |
4369
4369
  | `reason` | `string` | Shown to them, and recorded in the match's logs. |
4370
4370
 
4371
- ## GemMatchServer.log
4371
+ <a id="gemmatchserver-log"></a>
4372
4372
 
4373
- [GemMatchServer.log](https://gemarcade.com/developer/api/server#gemmatchserver-log)
4373
+ ## GemMatchServer.log
4374
4374
 
4375
4375
  ```ts
4376
4376
  log(message: string): void
@@ -4386,15 +4386,15 @@ corrupts that conversation.
4386
4386
  | --- | --- |
4387
4387
  | `message` | `string` |
4388
4388
 
4389
- ## GemMatchServer.warn
4389
+ <a id="gemmatchserver-warn"></a>
4390
4390
 
4391
- [GemMatchServer.warn](https://gemarcade.com/developer/api/server#gemmatchserver-warn)
4391
+ ## GemMatchServer.warn
4392
4392
 
4393
4393
  ```ts
4394
4394
  warn(message: string): void
4395
4395
  ```
4396
4396
 
4397
- Like [GemMatchServer.log](https://gemarcade.com/developer/api/server#gemmatchserver-log), marked as a warning.
4397
+ Like [GemMatchServer.log](#gemmatchserver-log), marked as a warning.
4398
4398
 
4399
4399
  | Parameter | Type |
4400
4400
  | --- | --- |