@crowdedkingdomstudios/crowdyjs 5.2.0 → 5.3.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 (74) hide show
  1. package/MIGRATION.md +22 -0
  2. package/dist/client.d.ts +98 -5
  3. package/dist/client.d.ts.map +1 -1
  4. package/dist/client.js +74 -5
  5. package/dist/crowdy-client.d.ts +31 -0
  6. package/dist/crowdy-client.d.ts.map +1 -1
  7. package/dist/crowdy-client.js +8 -0
  8. package/dist/domains/actors.d.ts +87 -4
  9. package/dist/domains/actors.d.ts.map +1 -1
  10. package/dist/domains/actors.js +87 -4
  11. package/dist/domains/apps.d.ts +95 -41
  12. package/dist/domains/apps.d.ts.map +1 -1
  13. package/dist/domains/apps.js +80 -33
  14. package/dist/domains/auth.d.ts +139 -19
  15. package/dist/domains/auth.d.ts.map +1 -1
  16. package/dist/domains/auth.js +137 -17
  17. package/dist/domains/channels.d.ts +264 -5
  18. package/dist/domains/channels.d.ts.map +1 -1
  19. package/dist/domains/channels.js +264 -5
  20. package/dist/domains/chunks.d.ts +116 -3
  21. package/dist/domains/chunks.d.ts.map +1 -1
  22. package/dist/domains/chunks.js +116 -3
  23. package/dist/domains/gameModel.d.ts +412 -6
  24. package/dist/domains/gameModel.d.ts.map +1 -1
  25. package/dist/domains/gameModel.js +412 -6
  26. package/dist/domains/platform.d.ts +36 -20
  27. package/dist/domains/platform.d.ts.map +1 -1
  28. package/dist/domains/platform.js +29 -18
  29. package/dist/domains/serverStatus.d.ts +74 -6
  30. package/dist/domains/serverStatus.d.ts.map +1 -1
  31. package/dist/domains/serverStatus.js +74 -6
  32. package/dist/domains/state.d.ts +50 -2
  33. package/dist/domains/state.d.ts.map +1 -1
  34. package/dist/domains/state.js +50 -2
  35. package/dist/domains/teams.d.ts +263 -5
  36. package/dist/domains/teams.d.ts.map +1 -1
  37. package/dist/domains/teams.js +263 -5
  38. package/dist/domains/teleport.d.ts +30 -2
  39. package/dist/domains/teleport.d.ts.map +1 -1
  40. package/dist/domains/teleport.js +30 -2
  41. package/dist/domains/udp.d.ts +341 -5
  42. package/dist/domains/udp.d.ts.map +1 -1
  43. package/dist/domains/udp.js +341 -5
  44. package/dist/domains/users.d.ts +42 -11
  45. package/dist/domains/users.d.ts.map +1 -1
  46. package/dist/domains/users.js +41 -10
  47. package/dist/domains/voxels.d.ts +107 -2
  48. package/dist/domains/voxels.d.ts.map +1 -1
  49. package/dist/domains/voxels.js +107 -2
  50. package/dist/errors.d.ts +116 -0
  51. package/dist/errors.d.ts.map +1 -1
  52. package/dist/errors.js +100 -0
  53. package/dist/generated/graphql.d.ts +5 -1
  54. package/dist/generated/graphql.d.ts.map +1 -1
  55. package/dist/generated/graphql.js +5 -1
  56. package/dist/index.d.ts +2 -1
  57. package/dist/index.d.ts.map +1 -1
  58. package/dist/index.js +2 -1
  59. package/dist/realtime.d.ts +226 -0
  60. package/dist/realtime.d.ts.map +1 -1
  61. package/dist/realtime.js +90 -0
  62. package/dist/session.d.ts +46 -0
  63. package/dist/session.d.ts.map +1 -1
  64. package/dist/session.js +35 -0
  65. package/dist/types.d.ts +429 -0
  66. package/dist/types.d.ts.map +1 -1
  67. package/dist/types.js +53 -0
  68. package/dist/utils.d.ts +86 -0
  69. package/dist/utils.d.ts.map +1 -1
  70. package/dist/utils.js +86 -0
  71. package/dist/world.d.ts +192 -0
  72. package/dist/world.d.ts.map +1 -1
  73. package/dist/world.js +170 -0
  74. package/package.json +1 -1
@@ -1,92 +1,351 @@
1
1
  import { MyChannelsDocument, ChannelsDocument, ChannelDocument, ChannelMembersDocument, ChannelRolesDocument, ChannelPolicyDocument, SetChannelPolicyDocument, CreateChannelDocument, UpdateChannelDocument, DeleteChannelDocument, JoinChannelDocument, RequestToJoinChannelDocument, LeaveChannelDocument, AddChannelMemberDocument, RemoveChannelMemberDocument, SetChannelMemberRolesDocument, CreateChannelRoleDocument, UpdateChannelRoleDocument, DeleteChannelRoleDocument, } from '../generated/graphql.js';
2
2
  /**
3
- * Channels: app-scoped player groups with role-gated messaging, built on the
4
- * same generic groups subsystem as Teams (group_type='channel'). Channel
5
- * CRUD/membership runs over the game-api GraphQL endpoint; publishing and
6
- * receiving channel messages happens over the realtime UDP path (`client.udp`).
3
+ * Channels: app-scoped, location-independent pub/sub messaging groups on the
4
+ * **game-api**. Exposed as `client.channels`.
7
5
  *
8
- * Exposed as `client.channels`.
6
+ * A channel is a named subscriber set with role-gated messaging, built on the
7
+ * same generic groups subsystem as Teams (`group_type='channel'`). The methods
8
+ * here manage a channel's lifecycle and configuration — creating channels,
9
+ * listing/fetching them, managing membership (join/leave/add/remove), roles,
10
+ * and the per-app channel policy — all over the game-api GraphQL endpoint.
11
+ *
12
+ * Realtime message delivery is a **separate** path: publish to a channel with
13
+ * `client.udp.sendChannelMessage(...)` over UDP. That call requires the
14
+ * channel's `send_messages` permission and fans the payload out to every
15
+ * active member (as a `ChannelMessageNotification` on `client.udp`
16
+ * notifications) rather than chunk-routing it. The methods on this class never
17
+ * carry message payloads — they only manage who belongs to a channel and what
18
+ * each member may do.
19
+ *
20
+ * `BigInt` ids (`appId`, `groupId`, `userId`, `groupRoleId`) are sent and
21
+ * received as decimal strings.
22
+ *
23
+ * Every method requires an authenticated session (a Bearer token set via
24
+ * `client.auth.login()` or `client.setToken()`) and the caller must be
25
+ * entitled to the target app; membership/role/policy mutations additionally
26
+ * require a specific channel permission (e.g. `manage_group`, `manage_members`,
27
+ * `manage_roles`) or app-admin (`manage_apps`). Failures throw
28
+ * {@link CrowdyGraphQLError} carrying a stable `extensions.code` such as
29
+ * `UNAUTHENTICATED`, `SCOPE_MISSING`, or `FORBIDDEN`.
9
30
  */
