@crowdedkingdoms/crowdyjs 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/LICENSE +21 -0
  2. package/MIGRATION.md +247 -0
  3. package/README.md +303 -0
  4. package/dist/auth-state.d.ts +11 -0
  5. package/dist/auth-state.d.ts.map +1 -0
  6. package/dist/auth-state.js +13 -0
  7. package/dist/client.d.ts +135 -0
  8. package/dist/client.d.ts.map +1 -0
  9. package/dist/client.js +150 -0
  10. package/dist/crowdy-client.d.ts +182 -0
  11. package/dist/crowdy-client.d.ts.map +1 -0
  12. package/dist/crowdy-client.js +146 -0
  13. package/dist/domains/actors.d.ts +117 -0
  14. package/dist/domains/actors.d.ts.map +1 -0
  15. package/dist/domains/actors.js +140 -0
  16. package/dist/domains/admin.d.ts +61 -0
  17. package/dist/domains/admin.d.ts.map +1 -0
  18. package/dist/domains/admin.js +33 -0
  19. package/dist/domains/appAccess.d.ts +141 -0
  20. package/dist/domains/appAccess.d.ts.map +1 -0
  21. package/dist/domains/appAccess.js +198 -0
  22. package/dist/domains/apps.d.ts +192 -0
  23. package/dist/domains/apps.d.ts.map +1 -0
  24. package/dist/domains/apps.js +217 -0
  25. package/dist/domains/auth.d.ts +163 -0
  26. package/dist/domains/auth.d.ts.map +1 -0
  27. package/dist/domains/auth.js +208 -0
  28. package/dist/domains/avatars.d.ts +94 -0
  29. package/dist/domains/avatars.d.ts.map +1 -0
  30. package/dist/domains/avatars.js +137 -0
  31. package/dist/domains/billing.d.ts +97 -0
  32. package/dist/domains/billing.d.ts.map +1 -0
  33. package/dist/domains/billing.js +131 -0
  34. package/dist/domains/channels.d.ts +293 -0
  35. package/dist/domains/channels.d.ts.map +1 -0
  36. package/dist/domains/channels.js +353 -0
  37. package/dist/domains/chunks.d.ts +133 -0
  38. package/dist/domains/chunks.d.ts.map +1 -0
  39. package/dist/domains/chunks.js +153 -0
  40. package/dist/domains/controlPlane.d.ts +174 -0
  41. package/dist/domains/controlPlane.d.ts.map +1 -0
  42. package/dist/domains/controlPlane.js +252 -0
  43. package/dist/domains/environments.d.ts +155 -0
  44. package/dist/domains/environments.d.ts.map +1 -0
  45. package/dist/domains/environments.js +223 -0
  46. package/dist/domains/gameApps.d.ts +114 -0
  47. package/dist/domains/gameApps.d.ts.map +1 -0
  48. package/dist/domains/gameApps.js +169 -0
  49. package/dist/domains/gameModel.d.ts +668 -0
  50. package/dist/domains/gameModel.d.ts.map +1 -0
  51. package/dist/domains/gameModel.js +816 -0
  52. package/dist/domains/host.d.ts +35 -0
  53. package/dist/domains/host.d.ts.map +1 -0
  54. package/dist/domains/host.js +40 -0
  55. package/dist/domains/organizations.d.ts +179 -0
  56. package/dist/domains/organizations.d.ts.map +1 -0
  57. package/dist/domains/organizations.js +269 -0
  58. package/dist/domains/payments.d.ts +104 -0
  59. package/dist/domains/payments.d.ts.map +1 -0
  60. package/dist/domains/payments.js +129 -0
  61. package/dist/domains/platform.d.ts +49 -0
  62. package/dist/domains/platform.d.ts.map +1 -0
  63. package/dist/domains/platform.js +50 -0
  64. package/dist/domains/quotas.d.ts +62 -0
  65. package/dist/domains/quotas.d.ts.map +1 -0
  66. package/dist/domains/quotas.js +79 -0
  67. package/dist/domains/serverStatus.d.ts +90 -0
  68. package/dist/domains/serverStatus.d.ts.map +1 -0
  69. package/dist/domains/serverStatus.js +104 -0
  70. package/dist/domains/sharedEnvironment.d.ts +133 -0
  71. package/dist/domains/sharedEnvironment.d.ts.map +1 -0
  72. package/dist/domains/sharedEnvironment.js +179 -0
  73. package/dist/domains/state.d.ts +64 -0
  74. package/dist/domains/state.d.ts.map +1 -0
  75. package/dist/domains/state.js +75 -0
  76. package/dist/domains/teams.d.ts +292 -0
  77. package/dist/domains/teams.d.ts.map +1 -0
  78. package/dist/domains/teams.js +352 -0
  79. package/dist/domains/teleport.d.ts +41 -0
  80. package/dist/domains/teleport.d.ts.map +1 -0
  81. package/dist/domains/teleport.js +43 -0
  82. package/dist/domains/udp.d.ts +405 -0
  83. package/dist/domains/udp.d.ts.map +1 -0
  84. package/dist/domains/udp.js +457 -0
  85. package/dist/domains/usage.d.ts +76 -0
  86. package/dist/domains/usage.d.ts.map +1 -0
  87. package/dist/domains/usage.js +110 -0
  88. package/dist/domains/users.d.ts +147 -0
  89. package/dist/domains/users.d.ts.map +1 -0
  90. package/dist/domains/users.js +195 -0
  91. package/dist/domains/voxels.d.ts +136 -0
  92. package/dist/domains/voxels.d.ts.map +1 -0
  93. package/dist/domains/voxels.js +153 -0
  94. package/dist/errors.d.ts +158 -0
  95. package/dist/errors.d.ts.map +1 -0
  96. package/dist/errors.js +142 -0
  97. package/dist/generated/graphql.d.ts +12206 -0
  98. package/dist/generated/graphql.d.ts.map +1 -0
  99. package/dist/generated/graphql.js +474 -0
  100. package/dist/index.d.ts +84 -0
  101. package/dist/index.d.ts.map +1 -0
  102. package/dist/index.js +85 -0
  103. package/dist/logger.d.ts +8 -0
  104. package/dist/logger.d.ts.map +1 -0
  105. package/dist/logger.js +1 -0
  106. package/dist/realtime.d.ts +319 -0
  107. package/dist/realtime.d.ts.map +1 -0
  108. package/dist/realtime.js +390 -0
  109. package/dist/session.d.ts +73 -0
  110. package/dist/session.d.ts.map +1 -0
  111. package/dist/session.js +96 -0
  112. package/dist/subscriptions.d.ts +2 -0
  113. package/dist/subscriptions.d.ts.map +1 -0
  114. package/dist/subscriptions.js +1 -0
  115. package/dist/types.d.ts +658 -0
  116. package/dist/types.d.ts.map +1 -0
  117. package/dist/types.js +61 -0
  118. package/dist/utils.d.ts +98 -0
  119. package/dist/utils.d.ts.map +1 -0
  120. package/dist/utils.js +136 -0
  121. package/dist/world.d.ts +236 -0
  122. package/dist/world.d.ts.map +1 -0
  123. package/dist/world.js +275 -0
  124. package/package.json +73 -0
