@crowdedkingdoms/crowdyjs 18.8.0-dev.1 → 18.9.0-test.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/MIGRATION.md +86 -7
- package/README.md +10 -0
- package/dist/client.d.ts +2 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +20 -3
- package/dist/default-origin.d.ts +6 -6
- package/dist/default-origin.d.ts.map +1 -1
- package/dist/default-origin.js +6 -6
- package/dist/domains/inputLog.d.ts +44 -10
- package/dist/domains/inputLog.d.ts.map +1 -1
- package/dist/domains/inputLog.js +39 -23
- package/dist/generated/graphql-schema.d.ts +10 -8
- package/dist/generated/graphql-schema.d.ts.map +1 -1
- package/dist/generated/graphql.d.ts +1 -0
- package/dist/generated/graphql.d.ts.map +1 -1
- package/dist/generated/graphql.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/MIGRATION.md
CHANGED
|
@@ -120,6 +120,80 @@ super-admin's session). There is no SDK replacement.
|
|
|
120
120
|
`dev`'s SDL after #417 merged (`npm run schema:sync:paths -- --schema <that schema.gql>`, then
|
|
121
121
|
`npm run codegen`).
|
|
122
122
|
|
|
123
|
+
## 18.9.0: the input log says what it lost, and when to retry
|
|
124
|
+
|
|
125
|
+
Additive. **Needs the Game API release after v2.40.2** (`InputLogSession.missingRecords` is
|
|
126
|
+
selected on every sessions read; an older Game API refuses the selection). Promote the Game API
|
|
127
|
+
first on each tier.
|
|
128
|
+
|
|
129
|
+
- **`missingRecords`** on every recorded session: inputs the replication server accepted that
|
|
130
|
+
never reached the log (an overloaded server or a broker outage). 0 when the recording is
|
|
131
|
+
complete as far as it is known: a lost end of the recording cannot be counted (its session
|
|
132
|
+
closes as `unrecorded` instead). Null on one recorded before the count existed. Recording is
|
|
133
|
+
best-effort, and this is how a gap shows.
|
|
134
|
+
- **Three end reasons** in `endReason`: `logging_off` (replay logging was turned off during the
|
|
135
|
+
session), `shutdown` (the replication server recording it stopped, a deploy; the client
|
|
136
|
+
reconnects to another server, where the same session continues and its end is replaced), and
|
|
137
|
+
`paused` (recording paused because the app's logging setting could not be confirmed; the
|
|
138
|
+
client sees nothing, and a later record reopens the session). `unrecorded` (no end was
|
|
139
|
+
recorded; closed an hour after its last input) comes with the same Game API release.
|
|
140
|
+
- **Reads say when to retry.** `messages` throws `INPUT_LOG_TEMPORARILY_UNAVAILABLE` when the log
|
|
141
|
+
cannot be read right now (it used to answer an empty page, or `INPUT_LOG_UNAVAILABLE`). A server
|
|
142
|
+
runs one read per user, and two per app and per org, at a time: a read over that share waits
|
|
143
|
+
for an earlier one within its own few-second deadline, and `INPUT_LOG_RATE_LIMITED` means the
|
|
144
|
+
wait ran out. Both carry `extensions.retryable`: retry with the same cursor after a short
|
|
145
|
+
back-off, a bounded number of times. A read whose request is closed stops and frees its
|
|
146
|
+
share: pass `signal` in the options of `sessions` / `messages` (new) and abort a read nobody
|
|
147
|
+
will see, say when the user opens another session. An operation may select
|
|
148
|
+
`inputLogMessages` only once.
|
|
149
|
+
- **A `signal` keeps the request timeout.** A `signal` passed to a request
|
|
150
|
+
(`client.gql.request(document, variables, { signal })`, and the input log's new option) used
|
|
151
|
+
to replace the client's timeout, so a request with one could wait for ever, and an abort
|
|
152
|
+
rejected as `CrowdyTimeoutError`. It now runs alongside the timeout, and aborting rejects with
|
|
153
|
+
the signal's reason.
|
|
154
|
+
- **A cursor belongs to its session.** `BAD_USER_INPUT` for a malformed cursor or one from another
|
|
155
|
+
session (until this Game API, a cursor from another session was not detected and the read just
|
|
156
|
+
returned fewer inputs).
|
|
157
|
+
- **`apps.update(appId, { replayLoggingEnabled: true })`** needs a spendable balance that covers
|
|
158
|
+
the projected cost of keeping one retention period of the app's recent traffic; the
|
|
159
|
+
`INPUT_LOG_FUNDS_NEEDED` refusal's `extensions.requiredMicrousd` and `spendableMicrousd` say how
|
|
160
|
+
much. Recordings already kept go on billing after logging is turned off, until they age out.
|
|
161
|
+
|
|
162
|
+
Paging a session, the loop that does not stop early:
|
|
163
|
+
|
|
164
|
+
```ts
|
|
165
|
+
import { CrowdyGraphQLError, decodeBase64 } from '@crowdedkingdoms/crowdyjs';
|
|
166
|
+
|
|
167
|
+
const RETRY = new Set(['INPUT_LOG_TEMPORARILY_UNAVAILABLE', 'INPUT_LOG_RATE_LIMITED']);
|
|
168
|
+
const MAX_RETRIES = 8; // in a row, about half a minute of back-off; then give up
|
|
169
|
+
const reading = new AbortController(); // abort() when the user moves on
|
|
170
|
+
let after: string | undefined;
|
|
171
|
+
for (let backoffMs = 250, retries = 0; ; ) {
|
|
172
|
+
let page;
|
|
173
|
+
try {
|
|
174
|
+
page = await game.inputLog.messages(appId, gameTokenId, {
|
|
175
|
+
first: 200,
|
|
176
|
+
after,
|
|
177
|
+
signal: reading.signal,
|
|
178
|
+
});
|
|
179
|
+
} catch (err) {
|
|
180
|
+
if (!(err instanceof CrowdyGraphQLError) || !RETRY.has(String(err.code))) throw err;
|
|
181
|
+
if (++retries > MAX_RETRIES) throw err;
|
|
182
|
+
await new Promise((r) => setTimeout(r, backoffMs));
|
|
183
|
+
backoffMs = Math.min(backoffMs * 2, 5_000);
|
|
184
|
+
continue; // the same cursor: nothing is skipped
|
|
185
|
+
}
|
|
186
|
+
backoffMs = 250;
|
|
187
|
+
retries = 0;
|
|
188
|
+
for (const { node } of page.edges) handle(node.messageType, decodeBase64(node.body));
|
|
189
|
+
if (!page.pageInfo.hasNextPage) break;
|
|
190
|
+
after = page.pageInfo.endCursor ?? after;
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Nothing in the SDK changed shape; the regenerated types add `missingRecords` and the refreshed
|
|
195
|
+
descriptions.
|
|
196
|
+
|
|
123
197
|
## 18.8.0: builds report the SDK version they compiled against
|
|
124
198
|
|
|
125
199
|
Additive, with one new floor: **the build documents select `ExecBuild.sdkVersion`, so 18.8.0
|
|
@@ -230,16 +304,21 @@ Additive. An app with replay logging on has its client inputs recorded, and `cli
|
|
|
230
304
|
reads them back on the app-scoped client:
|
|
231
305
|
|
|
232
306
|
```ts
|
|
233
|
-
const { edges
|
|
307
|
+
const { edges } = await game.inputLog.sessions(appId, { first: 20 });
|
|
308
|
+
const gameTokenId = edges[0].node.gameTokenId;
|
|
234
309
|
let after: string | undefined;
|
|
235
|
-
|
|
236
|
-
const page = await game.inputLog.messages(appId,
|
|
310
|
+
for (;;) {
|
|
311
|
+
const page = await game.inputLog.messages(appId, gameTokenId, { first: 200, after });
|
|
237
312
|
for (const { node } of page.edges) handle(node.messageType, decodeBase64(node.body));
|
|
238
|
-
after = page.pageInfo.endCursor ?? undefined;
|
|
239
313
|
if (!page.pageInfo.hasNextPage) break;
|
|
240
|
-
|
|
314
|
+
after = page.pageInfo.endCursor ?? after;
|
|
315
|
+
}
|
|
241
316
|
```
|
|
242
317
|
|
|
318
|
+
(Corrected in 18.9.0: the first version of this example stopped when `endCursor` came back null
|
|
319
|
+
even with `hasNextPage` true, which a v2.39 Game API answers when a read cannot start in time.
|
|
320
|
+
18.9.0's section has the loop with retries.)
|
|
321
|
+
|
|
243
322
|
A player reads only the sessions and inputs they sent; a holder of `manage_apps` on the app reads
|
|
244
323
|
every session. Keep paging while `hasNextPage` is true: a messages page can be short, or empty,
|
|
245
324
|
when it stopped at the server's time or scan limit. Inputs are kept for the published retention,
|
|
@@ -250,8 +329,8 @@ with the same cursor, when the log cannot be read right now.
|
|
|
250
329
|
`App.replayLoggingEnabled` is selected on every app read and set with `apps.update(appId,
|
|
251
330
|
{ replayLoggingEnabled: true })` (`manage_apps`). Turning it on is refused with
|
|
252
331
|
`INPUT_LOG_FUNDS_NEEDED` unless the org's wallet has a spendable balance or the org is exempt from
|
|
253
|
-
billing, because stored input logs are billed (crowdedkingdoms.com/pricing). It needs ck-api
|
|
254
|
-
|
|
332
|
+
billing, because stored input logs are billed (crowdedkingdoms.com/pricing). It needs ck-api v2.39.0 or later: every app read selects
|
|
333
|
+
`replayLoggingEnabled`, which an older Game API refuses.
|
|
255
334
|
|
|
256
335
|
## 18.5.0: channel messages limited by distance
|
|
257
336
|
|
package/README.md
CHANGED
|
@@ -29,6 +29,16 @@ This README is the `dev` branch's: it documents the release on the `dev` dist-ta
|
|
|
29
29
|
(`npm view @crowdedkingdoms/crowdyjs dist-tags`), and its links go to the dev docs site. A
|
|
30
30
|
plain `npm install` resolves `latest`, the production release, which has no `client.exec`.
|
|
31
31
|
|
|
32
|
+
**Server compatibility.** A release selects fields the Game API it was built against has,
|
|
33
|
+
so it needs that Game API or newer; promote the Game API before the SDK on each tier.
|
|
34
|
+
|
|
35
|
+
| CrowdyJS | Needs the Game API (ck-api) |
|
|
36
|
+
| --- | --- |
|
|
37
|
+
| 18.9.0 | the release after v2.40.2 (`InputLogSession.missingRecords`) |
|
|
38
|
+
| 18.8.0 | v2.40.2 or later (`ExecBuild.sdkVersion`) |
|
|
39
|
+
| 18.7.0 | v2.40.0 or later (`runtimeGate`, channel audio) |
|
|
40
|
+
| 18.6.0 | v2.39.0 or later (every app read selects `replayLoggingEnabled`) |
|
|
41
|
+
|
|
32
42
|
CrowdyJS targets browsers by default and uses native `fetch`, `WebSocket`,
|
|
33
43
|
`crypto`, `btoa`, and `atob`. Node tools can still use the SDK, but must
|
|
34
44
|
provide browser-compatible globals when opening realtime connections (e.g.
|
package/dist/client.d.ts
CHANGED
|
@@ -147,7 +147,8 @@ export declare class GraphQLClient {
|
|
|
147
147
|
* @param variables - Variables for the operation; omit for operations that
|
|
148
148
|
* take none.
|
|
149
149
|
* @param options - Optional `signal` to abort the request from your own
|
|
150
|
-
* `AbortController` (in addition to the built-in timeout).
|
|
150
|
+
* `AbortController` (in addition to the built-in timeout). Aborting it rejects
|
|
151
|
+
* with the signal's reason, not a {@link CrowdyTimeoutError}.
|
|
151
152
|
* @returns The operation's `data` payload.
|
|
152
153
|
* @throws {CrowdyHttpError} on a non-2xx HTTP status.
|
|
153
154
|
* @throws {CrowdyGraphQLError} when the response carries a non-empty
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AAC3E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAahD,OAAO,EAAkB,KAAK,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC/E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAG1D;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;;;;;;;;OAYG;IACH,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACzE;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB;;;;OAIG;IACH,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,aAAa;IACxB,yEAAyE;IACzE,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAe;IACvC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAe;IACtC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAgB;IAC/C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAe;IAC1C,OAAO,CAAC,iBAAiB,CAAC,CAEM;IAEhC,IAAI,QAAQ,IAAI,MAAM,CAErB;IAED;;;;;;OAMG;gBACS,MAAM,EAAE,mBAAmB,YAAK,EAAE,OAAO,EAAE,YAAY;IAmBnE;;;;;;;OAOG;IACH,yBAAyB,CACvB,OAAO,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,GAC5D,IAAI;IAIP;;;;OAIG;IACH,WAAW,IAAI,MAAM;IAIrB;;;;;;;;;;;OAWG;IACH,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAInC
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AAC3E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAahD,OAAO,EAAkB,KAAK,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC/E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAG1D;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;;;;;;;;OAYG;IACH,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACzE;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB;;;;OAIG;IACH,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,aAAa;IACxB,yEAAyE;IACzE,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAe;IACvC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAe;IACtC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAgB;IAC/C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAe;IAC1C,OAAO,CAAC,iBAAiB,CAAC,CAEM;IAEhC,IAAI,QAAQ,IAAI,MAAM,CAErB;IAED;;;;;;OAMG;gBACS,MAAM,EAAE,mBAAmB,YAAK,EAAE,OAAO,EAAE,YAAY;IAmBnE;;;;;;;OAOG;IACH,yBAAyB,CACvB,OAAO,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,GAC5D,IAAI;IAIP;;;;OAIG;IACH,WAAW,IAAI,MAAM;IAIrB;;;;;;;;;;;OAWG;IACH,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAInC;;;;;;;;;;;;;;;;;;;;;OAqBG;IACG,OAAO,CAAC,OAAO,EAAE,UAAU,EAC/B,QAAQ,EAAE,iBAAiB,CAAC,OAAO,EAAE,UAAU,CAAC,EAChD,SAAS,CAAC,EAAE,UAAU,EACtB,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,WAAW,CAAA;KAAO,GACrC,OAAO,CAAC,OAAO,CAAC;IASnB;;;;;;;;;;;;;;;;;;;OAmBG;IACG,KAAK,CAAC,CAAC,GAAG,GAAG,EACjB,KAAK,EAAE,MAAM,EACb,SAAS,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACvC,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,WAAW,CAAA;KAAO,GACrC,OAAO,CAAC,CAAC,CAAC;IA0Cb,0EAA0E;YAC5D,OAAO;CAqFtB;AAED;;;GAGG;AACH,OAAO,EAAE,aAAa,IAAI,gBAAgB,EAAE,CAAC"}
|
package/dist/client.js
CHANGED
|
@@ -107,7 +107,8 @@ export class GraphQLClient {
|
|
|
107
107
|
* @param variables - Variables for the operation; omit for operations that
|
|
108
108
|
* take none.
|
|
109
109
|
* @param options - Optional `signal` to abort the request from your own
|
|
110
|
-
* `AbortController` (in addition to the built-in timeout).
|
|
110
|
+
* `AbortController` (in addition to the built-in timeout). Aborting it rejects
|
|
111
|
+
* with the signal's reason, not a {@link CrowdyTimeoutError}.
|
|
111
112
|
* @returns The operation's `data` payload.
|
|
112
113
|
* @throws {CrowdyHttpError} on a non-2xx HTTP status.
|
|
113
114
|
* @throws {CrowdyGraphQLError} when the response carries a non-empty
|
|
@@ -180,9 +181,20 @@ export class GraphQLClient {
|
|
|
180
181
|
/** One HTTP round trip. {@link query} wraps this to handle a redirect. */
|
|
181
182
|
async attempt(query, variables, options) {
|
|
182
183
|
const controller = new AbortController();
|
|
183
|
-
|
|
184
|
+
let timedOut = false;
|
|
185
|
+
const timeoutId = setTimeout(() => {
|
|
186
|
+
timedOut = true;
|
|
187
|
+
controller.abort();
|
|
188
|
+
}, this.timeout);
|
|
189
|
+
// The caller's signal is followed as well as the timeout, never instead of it.
|
|
190
|
+
const caller = options.signal;
|
|
191
|
+
const followCaller = () => controller.abort(caller?.reason);
|
|
192
|
+
if (caller?.aborted)
|
|
193
|
+
followCaller();
|
|
194
|
+
else
|
|
195
|
+
caller?.addEventListener('abort', followCaller, { once: true });
|
|
184
196
|
const token = this.session.getToken();
|
|
185
|
-
const signal =
|
|
197
|
+
const signal = controller.signal;
|
|
186
198
|
const lbCookie = this.lbCookieStore?.headerValue();
|
|
187
199
|
try {
|
|
188
200
|
const requestBody = { query, variables };
|
|
@@ -227,6 +239,8 @@ export class GraphQLClient {
|
|
|
227
239
|
}
|
|
228
240
|
catch (error) {
|
|
229
241
|
clearTimeout(timeoutId);
|
|
242
|
+
if (!timedOut && caller?.aborted)
|
|
243
|
+
throw caller.reason ?? error;
|
|
230
244
|
if (error instanceof Error && error.name === 'AbortError') {
|
|
231
245
|
throw new CrowdyTimeoutError(this.timeout);
|
|
232
246
|
}
|
|
@@ -239,6 +253,9 @@ export class GraphQLClient {
|
|
|
239
253
|
this.logger.error?.('GraphQL request failed', error);
|
|
240
254
|
throw new CrowdyNetworkError(error);
|
|
241
255
|
}
|
|
256
|
+
finally {
|
|
257
|
+
caller?.removeEventListener('abort', followCaller);
|
|
258
|
+
}
|
|
242
259
|
}
|
|
243
260
|
}
|
|
244
261
|
/**
|
package/dist/default-origin.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* GENERATED — do not edit. The operator tooling's
|
|
3
|
-
* `sync-client-origins.mjs --write --tier
|
|
3
|
+
* `sync-client-origins.mjs --write --tier test` writes this file, and
|
|
4
4
|
* `check-sdk-default-origin.mjs` refuses it when it names the wrong tier or a
|
|
5
5
|
* host that declaration does not carry. Regenerate; never hand-edit.
|
|
6
6
|
*
|
|
@@ -10,14 +10,14 @@
|
|
|
10
10
|
* than no default at all. Every fallback this SDK ever removed named a host that
|
|
11
11
|
* was already dead. A generated file plus a gate is what makes this one different.
|
|
12
12
|
*
|
|
13
|
-
* Source: the operator's per-tier public CK API origin declaration, tier '
|
|
13
|
+
* Source: the operator's per-tier public CK API origin declaration, tier 'test'
|
|
14
14
|
*/
|
|
15
15
|
/** The tier this build of the SDK is published for. */
|
|
16
|
-
export declare const CROWDY_DEFAULT_TIER = "
|
|
16
|
+
export declare const CROWDY_DEFAULT_TIER = "test";
|
|
17
17
|
/** The public CK API origin for that tier. */
|
|
18
|
-
export declare const CROWDY_DEFAULT_HTTP_ORIGIN = "https://ck.
|
|
18
|
+
export declare const CROWDY_DEFAULT_HTTP_ORIGIN = "https://ck.test.crowdedkingdoms.com";
|
|
19
19
|
/** The same host over WebSocket. A scheme is composed; a hostname is looked up. */
|
|
20
|
-
export declare const CROWDY_DEFAULT_WS_ORIGIN = "wss://ck.
|
|
20
|
+
export declare const CROWDY_DEFAULT_WS_ORIGIN = "wss://ck.test.crowdedkingdoms.com";
|
|
21
21
|
/** The bare hostname, for callers that need to compare rather than dial. */
|
|
22
|
-
export declare const CROWDY_DEFAULT_HOST = "ck.
|
|
22
|
+
export declare const CROWDY_DEFAULT_HOST = "ck.test.crowdedkingdoms.com";
|
|
23
23
|
//# sourceMappingURL=default-origin.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"default-origin.d.ts","sourceRoot":"","sources":["../src/default-origin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,uDAAuD;AACvD,eAAO,MAAM,mBAAmB,
|
|
1
|
+
{"version":3,"file":"default-origin.d.ts","sourceRoot":"","sources":["../src/default-origin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,uDAAuD;AACvD,eAAO,MAAM,mBAAmB,SAAS,CAAC;AAE1C,8CAA8C;AAC9C,eAAO,MAAM,0BAA0B,wCAAwC,CAAC;AAEhF,mFAAmF;AACnF,eAAO,MAAM,wBAAwB,sCAAsC,CAAC;AAE5E,4EAA4E;AAC5E,eAAO,MAAM,mBAAmB,gCAAgC,CAAC"}
|
package/dist/default-origin.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* GENERATED — do not edit. The operator tooling's
|
|
3
|
-
* `sync-client-origins.mjs --write --tier
|
|
3
|
+
* `sync-client-origins.mjs --write --tier test` writes this file, and
|
|
4
4
|
* `check-sdk-default-origin.mjs` refuses it when it names the wrong tier or a
|
|
5
5
|
* host that declaration does not carry. Regenerate; never hand-edit.
|
|
6
6
|
*
|
|
@@ -10,13 +10,13 @@
|
|
|
10
10
|
* than no default at all. Every fallback this SDK ever removed named a host that
|
|
11
11
|
* was already dead. A generated file plus a gate is what makes this one different.
|
|
12
12
|
*
|
|
13
|
-
* Source: the operator's per-tier public CK API origin declaration, tier '
|
|
13
|
+
* Source: the operator's per-tier public CK API origin declaration, tier 'test'
|
|
14
14
|
*/
|
|
15
15
|
/** The tier this build of the SDK is published for. */
|
|
16
|
-
export const CROWDY_DEFAULT_TIER = '
|
|
16
|
+
export const CROWDY_DEFAULT_TIER = 'test';
|
|
17
17
|
/** The public CK API origin for that tier. */
|
|
18
|
-
export const CROWDY_DEFAULT_HTTP_ORIGIN = 'https://ck.
|
|
18
|
+
export const CROWDY_DEFAULT_HTTP_ORIGIN = 'https://ck.test.crowdedkingdoms.com';
|
|
19
19
|
/** The same host over WebSocket. A scheme is composed; a hostname is looked up. */
|
|
20
|
-
export const CROWDY_DEFAULT_WS_ORIGIN = 'wss://ck.
|
|
20
|
+
export const CROWDY_DEFAULT_WS_ORIGIN = 'wss://ck.test.crowdedkingdoms.com';
|
|
21
21
|
/** The bare hostname, for callers that need to compare rather than dial. */
|
|
22
|
-
export const CROWDY_DEFAULT_HOST = 'ck.
|
|
22
|
+
export const CROWDY_DEFAULT_HOST = 'ck.test.crowdedkingdoms.com';
|
|
@@ -15,6 +15,13 @@ export interface InputLogPageOptions<Filter> {
|
|
|
15
15
|
/** A previous page's `pageInfo.endCursor`; omit for the first page. */
|
|
16
16
|
after?: string;
|
|
17
17
|
filter?: Filter;
|
|
18
|
+
/**
|
|
19
|
+
* Abort the read: the call rejects with the signal's reason, and the server stops a
|
|
20
|
+
* `messages` read and frees its share at once (the Game API release after v2.40.2), so the
|
|
21
|
+
* next read, a different session's say, does not wait for it. Abort the one you no longer
|
|
22
|
+
* need when the user moves on.
|
|
23
|
+
*/
|
|
24
|
+
signal?: AbortSignal;
|
|
18
25
|
}
|
|
19
26
|
/**
|
|
20
27
|
* The input log: the client inputs the realtime servers recorded for an app with
|
|
@@ -28,11 +35,21 @@ export interface InputLogPageOptions<Filter> {
|
|
|
28
35
|
* **Who sees what.** A player reads only the sessions and inputs they sent. A holder of
|
|
29
36
|
* `manage_apps` on the app reads every session and may filter by `userId`.
|
|
30
37
|
*
|
|
31
|
-
* Inputs are kept for the input log's published retention (crowdedkingdoms.com/pricing
|
|
32
|
-
* and then deleted, so an old session can still be listed
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
38
|
+
* Inputs are kept for the input log's published retention (crowdedkingdoms.com/pricing,
|
|
39
|
+
* up to about an hour longer) and then deleted, so an old session can still be listed
|
|
40
|
+
* after its inputs are gone. Recording is best-effort: a session's `missingRecords` counts
|
|
41
|
+
* the inputs that were accepted but never reached the log.
|
|
42
|
+
*
|
|
43
|
+
* Without input logging on the deployment, both calls throw {@link CrowdyGraphQLError}
|
|
44
|
+
* `INPUT_LOG_UNAVAILABLE`. `messages` throws `INPUT_LOG_TEMPORARILY_UNAVAILABLE` when the
|
|
45
|
+
* log cannot be read right now. A server runs one read per user, and two per app and per
|
|
46
|
+
* org, at a time: a read over that share waits for an earlier one within its own few-second
|
|
47
|
+
* deadline, and `INPUT_LOG_RATE_LIMITED` means the wait ran out. Both carry
|
|
48
|
+
* `extensions.retryable`, so wait a moment and call again with the same cursor; a read
|
|
49
|
+
* whose request is closed (`opts.signal` aborted) stops and frees its share. An operation may select
|
|
50
|
+
* `inputLogMessages` only once. The Game API release after v2.40.2 raises those two;
|
|
51
|
+
* v2.39.0 to v2.40.2 answer `INPUT_LOG_UNAVAILABLE` instead, and an empty page when a read
|
|
52
|
+
* could not start in time.
|
|
36
53
|
*/
|
|
37
54
|
export declare class InputLogAPI {
|
|
38
55
|
private gql;
|
|
@@ -44,7 +61,18 @@ export declare class InputLogAPI {
|
|
|
44
61
|
* @param opts - Paging, and {@link InputLogSessionFilter}: `userId` (another user's
|
|
45
62
|
* needs `manage_apps`), `from` / `to`, and `messageType` (sessions containing it).
|
|
46
63
|
* @returns One page: `edges[].node` are the sessions, `pageInfo.endCursor` the next
|
|
47
|
-
* page's `after`, `totalCount` the matching sessions.
|
|
64
|
+
* page's `after`, `totalCount` the matching sessions. A node's `endReason` is how the
|
|
65
|
+
* session ended: `expired`, `revoked`, `reconnect` or `released` as the client saw it;
|
|
66
|
+
* `logging_off` when the app turned replay logging off during it; `shutdown` when the
|
|
67
|
+
* realtime server recording it stopped (the client reconnects to another server, where
|
|
68
|
+
* the same session continues and its end is replaced); `paused` when recording paused
|
|
69
|
+
* because the app's logging setting could not be confirmed (a later record reopens the
|
|
70
|
+
* session); `unrecorded` when no end was recorded and it was closed an hour after its last
|
|
71
|
+
* input (all three from the Game API release after v2.40.2); or null while it may still
|
|
72
|
+
* run. `missingRecords` counts the session's inputs that were accepted but never recorded,
|
|
73
|
+
* by an overloaded server or a broker outage: 0 when the recording is complete as far as
|
|
74
|
+
* it is known (a lost end cannot be counted), null on a session recorded before the count
|
|
75
|
+
* existed.
|
|
48
76
|
* @throws {CrowdyGraphQLError} `FORBIDDEN` for another user's sessions without
|
|
49
77
|
* `manage_apps`, or `INPUT_LOG_UNAVAILABLE`.
|
|
50
78
|
*/
|
|
@@ -57,8 +85,12 @@ export declare class InputLogAPI {
|
|
|
57
85
|
* input logs are billed on. Spatial inputs also carry their chunk and actor, and
|
|
58
86
|
* channel inputs their channel.
|
|
59
87
|
*
|
|
60
|
-
* **Keep paging while `pageInfo.hasNextPage` is true
|
|
61
|
-
* `first` inputs, or none, when it reached the server's time or scan
|
|
88
|
+
* **Keep paging while `pageInfo.hasNextPage` is true**, not while `endCursor` is set: a page
|
|
89
|
+
* can hold fewer than `first` inputs, or none, when it reached the server's time or scan
|
|
90
|
+
* limit, and its `endCursor` still moves the next page on. Keep the previous cursor if one
|
|
91
|
+
* comes back null, and retry `INPUT_LOG_TEMPORARILY_UNAVAILABLE` / `INPUT_LOG_RATE_LIMITED`
|
|
92
|
+
* with it after a short back-off, a bounded number of times (MIGRATION.md, 18.9.0). Pass
|
|
93
|
+
* `opts.signal` and abort a read nobody will see.
|
|
62
94
|
*
|
|
63
95
|
* @param appId - Numeric app id.
|
|
64
96
|
* @param gameTokenId - The session: {@link InputLogSessionRecord.gameTokenId}.
|
|
@@ -66,8 +98,10 @@ export declare class InputLogAPI {
|
|
|
66
98
|
* `messageTypes` (at most 64).
|
|
67
99
|
* @returns One page of inputs.
|
|
68
100
|
* @throws {CrowdyGraphQLError} `NOT_FOUND` for a session that is not yours without
|
|
69
|
-
* `manage_apps
|
|
70
|
-
*
|
|
101
|
+
* `manage_apps`; `BAD_USER_INPUT` for a malformed cursor, or (from the Game API release
|
|
102
|
+
* after v2.40.2, which binds a cursor to its session) one from another session;
|
|
103
|
+
* `INPUT_LOG_TEMPORARILY_UNAVAILABLE` or `INPUT_LOG_RATE_LIMITED`, both retryable with the
|
|
104
|
+
* same cursor; or `INPUT_LOG_UNAVAILABLE`.
|
|
71
105
|
*/
|
|
72
106
|
messages(appId: string, gameTokenId: string, opts?: InputLogPageOptions<InputLogMessageFilter>): Promise<InputLogMessagePage>;
|
|
73
107
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inputLog.d.ts","sourceRoot":"","sources":["../../src/domains/inputLog.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAElD,OAAO,EAGL,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,EAC3B,MAAM,yBAAyB,CAAC;AAEjC,gDAAgD;AAChD,MAAM,MAAM,mBAAmB,GAAG,qBAAqB,CAAC,kBAAkB,CAAC,CAAC;AAC5E,wEAAwE;AACxE,MAAM,MAAM,qBAAqB,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC;AACjF,gDAAgD;AAChD,MAAM,MAAM,mBAAmB,GAAG,qBAAqB,CAAC,kBAAkB,CAAC,CAAC;AAC5E,sEAAsE;AACtE,MAAM,MAAM,qBAAqB,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC;AAEjF,4FAA4F;AAC5F,MAAM,WAAW,mBAAmB,CAAC,MAAM;IACzC,iEAAiE;IACjE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uEAAuE;IACvE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"inputLog.d.ts","sourceRoot":"","sources":["../../src/domains/inputLog.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAElD,OAAO,EAGL,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,EAC3B,MAAM,yBAAyB,CAAC;AAEjC,gDAAgD;AAChD,MAAM,MAAM,mBAAmB,GAAG,qBAAqB,CAAC,kBAAkB,CAAC,CAAC;AAC5E,wEAAwE;AACxE,MAAM,MAAM,qBAAqB,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC;AACjF,gDAAgD;AAChD,MAAM,MAAM,mBAAmB,GAAG,qBAAqB,CAAC,kBAAkB,CAAC,CAAC;AAC5E,sEAAsE;AACtE,MAAM,MAAM,qBAAqB,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC;AAEjF,4FAA4F;AAC5F,MAAM,WAAW,mBAAmB,CAAC,MAAM;IACzC,iEAAiE;IACjE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uEAAuE;IACvE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,qBAAa,WAAW;IACV,OAAO,CAAC,GAAG;gBAAH,GAAG,EAAE,aAAa;IAEtC;;;;;;;;;;;;;;;;;;;;;OAqBG;IACG,QAAQ,CACZ,KAAK,EAAE,MAAM,EACb,IAAI,GAAE,mBAAmB,CAAC,qBAAqB,CAAM,GACpD,OAAO,CAAC,mBAAmB,CAAC;IAS/B;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IACG,QAAQ,CACZ,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,EACnB,IAAI,GAAE,mBAAmB,CAAC,qBAAqB,CAAM,GACpD,OAAO,CAAC,mBAAmB,CAAC;CAQhC"}
|
package/dist/domains/inputLog.js
CHANGED
|
@@ -11,11 +11,21 @@ import { InputLogMessagesDocument, InputLogSessionsDocument, } from '../generate
|
|
|
11
11
|
* **Who sees what.** A player reads only the sessions and inputs they sent. A holder of
|
|
12
12
|
* `manage_apps` on the app reads every session and may filter by `userId`.
|
|
13
13
|
*
|
|
14
|
-
* Inputs are kept for the input log's published retention (crowdedkingdoms.com/pricing
|
|
15
|
-
* and then deleted, so an old session can still be listed
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
14
|
+
* Inputs are kept for the input log's published retention (crowdedkingdoms.com/pricing,
|
|
15
|
+
* up to about an hour longer) and then deleted, so an old session can still be listed
|
|
16
|
+
* after its inputs are gone. Recording is best-effort: a session's `missingRecords` counts
|
|
17
|
+
* the inputs that were accepted but never reached the log.
|
|
18
|
+
*
|
|
19
|
+
* Without input logging on the deployment, both calls throw {@link CrowdyGraphQLError}
|
|
20
|
+
* `INPUT_LOG_UNAVAILABLE`. `messages` throws `INPUT_LOG_TEMPORARILY_UNAVAILABLE` when the
|
|
21
|
+
* log cannot be read right now. A server runs one read per user, and two per app and per
|
|
22
|
+
* org, at a time: a read over that share waits for an earlier one within its own few-second
|
|
23
|
+
* deadline, and `INPUT_LOG_RATE_LIMITED` means the wait ran out. Both carry
|
|
24
|
+
* `extensions.retryable`, so wait a moment and call again with the same cursor; a read
|
|
25
|
+
* whose request is closed (`opts.signal` aborted) stops and frees its share. An operation may select
|
|
26
|
+
* `inputLogMessages` only once. The Game API release after v2.40.2 raises those two;
|
|
27
|
+
* v2.39.0 to v2.40.2 answer `INPUT_LOG_UNAVAILABLE` instead, and an empty page when a read
|
|
28
|
+
* could not start in time.
|
|
19
29
|
*/
|
|
20
30
|
export class InputLogAPI {
|
|
21
31
|
constructor(gql) {
|
|
@@ -28,17 +38,23 @@ export class InputLogAPI {
|
|
|
28
38
|
* @param opts - Paging, and {@link InputLogSessionFilter}: `userId` (another user's
|
|
29
39
|
* needs `manage_apps`), `from` / `to`, and `messageType` (sessions containing it).
|
|
30
40
|
* @returns One page: `edges[].node` are the sessions, `pageInfo.endCursor` the next
|
|
31
|
-
* page's `after`, `totalCount` the matching sessions.
|
|
41
|
+
* page's `after`, `totalCount` the matching sessions. A node's `endReason` is how the
|
|
42
|
+
* session ended: `expired`, `revoked`, `reconnect` or `released` as the client saw it;
|
|
43
|
+
* `logging_off` when the app turned replay logging off during it; `shutdown` when the
|
|
44
|
+
* realtime server recording it stopped (the client reconnects to another server, where
|
|
45
|
+
* the same session continues and its end is replaced); `paused` when recording paused
|
|
46
|
+
* because the app's logging setting could not be confirmed (a later record reopens the
|
|
47
|
+
* session); `unrecorded` when no end was recorded and it was closed an hour after its last
|
|
48
|
+
* input (all three from the Game API release after v2.40.2); or null while it may still
|
|
49
|
+
* run. `missingRecords` counts the session's inputs that were accepted but never recorded,
|
|
50
|
+
* by an overloaded server or a broker outage: 0 when the recording is complete as far as
|
|
51
|
+
* it is known (a lost end cannot be counted), null on a session recorded before the count
|
|
52
|
+
* existed.
|
|
32
53
|
* @throws {CrowdyGraphQLError} `FORBIDDEN` for another user's sessions without
|
|
33
54
|
* `manage_apps`, or `INPUT_LOG_UNAVAILABLE`.
|
|
34
55
|
*/
|
|
35
56
|
async sessions(appId, opts = {}) {
|
|
36
|
-
const data = await this.gql.request(InputLogSessionsDocument, {
|
|
37
|
-
appId,
|
|
38
|
-
first: opts.first,
|
|
39
|
-
after: opts.after,
|
|
40
|
-
filter: opts.filter,
|
|
41
|
-
});
|
|
57
|
+
const data = await this.gql.request(InputLogSessionsDocument, { appId, first: opts.first, after: opts.after, filter: opts.filter }, { signal: opts.signal });
|
|
42
58
|
return data.inputLogSessions;
|
|
43
59
|
}
|
|
44
60
|
/**
|
|
@@ -49,8 +65,12 @@ export class InputLogAPI {
|
|
|
49
65
|
* input logs are billed on. Spatial inputs also carry their chunk and actor, and
|
|
50
66
|
* channel inputs their channel.
|
|
51
67
|
*
|
|
52
|
-
* **Keep paging while `pageInfo.hasNextPage` is true
|
|
53
|
-
* `first` inputs, or none, when it reached the server's time or scan
|
|
68
|
+
* **Keep paging while `pageInfo.hasNextPage` is true**, not while `endCursor` is set: a page
|
|
69
|
+
* can hold fewer than `first` inputs, or none, when it reached the server's time or scan
|
|
70
|
+
* limit, and its `endCursor` still moves the next page on. Keep the previous cursor if one
|
|
71
|
+
* comes back null, and retry `INPUT_LOG_TEMPORARILY_UNAVAILABLE` / `INPUT_LOG_RATE_LIMITED`
|
|
72
|
+
* with it after a short back-off, a bounded number of times (MIGRATION.md, 18.9.0). Pass
|
|
73
|
+
* `opts.signal` and abort a read nobody will see.
|
|
54
74
|
*
|
|
55
75
|
* @param appId - Numeric app id.
|
|
56
76
|
* @param gameTokenId - The session: {@link InputLogSessionRecord.gameTokenId}.
|
|
@@ -58,17 +78,13 @@ export class InputLogAPI {
|
|
|
58
78
|
* `messageTypes` (at most 64).
|
|
59
79
|
* @returns One page of inputs.
|
|
60
80
|
* @throws {CrowdyGraphQLError} `NOT_FOUND` for a session that is not yours without
|
|
61
|
-
* `manage_apps
|
|
62
|
-
*
|
|
81
|
+
* `manage_apps`; `BAD_USER_INPUT` for a malformed cursor, or (from the Game API release
|
|
82
|
+
* after v2.40.2, which binds a cursor to its session) one from another session;
|
|
83
|
+
* `INPUT_LOG_TEMPORARILY_UNAVAILABLE` or `INPUT_LOG_RATE_LIMITED`, both retryable with the
|
|
84
|
+
* same cursor; or `INPUT_LOG_UNAVAILABLE`.
|
|
63
85
|
*/
|
|
64
86
|
async messages(appId, gameTokenId, opts = {}) {
|
|
65
|
-
const data = await this.gql.request(InputLogMessagesDocument, {
|
|
66
|
-
appId,
|
|
67
|
-
gameTokenId,
|
|
68
|
-
first: opts.first,
|
|
69
|
-
after: opts.after,
|
|
70
|
-
filter: opts.filter,
|
|
71
|
-
});
|
|
87
|
+
const data = await this.gql.request(InputLogMessagesDocument, { appId, gameTokenId, first: opts.first, after: opts.after, filter: opts.filter }, { signal: opts.signal });
|
|
72
88
|
return data.inputLogMessages;
|
|
73
89
|
}
|
|
74
90
|
}
|
|
@@ -882,7 +882,7 @@ export type App = {
|
|
|
882
882
|
orgId: Scalars['BigInt']['output'];
|
|
883
883
|
/** OAuth-style redirect-URI allow-list for the portal handoff. A portal authorization code’s redirect_uri must match one of these by origin; empty disallows browser portal entry to this app. */
|
|
884
884
|
redirectUris: Array<Scalars['String']['output']>;
|
|
885
|
-
/** Whether replay logging is on: the realtime servers record every client
|
|
885
|
+
/** Whether replay logging is on: the realtime servers record every client message they accept for this app, searchable with inputLogSessions and readable with inputLogMessages, and billed as stored input logs. False (the default) records nothing ("No replication logging"). Set with updateApp (manage_apps); turning it on needs a wallet that covers the projected cost of one retention period, or a billing-exempt org. */
|
|
886
886
|
replayLoggingEnabled: Scalars['Boolean']['output'];
|
|
887
887
|
/**
|
|
888
888
|
* Reserved realtime (UDP) capacity in bytes/s for shared apps. Superseded by reservedUdpBytesPerSec.
|
|
@@ -1208,7 +1208,7 @@ export type AppUsageProjection = {
|
|
|
1208
1208
|
daysElapsed: Scalars['Float']['output'];
|
|
1209
1209
|
/** Per-app free monthly egress allowance in bytes (5 decimal GB). */
|
|
1210
1210
|
freeAllowanceBytes: Scalars['String']['output'];
|
|
1211
|
-
/** Stored input logs this calendar month so far, in byte-hours (input_log_storage_byte_hours, which has no free allowance).
|
|
1211
|
+
/** Stored input logs this calendar month so far, in byte-hours (input_log_storage_byte_hours, which has no free allowance). Recordings keep accruing after replay logging is turned off, until they age out at the end of the retention; 0 when nothing was held this month. */
|
|
1212
1212
|
inputLogByteHours: Scalars['String']['output'];
|
|
1213
1213
|
/** True when projected egress exceeds the free allowance, or null when insufficient data. */
|
|
1214
1214
|
onTrackToExceed: Maybe<Scalars['Boolean']['output']>;
|
|
@@ -1236,7 +1236,7 @@ export type AppUsageSummary = {
|
|
|
1236
1236
|
graphqlRecvBytes: Scalars['String']['output'];
|
|
1237
1237
|
/** Total GraphQL bytes sent (string counter). */
|
|
1238
1238
|
graphqlSendBytes: Scalars['String']['output'];
|
|
1239
|
-
/** Stored input logs over the window, in byte-hours (string counter): the sum of the hourly samples, which is the quantity input_log_storage_byte_hours bills. Each recorded byte (InputLogMessage.sizeBytes) is held for the input log retention, one byte-hour per hour
|
|
1239
|
+
/** Stored input logs over the window, in byte-hours (string counter): the sum of the hourly samples, which is the quantity input_log_storage_byte_hours bills. Each recorded byte (InputLogMessage.sizeBytes) is held for the input log retention, one byte-hour per hour, so recordings keep accruing byte-hours after replay logging is turned off, until they age out. 0 when nothing was held during the window. */
|
|
1240
1240
|
inputLogByteHours: Scalars['String']['output'];
|
|
1241
1241
|
/** Recorded input-log bytes the app holds now (string counter): the latest hourly sample, the bytes recorded within the trailing retention. 0 when replay logging has never been on. */
|
|
1242
1242
|
inputLogRetainedBytes: Scalars['String']['output'];
|
|
@@ -4403,9 +4403,9 @@ export type InputLogSession = {
|
|
|
4403
4403
|
byteCount: Scalars['BigInt']['output'];
|
|
4404
4404
|
/** The datacenter that recorded the session, when known. */
|
|
4405
4405
|
datacenter: Maybe<Scalars['String']['output']>;
|
|
4406
|
-
/** How the session ended, as the client saw it
|
|
4406
|
+
/** How the session ended: expired, revoked, reconnect or released, as the client saw it; logging_off, when the app turned replay logging off while the session was open; shutdown, when the realtime server recording it stopped (a deploy), after which the client reconnects elsewhere; paused, when recording paused because the app's logging setting could not be confirmed, which the client does not see (a later record reopens the session); or unrecorded, when its end was not recorded and it was closed an hour after its last record. Null while it may still be running. */
|
|
4407
4407
|
endReason: Maybe<Scalars['String']['output']>;
|
|
4408
|
-
/** When the session ended
|
|
4408
|
+
/** When the session ended; null while it may still be running. A session whose end was not recorded is closed at its last record once it has gone an hour without one. Later records reopen a session (endedAt returns to null). */
|
|
4409
4409
|
endedAt: Maybe<Scalars['DateTime']['output']>;
|
|
4410
4410
|
/** The game token the session ran on; pass it to inputLogMessages to read the inputs. */
|
|
4411
4411
|
gameTokenId: Scalars['BigInt']['output'];
|
|
@@ -4415,6 +4415,8 @@ export type InputLogSession = {
|
|
|
4415
4415
|
messageCount: Scalars['BigInt']['output'];
|
|
4416
4416
|
/** The client message types the session contains, ascending. */
|
|
4417
4417
|
messageTypes: Array<Scalars['Int']['output']>;
|
|
4418
|
+
/** Records the realtime servers numbered for this session that never reached the log: recording is best-effort, so an overloaded server or a broker outage can leave gaps, and this is how many. 0 when the recording is complete as far as it is known. A lost end of the recording cannot be counted (its session closes as unrecorded instead). Null for a session recorded before the realtime servers numbered their records. */
|
|
4419
|
+
missingRecords: Maybe<Scalars['BigInt']['output']>;
|
|
4418
4420
|
/** When the first recorded input arrived. */
|
|
4419
4421
|
startedAt: Scalars['DateTime']['output'];
|
|
4420
4422
|
/** The user whose client sent the inputs. */
|
|
@@ -4928,7 +4930,7 @@ export type Mutation = {
|
|
|
4928
4930
|
updateActor: Actor;
|
|
4929
4931
|
/** Replaces an actor’s `publicState` and/or `privateState` blobs (fields omitted from `input` are left unchanged). OWNER-EXCLUSIVE: only the actor’s owner may write (throws Unauthorized otherwise). Game-plane: requires an app token for the actor’s app (a session token or another app’s token is answered NotFound). `uuid` is the 32-character ASCII actor id; blobs are base64-encoded binary. */
|
|
4930
4932
|
updateActorState: Actor;
|
|
4931
|
-
/** Update mutable fields of an existing app (name, description, visibility, status, metadata, wildernessWritesOpen, replayLoggingEnabled); only fields present in the input are changed. Turning replayLoggingEnabled on is refused with INPUT_LOG_FUNDS_NEEDED unless the org's wallet
|
|
4933
|
+
/** Update mutable fields of an existing app (name, description, visibility, status, metadata, wildernessWritesOpen, replayLoggingEnabled); only fields present in the input are changed. Turning replayLoggingEnabled on is refused with INPUT_LOG_FUNDS_NEEDED unless the org's wallet can spend at least the projected cost of keeping one retention period of the app's recent traffic (extensions.requiredMicrousd and spendableMicrousd say how much), or the org is billing-exempt; recordings already kept go on billing after it is turned off, until they age out. Requires the 'manage_apps' permission on the app (resolved via its org); super admins bypass. Use this to publish (status=LIVE), change visibility, or restore an archived app (status back to DRAFT/LIVE). Throws if the app id does not exist. */
|
|
4932
4934
|
updateApp: App;
|
|
4933
4935
|
/** Updates an avatar’s mutable fields (currently `name`) and returns it. OWNER-EXCLUSIVE: only the avatar’s owner may call this (throws Unauthorized otherwise). Requires a valid game token. To change state blobs use `updateAvatarState`. */
|
|
4934
4936
|
updateAvatar: Avatar;
|
|
@@ -6787,7 +6789,7 @@ export type Query = {
|
|
|
6787
6789
|
hostedGamePublishes: Array<HostedGamePublish>;
|
|
6788
6790
|
/** The LIVE and LISTED hosted third-party games, for the Overworld lobby and the marketplace. PUBLIC. Empty when the tier does not host third-party games. Operators see every game with allHostedGames. */
|
|
6789
6791
|
hostedGames: Array<HostedGame>;
|
|
6790
|
-
/** The recorded inputs of one session, oldest first, each exactly as the client sent it (without its authentication tail). Read from the input log by the offset ranges its index holds, so inputs past the published retention are gone even while the session is still listed. With 'manage_apps' on the app you may read any session; anyone else may read only their own, and any other session answers NOT_FOUND. Game plane: send the app-scoped token for this app to the app's datacenter. Page with `first` (default 50, max 200) and `after
|
|
6792
|
+
/** The recorded inputs of one session, oldest first, each exactly as the client sent it (without its authentication tail). Read from the input log by the offset ranges its index holds, so inputs past the published retention are gone even while the session is still listed. With 'manage_apps' on the app you may read any session; anyone else may read only their own, and any other session answers NOT_FOUND. Game plane: send the app-scoped token for this app to the app's datacenter. Page with `first` (default 50, max 200) and `after`, the previous page's endCursor for this same session (a cursor from another session is BAD_USER_INPUT); a page can hold fewer than `first` inputs, or none, when it stopped at its time or scan limit, so keep paging while hasNextPage is true. An operation may select this field only once. A server runs one read per user (two per app and per org) at a time; a read over that share waits for the earlier one within its own few-second deadline, and a read whose request is closed stops. Errors: INPUT_LOG_UNAVAILABLE when input logging is not configured on this deployment, or this server may not read its log (do not retry); INPUT_LOG_RATE_LIMITED (429) when the wait for an earlier read ran out or left too little time to read, and INPUT_LOG_TEMPORARILY_UNAVAILABLE (503) when the log cannot be read just now, both with extensions.retryable: retry shortly with the same cursor. A read whose request closed stops as INPUT_LOG_READ_ABORTED (499), which nobody receives. */
|
|
6791
6793
|
inputLogMessages: InputLogMessageConnection;
|
|
6792
6794
|
/** Recorded client sessions of an app with replay logging on (App.replayLoggingEnabled), newest first. With 'manage_apps' on the app you see every session and may filter by userId; anyone else sees only their own, and asking for another user's is FORBIDDEN. Game plane: send the app-scoped token for this app to the app's datacenter. Page with `first` (default 50, max 200) and `after`. INPUT_LOG_UNAVAILABLE when input logging is not configured on this deployment. */
|
|
6793
6795
|
inputLogSessions: InputLogSessionConnection;
|
|
@@ -8630,7 +8632,7 @@ export type UpdateAppInput = {
|
|
|
8630
8632
|
metadata?: InputMaybe<Scalars['String']['input']>;
|
|
8631
8633
|
/** New display name (1-256 chars). Omit to leave unchanged. */
|
|
8632
8634
|
name?: InputMaybe<Scalars['String']['input']>;
|
|
8633
|
-
/** Turn replay logging on (true) or off (false): record every client
|
|
8635
|
+
/** Turn replay logging on (true) or off (false): record every client message the realtime servers accept for this app (keep-alives included), kept for the input log's published retention and billed as stored input logs (input_log_storage_byte_hours, no free allowance). Turning it ON is refused with INPUT_LOG_FUNDS_NEEDED unless the org's wallet can spend at least the projected cost of keeping one retention period of the app's recent traffic, or the org is billing-exempt; turning it off always succeeds, and what was already recorded goes on billing until it ages out. Recording starts and stops within about 15 seconds. Omit to leave unchanged. */
|
|
8634
8636
|
replayLoggingEnabled?: InputMaybe<Scalars['Boolean']['input']>;
|
|
8635
8637
|
/** New lifecycle status; set LIVE to publish, or DRAFT/LIVE to restore an archived app. Omit to leave unchanged. */
|
|
8636
8638
|
status?: InputMaybe<AppStatus>;
|