@crowdedkingdomstudios/crowdyjs 5.1.0 → 5.2.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.
Files changed (75) hide show
  1. package/MIGRATION.md +64 -0
  2. package/README.md +19 -0
  3. package/dist/client.d.ts +98 -5
  4. package/dist/client.d.ts.map +1 -1
  5. package/dist/client.js +74 -5
  6. package/dist/crowdy-client.d.ts +31 -0
  7. package/dist/crowdy-client.d.ts.map +1 -1
  8. package/dist/crowdy-client.js +8 -0
  9. package/dist/domains/actors.d.ts +88 -5
  10. package/dist/domains/actors.d.ts.map +1 -1
  11. package/dist/domains/actors.js +89 -6
  12. package/dist/domains/apps.d.ts +95 -41
  13. package/dist/domains/apps.d.ts.map +1 -1
  14. package/dist/domains/apps.js +80 -33
  15. package/dist/domains/auth.d.ts +139 -19
  16. package/dist/domains/auth.d.ts.map +1 -1
  17. package/dist/domains/auth.js +137 -17
  18. package/dist/domains/channels.d.ts +264 -5
  19. package/dist/domains/channels.d.ts.map +1 -1
  20. package/dist/domains/channels.js +264 -5
  21. package/dist/domains/chunks.d.ts +116 -3
  22. package/dist/domains/chunks.d.ts.map +1 -1
  23. package/dist/domains/chunks.js +116 -3
  24. package/dist/domains/gameModel.d.ts +412 -6
  25. package/dist/domains/gameModel.d.ts.map +1 -1
  26. package/dist/domains/gameModel.js +412 -6
  27. package/dist/domains/platform.d.ts +36 -20
  28. package/dist/domains/platform.d.ts.map +1 -1
  29. package/dist/domains/platform.js +29 -18
  30. package/dist/domains/serverStatus.d.ts +74 -6
  31. package/dist/domains/serverStatus.d.ts.map +1 -1
  32. package/dist/domains/serverStatus.js +74 -6
  33. package/dist/domains/state.d.ts +50 -2
  34. package/dist/domains/state.d.ts.map +1 -1
  35. package/dist/domains/state.js +50 -2
  36. package/dist/domains/teams.d.ts +265 -7
  37. package/dist/domains/teams.d.ts.map +1 -1
  38. package/dist/domains/teams.js +267 -9
  39. package/dist/domains/teleport.d.ts +30 -2
  40. package/dist/domains/teleport.d.ts.map +1 -1
  41. package/dist/domains/teleport.js +30 -2
  42. package/dist/domains/udp.d.ts +341 -5
  43. package/dist/domains/udp.d.ts.map +1 -1
  44. package/dist/domains/udp.js +341 -5
  45. package/dist/domains/users.d.ts +42 -11
  46. package/dist/domains/users.d.ts.map +1 -1
  47. package/dist/domains/users.js +41 -10
  48. package/dist/domains/voxels.d.ts +107 -2
  49. package/dist/domains/voxels.d.ts.map +1 -1
  50. package/dist/domains/voxels.js +107 -2
  51. package/dist/errors.d.ts +116 -0
  52. package/dist/errors.d.ts.map +1 -1
  53. package/dist/errors.js +100 -0
  54. package/dist/generated/graphql.d.ts +1787 -110
  55. package/dist/generated/graphql.d.ts.map +1 -1
  56. package/dist/generated/graphql.js +75 -9
  57. package/dist/index.d.ts +2 -1
  58. package/dist/index.d.ts.map +1 -1
  59. package/dist/index.js +2 -1
  60. package/dist/realtime.d.ts +226 -0
  61. package/dist/realtime.d.ts.map +1 -1
  62. package/dist/realtime.js +90 -0
  63. package/dist/session.d.ts +46 -0
  64. package/dist/session.d.ts.map +1 -1
  65. package/dist/session.js +35 -0
  66. package/dist/types.d.ts +429 -0
  67. package/dist/types.d.ts.map +1 -1
  68. package/dist/types.js +53 -0
  69. package/dist/utils.d.ts +86 -0
  70. package/dist/utils.d.ts.map +1 -1
  71. package/dist/utils.js +86 -0
  72. package/dist/world.d.ts +192 -0
  73. package/dist/world.d.ts.map +1 -1
  74. package/dist/world.js +170 -0
  75. package/package.json +1 -1
@@ -1,17 +1,122 @@
1
1
  import type { GraphQLClient } from '../client.js';
2
2
  import { type ListVoxelsQuery, type ListVoxelsQueryVariables, type ListVoxelUpdatesByDistanceQuery, type ListVoxelUpdatesByDistanceQueryVariables, type UpdateVoxelMutation, type UpdateVoxelMutationVariables, type VoxelUpdateHistoryQuery, type VoxelUpdateHistoryQueryVariables, type RollbackVoxelUpdatesMutation, type RollbackVoxelUpdatesMutationVariables } from '../generated/graphql.js';