@@ -0,0 +1,457 @@
1
+ import { ConnectUdpProxyDocument, DisconnectUdpProxyDocument, UdpProxyConnectionStatusDocument, SendActorUpdateDocument, SendVoxelUpdateDocument, SendAudioPacketDocument, SendTextPacketDocument, SendClientEventDocument, SendSingleActorMessageDocument, SendChannelMessageDocument, } from '../generated/graphql.js';
2
+ import { SequenceAllocator } from '../utils.js';
3
+ /**
4
+ * UDP proxy access for browser-style clients that can't open raw UDP sockets.
5
+ * Exposed as `client.udp`; for game loops prefer the ergonomic, app-scoped
6
+ * facade `client.world(appId)`, which passes `appId` for you and tracks the
7
+ * current chunk so you don't repeat it on every send.
8
+ *
9
+ * All send mutations go over the **game-api** GraphQL HTTP endpoint and are
10
+ * forwarded to the assigned game server by the API's UDP proxy module;
11
+ * notifications come back over a single shared `graphql-transport-ws`
12
+ * WebSocket (same game-api endpoint) managed by `SubscriptionManager` — the
13
+ * first subscriber opens it and the last to unsubscribe closes it. Every call
14
+ * requires an authenticated session carrying a **bearer game token** (minted
15
+ * by the management-api, set via `client.auth.login()` or `client.setToken()`);
16
+ * a UDP proxy session is opened lazily on the first send or subscribe if you
17
+ * didn't call {@link connect} first.
18
+ *
19
+ * Each packet type comes in two shapes:
20
+ * - Plain `send*` — fire-and-forget. Resolves to a `boolean` ack that the proxy
21
+ * *accepted the datagram for sending*; it does **not** confirm the world
22
+ * applied it or that it was delivered.
23
+ * - `send*AndWait` — allocates a `sequenceNumber`, then resolves with the
24
+ * server's matching echo (a {@link SpatialNotification}) correlated by that
25
+ * sequence, or rejects on timeout. Only actor and voxel updates are echoed
26
+ * back to the sender (`ActorUpdateResponse` / `VoxelUpdateResponse`); audio,
27
+ * text, and event sends are not.
28
+ *
29
+ * Spatial caveats: a `sequenceNumber` is a uint8 (0-255) used for
30
+ * **correlation only** — not an idempotency key, and the server does not dedupe
31
+ * replays. The **first** spatial message to a brand-new chunk may be dropped
32
+ * server-side while grid permissions load, so clients should re-send (the
33
+ * two-client tests register twice for this reason). To receive any
34
+ * `...AndWait` echo or notification you must have an active {@link subscribe}.
35
+ */
36
+ export class UdpAPI {
37
+ constructor(gql, subs) {
38
+ this.gql = gql;
39
+ this.subs = subs;
40
+ this.sequences = new SequenceAllocator();
41
+ }
42
+ /**
43
+ * Open (or re-fetch) the UDP proxy session for this game token. Idempotent:
44
+ * if a session is already open it returns the existing status; on first open
45
+ * it binds a socket and selects the game server with the fewest clients.
46
+ * Calling this is optional — any `send*` mutation or {@link subscribe} opens a
47
+ * session lazily — but use it to pre-warm the socket or surface auth/
48
+ * connectivity problems eagerly. To force a fresh socket, call
49
+ * {@link disconnect} first.
50
+ *
51
+ * @returns The {@link UdpProxyConnectionStatus}: `connected`, plus (when
52
+ * connected) `serverIp6`, `serverClientPort`, and `lastMessageTime`.
53
+ * @throws {CrowdyGraphQLError} `UNAUTHENTICATED` if the request carries no
54
+ * valid bearer game token.
55
+ */
56
+ async connect() {
57
+ const data = await this.gql.request(ConnectUdpProxyDocument, undefined);
58
+ return data.connectUdpProxy;
59
+ }
60
+ /**
61
+ * Close the UDP proxy session and socket for this game token. Note that
62
+ * unsubscribing from notifications does **not** disconnect — call this (or
63
+ * rely on the server's inactivity timeout) to release the session, e.g. to
64
+ * force a fresh socket on the next {@link connect} or send.
65
+ *
66
+ * @returns `true` once the session has been closed.
67
+ * @throws {CrowdyGraphQLError} on auth failures.
68
+ */
69
+ async disconnect() {
70
+ const data = await this.gql.request(DisconnectUdpProxyDocument, undefined);
71
+ return data.disconnectUdpProxy;
72
+ }
73
+ /**
74
+ * Read the current UDP proxy session status **without** opening one. With no
75
+ * game token it simply reports `connected: false` rather than throwing.
76
+ * Inspect `lastMessageTime` to gauge connection health.
77
+ *
78
+ * @returns The {@link UdpProxyConnectionStatus} (`connected`, plus when
79
+ * connected `serverIp6`, `serverClientPort`, and `lastMessageTime`).
80
+ */
81
+ async connectionStatus() {
82
+ const data = await this.gql.request(UdpProxyConnectionStatusDocument, undefined);
83
+ return data.udpProxyConnectionStatus;
84
+ }
85
+ /**
86
+ * Send an actor (player/NPC) state update for spatial replication to nearby
87
+ * chunks. Fire-and-forget; opens a UDP proxy session automatically if none
88
+ * exists. The applied echo (`ActorUpdateResponse`) and any failure
89
+ * (`GenericErrorResponse`) arrive asynchronously on {@link subscribe},
90
+ * correlated by `sequenceNumber`; use {@link sendActorUpdateAndWait} to await
91
+ * that echo inline.
92
+ *
93
+ * @param input - {@link ActorUpdateRequestInput}:
94
+ * - `appId` — owning app id (`BigInt` as a decimal string).
95
+ * - `chunk` — `{ x, y, z }` chunk address; each axis is a signed int64
96
+ * decimal string. A chunk is a 16x16x16 voxel cube.
97
+ * - `uuid` — the actor id: exactly 32 ASCII characters (the UDP-wire id),
98
+ * **not** an RFC-4122 UUID.
99
+ * - `state` — actor state blob, base64-encoded; may be `''` for a
100
+ * registration-only update.
101
+ * - `distance` — replication radius in chunk units, 0-8 (clamped); defaults
102
+ * to 8 for actor updates.
103
+ * - `decayRate` — replication decay algorithm: 0 none, 1 exponential,
104
+ * 2 linear 50%, 3 linear 25%, 4 linear 10%, 5 linear 5%; defaults to 1
105
+ * (exponential) for actor updates.
106
+ * - `sequenceNumber` — optional uint8 (0-255) correlation id; not an
107
+ * idempotency key.
108
+ * @returns `true` when the datagram was accepted for sending to the game
109
+ * server — **not** confirmation that the world applied it.
110
+ * @throws {CrowdyGraphQLError} e.g. `UNAUTHENTICATED` without a valid game
111
+ * token, or `BAD_USER_INPUT` for a malformed packet.
112
+ */
113
+ async sendActorUpdate(input) {
114
+ const data = await this.gql.request(SendActorUpdateDocument, { input });
115
+ return data.sendActorUpdate;
116
+ }
117
+ /**
118
+ * Send an actor update and wait for the server's applied echo. Allocates a
119
+ * `sequenceNumber` when `input.sequenceNumber` is omitted, registers the wait
120
+ * *before* sending, then resolves with the matching `ActorUpdateResponse`.
121
+ * Requires an active {@link subscribe} so the echo can be delivered over the
122
+ * shared WebSocket.
123
+ *
124
+ * @param input - {@link ActorUpdateRequestInput} (see {@link sendActorUpdate}
125
+ * for field units/encoding). Omit `sequenceNumber` to let the SDK allocate
126
+ * one.
127
+ * @param options - `timeoutMs`: how long to wait for the echo, in
128
+ * milliseconds; defaults to the client's realtime `waitTimeoutMs` (5000).
129
+ * @returns The correlated {@link SpatialNotification} (the `ActorUpdateResponse`
130
+ * echo).
131
+ * @throws {CrowdyGraphQLError} if the underlying send is rejected.
132
+ * @throws {CrowdyTimeoutError} if the HTTP send leg exceeds the client's
133
+ * request timeout.
134
+ * @throws {CrowdyRealtimeError} if no echo arrives within `timeoutMs`
135
+ * (`code === 'UDP_SEQUENCE_TIMEOUT'`, retryable), or the server returns a
136
+ * matching `GenericErrorResponse` (its `code` is the server's `errorCode`).
137
+ * @example
138
+ * ```ts
139
+ * const unsubscribe = client.udp.subscribe({}, appId); // echoes need an open WS
140
+ * const echo = await client.udp.sendActorUpdateAndWait({
141
+ * appId,
142
+ * chunk: { x: '0', y: '0', z: '0' },
143
+ * uuid,
144
+ * state: 'AA==',
145
+ * distance: 8,
146
+ * });
147
+ * console.log(echo.__typename, echo.sequenceNumber);
148
+ * ```
149
+ */
150
+ async sendActorUpdateAndWait(input, options = {}) {
151
+ const request = this.withSequence(input);
152
+ const wait = this.subs.waitForSequence(request.sequenceNumber, options.timeoutMs);
153
+ await this.sendActorUpdate(request);
154
+ return wait;
155
+ }
156
+ /**
157
+ * Send a single voxel (block) update for spatial replication to nearby
158
+ * chunks. Fire-and-forget; opens a UDP proxy session automatically. The
159
+ * applied echo (`VoxelUpdateResponse`) and failures (`GenericErrorResponse`)
160
+ * arrive asynchronously on {@link subscribe}; use
161
+ * {@link sendVoxelUpdateAndWait} to await the echo inline.
162
+ *
163
+ * @param input - {@link VoxelUpdateRequestInput}:
164
+ * - `appId` — owning app id (`BigInt` as a decimal string).
165
+ * - `chunk` — `{ x, y, z }` chunk address (signed int64 decimal strings); a
166
+ * chunk is a 16x16x16 voxel cube.
167
+ * - `uuid` — 32-ASCII-character actor/source id (not RFC-4122).
168
+ * - `voxel` — `{ x, y, z }` voxel coordinates within the chunk; each is an
169
+ * int16 (-32768 to 32767).
170
+ * - `voxelType` — the new voxel type id.
171
+ * - `voxelState` — voxel state blob, base64-encoded.
172
+ * - `distance` — replication radius in chunk units, 0-8 (clamped); defaults
173
+ * to 8 for voxel updates.
174
+ * - `decayRate` — decay algorithm 0-5 (see {@link sendActorUpdate});
175
+ * defaults to 0 (none) for voxel updates.
176
+ * - `sequenceNumber` — optional uint8 (0-255) correlation id.
177
+ * @returns `true` when accepted for sending — **not** confirmation the world
178
+ * applied the change.
179
+ * @throws {CrowdyGraphQLError} on auth/validation failures.
180
+ */
181
+ async sendVoxelUpdate(input) {
182
+ const data = await this.gql.request(SendVoxelUpdateDocument, { input });
183
+ return data.sendVoxelUpdate;
184
+ }
185
+ /**
186
+ * Send a voxel update and wait for the server's applied `VoxelUpdateResponse`
187
+ * echo. Allocates a `sequenceNumber` when omitted and requires an active
188
+ * {@link subscribe}.
189
+ *
190
+ * @param input - {@link VoxelUpdateRequestInput} (see {@link sendVoxelUpdate}
191
+ * for field units/encoding).
192
+ * @param options - `timeoutMs`: echo wait in milliseconds; defaults to the
193
+ * client's realtime `waitTimeoutMs` (5000).
194
+ * @returns The correlated {@link SpatialNotification} (`VoxelUpdateResponse`).
195
+ * @throws {CrowdyGraphQLError} if the underlying send is rejected.
196
+ * @throws {CrowdyTimeoutError} if the HTTP send leg times out.
197
+ * @throws {CrowdyRealtimeError} on echo timeout
198
+ * (`code === 'UDP_SEQUENCE_TIMEOUT'`) or a matching `GenericErrorResponse`
199
+ * (its `code` is the server's `errorCode`).
200
+ */
201
+ async sendVoxelUpdateAndWait(input, options = {}) {
202
+ const request = this.withSequence(input);
203
+ const wait = this.subs.waitForSequence(request.sequenceNumber, options.timeoutMs);
204
+ await this.sendVoxelUpdate(request);
205
+ return wait;
206
+ }
207
+ /**
208
+ * Send a spatial voice/audio packet, fanned out to nearby actors as a
209
+ * `ClientAudioNotification`. Fire-and-forget; opens a UDP proxy session
210
+ * automatically. Voice may be gated by a runtime grid permission
211
+ * (`use_voice_chat`) for the region — if the caller lacks it the game server
212
+ * replies asynchronously with a `GenericErrorResponse` (`errorCode`
213
+ * `UNAUTHORIZED`) on {@link subscribe}. The sender receives no success echo.
214
+ *
215
+ * @param input - {@link ClientAudioPacketInput}:
216
+ * - `appId` — owning app id (`BigInt` as a decimal string).
217
+ * - `chunk` — `{ x, y, z }` chunk address (signed int64 decimal strings).
218
+ * - `uuid` — 32-ASCII-character source id (typically the player; not
219
+ * RFC-4122).
220
+ * - `audioData` — compressed audio, base64-encoded.
221
+ * - `distance` — replication radius in chunk units, 0-8 (clamped); defaults
222
+ * to 1 for audio packets.
223
+ * - `decayRate` — decay algorithm 0-5 (see {@link sendActorUpdate});
224
+ * defaults to 0 (none) for audio packets.
225
+ * - `sequenceNumber` — optional uint8 (0-255) correlation id.
226
+ * @returns `true` when accepted for sending — **not** confirmation of
227
+ * delivery.
228
+ * @throws {CrowdyGraphQLError} on auth/validation failures.
229
+ */
230
+ async sendAudioPacket(input) {
231
+ const data = await this.gql.request(SendAudioPacketDocument, { input });
232
+ return data.sendAudioPacket;
233
+ }
234
+ /**
235
+ * Send a voice/audio packet and wait for a response correlated by
236
+ * `sequenceNumber`. **Note:** the server does not echo audio packets back to
237
+ * the sender (only `GenericErrorResponse` failures are correlated), so on
238
+ * success there is usually nothing to resolve and this rejects on timeout —
239
+ * prefer the fire-and-forget {@link sendAudioPacket} unless you specifically
240
+ * want to surface a send error inline. Requires an active {@link subscribe}.
241
+ *
242
+ * @param input - {@link ClientAudioPacketInput} (see {@link sendAudioPacket}).
243
+ * @param options - `timeoutMs`: wait in milliseconds; defaults to the client's
244
+ * realtime `waitTimeoutMs` (5000).
245
+ * @returns The correlated {@link SpatialNotification}, if one is delivered.
246
+ * @throws {CrowdyGraphQLError} if the underlying send is rejected.
247
+ * @throws {CrowdyTimeoutError} if the HTTP send leg times out.
248
+ * @throws {CrowdyRealtimeError} on timeout (`code === 'UDP_SEQUENCE_TIMEOUT'`)
249
+ * or a matching `GenericErrorResponse` (its `code` is the server's
250
+ * `errorCode`, e.g. `UNAUTHORIZED` for a missing voice permission).
251
+ */
252
+ async sendAudioPacketAndWait(input, options = {}) {
253
+ const request = this.withSequence(input);
254
+ const wait = this.subs.waitForSequence(request.sequenceNumber, options.timeoutMs);
255
+ await this.sendAudioPacket(request);
256
+ return wait;
257
+ }
258
+ /**
259
+ * Send a spatial text/chat packet, fanned out to nearby actors as a
260
+ * `ClientTextNotification`. Fire-and-forget; opens a UDP proxy session
261
+ * automatically. The sender receives no success echo; failures arrive
262
+ * asynchronously as `GenericErrorResponse` on {@link subscribe}.
263
+ *
264
+ * @param input - {@link ClientTextPacketInput}:
265
+ * - `appId` — owning app id (`BigInt` as a decimal string).
266
+ * - `chunk` — `{ x, y, z }` chunk address (signed int64 decimal strings).
267
+ * - `uuid` — 32-ASCII-character source id (not RFC-4122).
268
+ * - `text` — message content, UTF-8 (sent as plain text, not base64).
269
+ * - `distance` — replication radius in chunk units, 0-8 (clamped); defaults
270
+ * to 8 for text packets.
271
+ * - `decayRate` — decay algorithm 0-5 (see {@link sendActorUpdate});
272
+ * defaults to 0 (none) for text packets.
273
+ * - `sequenceNumber` — optional uint8 (0-255) correlation id.
274
+ * @returns `true` when accepted for sending — **not** confirmation of
275
+ * delivery.
276
+ * @throws {CrowdyGraphQLError} on auth/validation failures.
277
+ */
278
+ async sendTextPacket(input) {
279
+ const data = await this.gql.request(SendTextPacketDocument, { input });
280
+ return data.sendTextPacket;
281
+ }
282
+ /**
283
+ * Send a text/chat packet and wait for a response correlated by
284
+ * `sequenceNumber`. **Note:** the server does not echo text packets back to
285
+ * the sender (only `GenericErrorResponse` failures are correlated), so on
286
+ * success there is usually nothing to resolve and this rejects on timeout —
287
+ * prefer the fire-and-forget {@link sendTextPacket} unless you specifically
288
+ * want to surface a send error inline. Requires an active {@link subscribe}.
289
+ *
290
+ * @param input - {@link ClientTextPacketInput} (see {@link sendTextPacket}).
291
+ * @param options - `timeoutMs`: wait in milliseconds; defaults to the client's
292
+ * realtime `waitTimeoutMs` (5000).
293
+ * @returns The correlated {@link SpatialNotification}, if one is delivered.
294
+ * @throws {CrowdyGraphQLError} if the underlying send is rejected.
295
+ * @throws {CrowdyTimeoutError} if the HTTP send leg times out.
296
+ * @throws {CrowdyRealtimeError} on timeout (`code === 'UDP_SEQUENCE_TIMEOUT'`)
297
+ * or a matching `GenericErrorResponse` (its `code` is the server's
298
+ * `errorCode`).
299
+ */
300
+ async sendTextPacketAndWait(input, options = {}) {
301
+ const request = this.withSequence(input);
302
+ const wait = this.subs.waitForSequence(request.sequenceNumber, options.timeoutMs);
303
+ await this.sendTextPacket(request);
304
+ return wait;
305
+ }
306
+ /**
307
+ * Send a custom, app-defined client event for spatial replication to nearby
308
+ * chunks; nearby actors receive it as a `ClientEventNotification`.
309
+ * Fire-and-forget; opens a UDP proxy session automatically. The sender
310
+ * receives no success echo; failures arrive asynchronously as
311
+ * `GenericErrorResponse` on {@link subscribe}.
312
+ *
313
+ * @param input - {@link ClientEventNotificationInput}:
314
+ * - `appId` — owning app id (`BigInt` as a decimal string).
315
+ * - `chunk` — `{ x, y, z }` chunk address (signed int64 decimal strings).
316
+ * - `uuid` — 32-ASCII-character id of the object controlling the event
317
+ * (not RFC-4122).
318
+ * - `eventType` — app-defined event id, a uint16 (0-65535).
319
+ * - `state` — event state blob, base64-encoded; format defined by the event
320
+ * type.
321
+ * - `distance` — replication radius in chunk units, 0-8 (clamped); defaults
322
+ * to 8 for events.
323
+ * - `decayRate` — decay algorithm 0-5 (see {@link sendActorUpdate});
324
+ * defaults to 0 (none) for events.
325
+ * - `sequenceNumber` — optional uint8 (0-255) correlation id.
326
+ * @returns `true` when accepted for sending — **not** confirmation the world
327
+ * processed it.
328
+ * @throws {CrowdyGraphQLError} on auth/validation failures.
329
+ */
330
+ async sendClientEvent(input) {
331
+ const data = await this.gql.request(SendClientEventDocument, { input });
332
+ return data.sendClientEvent;
333
+ }
334
+ /**
335
+ * Send a client event and wait for a response correlated by `sequenceNumber`.
336
+ * **Note:** the server does not echo events back to the sender (only
337
+ * `GenericErrorResponse` failures are correlated), so on success there is
338
+ * usually nothing to resolve and this rejects on timeout — prefer the
339
+ * fire-and-forget {@link sendClientEvent} unless you specifically want to
340
+ * surface a send error inline. Requires an active {@link subscribe}.
341
+ *
342
+ * @param input - {@link ClientEventNotificationInput} (see
343
+ * {@link sendClientEvent}).
344
+ * @param options - `timeoutMs`: wait in milliseconds; defaults to the client's
345
+ * realtime `waitTimeoutMs` (5000).
346
+ * @returns The correlated {@link SpatialNotification}, if one is delivered.
347
+ * @throws {CrowdyGraphQLError} if the underlying send is rejected.
348
+ * @throws {CrowdyTimeoutError} if the HTTP send leg times out.
349
+ * @throws {CrowdyRealtimeError} on timeout (`code === 'UDP_SEQUENCE_TIMEOUT'`)
350
+ * or a matching `GenericErrorResponse` (its `code` is the server's
351
+ * `errorCode`).
352
+ */
353
+ async sendClientEventAndWait(input, options = {}) {
354
+ const request = this.withSequence(input);
355
+ const wait = this.subs.waitForSequence(request.sequenceNumber, options.timeoutMs);
356
+ await this.sendClientEvent(request);
357
+ return wait;
358
+ }
359
+ /**
360
+ * Send a direct actor-to-actor message, delivered only to the actor whose
361
+ * UUID matches `input.targetUuid` (the sender must know that actor's chunk).
362
+ * Fire-and-forget: the sender receives no echo, so there is no
363
+ * `sendSingleActorMessageAndWait` variant. The target receives a
364
+ * `SingleActorMessageNotification` on its `udpNotifications` subscription.
365
+ *
366
+ * @param input - {@link SingleActorMessageInput}:
367
+ * - `appId` — the destination actor's app id (`BigInt` as a decimal string).
368
+ * - `chunk` — the **destination** actor's current `{ x, y, z }` chunk
369
+ * (signed int64 decimal strings); the sender must know it.
370
+ * - `targetUuid` — the destination actor's id: exactly 32 ASCII characters
371
+ * (not RFC-4122).
372
+ * - `payload` — message body, base64-encoded; opaque to the server (embed
373
+ * the sender identity yourself if needed).
374
+ * - `sequenceNumber` — optional uint8 (0-255) correlation id (used only to
375
+ * correlate a `GenericErrorResponse`).
376
+ * @returns `true` when accepted for sending — **not** confirmation of
377
+ * delivery.
378
+ * @throws {CrowdyGraphQLError} on auth/validation failures.
379
+ */
380
+ async sendSingleActorMessage(input) {
381
+ const data = await this.gql.request(SendSingleActorMessageDocument, {
382
+ input,
383
+ });
384
+ return data.sendSingleActorMessage;
385
+ }
386
+ /**
387
+ * Publish a message to a channel. Delivered to every active member of the
388
+ * channel (regardless of location) as a `ChannelMessageNotification` on their
389
+ * `udpNotifications` subscription. Requires the channel `send_messages`
390
+ * permission; the server drops the message otherwise. Fire-and-forget: the
391
+ * sender receives no echo.
392
+ *
393
+ * @param input - {@link ChannelMessageInput}:
394
+ * - `channelId` — the channel id (`groups.group_id`) as a `BigInt` decimal
395
+ * string.
396
+ * - `uuid` — the sender's own actor id: exactly 32 ASCII characters (not
397
+ * RFC-4122).
398
+ * - `payload` — message body, base64-encoded; opaque to the server, max
399
+ * 1024 bytes.
400
+ * - `sequenceNumber` — optional uint8 (0-255) correlation id.
401
+ * @returns `true` when accepted for sending — **not** confirmation of
402
+ * delivery; note that a message dropped for a missing `send_messages`
403
+ * permission still returns `true`.
404
+ * @throws {CrowdyGraphQLError} on auth/validation failures.
405
+ */
406
+ async sendChannelMessage(input) {
407
+ const data = await this.gql.request(SendChannelMessageDocument, { input });
408
+ return data.sendChannelMessage;
409
+ }
410
+ /**
411
+ * Subscribe to udpNotifications for a single app. Pass any combination of
412
+ * typename handlers; the returned function detaches all of them. The first
413
+ * subscriber opens the shared WebSocket; the last one to leave closes it.
414
+ *
415
+ * `appId` is required and scopes the subscription to one app: the game-api
416
+ * only delivers that app's spatial notifications and rejects app-agnostic
417
+ * subscriptions (a single game token reused across apps would otherwise
418
+ * cross-deliver). Use a separate client per app (sharing the token store).
419
+ *
420
+ * A missing `appId` is rejected by the game-api with a `RealtimeConnectionEvent`
421
+ * (`code === 'APP_ID_REQUIRED'`) delivered to your `connectionEvent` handler,
422
+ * after which the stream ends. Subscribing without a session token throws
423
+ * synchronously instead.
424
+ *
425
+ * @param handlers - {@link UdpNotificationHandlers}: optional per-typename
426
+ * callbacks (`actorUpdate`, `voxelUpdate`, `audio`, `text`, `clientEvent`,
427
+ * `singleActorMessage`, `channelMessage`, `genericError`, `connectionEvent`,
428
+ * etc.) plus `any` (every notification) and `error` (a
429
+ * {@link CrowdyRealtimeError}).
430
+ * @param appId - The app to scope delivery to (`BigInt` as a decimal string).
431
+ * Required.
432
+ * @returns An unsubscribe function that detaches these handlers (and closes
433
+ * the shared WebSocket if it was the last subscription).
434
+ * @throws {CrowdyRealtimeError} `code === 'AUTH_REQUIRED'` if called with no
435
+ * session token. Server-side connection problems (e.g. `APP_ID_REQUIRED`,
436
+ * `UDP_PROXY_CONNECTION_FAILED`) are delivered to the `connectionEvent` /
437
+ * `error` handlers rather than thrown.
438
+ * @example
439
+ * ```ts
440
+ * const unsubscribe = client.udp.subscribe({
441
+ * actorUpdate: (n) => console.log('actor', n.uuid, n.sequenceNumber),
442
+ * genericError: (e) => console.warn('udp error', e.errorCode),
443
+ * }, appId);
444
+ * // later…
445
+ * unsubscribe();
446
+ * ```
447
+ */
448
+ subscribe(handlers, appId) {
449
+ return this.subs.subscribe(handlers, appId);
450
+ }
451
+ withSequence(input) {
452
+ return {
453
+ ...input,
454
+ sequenceNumber: input.sequenceNumber ?? this.sequences.next(),
455
+ };
456
+ }
457
+ }
@@ -0,0 +1,76 @@
1
+ import type { GraphQLClient } from '../client.js';
2
+ import { type EnvironmentUsageSummaryQuery, type OrgUsageByEnvironmentQuery, type EnvironmentUsageByAppQuery, type AppGraphqlOperationsQuery, type AppUsageSummaryQuery, type PlayerPulseQuery } from '../generated/graphql.js';
3
+ /**
4
+ * Replication + GraphQL usage reporting — exposed as `client.usage` (and
5
+ * grouped under `client.admin`).
6
+ *
7
+ * Targets the **management-api**. All operations are read-only observability and
8
+ * require the `view_usage` org permission. `since` args are ISO-8601 `DateTime`
9
+ * strings; byte/message counters are returned as string counters because they
10
+ * can exceed the 32-bit Int range. `BigInt` ids are decimal strings.
11
+ *
12
+ * @throws {CrowdyGraphQLError} `UNAUTHENTICATED` / `FORBIDDEN` / `SCOPE_MISSING`
13
+ * per the permission notes above.
14
+ */
15
+ export declare class UsageAPI {
16
+ private readonly management;
17
+ constructor(management: GraphQLClient);
18
+ /**
19
+ * Per-minute replication + GraphQL usage time series for one environment,
20
+ * with rate peaks and live Buddy rates.
21
+ *
22
+ * @param orgId - Numeric org id.
23
+ * @param environmentSlug - Environment slug to report on.
24
+ * @param since - Start of the window (ISO-8601 DateTime) up to now.
25
+ * @returns The {@link EnvironmentUsageSummary}.
26
+ */
27
+ environmentSummary(orgId: string, environmentSlug: string, since: string): Promise<EnvironmentUsageSummaryQuery['environmentUsageSummary']>;
28
+ /**
29
+ * Aggregate byte totals per environment across an org for a window.
30
+ *
31
+ * @param orgId - Numeric org id.
32
+ * @param since - Start of the window (ISO-8601 DateTime) up to now.
33
+ * @returns One rollup row per environment.
34
+ */
35
+ orgByEnvironment(orgId: string, since: string): Promise<OrgUsageByEnvironmentQuery['orgUsageByEnvironment']>;
36
+ /**
37
+ * Aggregate byte totals per app for the apps linked to an environment.
38
+ *
39
+ * @param orgId - Numeric org id.
40
+ * @param environmentSlug - Environment slug whose apps to roll up.
41
+ * @param since - Start of the window (ISO-8601 DateTime) up to now.
42
+ * @returns One rollup row per app.
43
+ */
44
+ environmentByApp(orgId: string, environmentSlug: string, since: string): Promise<EnvironmentUsageByAppQuery['environmentUsageByApp']>;
45
+ /**
46
+ * Top GraphQL operations for an app ranked by bytes.
47
+ *
48
+ * @param orgId - Numeric org id.
49
+ * @param appId - Numeric app id.
50
+ * @param since - Start of the window (ISO-8601 DateTime) up to now.
51
+ * @param limit - Max operations to return (default 20).
52
+ * @returns The top operations by usage.
53
+ */
54
+ appGraphqlOperations(orgId: string, appId: string, since: string, limit?: number): Promise<AppGraphqlOperationsQuery['appGraphqlOperations']>;
55
+ /**
56
+ * Byte totals plus top GraphQL operations for one app over a window.
57
+ *
58
+ * @param orgId - Numeric org id.
59
+ * @param appId - Numeric app id.
60
+ * @param since - Start of the window (ISO-8601 DateTime) up to now.
61
+ * @param operationLimit - Max top operations to include (default 20).
62
+ * @returns The {@link AppUsageSummary}.
63
+ */
64
+ appSummary(orgId: string, appId: string, since: string, operationLimit?: number): Promise<AppUsageSummaryQuery['appUsageSummary']>;
65
+ /**
66
+ * Live concurrent-player "pulse" for an org: current and all-time-peak
67
+ * concurrents for the org, the site-wide live total (aggregate only), and the
68
+ * org's percentile within the studio pool. Requires the `view_usage` org
69
+ * permission.
70
+ *
71
+ * @param orgId - Numeric org id.
72
+ * @returns The {@link PlayerPulse} snapshot.
73
+ */
74
+ playerPulse(orgId: string): Promise<PlayerPulseQuery['playerPulse']>;
75
+ }
76
+ //# sourceMappingURL=usage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usage.d.ts","sourceRoot":"","sources":["../../src/domains/usage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAOL,KAAK,4BAA4B,EACjC,KAAK,0BAA0B,EAC/B,KAAK,0BAA0B,EAC/B,KAAK,yBAAyB,EAC9B,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,EACtB,MAAM,yBAAyB,CAAC;AAEjC;;;;;;;;;;;GAWG;AACH,qBAAa,QAAQ;IACP,OAAO,CAAC,QAAQ,CAAC,UAAU;gBAAV,UAAU,EAAE,aAAa;IAEtD;;;;;;;;OAQG;IACG,kBAAkB,CACtB,KAAK,EAAE,MAAM,EACb,eAAe,EAAE,MAAM,EACvB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,4BAA4B,CAAC,yBAAyB,CAAC,CAAC;IAQnE;;;;;;OAMG;IACG,gBAAgB,CACpB,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,0BAA0B,CAAC,uBAAuB,CAAC,CAAC;IAQ/D;;;;;;;OAOG;IACG,gBAAgB,CACpB,KAAK,EAAE,MAAM,EACb,eAAe,EAAE,MAAM,EACvB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,0BAA0B,CAAC,uBAAuB,CAAC,CAAC;IAS/D;;;;;;;;OAQG;IACG,oBAAoB,CACxB,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,yBAAyB,CAAC,sBAAsB,CAAC,CAAC;IAU7D;;;;;;;;OAQG;IACG,UAAU,CACd,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,cAAc,CAAC,EAAE,MAAM,GACtB,OAAO,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,CAAC;IAUnD;;;;;;;;OAQG;IACG,WAAW,CACf,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,gBAAgB,CAAC,aAAa,CAAC,CAAC;CAI5C"}
@@ -0,0 +1,110 @@
1
+ import { EnvironmentUsageSummaryDocument, OrgUsageByEnvironmentDocument, EnvironmentUsageByAppDocument, AppGraphqlOperationsDocument, AppUsageSummaryDocument, PlayerPulseDocument, } from '../generated/graphql.js';
2
+ /**
3
+ * Replication + GraphQL usage reporting — exposed as `client.usage` (and
4
+ * grouped under `client.admin`).
5
+ *
6
+ * Targets the **management-api**. All operations are read-only observability and
7
+ * require the `view_usage` org permission. `since` args are ISO-8601 `DateTime`
8
+ * strings; byte/message counters are returned as string counters because they
9
+ * can exceed the 32-bit Int range. `BigInt` ids are decimal strings.
10
+ *
11
+ * @throws {CrowdyGraphQLError} `UNAUTHENTICATED` / `FORBIDDEN` / `SCOPE_MISSING`
12
+ * per the permission notes above.
13
+ */
14
+ export class UsageAPI {
15
+ constructor(management) {
16
+ this.management = management;
17
+ }
18
+ /**
19
+ * Per-minute replication + GraphQL usage time series for one environment,
20
+ * with rate peaks and live Buddy rates.
21
+ *
22
+ * @param orgId - Numeric org id.
23
+ * @param environmentSlug - Environment slug to report on.
24
+ * @param since - Start of the window (ISO-8601 DateTime) up to now.
25
+ * @returns The {@link EnvironmentUsageSummary}.
26
+ */
27
+ async environmentSummary(orgId, environmentSlug, since) {
28
+ const data = await this.management.request(EnvironmentUsageSummaryDocument, { orgId, environmentSlug, since });
29
+ return data.environmentUsageSummary;
30
+ }
31
+ /**
32
+ * Aggregate byte totals per environment across an org for a window.
33
+ *
34
+ * @param orgId - Numeric org id.
35
+ * @param since - Start of the window (ISO-8601 DateTime) up to now.
36
+ * @returns One rollup row per environment.
37
+ */
38
+ async orgByEnvironment(orgId, since) {
39
+ const data = await this.management.request(OrgUsageByEnvironmentDocument, {
40
+ orgId,
41
+ since,
42
+ });
43
+ return data.orgUsageByEnvironment;
44
+ }
45
+ /**
46
+ * Aggregate byte totals per app for the apps linked to an environment.
47
+ *
48
+ * @param orgId - Numeric org id.
49
+ * @param environmentSlug - Environment slug whose apps to roll up.
50
+ * @param since - Start of the window (ISO-8601 DateTime) up to now.
51
+ * @returns One rollup row per app.
52
+ */
53
+ async environmentByApp(orgId, environmentSlug, since) {
54
+ const data = await this.management.request(EnvironmentUsageByAppDocument, {
55
+ orgId,
56
+ environmentSlug,
57
+ since,
58
+ });
59
+ return data.environmentUsageByApp;
60
+ }
61
+ /**
62
+ * Top GraphQL operations for an app ranked by bytes.
63
+ *
64
+ * @param orgId - Numeric org id.
65
+ * @param appId - Numeric app id.
66
+ * @param since - Start of the window (ISO-8601 DateTime) up to now.
67
+ * @param limit - Max operations to return (default 20).
68
+ * @returns The top operations by usage.
69
+ */
70
+ async appGraphqlOperations(orgId, appId, since, limit) {
71
+ const data = await this.management.request(AppGraphqlOperationsDocument, {
72
+ orgId,
73
+ appId,
74
+ since,
75
+ limit,
76
+ });
77
+ return data.appGraphqlOperations;
78
+ }
79
+ /**
80
+ * Byte totals plus top GraphQL operations for one app over a window.
81
+ *
82
+ * @param orgId - Numeric org id.
83
+ * @param appId - Numeric app id.
84
+ * @param since - Start of the window (ISO-8601 DateTime) up to now.
85
+ * @param operationLimit - Max top operations to include (default 20).
86
+ * @returns The {@link AppUsageSummary}.
87
+ */
88
+ async appSummary(orgId, appId, since, operationLimit) {
89
+ const data = await this.management.request(AppUsageSummaryDocument, {
90
+ orgId,
91
+ appId,
92
+ since,
93
+ operationLimit,
94
+ });
95
+ return data.appUsageSummary;
96
+ }
97
+ /**
98
+ * Live concurrent-player "pulse" for an org: current and all-time-peak
99
+ * concurrents for the org, the site-wide live total (aggregate only), and the
100
+ * org's percentile within the studio pool. Requires the `view_usage` org
101
+ * permission.
102
+ *
103
+ * @param orgId - Numeric org id.
104
+ * @returns The {@link PlayerPulse} snapshot.
105
+ */
106
+ async playerPulse(orgId) {
107
+ const data = await this.management.request(PlayerPulseDocument, { orgId });
108
+ return data.playerPulse;
109
+ }
110
+ }