10
31
  export class ChannelsAPI {
11
32
  constructor(gql) {
12
33
  this.gql = gql;
13
34
  }
14
35
  // -- Queries --------------------------------------------------------------
36
+ /**
37
+ * List the caller's own channels in an app, each with the caller's roles and
38
+ * effective channel permissions (e.g. whether they hold `send_messages`). Use
39
+ * this to discover which channels the current user can read and post in.
40
+ *
41
+ * @param appId - The app (tenant) to list the caller's channels within, as a
42
+ * decimal `BigInt` string.
43
+ * @returns One {@link GroupMembership} per channel the caller belongs to
44
+ * (the channel {@link Group}, the caller's {@link GroupRole}s, their
45
+ * effective permission keys, and the join time).
46
+ * @throws {CrowdyGraphQLError} `UNAUTHENTICATED` if there is no valid token,
47
+ * or `SCOPE_MISSING` / `FORBIDDEN` if the caller isn't entitled to the app.
48
+ */
15
49
  async mine(appId) {
16
50
  const data = await this.gql.request(MyChannelsDocument, { appId });
17
51
  return data.myChannels;
18
52
  }
53
+ /**
54
+ * List all active channels in an app — not just the caller's. Use
55
+ * {@link mine} instead when you only want the channels the caller belongs to.
56
+ *
57
+ * @param appId - The app (tenant) whose channels to list, as a decimal
58
+ * `BigInt` string.
59
+ * @returns Every active channel in the app as {@link Group} records.
60
+ * @throws {CrowdyGraphQLError} `UNAUTHENTICATED`, or `SCOPE_MISSING` /
61
+ * `FORBIDDEN` if the caller isn't entitled to the app.
62
+ */
19
63
  async list(appId) {
20
64
  const data = await this.gql.request(ChannelsDocument, { appId });
21
65
  return data.channels;
22
66
  }
67
+ /**
68
+ * Fetch a single channel by its group id.
69
+ *
70
+ * @param groupId - The channel's group id, as a decimal `BigInt` string.
71
+ * @returns The channel as a {@link Group}.
72
+ * @throws {CrowdyGraphQLError} if the id does not resolve to a channel (e.g.
73
+ * it is a team, or does not exist), or `UNAUTHENTICATED` / `FORBIDDEN` if
74
+ * the caller isn't entitled to the app.
75
+ */
23
76
  async get(groupId) {
24
77
  const data = await this.gql.request(ChannelDocument, { groupId });
25
78
  return data.channel;
26
79
  }
80
+ /**
81
+ * List a channel's members — its subscriber set, including pending join
82
+ * requests — each with their membership status and roles.
83
+ *
84
+ * @param groupId - The channel whose members to list, as a decimal `BigInt`
85
+ * string.
86
+ * @returns The channel's members as {@link GroupMember} records.
87
+ * @throws {CrowdyGraphQLError} `UNAUTHENTICATED` / `FORBIDDEN` if the caller
88
+ * isn't entitled to the channel's app.
89
+ */
27
90
  async members(groupId) {
28
91
  const data = await this.gql.request(ChannelMembersDocument, { groupId });
29
92
  return data.channelMembers;
30
93
  }
94
+ /**
95
+ * List a channel's roles, including the system `leader` role and any default
96
+ * `member` role (which typically grants `send_messages`).
97
+ *
98
+ * @param groupId - The channel whose roles to list, as a decimal `BigInt`
99
+ * string.
100
+ * @returns The channel's roles as {@link GroupRole} records, each with its
101
+ * granted permission keys.
102
+ * @throws {CrowdyGraphQLError} `UNAUTHENTICATED` / `FORBIDDEN` if the caller
103
+ * isn't entitled to the channel's app.
104
+ */
31
105
  async roles(groupId) {
32
106
  const data = await this.gql.request(ChannelRolesDocument, { groupId });
33
107
  return data.channelRoles;
34
108
  }
109
+ /**
110
+ * Read an app's current channel policy: who may create channels and the
111
+ * default membership policy applied to new channels. Falls back to app
112
+ * defaults when unset.
113
+ *
114
+ * @param appId - The app (tenant) whose channel policy to read, as a decimal
115
+ * `BigInt` string.
116
+ * @returns The effective {@link AppGroupPolicy} for channels in the app
117
+ * (creation policy, default membership policy, and any member/group caps).
118
+ * @throws {CrowdyGraphQLError} `UNAUTHENTICATED` / `FORBIDDEN` if the caller
119
+ * isn't entitled to the app.
120
+ */
35
121
  async policy(appId) {
36
122
  const data = await this.gql.request(ChannelPolicyDocument, { appId });
37
123
  return data.channelPolicy;
38
124
  }
39
125
  // -- Channel mutations ----------------------------------------------------
126
+ /**
127
+ * Create a channel in an app. Whether the caller may create one is governed
128
+ * by the per-app channel creation policy (`admin` | `member` | `anyone`).
129
+ * The caller becomes the owner with a system `leader` role. When
130
+ * `input.membersCanSend` is true (the default) a default `member` role
131
+ * granting `send_messages` is created and auto-assigned to joiners (an open
132
+ * chat channel); when false, only roles you explicitly grant may post (an
133
+ * announce / read-only channel).
134
+ *
135
+ * @param input - {@link CreateChannelInput}: the owning `appId`, the channel
136
+ * `name` (max 128 chars, unique per app + type), and optional
137
+ * `description`, `membershipPolicy` (`open` | `request` | `invite` |
138
+ * `admin`; defaults to the app policy), and `membersCanSend` flag.
139
+ * @returns The newly created channel as a {@link Group}.
140
+ * @throws {CrowdyGraphQLError} `BAD_USER_INPUT` (e.g. name too long or a
141
+ * duplicate name), `FORBIDDEN` if the channel policy disallows creation, or
142
+ * `UNAUTHENTICATED`.
143
+ */
40
144
  async create(input) {
41
145
  const data = await this.gql.request(CreateChannelDocument, { input });
42
146
  return data.createChannel;
43
147
  }
148
+ /**
149
+ * Update a channel's name, description, and/or membership policy. Only the
150
+ * fields present on `input` change; omitted fields are left as-is. Requires
151
+ * the `manage_group` channel permission (app admins bypass).
152
+ *
153
+ * @param input - {@link UpdateChannelInput}: the `groupId` plus the fields to
154
+ * change — `name` (max 128 chars), `description`, and/or `membershipPolicy`
155
+ * (`open` | `request` | `invite` | `admin`).
156
+ * @returns The updated channel as a {@link Group}.
157
+ * @throws {CrowdyGraphQLError} `FORBIDDEN` / `SCOPE_MISSING` if the caller
158
+ * lacks `manage_group`, `BAD_USER_INPUT` on a validation failure, or
159
+ * `UNAUTHENTICATED`.
160
+ */
44
161
  async update(input) {
45
162
  const data = await this.gql.request(UpdateChannelDocument, { input });
46
163
  return data.updateChannel;
47
164
  }
165
+ /**
166
+ * Delete a channel. Requires the `manage_group` channel permission (app
167
+ * admins bypass). **Destructive**: cascades to the channel's members and
168
+ * roles and notifies Buddy servers to tear down message routing for the
169
+ * channel.
170
+ *
171
+ * @param groupId - The channel to delete, as a decimal `BigInt` string.
172
+ * @returns `true` on success.
173
+ * @throws {CrowdyGraphQLError} `FORBIDDEN` / `SCOPE_MISSING` if the caller
174
+ * lacks `manage_group`, or `UNAUTHENTICATED`.
175
+ */
48
176
  async remove(groupId) {
49
177
  const data = await this.gql.request(DeleteChannelDocument, { groupId });
50
178
  return data.deleteChannel;
51
179
  }
180
+ /**
181
+ * Set who may create channels in an app and the default membership policy
182
+ * for new channels. Requires app-admin (`manage_apps`). Affects future
183
+ * channel creation only — existing channels are unchanged.
184
+ *
185
+ * @param input - {@link SetChannelPolicyInput}: the `appId`, the
186
+ * `creationPolicy` (`admin` | `member` | `anyone`), the
187
+ * `defaultMembershipPolicy` (`open` | `request` | `invite` | `admin`), and
188
+ * optional `maxMembers` / `maxGroupsPerUser` caps (`null` = unlimited).
189
+ * @returns The updated {@link AppGroupPolicy}.
190
+ * @throws {CrowdyGraphQLError} `FORBIDDEN` / `SCOPE_MISSING` if the caller
191
+ * isn't app-admin, `BAD_USER_INPUT` on an invalid policy value, or
192
+ * `UNAUTHENTICATED`.
193
+ */
52
194
  async setPolicy(input) {
53
195
  const data = await this.gql.request(SetChannelPolicyDocument, { input });
54
196
  return data.setChannelPolicy;
55
197
  }
56
198
  // -- Membership mutations -------------------------------------------------
199
+ /**
200
+ * Join a channel as the caller (subscribe to it). Honors the channel's
201
+ * membership policy: `open` → active immediately; `request` → pending until
202
+ * a manager approves; `invite` / `admin` → rejected. On becoming active,
203
+ * Buddy is notified with the caller's effective send permission so message
204
+ * routing starts.
205
+ *
206
+ * @param groupId - The channel to join, as a decimal `BigInt` string.
207
+ * @returns The caller's {@link GroupMember} record; its `status` reflects
208
+ * `active` vs. `pending`.
209
+ * @throws {CrowdyGraphQLError} `FORBIDDEN` if the membership policy rejects
210
+ * the join, or `UNAUTHENTICATED`.
211
+ */
57
212
  async join(groupId) {
58
213
  const data = await this.gql.request(JoinChannelDocument, { groupId });
59
214
  return data.joinChannel;
60
215
  }
216
+ /**
217
+ * Request to join a request-only channel, creating a pending membership a
218
+ * manager can later approve via {@link addMember}. Behaves identically to
219
+ * {@link join}; it exists as a clearer name for request-policy UIs.
220
+ *
221
+ * @param groupId - The request-only channel to request to join, as a decimal
222
+ * `BigInt` string.
223
+ * @returns The caller's pending {@link GroupMember} record.
224
+ * @throws {CrowdyGraphQLError} `FORBIDDEN` if the membership policy rejects
225
+ * the request, or `UNAUTHENTICATED`.
226
+ */
61
227
  async requestToJoin(groupId) {
62
228
  const data = await this.gql.request(RequestToJoinChannelDocument, { groupId });
63
229
  return data.requestToJoinChannel;
64
230
  }
231
+ /**
232
+ * Leave a channel (unsubscribe the caller). Notifies Buddy to stop routing
233
+ * messages to the caller.
234
+ *
235
+ * @param groupId - The channel to leave, as a decimal `BigInt` string.
236
+ * @returns `true` if a membership was removed (`false` if the caller wasn't
237
+ * a member).
238
+ * @throws {CrowdyGraphQLError} `UNAUTHENTICATED`.
239
+ */
65
240
  async leave(groupId) {
66
241
  const data = await this.gql.request(LeaveChannelDocument, { groupId });
67
242
  return data.leaveChannel;
68
243
  }
244
+ /**
245
+ * Add a user to a channel, or approve their pending join request (upserts
246
+ * the membership to `active`). Requires the `manage_members` channel
247
+ * permission (app admins bypass). Auto-assigns the channel's default role if
248
+ * configured and notifies Buddy with the member's effective send permission.
249
+ *
250
+ * @param groupId - The channel to add the user to, as a decimal `BigInt`
251
+ * string.
252
+ * @param userId - The user to add or approve, as a decimal `BigInt` string.
253
+ * @returns The added/approved {@link GroupMember} record.
254
+ * @throws {CrowdyGraphQLError} `FORBIDDEN` / `SCOPE_MISSING` if the caller
255
+ * lacks `manage_members`, or `UNAUTHENTICATED`.
256
+ */
69
257
  async addMember(groupId, userId) {
70
258
  const data = await this.gql.request(AddChannelMemberDocument, { groupId, userId });
71
259
  return data.addChannelMember;
72
260
  }
261
+ /**
262
+ * Remove a member from a channel. Requires the `manage_members` channel
263
+ * permission, except that any member may remove themselves (pass their own
264
+ * `userId`). Notifies Buddy to stop routing messages to the removed member.
265
+ *
266
+ * @param groupId - The channel to remove the user from, as a decimal
267
+ * `BigInt` string.
268
+ * @param userId - The user to remove; may be the caller's own id to
269
+ * self-remove. Decimal `BigInt` string.
270
+ * @returns `true` if a membership was removed.
271
+ * @throws {CrowdyGraphQLError} `FORBIDDEN` / `SCOPE_MISSING` when removing
272
+ * another member without `manage_members`, or `UNAUTHENTICATED`.
273
+ */
73
274
  async removeMember(groupId, userId) {
74
275
  const data = await this.gql.request(RemoveChannelMemberDocument, { groupId, userId });
75
276
  return data.removeChannelMember;
76
277
  }
278
+ /**
279
+ * Replace a member's channel roles with the given set. This is **not
280
+ * additive** — roles not listed are removed. Requires the `manage_roles`
281
+ * channel permission (app admins bypass). Re-pushes the member's effective
282
+ * send permission to Buddy so their ability to post updates immediately.
283
+ *
284
+ * @param input - {@link SetMemberRolesInput}: the `groupId`, the target
285
+ * `userId`, and `roleIds` — the complete set of channel role ids the member
286
+ * should have (ids that are unknown or belong to another group are
287
+ * ignored).
288
+ * @returns The updated {@link GroupMember} record with its new roles.
289
+ * @throws {CrowdyGraphQLError} `FORBIDDEN` / `SCOPE_MISSING` if the caller
290
+ * lacks `manage_roles`, or `UNAUTHENTICATED`.
291
+ */
77
292
  async setMemberRoles(input) {
78
293
  const data = await this.gql.request(SetChannelMemberRolesDocument, { input });
79
294
  return data.setChannelMemberRoles;
80
295
  }
81
296
  // -- Role mutations -------------------------------------------------------
297
+ /**
298
+ * Create a custom (non-system) channel role granting the given channel
299
+ * permission keys (e.g. `send_messages` for posting rights). Requires the
300
+ * `manage_roles` channel permission (app admins bypass).
301
+ *
302
+ * @param input - {@link CreateGroupRoleInput}: the `groupId`, the `roleName`
303
+ * (max 128 chars, unique within the channel), the `permissions` keys to
304
+ * grant (e.g. `send_messages`, `manage_members`; each max 64 chars,
305
+ * defaults to none), and an optional `rank` (higher = more senior;
306
+ * defaults to 0).
307
+ * @returns The created {@link GroupRole}.
308
+ * @throws {CrowdyGraphQLError} `BAD_USER_INPUT` on an invalid/duplicate name
309
+ * or unknown permission key, `FORBIDDEN` / `SCOPE_MISSING` if the caller
310
+ * lacks `manage_roles`, or `UNAUTHENTICATED`.
311
+ */
82
312
  async createRole(input) {
83
313
  const data = await this.gql.request(CreateChannelRoleDocument, { input });
84
314
  return data.createChannelRole;
85
315
  }
316
+ /**
317
+ * Update a channel role's name, rank, and/or permission keys (system roles
318
+ * cannot be renamed or re-ranked). When `input.permissions` is supplied it
319
+ * **replaces** the role's existing keys. Requires the `manage_roles` channel
320
+ * permission (app admins bypass).
321
+ *
322
+ * Note: changing `send_messages` here does not re-push to Buddy until each
323
+ * affected member's roles are re-applied via {@link setMemberRoles}.
324
+ *
325
+ * @param input - {@link UpdateGroupRoleInput}: the `groupRoleId` plus the
326
+ * fields to change (`roleName`, `permissions`, `rank`); omitted fields are
327
+ * left unchanged.
328
+ * @returns The updated {@link GroupRole}.
329
+ * @throws {CrowdyGraphQLError} `BAD_USER_INPUT` on a validation failure,
330
+ * `FORBIDDEN` / `SCOPE_MISSING` if the caller lacks `manage_roles`, or
331
+ * `UNAUTHENTICATED`.
332
+ */
86
333
  async updateRole(input) {
87
334
  const data = await this.gql.request(UpdateChannelRoleDocument, { input });
88
335
  return data.updateChannelRole;
89
336
  }
337
+ /**
338
+ * Delete a non-system channel role. Requires the `manage_roles` channel
339
+ * permission (app admins bypass). The system `leader` role cannot be
340
+ * deleted. **Destructive**: removes the role from any members that held it.
341
+ *
342
+ * @param groupRoleId - The channel role to delete (must be a non-system
343
+ * role), as a decimal `BigInt` string.
344
+ * @returns `true` if a role was deleted.
345
+ * @throws {CrowdyGraphQLError} `FORBIDDEN` / `SCOPE_MISSING` if the caller
346
+ * lacks `manage_roles`, `BAD_USER_INPUT` if targeting a system role, or
347
+ * `UNAUTHENTICATED`.
348
+ */
90
349
  async deleteRole(groupRoleId) {
91
350
  const data = await this.gql.request(DeleteChannelRoleDocument, { groupRoleId });
92
351
  return data.deleteChannelRole;
@@ -1,20 +1,133 @@
1
1
  import type { GraphQLClient } from '../client.js';
2
2
  import { type GetChunkQuery, type GetChunkQueryVariables, type GetChunkLodsQuery, type GetChunkLodsQueryVariables, type GetChunksByDistanceQuery, type GetChunksByDistanceQueryVariables, type GetVoxelListQuery, type GetVoxelListQueryVariables, type UpdateChunkMutation, type UpdateChunkMutationVariables, type UpdateChunkStateMutation, type UpdateChunkStateMutationVariables, type UpdateChunkLodsMutation, type UpdateChunkLodsMutationVariables } from '../generated/graphql.js';
3
3
  /**
4
- * Chunk-level queries and mutations: lookup, LODs, distance scans,
5
- * voxel lists, and chunk state/LOD updates.
6
- *
4
+ * Chunk-level reads and writes for an app's voxel world on the **game-api**.
7
5
  * Exposed as `client.chunks`.
6
+ *
7
+ * A "chunk" is a persisted 16×16×16-voxel cube (4096 voxels) of an app's world.
8
+ * It holds the packed voxel-type grid (`voxels`), sparse per-voxel state
9
+ * overrides (`voxelStates`), an optional opaque chunk-level state blob
10
+ * (`chunkState`), and level-of-detail meshes (`lods`). Use this API for
11
+ * authoritative world persistence and bulk region loads; for high-frequency,
12
+ * per-voxel realtime edits prefer the UDP path
13
+ * (`client.udp.sendVoxelUpdate(...)`), which is far cheaper per update.
14
+ *
15
+ * Coordinate & encoding conventions used throughout:
16
+ * - **Chunk coordinates** (`coordinates.x/y/z`) are int64 **decimal strings**;
17
+ * `+1` on an axis is one chunk (16 voxels) further along it.
18
+ * - **Voxel positions** (`location` / `voxelCoord`) are signed 16-bit ints,
19
+ * `0-15` per axis for in-bounds voxels.
20
+ * - Binary blobs — the dense `voxels` grid (exactly 4096 bytes once decoded),
21
+ * per-voxel `state`, `chunkState`, and LOD `data` — are **base64-encoded**.
22
+ * - `appId` is a `BigInt` sent and received as a decimal string.
23
+ *
24
+ * Every method requires an authenticated session (a Bearer token set via
25
+ * `client.auth.login()` or `client.setToken()`); an app-scoped token may only
26
+ * touch its own app, otherwise {@link CrowdyGraphQLError} is thrown
27
+ * (`UNAUTHENTICATED` / `FORBIDDEN`). The two privileged writes
28
+ * ({@link ChunksAPI.updateState}, {@link ChunksAPI.updateLods}) additionally
29
+ * require the `manage_apps` permission on the owning org
30
+ * (`SCOPE_MISSING` / `FORBIDDEN`).
8
31
  */
9
32
  export declare class ChunksAPI {
10
33
  private gql;
11
34
  constructor(gql: GraphQLClient);
35
+ /**
36
+ * Fetch a single chunk — its base64 voxel grid, per-voxel states, chunk-level
37
+ * state and LODs — by app id and chunk coordinates. Read-only. Use the input's
38
+ * LOD options (`requestedLodLevels` / `includeAllLods`) to limit which LODs
39
+ * come back.
40
+ *
41
+ * @param input - {@link GetChunkInput}: `appId` (decimal string), chunk
42
+ * `coordinates` (int64 decimal strings), and optional LOD filtering.
43
+ * @returns The {@link Chunk}, or `null` if no chunk exists at those
44
+ * coordinates. Binary fields (`voxels`, `chunkState`, per-voxel/LOD `data`)
45
+ * are base64-encoded.
46
+ * @throws {CrowdyGraphQLError} `UNAUTHENTICATED`, or `FORBIDDEN` if an
47
+ * app-scoped token is used against a different app.
48
+ */
12
49
  get(input: GetChunkQueryVariables['input']): Promise<GetChunkQuery['getChunk']>;
50
+ /**
51
+ * Fetch only the requested level-of-detail (LOD) meshes for one chunk —
52
+ * cheaper than {@link ChunksAPI.get} when you only need LODs. Read-only.
53
+ *
54
+ * @param input - {@link GetChunkLodsInput}: `appId`, chunk `coordinates`, and
55
+ * `lodLevels` (the LOD levels to return; each `>= 0`, where `0` is finest).
56
+ * @returns A {@link ChunkLodsResponse} (chunk identity plus the matching
57
+ * `lods`, each carrying base64 `data`), or `null` if the chunk does not
58
+ * exist.
59
+ * @throws {CrowdyGraphQLError} `UNAUTHENTICATED` / `FORBIDDEN`.
60
+ */
13
61
  getLods(input: GetChunkLodsQueryVariables['input']): Promise<GetChunkLodsQuery['getChunkLods']>;
62
+ /**
63
+ * Return all chunks for an app within a cubic (Chebyshev-distance) radius of a
64
+ * center chunk, paginated — the cube spans center ± `maxDistance` chunks on
65
+ * each axis (a `(2·maxDistance+1)³` cube). Use this for bulk region loads; use
66
+ * {@link ChunksAPI.get} for a single chunk. Read-only.
67
+ *
68
+ * @param input - {@link GetChunksByDistanceInput}: `appId`, `centerCoordinate`
69
+ * (int64 decimal strings), `maxDistance` (in chunk units, integer `1-8`),
70
+ * and optional `limit` (max chunks, default 1000) / `skip` (default 0)
71
+ * pagination.
72
+ * @returns A {@link ChunksByDistanceResponse}: the matching `chunks` plus an
73
+ * echo of the applied `limit`/`skip`.
74
+ * @throws {CrowdyGraphQLError} `BAD_USER_INPUT` (e.g. `maxDistance` outside
75
+ * `1-8`), `UNAUTHENTICATED`, or `FORBIDDEN`.
76
+ */
14
77
  byDistance(input: GetChunksByDistanceQueryVariables['input']): Promise<GetChunksByDistanceQuery['getChunksByDistance']>;
78
+ /**
79
+ * List every recorded voxel edit (the `voxel_updates` log) for a single chunk,
80
+ * newest first. Use {@link ChunksAPI.get} instead when you want the packed
81
+ * voxel grid rather than the individual edit log. Read-only.
82
+ *
83
+ * @param input - {@link GetVoxelListInput}: `appId` and chunk `coordinates`.
84
+ * @returns A {@link ChunkVoxelResponse}: the chunk address plus its
85
+ * {@link Voxel} edits (each `state` blob base64-encoded), newest first.
86
+ * @throws {CrowdyGraphQLError} `UNAUTHENTICATED` / `FORBIDDEN`.
87
+ */
15
88
  voxelList(input: GetVoxelListQueryVariables['input']): Promise<GetVoxelListQuery['getVoxelList']>;
89
+ /**
90
+ * Create or replace a chunk's dense voxel grid and/or per-voxel states for the
91
+ * given app and coordinates, recording each provided voxel state as an
92
+ * individual voxel update and asynchronously uploading the chunk to the CDN.
93
+ * **Writes world state.** Leaves `chunkState` and LODs untouched.
94
+ *
95
+ * @param input - {@link ChunkUpdateInput}: `appId`, `coordinates`, an optional
96
+ * base64 `voxels` grid (the decoded buffer must be exactly 4096 bytes — one
97
+ * voxel-type byte `0-255` per voxel, indexed `x + y*16 + z*256`), and
98
+ * optional `voxelStates` overrides.
99
+ * @returns The updated {@link Chunk}.
100
+ * @throws {CrowdyGraphQLError} `BAD_USER_INPUT` (e.g. a `voxels` buffer that
101
+ * isn't 4096 bytes), `UNAUTHENTICATED`, or `FORBIDDEN` if an app-scoped token
102
+ * targets another app.
103
+ */
16
104
  update(input: UpdateChunkMutationVariables['input']): Promise<UpdateChunkMutation['updateChunk']>;
105
+ /**
106
+ * Upsert **only** the opaque base64 chunk-level state blob for a chunk,
107
+ * preserving its voxels, per-voxel states and LODs. **Writes world state.**
108
+ * Privileged: requires the `manage_apps` permission on the org that owns
109
+ * `input.appId` (super admins bypass).
110
+ *
111
+ * @param input - {@link UpdateChunkStateInput}: `appId`, `coordinates`, and the
112
+ * base64-encoded `chunkState` blob (omit/null to store none).
113
+ * @returns The updated {@link Chunk}, or `null` if it could not be written.
114
+ * @throws {CrowdyGraphQLError} `SCOPE_MISSING` / `FORBIDDEN` (missing
115
+ * `manage_apps`), or `UNAUTHENTICATED`.
116
+ */
17
117
  updateState(input: UpdateChunkStateMutationVariables['input']): Promise<UpdateChunkStateMutation['updateChunkState']>;
118
+ /**
119
+ * Replace the entire level-of-detail (LOD) set for a chunk, preserving its
120
+ * voxels, per-voxel states, chunk state and owner. **Writes world state.**
121
+ * Privileged: requires the `manage_apps` permission on the org that owns
122
+ * `input.appId` (super admins bypass).
123
+ *
124
+ * @param input - {@link UpdateChunkLodsInput}: `appId`, `coordinates`, and the
125
+ * full `lods` set (each entry a `level` `>= 0` plus base64 `data`); this
126
+ * REPLACES any existing LODs.
127
+ * @returns The updated {@link Chunk}, or `null` if it could not be written.
128
+ * @throws {CrowdyGraphQLError} `SCOPE_MISSING` / `FORBIDDEN` (missing
129
+ * `manage_apps`), or `UNAUTHENTICATED`.
130
+ */
18
131
  updateLods(input: UpdateChunkLodsMutationVariables['input']): Promise<UpdateChunkLodsMutation['updateChunkLods']>;
19
132
  }
20
133
  //# sourceMappingURL=chunks.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"chunks.d.ts","sourceRoot":"","sources":["../../src/domains/chunks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAElD,OAAO,EAEL,KAAK,aAAa,EAClB,KAAK,sBAAsB,EAE3B,KAAK,iBAAiB,EACtB,KAAK,0BAA0B,EAE/B,KAAK,wBAAwB,EAC7B,KAAK,iCAAiC,EAEtC,KAAK,iBAAiB,EACtB,KAAK,0BAA0B,EAE/B,KAAK,mBAAmB,EACxB,KAAK,4BAA4B,EAEjC,KAAK,wBAAwB,EAC7B,KAAK,iCAAiC,EAEtC,KAAK,uBAAuB,EAC5B,KAAK,gCAAgC,EACtC,MAAM,yBAAyB,CAAC;AAEjC;;;;;GAKG;AACH,qBAAa,SAAS;IACR,OAAO,CAAC,GAAG;gBAAH,GAAG,EAAE,aAAa;IAEhC,GAAG,CACP,KAAK,EAAE,sBAAsB,CAAC,OAAO,CAAC,GACrC,OAAO,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;IAK/B,OAAO,CACX,KAAK,EAAE,0BAA0B,CAAC,OAAO,CAAC,GACzC,OAAO,CAAC,iBAAiB,CAAC,cAAc,CAAC,CAAC;IAKvC,UAAU,CACd,KAAK,EAAE,iCAAiC,CAAC,OAAO,CAAC,GAChD,OAAO,CAAC,wBAAwB,CAAC,qBAAqB,CAAC,CAAC;IAKrD,SAAS,CACb,KAAK,EAAE,0BAA0B,CAAC,OAAO,CAAC,GACzC,OAAO,CAAC,iBAAiB,CAAC,cAAc,CAAC,CAAC;IAKvC,MAAM,CACV,KAAK,EAAE,4BAA4B,CAAC,OAAO,CAAC,GAC3C,OAAO,CAAC,mBAAmB,CAAC,aAAa,CAAC,CAAC;IAKxC,WAAW,CACf,KAAK,EAAE,iCAAiC,CAAC,OAAO,CAAC,GAChD,OAAO,CAAC,wBAAwB,CAAC,kBAAkB,CAAC,CAAC;IAKlD,UAAU,CACd,KAAK,EAAE,gCAAgC,CAAC,OAAO,CAAC,GAC/C,OAAO,CAAC,uBAAuB,CAAC,iBAAiB,CAAC,CAAC;CAIvD"}
1
+ {"version":3,"file":"chunks.d.ts","sourceRoot":"","sources":["../../src/domains/chunks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAElD,OAAO,EAEL,KAAK,aAAa,EAClB,KAAK,sBAAsB,EAE3B,KAAK,iBAAiB,EACtB,KAAK,0BAA0B,EAE/B,KAAK,wBAAwB,EAC7B,KAAK,iCAAiC,EAEtC,KAAK,iBAAiB,EACtB,KAAK,0BAA0B,EAE/B,KAAK,mBAAmB,EACxB,KAAK,4BAA4B,EAEjC,KAAK,wBAAwB,EAC7B,KAAK,iCAAiC,EAEtC,KAAK,uBAAuB,EAC5B,KAAK,gCAAgC,EACtC,MAAM,yBAAyB,CAAC;AAEjC;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,qBAAa,SAAS;IACR,OAAO,CAAC,GAAG;gBAAH,GAAG,EAAE,aAAa;IAEtC;;;;;;;;;;;;;OAaG;IACG,GAAG,CACP,KAAK,EAAE,sBAAsB,CAAC,OAAO,CAAC,GACrC,OAAO,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;IAKrC;;;;;;;;;;OAUG;IACG,OAAO,CACX,KAAK,EAAE,0BAA0B,CAAC,OAAO,CAAC,GACzC,OAAO,CAAC,iBAAiB,CAAC,cAAc,CAAC,CAAC;IAK7C;;;;;;;;;;;;;;OAcG;IACG,UAAU,CACd,KAAK,EAAE,iCAAiC,CAAC,OAAO,CAAC,GAChD,OAAO,CAAC,wBAAwB,CAAC,qBAAqB,CAAC,CAAC;IAK3D;;;;;;;;;OASG;IACG,SAAS,CACb,KAAK,EAAE,0BAA0B,CAAC,OAAO,CAAC,GACzC,OAAO,CAAC,iBAAiB,CAAC,cAAc,CAAC,CAAC;IAK7C;;;;;;;;;;;;;;OAcG;IACG,MAAM,CACV,KAAK,EAAE,4BAA4B,CAAC,OAAO,CAAC,GAC3C,OAAO,CAAC,mBAAmB,CAAC,aAAa,CAAC,CAAC;IAK9C;;;;;;;;;;;OAWG;IACG,WAAW,CACf,KAAK,EAAE,iCAAiC,CAAC,OAAO,CAAC,GAChD,OAAO,CAAC,wBAAwB,CAAC,kBAAkB,CAAC,CAAC;IAKxD;;;;;;;;;;;;OAYG;IACG,UAAU,CACd,KAAK,EAAE,gCAAgC,CAAC,OAAO,CAAC,GAC/C,OAAO,CAAC,uBAAuB,CAAC,iBAAiB,CAAC,CAAC;CAIvD"}
@@ -1,38 +1,151 @@
1
1
  import { GetChunkDocument, GetChunkLodsDocument, GetChunksByDistanceDocument, GetVoxelListDocument, UpdateChunkDocument, UpdateChunkStateDocument, UpdateChunkLodsDocument, } from '../generated/graphql.js';
2
2
  /**
3
- * Chunk-level queries and mutations: lookup, LODs, distance scans,
4
- * voxel lists, and chunk state/LOD updates.
5
- *
3
+ * Chunk-level reads and writes for an app's voxel world on the **game-api**.
6
4
  * Exposed as `client.chunks`.
5
+ *
6
+ * A "chunk" is a persisted 16×16×16-voxel cube (4096 voxels) of an app's world.
7
+ * It holds the packed voxel-type grid (`voxels`), sparse per-voxel state
8
+ * overrides (`voxelStates`), an optional opaque chunk-level state blob
9
+ * (`chunkState`), and level-of-detail meshes (`lods`). Use this API for
10
+ * authoritative world persistence and bulk region loads; for high-frequency,
11
+ * per-voxel realtime edits prefer the UDP path
12
+ * (`client.udp.sendVoxelUpdate(...)`), which is far cheaper per update.
13
+ *
14
+ * Coordinate & encoding conventions used throughout:
15
+ * - **Chunk coordinates** (`coordinates.x/y/z`) are int64 **decimal strings**;
16
+ * `+1` on an axis is one chunk (16 voxels) further along it.
17
+ * - **Voxel positions** (`location` / `voxelCoord`) are signed 16-bit ints,
18
+ * `0-15` per axis for in-bounds voxels.
19
+ * - Binary blobs — the dense `voxels` grid (exactly 4096 bytes once decoded),
20
+ * per-voxel `state`, `chunkState`, and LOD `data` — are **base64-encoded**.
21
+ * - `appId` is a `BigInt` sent and received as a decimal string.
22
+ *
23
+ * Every method requires an authenticated session (a Bearer token set via
24
+ * `client.auth.login()` or `client.setToken()`); an app-scoped token may only
25
+ * touch its own app, otherwise {@link CrowdyGraphQLError} is thrown
26
+ * (`UNAUTHENTICATED` / `FORBIDDEN`). The two privileged writes
27
+ * ({@link ChunksAPI.updateState}, {@link ChunksAPI.updateLods}) additionally
28
+ * require the `manage_apps` permission on the owning org
29
+ * (`SCOPE_MISSING` / `FORBIDDEN`).
7
30
  */
8
31
  export class ChunksAPI {
9
32
  constructor(gql) {
10
33
  this.gql = gql;
11
34
  }
35
+ /**
36
+ * Fetch a single chunk — its base64 voxel grid, per-voxel states, chunk-level
37
+ * state and LODs — by app id and chunk coordinates. Read-only. Use the input's
38
+ * LOD options (`requestedLodLevels` / `includeAllLods`) to limit which LODs
39
+ * come back.
40
+ *
41
+ * @param input - {@link GetChunkInput}: `appId` (decimal string), chunk
42
+ * `coordinates` (int64 decimal strings), and optional LOD filtering.
43
+ * @returns The {@link Chunk}, or `null` if no chunk exists at those
44
+ * coordinates. Binary fields (`voxels`, `chunkState`, per-voxel/LOD `data`)
45
+ * are base64-encoded.
46
+ * @throws {CrowdyGraphQLError} `UNAUTHENTICATED`, or `FORBIDDEN` if an
47
+ * app-scoped token is used against a different app.
48
+ */
12
49
  async get(input) {
13
50
  const data = await this.gql.request(GetChunkDocument, { input });
14
51
  return data.getChunk;
15
52
  }
53
+ /**
54
+ * Fetch only the requested level-of-detail (LOD) meshes for one chunk —
55
+ * cheaper than {@link ChunksAPI.get} when you only need LODs. Read-only.
56
+ *
57
+ * @param input - {@link GetChunkLodsInput}: `appId`, chunk `coordinates`, and
58
+ * `lodLevels` (the LOD levels to return; each `>= 0`, where `0` is finest).
59
+ * @returns A {@link ChunkLodsResponse} (chunk identity plus the matching
60
+ * `lods`, each carrying base64 `data`), or `null` if the chunk does not
61
+ * exist.
62
+ * @throws {CrowdyGraphQLError} `UNAUTHENTICATED` / `FORBIDDEN`.
63
+ */
16
64
  async getLods(input) {
17
65
  const data = await this.gql.request(GetChunkLodsDocument, { input });
18
66
  return data.getChunkLods;
19
67
  }
68
+ /**
69
+ * Return all chunks for an app within a cubic (Chebyshev-distance) radius of a
70
+ * center chunk, paginated — the cube spans center ± `maxDistance` chunks on
71
+ * each axis (a `(2·maxDistance+1)³` cube). Use this for bulk region loads; use
72
+ * {@link ChunksAPI.get} for a single chunk. Read-only.
73
+ *
74
+ * @param input - {@link GetChunksByDistanceInput}: `appId`, `centerCoordinate`
75
+ * (int64 decimal strings), `maxDistance` (in chunk units, integer `1-8`),
76
+ * and optional `limit` (max chunks, default 1000) / `skip` (default 0)
77
+ * pagination.
78
+ * @returns A {@link ChunksByDistanceResponse}: the matching `chunks` plus an
79
+ * echo of the applied `limit`/`skip`.
80
+ * @throws {CrowdyGraphQLError} `BAD_USER_INPUT` (e.g. `maxDistance` outside
81
+ * `1-8`), `UNAUTHENTICATED`, or `FORBIDDEN`.
82
+ */
20
83
  async byDistance(input) {
21
84
  const data = await this.gql.request(GetChunksByDistanceDocument, { input });
22
85
  return data.getChunksByDistance;
23
86
  }
87
+ /**
88
+ * List every recorded voxel edit (the `voxel_updates` log) for a single chunk,
89
+ * newest first. Use {@link ChunksAPI.get} instead when you want the packed
90
+ * voxel grid rather than the individual edit log. Read-only.
91
+ *
92
+ * @param input - {@link GetVoxelListInput}: `appId` and chunk `coordinates`.
93
+ * @returns A {@link ChunkVoxelResponse}: the chunk address plus its
94
+ * {@link Voxel} edits (each `state` blob base64-encoded), newest first.
95
+ * @throws {CrowdyGraphQLError} `UNAUTHENTICATED` / `FORBIDDEN`.
96
+ */
24
97
  async voxelList(input) {
25
98
  const data = await this.gql.request(GetVoxelListDocument, { input });
26
99
  return data.getVoxelList;
27
100
  }
101
+ /**
102
+ * Create or replace a chunk's dense voxel grid and/or per-voxel states for the
103
+ * given app and coordinates, recording each provided voxel state as an
104
+ * individual voxel update and asynchronously uploading the chunk to the CDN.
105
+ * **Writes world state.** Leaves `chunkState` and LODs untouched.
106
+ *
107
+ * @param input - {@link ChunkUpdateInput}: `appId`, `coordinates`, an optional
108
+ * base64 `voxels` grid (the decoded buffer must be exactly 4096 bytes — one
109
+ * voxel-type byte `0-255` per voxel, indexed `x + y*16 + z*256`), and
110
+ * optional `voxelStates` overrides.
111
+ * @returns The updated {@link Chunk}.
112
+ * @throws {CrowdyGraphQLError} `BAD_USER_INPUT` (e.g. a `voxels` buffer that
113
+ * isn't 4096 bytes), `UNAUTHENTICATED`, or `FORBIDDEN` if an app-scoped token
114
+ * targets another app.
115
+ */
28
116
  async update(input) {
29
117
  const data = await this.gql.request(UpdateChunkDocument, { input });
30
118
  return data.updateChunk;
31
119
  }
120
+ /**
121
+ * Upsert **only** the opaque base64 chunk-level state blob for a chunk,
122
+ * preserving its voxels, per-voxel states and LODs. **Writes world state.**
123
+ * Privileged: requires the `manage_apps` permission on the org that owns
124
+ * `input.appId` (super admins bypass).
125
+ *
126
+ * @param input - {@link UpdateChunkStateInput}: `appId`, `coordinates`, and the
127
+ * base64-encoded `chunkState` blob (omit/null to store none).
128
+ * @returns The updated {@link Chunk}, or `null` if it could not be written.
129
+ * @throws {CrowdyGraphQLError} `SCOPE_MISSING` / `FORBIDDEN` (missing
130
+ * `manage_apps`), or `UNAUTHENTICATED`.
131
+ */
32
132
  async updateState(input) {
33
133
  const data = await this.gql.request(UpdateChunkStateDocument, { input });
34
134
  return data.updateChunkState;
35
135
  }
136
+ /**
137
+ * Replace the entire level-of-detail (LOD) set for a chunk, preserving its
138
+ * voxels, per-voxel states, chunk state and owner. **Writes world state.**
139
+ * Privileged: requires the `manage_apps` permission on the org that owns
140
+ * `input.appId` (super admins bypass).
141
+ *
142
+ * @param input - {@link UpdateChunkLodsInput}: `appId`, `coordinates`, and the
143
+ * full `lods` set (each entry a `level` `>= 0` plus base64 `data`); this
144
+ * REPLACES any existing LODs.
145
+ * @returns The updated {@link Chunk}, or `null` if it could not be written.
146
+ * @throws {CrowdyGraphQLError} `SCOPE_MISSING` / `FORBIDDEN` (missing
147
+ * `manage_apps`), or `UNAUTHENTICATED`.
148
+ */
36
149
  async updateLods(input) {
37
150
  const data = await this.gql.request(UpdateChunkLodsDocument, { input });
38
151
  return data.updateChunkLods;