3
3
  /**
4
- * Voxel queries and mutations: list, history, rollback, single-voxel update.
4
+ * Voxel-edit queries and mutations for an app's world on the **game-api**: list,
5
+ * distance scans, history, rollback, and single-voxel writes. Exposed as
6
+ * `client.voxels`.
5
7
  *
6
- * Exposed as `client.voxels`.
8
+ * A "voxel edit" is one row of the `voxel_updates` log (a {@link Voxel}): the
9
+ * app / chunk / local position that changed, the new voxel type, an optional
10
+ * base64 state blob, and who/when. A background maintenance job later folds these
11
+ * edits into the chunk's packed grid (see {@link ChunksAPI}). For high-frequency
12
+ * realtime edits prefer the UDP path (`client.udp.sendVoxelUpdate(...)`); use
13
+ * this API for authoritative reads, audit history, and administrative rollback.
14
+ *
15
+ * Coordinate & encoding conventions:
16
+ * - **Chunk coordinates** are int64 **decimal strings**; **voxel positions**
17
+ * (`location`) are signed 16-bit ints, `0-15` per axis for in-bounds voxels.
18
+ * - `appId` / `userId` are `BigInt` sent and received as decimal strings.
19
+ * - Voxel `state` blobs are **base64-encoded** binary.
20
+ *
21
+ * Every method requires an authenticated session (a Bearer token set via
22
+ * `client.auth.login()` or `client.setToken()`); an app-scoped token may only
23
+ * touch its own app, otherwise {@link CrowdyGraphQLError} is thrown
24
+ * (`UNAUTHENTICATED` / `FORBIDDEN`). {@link VoxelsAPI.update} additionally
25
+ * requires the `update_voxel_data` runtime permission and
26
+ * {@link VoxelsAPI.rollback} the `manage_apps` permission
27
+ * (`SCOPE_MISSING` / `FORBIDDEN`).
7
28
  */
8
29
  export declare class VoxelsAPI {
9
30
  private gql;
10
31
  constructor(gql: GraphQLClient);
32
+ /**
33
+ * List recorded voxel edits for a single chunk, newest first (optionally only
34
+ * those at/after a timestamp). Read-only.
35
+ *
36
+ * @param input - {@link ListVoxelsInput}: `appId`, chunk `coordinates`, and an
37
+ * optional inclusive `since` lower time bound (only edits with
38
+ * `createdAt >= since`).
39
+ * @returns The matching {@link Voxel} edits, newest first (each `state` blob
40
+ * base64-encoded).
41
+ * @throws {CrowdyGraphQLError} `UNAUTHENTICATED` / `FORBIDDEN`.
42
+ */
11
43
  list(input: ListVoxelsQueryVariables['input']): Promise<ListVoxelsQuery['listVoxels']>;
44
+ /**
45
+ * List recorded voxel edits for all chunks within a cubic (Chebyshev) radius of
46
+ * a center chunk, grouped per chunk and ordered by increasing distance,
47
+ * paginated over chunks. Read-only.
48
+ *
49
+ * @param input - {@link ListVoxelUpdatesByDistanceInput}: `appId`,
50
+ * `centerCoordinate`, `maxDistance` (in chunk units, integer `1-8`), optional
51
+ * `limit` (max **chunks**, not voxels — default 1000) / `skip` (default 0),
52
+ * and an optional `since` lower time bound.
53
+ * @returns A {@link VoxelUpdatesByDistanceResponse}: per-chunk groups of voxel
54
+ * edits ordered by increasing distance from `centerCoordinate`, plus an echo
55
+ * of the applied `limit`/`skip`.
56
+ * @throws {CrowdyGraphQLError} `BAD_USER_INPUT` (e.g. `maxDistance` outside
57
+ * `1-8`), `UNAUTHENTICATED`, or `FORBIDDEN`.
58
+ */
12
59
  listByDistance(input: ListVoxelUpdatesByDistanceQueryVariables['input']): Promise<ListVoxelUpdatesByDistanceQuery['listVoxelUpdatesByDistance']>;
60
+ /**
61
+ * Record (upsert) a single voxel edit in the `voxel_updates` log for one chunk.
62
+ * **Writes world state**; a background job later folds the edit into the
63
+ * chunk's packed grid. Requires voxel-edit permission for the target region:
64
+ * active app access plus the `update_voxel_data` runtime permission (and, where
65
+ * grids cover the chunk, `update_voxel_data` on a covering grid).
66
+ *
67
+ * @param input - {@link UpdateVoxelInput}: `appId`, chunk `coordinates`, the
68
+ * local voxel `location` (`0-15` per axis), the `voxelType` to write
69
+ * (`0-255`), and an optional base64 `state` blob.
70
+ * @returns The resulting {@link Voxel}.
71
+ * @throws {CrowdyGraphQLError} `SCOPE_MISSING` / `FORBIDDEN` (missing
72
+ * `update_voxel_data`), `BAD_USER_INPUT`, or `UNAUTHENTICATED`.
73
+ */
13
74
  update(input: UpdateVoxelMutationVariables['input']): Promise<UpdateVoxelMutation['updateVoxel']>;
75
+ /**
76
+ * Read entries from the immutable voxel edit history (`voxel_updates_history`)
77
+ * for an app, newest first, optionally filtered by user id and a changed-at
78
+ * time window. Read-only.
79
+ *
80
+ * @param args - Query variables:
81
+ * - `appId` — app whose history to read (`BigInt` decimal string).
82
+ * - `userId` — optional filter: only edits made by this user (decimal string).
83
+ * - `from` / `to` — optional inclusive changed-at time window (ISO-8601).
84
+ * - `limit` — **deprecated** max entries (default 500, range `1-50000`).
85
+ * - `offset` — **deprecated** entries to skip (default 0, range `0-1000000`).
86
+ * @returns The matching {@link VoxelUpdateHistoryEvent} entries, newest first.
87
+ * @throws {CrowdyGraphQLError} `UNAUTHENTICATED` / `FORBIDDEN`.
88
+ * @remarks The `limit`/`offset` arguments use deprecated offset pagination.
89
+ * For large histories prefer the Relay-style `voxelUpdateHistoryConnection(first:,
90
+ * after:)` query (available on the schema via `client.graphql`) — page with
91
+ * `first` plus the previous page's `after` cursor. See
92
+ * https://docs.crowdedkingdoms.com/overview/pagination.
93
+ */
14
94
  history(args: VoxelUpdateHistoryQueryVariables): Promise<VoxelUpdateHistoryQuery['voxelUpdateHistory']>;
95
+ /**
96
+ * Revert every voxel edit made by `userId` in `appId` between `from` and `to`,
97
+ * returning one result per affected voxel. **Defaults to a dry run**
98
+ * (`dryRun: true`) that only PREVIEWS the planned reversions without writing;
99
+ * pass `dryRun: false` to actually apply them (**destructive** — mutates world
100
+ * state). Privileged: requires the `manage_apps` permission on the org that
101
+ * owns `appId` (super admins bypass). Requires game-api ≥ v0.10.3.
102
+ *
103
+ * Pass `idempotencyKey` to make retries safe: replaying with the same key and
104
+ * identical input returns the first result instead of re-applying, while the
105
+ * same key with **different** input throws {@link CrowdyGraphQLError} with
106
+ * `code === 'IDEMPOTENCY_CONFLICT'`. Keys expire server-side after 24h. Unlike
107
+ * {@link ActorsAPI.delete}, the key is a **field on the input object**
108
+ * ({@link RollbackVoxelUpdatesInput}), not a separate argument.
109
+ *
110
+ * @param input - {@link RollbackVoxelUpdatesInput}: `appId`, `userId` (decimal
111
+ * strings), the inclusive `from`/`to` window, `dryRun` (default `true`), and
112
+ * an optional `idempotencyKey`.
113
+ * @returns One {@link RollbackVoxelEventResult} per affected voxel; each
114
+ * `applied` flag is `true` only when actually written (always `false` in a
115
+ * dry run).
116
+ * @throws {CrowdyGraphQLError} `IDEMPOTENCY_CONFLICT`, `SCOPE_MISSING` /
117
+ * `FORBIDDEN` (missing `manage_apps`), `BAD_USER_INPUT`, or
118
+ * `UNAUTHENTICATED`.
119
+ */
15
120
  rollback(input: RollbackVoxelUpdatesMutationVariables['input']): Promise<RollbackVoxelUpdatesMutation['rollbackVoxelUpdates']>;
16
121
  }
17
122
  //# sourceMappingURL=voxels.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"voxels.d.ts","sourceRoot":"","sources":["../../src/domains/voxels.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAElD,OAAO,EAEL,KAAK,eAAe,EACpB,KAAK,wBAAwB,EAE7B,KAAK,+BAA+B,EACpC,KAAK,wCAAwC,EAE7C,KAAK,mBAAmB,EACxB,KAAK,4BAA4B,EAEjC,KAAK,uBAAuB,EAC5B,KAAK,gCAAgC,EAErC,KAAK,4BAA4B,EACjC,KAAK,qCAAqC,EAC3C,MAAM,yBAAyB,CAAC;AAEjC;;;;GAIG;AACH,qBAAa,SAAS;IACR,OAAO,CAAC,GAAG;gBAAH,GAAG,EAAE,aAAa;IAEhC,IAAI,CACR,KAAK,EAAE,wBAAwB,CAAC,OAAO,CAAC,GACvC,OAAO,CAAC,eAAe,CAAC,YAAY,CAAC,CAAC;IAKnC,cAAc,CAClB,KAAK,EAAE,wCAAwC,CAAC,OAAO,CAAC,GACvD,OAAO,CAAC,+BAA+B,CAAC,4BAA4B,CAAC,CAAC;IAKnE,MAAM,CACV,KAAK,EAAE,4BAA4B,CAAC,OAAO,CAAC,GAC3C,OAAO,CAAC,mBAAmB,CAAC,aAAa,CAAC,CAAC;IAKxC,OAAO,CACX,IAAI,EAAE,gCAAgC,GACrC,OAAO,CAAC,uBAAuB,CAAC,oBAAoB,CAAC,CAAC;IAKnD,QAAQ,CACZ,KAAK,EAAE,qCAAqC,CAAC,OAAO,CAAC,GACpD,OAAO,CAAC,4BAA4B,CAAC,sBAAsB,CAAC,CAAC;CAIjE"}
1
+ {"version":3,"file":"voxels.d.ts","sourceRoot":"","sources":["../../src/domains/voxels.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAElD,OAAO,EAEL,KAAK,eAAe,EACpB,KAAK,wBAAwB,EAE7B,KAAK,+BAA+B,EACpC,KAAK,wCAAwC,EAE7C,KAAK,mBAAmB,EACxB,KAAK,4BAA4B,EAEjC,KAAK,uBAAuB,EAC5B,KAAK,gCAAgC,EAErC,KAAK,4BAA4B,EACjC,KAAK,qCAAqC,EAC3C,MAAM,yBAAyB,CAAC;AAEjC;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,qBAAa,SAAS;IACR,OAAO,CAAC,GAAG;gBAAH,GAAG,EAAE,aAAa;IAEtC;;;;;;;;;;OAUG;IACG,IAAI,CACR,KAAK,EAAE,wBAAwB,CAAC,OAAO,CAAC,GACvC,OAAO,CAAC,eAAe,CAAC,YAAY,CAAC,CAAC;IAKzC;;;;;;;;;;;;;;OAcG;IACG,cAAc,CAClB,KAAK,EAAE,wCAAwC,CAAC,OAAO,CAAC,GACvD,OAAO,CAAC,+BAA+B,CAAC,4BAA4B,CAAC,CAAC;IAKzE;;;;;;;;;;;;;OAaG;IACG,MAAM,CACV,KAAK,EAAE,4BAA4B,CAAC,OAAO,CAAC,GAC3C,OAAO,CAAC,mBAAmB,CAAC,aAAa,CAAC,CAAC;IAK9C;;;;;;;;;;;;;;;;;;OAkBG;IACG,OAAO,CACX,IAAI,EAAE,gCAAgC,GACrC,OAAO,CAAC,uBAAuB,CAAC,oBAAoB,CAAC,CAAC;IAKzD;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACG,QAAQ,CACZ,KAAK,EAAE,qCAAqC,CAAC,OAAO,CAAC,GACpD,OAAO,CAAC,4BAA4B,CAAC,sBAAsB,CAAC,CAAC;CAIjE"}
@@ -1,29 +1,134 @@
1
1
  import { ListVoxelsDocument, ListVoxelUpdatesByDistanceDocument, UpdateVoxelDocument, VoxelUpdateHistoryDocument, RollbackVoxelUpdatesDocument, } from '../generated/graphql.js';
2
2
  /**
3
- * Voxel queries and mutations: list, history, rollback, single-voxel update.
3
+ * Voxel-edit queries and mutations for an app's world on the **game-api**: list,
4
+ * distance scans, history, rollback, and single-voxel writes. Exposed as
5
+ * `client.voxels`.
4
6
  *
5
- * Exposed as `client.voxels`.
7
+ * A "voxel edit" is one row of the `voxel_updates` log (a {@link Voxel}): the
8
+ * app / chunk / local position that changed, the new voxel type, an optional
9
+ * base64 state blob, and who/when. A background maintenance job later folds these
10
+ * edits into the chunk's packed grid (see {@link ChunksAPI}). For high-frequency
11
+ * realtime edits prefer the UDP path (`client.udp.sendVoxelUpdate(...)`); use
12
+ * this API for authoritative reads, audit history, and administrative rollback.
13
+ *
14
+ * Coordinate & encoding conventions:
15
+ * - **Chunk coordinates** are int64 **decimal strings**; **voxel positions**
16
+ * (`location`) are signed 16-bit ints, `0-15` per axis for in-bounds voxels.
17
+ * - `appId` / `userId` are `BigInt` sent and received as decimal strings.
18
+ * - Voxel `state` blobs are **base64-encoded** binary.
19
+ *
20
+ * Every method requires an authenticated session (a Bearer token set via
21
+ * `client.auth.login()` or `client.setToken()`); an app-scoped token may only
22
+ * touch its own app, otherwise {@link CrowdyGraphQLError} is thrown
23
+ * (`UNAUTHENTICATED` / `FORBIDDEN`). {@link VoxelsAPI.update} additionally
24
+ * requires the `update_voxel_data` runtime permission and
25
+ * {@link VoxelsAPI.rollback} the `manage_apps` permission
26
+ * (`SCOPE_MISSING` / `FORBIDDEN`).
6
27
  */
7
28
  export class VoxelsAPI {
8
29
  constructor(gql) {
9
30
  this.gql = gql;
10
31
  }
32
+ /**
33
+ * List recorded voxel edits for a single chunk, newest first (optionally only
34
+ * those at/after a timestamp). Read-only.
35
+ *
36
+ * @param input - {@link ListVoxelsInput}: `appId`, chunk `coordinates`, and an
37
+ * optional inclusive `since` lower time bound (only edits with
38
+ * `createdAt >= since`).
39
+ * @returns The matching {@link Voxel} edits, newest first (each `state` blob
40
+ * base64-encoded).
41
+ * @throws {CrowdyGraphQLError} `UNAUTHENTICATED` / `FORBIDDEN`.
42
+ */
11
43
  async list(input) {
12
44
  const data = await this.gql.request(ListVoxelsDocument, { input });
13
45
  return data.listVoxels;
14
46
  }
47
+ /**
48
+ * List recorded voxel edits for all chunks within a cubic (Chebyshev) radius of
49
+ * a center chunk, grouped per chunk and ordered by increasing distance,
50
+ * paginated over chunks. Read-only.
51
+ *
52
+ * @param input - {@link ListVoxelUpdatesByDistanceInput}: `appId`,
53
+ * `centerCoordinate`, `maxDistance` (in chunk units, integer `1-8`), optional
54
+ * `limit` (max **chunks**, not voxels — default 1000) / `skip` (default 0),
55
+ * and an optional `since` lower time bound.
56
+ * @returns A {@link VoxelUpdatesByDistanceResponse}: per-chunk groups of voxel
57
+ * edits ordered by increasing distance from `centerCoordinate`, plus an echo
58
+ * of the applied `limit`/`skip`.
59
+ * @throws {CrowdyGraphQLError} `BAD_USER_INPUT` (e.g. `maxDistance` outside
60
+ * `1-8`), `UNAUTHENTICATED`, or `FORBIDDEN`.
61
+ */
15
62
  async listByDistance(input) {
16
63
  const data = await this.gql.request(ListVoxelUpdatesByDistanceDocument, { input });
17
64
  return data.listVoxelUpdatesByDistance;
18
65
  }
66
+ /**
67
+ * Record (upsert) a single voxel edit in the `voxel_updates` log for one chunk.
68
+ * **Writes world state**; a background job later folds the edit into the
69
+ * chunk's packed grid. Requires voxel-edit permission for the target region:
70
+ * active app access plus the `update_voxel_data` runtime permission (and, where
71
+ * grids cover the chunk, `update_voxel_data` on a covering grid).
72
+ *
73
+ * @param input - {@link UpdateVoxelInput}: `appId`, chunk `coordinates`, the
74
+ * local voxel `location` (`0-15` per axis), the `voxelType` to write
75
+ * (`0-255`), and an optional base64 `state` blob.
76
+ * @returns The resulting {@link Voxel}.
77
+ * @throws {CrowdyGraphQLError} `SCOPE_MISSING` / `FORBIDDEN` (missing
78
+ * `update_voxel_data`), `BAD_USER_INPUT`, or `UNAUTHENTICATED`.
79
+ */
19
80
  async update(input) {
20
81
  const data = await this.gql.request(UpdateVoxelDocument, { input });
21
82
  return data.updateVoxel;
22
83
  }
84
+ /**
85
+ * Read entries from the immutable voxel edit history (`voxel_updates_history`)
86
+ * for an app, newest first, optionally filtered by user id and a changed-at
87
+ * time window. Read-only.
88
+ *
89
+ * @param args - Query variables:
90
+ * - `appId` — app whose history to read (`BigInt` decimal string).
91
+ * - `userId` — optional filter: only edits made by this user (decimal string).
92
+ * - `from` / `to` — optional inclusive changed-at time window (ISO-8601).
93
+ * - `limit` — **deprecated** max entries (default 500, range `1-50000`).
94
+ * - `offset` — **deprecated** entries to skip (default 0, range `0-1000000`).
95
+ * @returns The matching {@link VoxelUpdateHistoryEvent} entries, newest first.
96
+ * @throws {CrowdyGraphQLError} `UNAUTHENTICATED` / `FORBIDDEN`.
97
+ * @remarks The `limit`/`offset` arguments use deprecated offset pagination.
98
+ * For large histories prefer the Relay-style `voxelUpdateHistoryConnection(first:,
99
+ * after:)` query (available on the schema via `client.graphql`) — page with
100
+ * `first` plus the previous page's `after` cursor. See
101
+ * https://docs.crowdedkingdoms.com/overview/pagination.
102
+ */
23
103
  async history(args) {
24
104
  const data = await this.gql.request(VoxelUpdateHistoryDocument, args);
25
105
  return data.voxelUpdateHistory;
26
106
  }
107
+ /**
108
+ * Revert every voxel edit made by `userId` in `appId` between `from` and `to`,
109
+ * returning one result per affected voxel. **Defaults to a dry run**
110
+ * (`dryRun: true`) that only PREVIEWS the planned reversions without writing;
111
+ * pass `dryRun: false` to actually apply them (**destructive** — mutates world
112
+ * state). Privileged: requires the `manage_apps` permission on the org that
113
+ * owns `appId` (super admins bypass). Requires game-api ≥ v0.10.3.
114
+ *
115
+ * Pass `idempotencyKey` to make retries safe: replaying with the same key and
116
+ * identical input returns the first result instead of re-applying, while the
117
+ * same key with **different** input throws {@link CrowdyGraphQLError} with
118
+ * `code === 'IDEMPOTENCY_CONFLICT'`. Keys expire server-side after 24h. Unlike
119
+ * {@link ActorsAPI.delete}, the key is a **field on the input object**
120
+ * ({@link RollbackVoxelUpdatesInput}), not a separate argument.
121
+ *
122
+ * @param input - {@link RollbackVoxelUpdatesInput}: `appId`, `userId` (decimal
123
+ * strings), the inclusive `from`/`to` window, `dryRun` (default `true`), and
124
+ * an optional `idempotencyKey`.
125
+ * @returns One {@link RollbackVoxelEventResult} per affected voxel; each
126
+ * `applied` flag is `true` only when actually written (always `false` in a
127
+ * dry run).
128
+ * @throws {CrowdyGraphQLError} `IDEMPOTENCY_CONFLICT`, `SCOPE_MISSING` /
129
+ * `FORBIDDEN` (missing `manage_apps`), `BAD_USER_INPUT`, or
130
+ * `UNAUTHENTICATED`.
131
+ */
27
132
  async rollback(input) {
28
133
  const data = await this.gql.request(RollbackVoxelUpdatesDocument, { input });
29
134
  return data.rollbackVoxelUpdates;
package/dist/errors.d.ts CHANGED
@@ -1,35 +1,145 @@
1
+ /**
2
+ * Structured error classes thrown by CrowdyJS.
3
+ *
4
+ * Every failure the SDK raises is an instance of {@link CrowdyError} (which
5
+ * extends the native `Error`), so you can catch the base class and branch on
6
+ * the concrete subclass with `instanceof`. Transport-level problems
7
+ * (`CrowdyHttpError`, `CrowdyNetworkError`, `CrowdyTimeoutError`) are distinct
8
+ * from API-level problems (`CrowdyGraphQLError`), which lets you retry network
9
+ * blips without retrying a rejected mutation.
10
+ *
11
+ * For API errors prefer branching on the **stable** `extensions.code`
12
+ * (e.g. `UNAUTHENTICATED`, `SCOPE_MISSING`, `FORBIDDEN`, `IDEMPOTENCY_CONFLICT`,
13
+ * `RATE_LIMITED`) rather than the human-readable message — see the
14
+ * [error-code reference](https://docs.crowdedkingdoms.com/overview/error-codes).
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * import { CrowdyGraphQLError, CrowdyTimeoutError } from '@crowdedkingdomstudios/crowdyjs';
19
+ * try {
20
+ * await client.actors.delete(uuid, key);
21
+ * } catch (err) {
22
+ * if (err instanceof CrowdyGraphQLError && err.code === 'IDEMPOTENCY_CONFLICT') {
23
+ * // same key was already used with different arguments — don't retry blindly
24
+ * } else if (err instanceof CrowdyTimeoutError) {
25
+ * // safe to retry with the same idempotency key
26
+ * }
27
+ * }
28
+ * ```
29
+ */
30
+ /** A single GraphQL error entry as returned in a response's `errors[]` array. */
1
31
  export interface CrowdyGraphQLErrorPayload {
32
+ /** Human-readable message. Do not branch on this — use {@link extensions}.code. */
2
33
  message: string;
34
+ /** Source locations in the operation document, when the server reports them. */
3
35
  locations?: readonly unknown[];
36
+ /** Response path to the field that errored (e.g. `['createCheckout']`). */
4
37
  path?: readonly (string | number)[];
38
+ /**
39
+ * Server-provided metadata. Carries the stable `code` plus, where applicable,
40
+ * `remediation` (a short hint on how to resolve it) and `requiredPermission`
41
+ * (the scope the caller is missing). See the published error-code reference.
42
+ */
5
43
  extensions?: Record<string, unknown>;
6
44
  }
45
+ /** Options accepted by the {@link CrowdyError} base constructor. */
7
46
  export interface CrowdyErrorOptions {
47
+ /** Message passed to the native `Error`. */
8
48
  message: string;
49
+ /** Underlying cause (an inner error, rejected promise value, etc.). */
9
50
  cause?: unknown;
10
51
  }
52
+ /**
53
+ * Base class for every error thrown by the SDK. Catch this to handle any
54
+ * CrowdyJS failure uniformly; `error.name` is set to the concrete subclass
55
+ * name. Prefer `instanceof` checks on the subclasses below for branching.
56
+ */
11
57
  export declare class CrowdyError extends Error {
58
+ /** The underlying cause, if this error wraps another. */
12
59
  readonly cause?: unknown;
13
60
  constructor(options: CrowdyErrorOptions);
14
61
  }
62
+ /**
63
+ * A GraphQL endpoint returned a non-2xx HTTP status. This is a transport-level
64
+ * failure (the request never reached resolver execution cleanly) — distinct
65
+ * from {@link CrowdyGraphQLError}, which carries structured `errors[]` from a
66
+ * 200 response. Typical causes: a `401` from an expired token at the gateway,
67
+ * a `413`/`400` malformed request, or a `5xx`.
68
+ */
15
69
  export declare class CrowdyHttpError extends CrowdyError {
70
+ /** HTTP status code of the response. */
16
71
  readonly status: number;
72
+ /** Raw response body (often JSON text or an error page). */
17
73
  readonly body: string;
18
74
  constructor(status: number, body: string);
19
75
  }
76
+ /**
77
+ * The server returned a 200 response whose `errors[]` array was non-empty.
78
+ * This is the SDK's primary API-error type: authentication, authorization,
79
+ * validation, idempotency conflicts, and business-rule rejections all surface
80
+ * here.
81
+ *
82
+ * Branch on {@link code} (the first error's `extensions.code`) — it is a stable
83
+ * contract; the message text is not. The full array is preserved on
84
+ * {@link graphqlErrors} for multi-error responses.
85
+ */
20
86
  export declare class CrowdyGraphQLError extends CrowdyError {
87
+ /** Every GraphQL error entry from the response, in server order. */
21
88
  readonly graphqlErrors: CrowdyGraphQLErrorPayload[];
22
89
  constructor(errors: CrowdyGraphQLErrorPayload[]);
90
+ /**
91
+ * Stable machine-readable code of the first error (its `extensions.code`),
92
+ * e.g. `'UNAUTHENTICATED'`, `'SCOPE_MISSING'`, `'FORBIDDEN'`,
93
+ * `'IDEMPOTENCY_CONFLICT'`, `'RATE_LIMITED'`, `'BAD_USER_INPUT'`. Returns
94
+ * `undefined` when the server didn't attach a code. Branch on this rather
95
+ * than parsing {@link message}.
96
+ */
23
97
  get code(): unknown;
98
+ /**
99
+ * The `extensions` bag of the first error: may include `remediation` (a hint
100
+ * on how to fix it) and `requiredPermission` (the missing scope for
101
+ * `FORBIDDEN`/`SCOPE_MISSING`).
102
+ */
103
+ get extensions(): Record<string, unknown> | undefined;
24
104
  }
105
+ /**
106
+ * A network-level failure before any HTTP response was received: DNS failure,
107
+ * TLS error, connection refused, or an aborted `fetch`. Generally retryable
108
+ * with backoff. The original failure is on {@link CrowdyError.cause}.
109
+ */
25
110
  export declare class CrowdyNetworkError extends CrowdyError {
26
111
  constructor(cause: unknown);
27
112
  }
113
+ /**
114
+ * An HTTP request to a GraphQL endpoint exceeded the configured `timeout`.
115
+ *
116
+ * Note: realtime `...AndWait` echo timeouts do **not** throw this — they reject
117
+ * with {@link CrowdyRealtimeError} (`code === 'UDP_SEQUENCE_TIMEOUT'`). For
118
+ * idempotent operations — or any mutation you passed an `idempotencyKey` — a
119
+ * retry is safe; the server replays the first result.
120
+ */
28
121
  export declare class CrowdyTimeoutError extends CrowdyError {
29
122
  constructor(timeoutMs: number);
30
123
  }
124
+ /**
125
+ * A realtime/WebSocket failure: a subscription couldn't be established, was
126
+ * rejected, or dropped — or an `...AndWait` spatial send didn't receive its
127
+ * matching echo in time.
128
+ *
129
+ * Branch on {@link code}:
130
+ * - `'UDP_SEQUENCE_TIMEOUT'` — an `...AndWait` send timed out (retryable).
131
+ * - `'APP_ID_REQUIRED'` — subscribed without an `appId` (not retryable).
132
+ * - `'AUTH_REQUIRED'` / `'AUTH_CLEARED'` — no/!cleared session token.
133
+ * - `'WEBSOCKET_ERROR'` / `'SUBSCRIPTION_FAILED'` — transport-level drops.
134
+ *
135
+ * When an `...AndWait` send is answered by a server `GenericErrorResponse`,
136
+ * {@link code} carries that server error code instead. Use {@link retryable}
137
+ * to decide whether to reconnect/retry.
138
+ */
31
139
  export declare class CrowdyRealtimeError extends CrowdyError {
140
+ /** Server- or client-assigned reason code, when available. */
32
141
  readonly code?: string;
142
+ /** Whether reconnecting is expected to succeed (transient vs. fatal). */
33
143
  readonly retryable?: boolean;
34
144
  constructor(message: string, options?: {
35
145
  code?: string;
@@ -37,6 +147,12 @@ export declare class CrowdyRealtimeError extends CrowdyError {
37
147
  cause?: unknown;
38
148
  });
39
149
  }
150
+ /**
151
+ * A server response failed the SDK's structural validation — the payload was
152
+ * shaped unexpectedly (e.g. a missing required field on a notification). Almost
153
+ * always indicates an SDK/server version mismatch; check the server
154
+ * compatibility floor in the README.
155
+ */
40
156
  export declare class CrowdyProtocolError extends CrowdyError {
41
157
  }
42
158
  //# sourceMappingURL=errors.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,yBAAyB;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,SAAS,OAAO,EAAE,CAAC;IAC/B,IAAI,CAAC,EAAE,SAAS,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;IACpC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACtC;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,qBAAa,WAAY,SAAQ,KAAK;IACpC,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;gBAEb,OAAO,EAAE,kBAAkB;CAKxC;AAED,qBAAa,eAAgB,SAAQ,WAAW;IAC9C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBAEV,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;CAKzC;AAED,qBAAa,kBAAmB,SAAQ,WAAW;IACjD,QAAQ,CAAC,aAAa,EAAE,yBAAyB,EAAE,CAAC;gBAExC,MAAM,EAAE,yBAAyB,EAAE;IAK/C,IAAI,IAAI,IAAI,OAAO,CAElB;CACF;AAED,qBAAa,kBAAmB,SAAQ,WAAW;gBACrC,KAAK,EAAE,OAAO;CAG3B;AAED,qBAAa,kBAAmB,SAAQ,WAAW;gBACrC,SAAS,EAAE,MAAM;CAG9B;AAED,qBAAa,mBAAoB,SAAQ,WAAW;IAClD,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;gBAEjB,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAO;CAKnG;AAED,qBAAa,mBAAoB,SAAQ,WAAW;CAAG"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,iFAAiF;AACjF,MAAM,WAAW,yBAAyB;IACxC,mFAAmF;IACnF,OAAO,EAAE,MAAM,CAAC;IAChB,gFAAgF;IAChF,SAAS,CAAC,EAAE,SAAS,OAAO,EAAE,CAAC;IAC/B,2EAA2E;IAC3E,IAAI,CAAC,EAAE,SAAS,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;IACpC;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACtC;AAED,oEAAoE;AACpE,MAAM,WAAW,kBAAkB;IACjC,4CAA4C;IAC5C,OAAO,EAAE,MAAM,CAAC;IAChB,uEAAuE;IACvE,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;;;GAIG;AACH,qBAAa,WAAY,SAAQ,KAAK;IACpC,yDAAyD;IACzD,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;gBAEb,OAAO,EAAE,kBAAkB;CAKxC;AAED;;;;;;GAMG;AACH,qBAAa,eAAgB,SAAQ,WAAW;IAC9C,wCAAwC;IACxC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,4DAA4D;IAC5D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBAEV,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;CAKzC;AAED;;;;;;;;;GASG;AACH,qBAAa,kBAAmB,SAAQ,WAAW;IACjD,oEAAoE;IACpE,QAAQ,CAAC,aAAa,EAAE,yBAAyB,EAAE,CAAC;gBAExC,MAAM,EAAE,yBAAyB,EAAE;IAK/C;;;;;;OAMG;IACH,IAAI,IAAI,IAAI,OAAO,CAElB;IAED;;;;OAIG;IACH,IAAI,UAAU,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAEpD;CACF;AAED;;;;GAIG;AACH,qBAAa,kBAAmB,SAAQ,WAAW;gBACrC,KAAK,EAAE,OAAO;CAG3B;AAED;;;;;;;GAOG;AACH,qBAAa,kBAAmB,SAAQ,WAAW;gBACrC,SAAS,EAAE,MAAM;CAG9B;AAED;;;;;;;;;;;;;;GAcG;AACH,qBAAa,mBAAoB,SAAQ,WAAW;IAClD,8DAA8D;IAC9D,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,yEAAyE;IACzE,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;gBAEjB,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAO;CAKnG;AAED;;;;;GAKG;AACH,qBAAa,mBAAoB,SAAQ,WAAW;CAAG"}
package/dist/errors.js CHANGED
@@ -1,3 +1,37 @@
1
+ /**
2
+ * Structured error classes thrown by CrowdyJS.
3
+ *
4
+ * Every failure the SDK raises is an instance of {@link CrowdyError} (which
5
+ * extends the native `Error`), so you can catch the base class and branch on
6
+ * the concrete subclass with `instanceof`. Transport-level problems
7
+ * (`CrowdyHttpError`, `CrowdyNetworkError`, `CrowdyTimeoutError`) are distinct
8
+ * from API-level problems (`CrowdyGraphQLError`), which lets you retry network
9
+ * blips without retrying a rejected mutation.
10
+ *
11
+ * For API errors prefer branching on the **stable** `extensions.code`
12
+ * (e.g. `UNAUTHENTICATED`, `SCOPE_MISSING`, `FORBIDDEN`, `IDEMPOTENCY_CONFLICT`,
13
+ * `RATE_LIMITED`) rather than the human-readable message — see the
14
+ * [error-code reference](https://docs.crowdedkingdoms.com/overview/error-codes).
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * import { CrowdyGraphQLError, CrowdyTimeoutError } from '@crowdedkingdomstudios/crowdyjs';
19
+ * try {
20
+ * await client.actors.delete(uuid, key);
21
+ * } catch (err) {
22
+ * if (err instanceof CrowdyGraphQLError && err.code === 'IDEMPOTENCY_CONFLICT') {
23
+ * // same key was already used with different arguments — don't retry blindly
24
+ * } else if (err instanceof CrowdyTimeoutError) {
25
+ * // safe to retry with the same idempotency key
26
+ * }
27
+ * }
28
+ * ```
29
+ */
30
+ /**
31
+ * Base class for every error thrown by the SDK. Catch this to handle any
32
+ * CrowdyJS failure uniformly; `error.name` is set to the concrete subclass
33
+ * name. Prefer `instanceof` checks on the subclasses below for branching.
34
+ */
1
35
  export class CrowdyError extends Error {
2
36
  constructor(options) {
3
37
  super(options.message);
@@ -5,6 +39,13 @@ export class CrowdyError extends Error {
5
39
  this.cause = options.cause;
6
40
  }
7
41
  }
42
+ /**
43
+ * A GraphQL endpoint returned a non-2xx HTTP status. This is a transport-level
44
+ * failure (the request never reached resolver execution cleanly) — distinct
45
+ * from {@link CrowdyGraphQLError}, which carries structured `errors[]` from a
46
+ * 200 response. Typical causes: a `401` from an expired token at the gateway,
47
+ * a `413`/`400` malformed request, or a `5xx`.
48
+ */
8
49
  export class CrowdyHttpError extends CrowdyError {
9
50
  constructor(status, body) {
10
51
  super({ message: `HTTP ${status}: ${body}` });
@@ -12,25 +53,78 @@ export class CrowdyHttpError extends CrowdyError {
12
53
  this.body = body;
13
54
  }
14
55
  }
56
+ /**
57
+ * The server returned a 200 response whose `errors[]` array was non-empty.
58
+ * This is the SDK's primary API-error type: authentication, authorization,
59
+ * validation, idempotency conflicts, and business-rule rejections all surface
60
+ * here.
61
+ *
62
+ * Branch on {@link code} (the first error's `extensions.code`) — it is a stable
63
+ * contract; the message text is not. The full array is preserved on
64
+ * {@link graphqlErrors} for multi-error responses.
65
+ */
15
66
  export class CrowdyGraphQLError extends CrowdyError {
16
67
  constructor(errors) {
17
68
  super({ message: errors.map((error) => error.message).join('; ') });
18
69
  this.graphqlErrors = errors;
19
70
  }
71
+ /**
72
+ * Stable machine-readable code of the first error (its `extensions.code`),
73
+ * e.g. `'UNAUTHENTICATED'`, `'SCOPE_MISSING'`, `'FORBIDDEN'`,
74
+ * `'IDEMPOTENCY_CONFLICT'`, `'RATE_LIMITED'`, `'BAD_USER_INPUT'`. Returns
75
+ * `undefined` when the server didn't attach a code. Branch on this rather
76
+ * than parsing {@link message}.
77
+ */
20
78
  get code() {
21
79
  return this.graphqlErrors[0]?.extensions?.code;
22
80
  }
81
+ /**
82
+ * The `extensions` bag of the first error: may include `remediation` (a hint
83
+ * on how to fix it) and `requiredPermission` (the missing scope for
84
+ * `FORBIDDEN`/`SCOPE_MISSING`).
85
+ */
86
+ get extensions() {
87
+ return this.graphqlErrors[0]?.extensions;
88
+ }
23
89
  }
90
+ /**
91
+ * A network-level failure before any HTTP response was received: DNS failure,
92
+ * TLS error, connection refused, or an aborted `fetch`. Generally retryable
93
+ * with backoff. The original failure is on {@link CrowdyError.cause}.
94
+ */
24
95
  export class CrowdyNetworkError extends CrowdyError {
25
96
  constructor(cause) {
26
97
  super({ message: `Network error: ${String(cause)}`, cause });
27
98
  }
28
99
  }
100
+ /**
101
+ * An HTTP request to a GraphQL endpoint exceeded the configured `timeout`.
102
+ *
103
+ * Note: realtime `...AndWait` echo timeouts do **not** throw this — they reject
104
+ * with {@link CrowdyRealtimeError} (`code === 'UDP_SEQUENCE_TIMEOUT'`). For
105
+ * idempotent operations — or any mutation you passed an `idempotencyKey` — a
106
+ * retry is safe; the server replays the first result.
107
+ */
29
108
  export class CrowdyTimeoutError extends CrowdyError {
30
109
  constructor(timeoutMs) {
31
110
  super({ message: `Request timed out after ${timeoutMs}ms` });
32
111
  }
33
112
  }
113
+ /**
114
+ * A realtime/WebSocket failure: a subscription couldn't be established, was
115
+ * rejected, or dropped — or an `...AndWait` spatial send didn't receive its
116
+ * matching echo in time.
117
+ *
118
+ * Branch on {@link code}:
119
+ * - `'UDP_SEQUENCE_TIMEOUT'` — an `...AndWait` send timed out (retryable).
120
+ * - `'APP_ID_REQUIRED'` — subscribed without an `appId` (not retryable).
121
+ * - `'AUTH_REQUIRED'` / `'AUTH_CLEARED'` — no/!cleared session token.
122
+ * - `'WEBSOCKET_ERROR'` / `'SUBSCRIPTION_FAILED'` — transport-level drops.
123
+ *
124
+ * When an `...AndWait` send is answered by a server `GenericErrorResponse`,
125
+ * {@link code} carries that server error code instead. Use {@link retryable}
126
+ * to decide whether to reconnect/retry.
127
+ */
34
128
  export class CrowdyRealtimeError extends CrowdyError {
35
129
  constructor(message, options = {}) {
36
130
  super({ message, cause: options.cause });
@@ -38,5 +132,11 @@ export class CrowdyRealtimeError extends CrowdyError {
38
132
  this.retryable = options.retryable;
39
133
  }
40
134
  }
135
+ /**
136
+ * A server response failed the SDK's structural validation — the payload was
137
+ * shaped unexpectedly (e.g. a missing required field on a notification). Almost
138
+ * always indicates an SDK/server version mismatch; check the server
139
+ * compatibility floor in the README.
140
+ */
41
141
  export class CrowdyProtocolError extends CrowdyError {
42
142
  }