@crowdedkingdomstudios/crowdyjs 2.0.0 → 2.1.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 (65) hide show
  1. package/README.md +433 -6
  2. package/dist/auth-state.d.ts +21 -0
  3. package/dist/auth-state.d.ts.map +1 -0
  4. package/dist/auth-state.js +30 -0
  5. package/dist/client.d.ts +27 -21
  6. package/dist/client.d.ts.map +1 -1
  7. package/dist/client.js +27 -196
  8. package/dist/crowdy-client.d.ts +44 -24
  9. package/dist/crowdy-client.d.ts.map +1 -1
  10. package/dist/crowdy-client.js +42 -81
  11. package/dist/domains/actors.d.ts +22 -0
  12. package/dist/domains/actors.d.ts.map +1 -0
  13. package/dist/domains/actors.js +42 -0
  14. package/dist/domains/appAccess.d.ts +23 -0
  15. package/dist/domains/appAccess.d.ts.map +1 -0
  16. package/dist/domains/appAccess.js +42 -0
  17. package/dist/domains/apps.d.ts +27 -0
  18. package/dist/domains/apps.d.ts.map +1 -0
  19. package/dist/domains/apps.js +49 -0
  20. package/dist/domains/auth.d.ts +32 -0
  21. package/dist/domains/auth.d.ts.map +1 -0
  22. package/dist/domains/auth.js +70 -0
  23. package/dist/domains/billing.d.ts +17 -0
  24. package/dist/domains/billing.d.ts.map +1 -0
  25. package/dist/domains/billing.js +31 -0
  26. package/dist/domains/chunks.d.ts +20 -0
  27. package/dist/domains/chunks.d.ts.map +1 -0
  28. package/dist/domains/chunks.js +40 -0
  29. package/dist/domains/organizations.d.ts +33 -0
  30. package/dist/domains/organizations.d.ts.map +1 -0
  31. package/dist/domains/organizations.js +90 -0
  32. package/dist/domains/payments.d.ts +20 -0
  33. package/dist/domains/payments.d.ts.map +1 -0
  34. package/dist/domains/payments.js +28 -0
  35. package/dist/domains/quotas.d.ts +20 -0
  36. package/dist/domains/quotas.d.ts.map +1 -0
  37. package/dist/domains/quotas.js +34 -0
  38. package/dist/domains/serverStatus.d.ts +21 -0
  39. package/dist/domains/serverStatus.d.ts.map +1 -0
  40. package/dist/domains/serverStatus.js +32 -0
  41. package/dist/domains/state.d.ts +16 -0
  42. package/dist/domains/state.d.ts.map +1 -0
  43. package/dist/domains/state.js +27 -0
  44. package/dist/domains/teleport.d.ts +13 -0
  45. package/dist/domains/teleport.d.ts.map +1 -0
  46. package/dist/domains/teleport.js +15 -0
  47. package/dist/domains/udp.d.ts +32 -0
  48. package/dist/domains/udp.d.ts.map +1 -0
  49. package/dist/domains/udp.js +55 -0
  50. package/dist/domains/users.d.ts +24 -0
  51. package/dist/domains/users.d.ts.map +1 -0
  52. package/dist/domains/users.js +53 -0
  53. package/dist/domains/voxels.d.ts +17 -0
  54. package/dist/domains/voxels.d.ts.map +1 -0
  55. package/dist/domains/voxels.js +31 -0
  56. package/dist/generated/graphql.d.ts +3571 -0
  57. package/dist/generated/graphql.d.ts.map +1 -0
  58. package/dist/generated/graphql.js +181 -0
  59. package/dist/index.d.ts +33 -3
  60. package/dist/index.d.ts.map +1 -1
  61. package/dist/index.js +34 -1
  62. package/dist/subscriptions.d.ts +36 -18
  63. package/dist/subscriptions.d.ts.map +1 -1
  64. package/dist/subscriptions.js +95 -181
  65. package/package.json +20 -1
package/dist/index.d.ts CHANGED
@@ -1,8 +1,38 @@
1
1
  /**
2
- * CrowdyJS SDK - Client SDK for Crowded Kingdoms GraphQL API
2
+ * CrowdyJS SDK - Client SDK for Crowded Kingdoms GraphQL API.
3
+ *
4
+ * Usage:
5
+ *
6
+ * import { CrowdyClient } from '@crowdedkingdomstudios/crowdyjs';
7
+ *
8
+ * const client = new CrowdyClient({ graphqlEndpoint, wsEndpoint });
9
+ * const { token } = await client.auth.login({ email, password });
10
+ * const me = await client.users.me();
11
+ * const myOrgs = await client.orgs.myOrganizations();
12
+ * const checkout = await client.payments.createCheckout({ ... });
13
+ * const unsub = client.udp.subscribe({ onActorUpdate: (n) => { ... } });
3
14
  */
4
15
  declare const VERSION: string;
5
16
  export { VERSION };
6
- export { CrowdyClient } from './crowdy-client.js';
7
- export type { CrowdyClientConfig, BigInt, ChunkCoordinates, ChunkCoordinatesInput, VoxelCoordinates, VoxelCoordinatesInput, UdpErrorCode, User, AuthResponse, UdpProxyConnectionStatus, ActorUpdateRequestInput, VoxelUpdateRequestInput, ClientAudioPacketInput, ClientTextPacketInput, ClientEventNotificationInput, ActorUpdateNotification, ActorUpdateResponse, VoxelUpdateNotification, VoxelUpdateResponse, ClientAudioNotification, ClientTextNotification, ClientEventNotification, ServerEventNotification, GenericErrorResponse, UdpNotification, ActorUpdateHandler, ActorUpdateResponseHandler, VoxelUpdateHandler, VoxelUpdateResponseHandler, ClientAudioHandler, ClientTextHandler, ClientEventHandler, ServerEventHandler, GenericErrorHandler, UnsubscribeFn, } from './types.js';
17
+ export { CrowdyClient, type CrowdyClientConfig } from './crowdy-client.js';
18
+ export type { BigInt, ChunkCoordinates, VoxelCoordinates, ActorUpdateNotification, ActorUpdateResponse, VoxelUpdateNotification, VoxelUpdateResponse, ClientAudioNotification, ClientTextNotification, ClientEventNotification, ServerEventNotification, GenericErrorResponse, UdpNotification, ActorUpdateHandler, ActorUpdateResponseHandler, VoxelUpdateHandler, VoxelUpdateResponseHandler, ClientAudioHandler, ClientTextHandler, ClientEventHandler, ServerEventHandler, GenericErrorHandler, UnsubscribeFn, } from './types.js';
19
+ export { UdpErrorCode } from './types.js';
20
+ export type { UdpNotificationHandlers } from './subscriptions.js';
21
+ export { AuthAPI } from './domains/auth.js';
22
+ export { UsersAPI } from './domains/users.js';
23
+ export { OrganizationsAPI } from './domains/organizations.js';
24
+ export { AppsAPI } from './domains/apps.js';
25
+ export { AppAccessAPI } from './domains/appAccess.js';
26
+ export { BillingAPI } from './domains/billing.js';
27
+ export { QuotasAPI } from './domains/quotas.js';
28
+ export { PaymentsAPI } from './domains/payments.js';
29
+ export { ChunksAPI } from './domains/chunks.js';
30
+ export { VoxelsAPI } from './domains/voxels.js';
31
+ export { ActorsAPI } from './domains/actors.js';
32
+ export { TeleportAPI } from './domains/teleport.js';
33
+ export { StateAPI } from './domains/state.js';
34
+ export { ServerStatusAPI } from './domains/serverStatus.js';
35
+ export { UdpAPI } from './domains/udp.js';
36
+ export type { ChunkCoordinatesInput, VoxelCoordinatesInput, ActorUpdateRequestInput, VoxelUpdateRequestInput, ClientAudioPacketInput, ClientTextPacketInput, ClientEventNotificationInput, UdpProxyConnectionStatus, LoginUserInput, RegisterUserInput, ResetPasswordInput, CreateOrganizationInput, CreateOrgTokenInput, UpdateOrgTokenInput, CreateOrgRoleInput, UpdateOrgRoleInput, InviteOrgMemberInput, CreateAppInput, UpdateAppInput, AppMarketplaceFilterInput, CreateAccessTierInput, UpdateAccessTierInput, GrantAppAccessInput, CreateCheckoutInput, CheckoutFilterInput, SetQuotaInput, CreateActorInput, UpdateActorInput, ActorFilterInput, BatchActorLookupInput, CreateUserAppStateInput, UpdateUserStateInput, UpdateAvatarStateInput, UpdateActorStateInput, UpdateGamertagInput, UpdateChunkStateInput, UpdateChunkLodsInput, ChunkUpdateInput, UpdateVoxelInput, RollbackVoxelUpdatesInput, GetChunkInput, GetChunkLodsInput, GetChunksByDistanceInput, GetVoxelListInput, ListVoxelsInput, ListVoxelUpdatesByDistanceInput, TeleportRequestInput, LodDataInput, VoxelStateInput, Organization, OrgMember, OrgRole, OrgToken, OrgPermission, OrgMembership, App, AppsPage, AppAccessTier, AppUserAccess, AppBudget, OrgWallet, WalletTransaction, Checkout, CheckoutsPage, ServiceQuota, Chunk, ChunkLodsResponse, ChunksByDistanceResponse, ChunkVoxelResponse, ChunkVoxelUpdatesResponse, Voxel, VoxelUpdatesByDistanceResponse, VoxelUpdateHistoryEvent, RollbackVoxelEventResult, Actor, Avatar, AvatarDto, TeleportResponse, UserAppState, ServerStatus, GraphQlServer, ServerVersionInfo, VersionInfo, PageInfo, UsersPage, Scalars, } from './generated/graphql.js';
37
+ export { PaymentProvider, CheckoutPurpose, CheckoutStatus, AppVisibility, AppStatus, ServerState, } from './generated/graphql.js';
8
38
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,QAAA,MAAiB,OAAO,QAAsD,CAAC;AAE/E,OAAO,EAAE,OAAO,EAAE,CAAC;AAGnB,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,YAAY,EAEV,kBAAkB,EAElB,MAAM,EACN,gBAAgB,EAChB,qBAAqB,EACrB,gBAAgB,EAChB,qBAAqB,EACrB,YAAY,EACZ,IAAI,EACJ,YAAY,EACZ,wBAAwB,EAExB,uBAAuB,EACvB,uBAAuB,EACvB,sBAAsB,EACtB,qBAAqB,EACrB,4BAA4B,EAE5B,uBAAuB,EACvB,mBAAmB,EACnB,uBAAuB,EACvB,mBAAmB,EACnB,uBAAuB,EACvB,sBAAsB,EACtB,uBAAuB,EACvB,uBAAuB,EACvB,oBAAoB,EACpB,eAAe,EAEf,kBAAkB,EAClB,0BAA0B,EAC1B,kBAAkB,EAClB,0BAA0B,EAC1B,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,EAClB,mBAAmB,EACnB,aAAa,GACd,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,5 +1,16 @@
1
1
  /**
2
- * CrowdyJS SDK - Client SDK for Crowded Kingdoms GraphQL API
2
+ * CrowdyJS SDK - Client SDK for Crowded Kingdoms GraphQL API.
3
+ *
4
+ * Usage:
5
+ *
6
+ * import { CrowdyClient } from '@crowdedkingdomstudios/crowdyjs';
7
+ *
8
+ * const client = new CrowdyClient({ graphqlEndpoint, wsEndpoint });
9
+ * const { token } = await client.auth.login({ email, password });
10
+ * const me = await client.users.me();
11
+ * const myOrgs = await client.orgs.myOrganizations();
12
+ * const checkout = await client.payments.createCheckout({ ... });
13
+ * const unsub = client.udp.subscribe({ onActorUpdate: (n) => { ... } });
3
14
  */
4
15
  import { createRequire } from 'node:module';
5
16
  const require = createRequire(import.meta.url);
@@ -7,3 +18,25 @@ const { version: VERSION } = require('../package.json');
7
18
  export { VERSION };
8
19
  console.log(`CrowdyJS v${VERSION}`);
9
20
  export { CrowdyClient } from './crowdy-client.js';
21
+ export { UdpErrorCode } from './types.js';
22
+ // -----------------------------------------------------------------------------
23
+ // Domain wrappers (exported so consumers can reference the API surface in
24
+ // their own type annotations).
25
+ // -----------------------------------------------------------------------------
26
+ export { AuthAPI } from './domains/auth.js';
27
+ export { UsersAPI } from './domains/users.js';
28
+ export { OrganizationsAPI } from './domains/organizations.js';
29
+ export { AppsAPI } from './domains/apps.js';
30
+ export { AppAccessAPI } from './domains/appAccess.js';
31
+ export { BillingAPI } from './domains/billing.js';
32
+ export { QuotasAPI } from './domains/quotas.js';
33
+ export { PaymentsAPI } from './domains/payments.js';
34
+ export { ChunksAPI } from './domains/chunks.js';
35
+ export { VoxelsAPI } from './domains/voxels.js';
36
+ export { ActorsAPI } from './domains/actors.js';
37
+ export { TeleportAPI } from './domains/teleport.js';
38
+ export { StateAPI } from './domains/state.js';
39
+ export { ServerStatusAPI } from './domains/serverStatus.js';
40
+ export { UdpAPI } from './domains/udp.js';
41
+ // Re-export schema enums as values (so consumers can switch on them).
42
+ export { PaymentProvider, CheckoutPurpose, CheckoutStatus, AppVisibility, AppStatus, ServerState, } from './generated/graphql.js';
@@ -1,30 +1,48 @@
1
1
  /**
2
- * WebSocket subscription manager with type-specific handlers
2
+ * WebSocket subscription manager for the udpNotifications stream.
3
+ *
4
+ * Owns one shared `graphql-transport-ws` socket and dispatches incoming
5
+ * `udpNotifications` payloads to per-typename handler arrays. Reads its
6
+ * bearer token from `AuthState` so HTTP and WS auth can never drift.
7
+ *
8
+ * Public API is now `subscribe(handlers)` which returns an unsubscribe
9
+ * function; the per-handler `onActorUpdate` etc. shims are gone.
3
10
  */
11
+ import { AuthState } from './auth-state.js';
4
12
  import type { ActorUpdateHandler, ActorUpdateResponseHandler, VoxelUpdateHandler, VoxelUpdateResponseHandler, ClientAudioHandler, ClientTextHandler, ClientEventHandler, ServerEventHandler, GenericErrorHandler, UnsubscribeFn } from './types.js';
13
+ export interface UdpNotificationHandlers {
14
+ onActorUpdate?: ActorUpdateHandler;
15
+ onActorUpdateResponse?: ActorUpdateResponseHandler;
16
+ onVoxelUpdate?: VoxelUpdateHandler;
17
+ onVoxelUpdateResponse?: VoxelUpdateResponseHandler;
18
+ onClientAudio?: ClientAudioHandler;
19
+ onClientText?: ClientTextHandler;
20
+ onClientEvent?: ClientEventHandler;
21
+ onServerEvent?: ServerEventHandler;
22
+ onGenericError?: GenericErrorHandler;
23
+ }
24
+ export interface SubscriptionManagerConfig {
25
+ wsEndpoint?: string;
26
+ }
5
27
  export declare class SubscriptionManager {
6
- private handlers;
28
+ private readonly wsEndpoint;
29
+ private readonly authState;
7
30
  private wsClient;
8
31
  private wsUnsubscribe;
9
32
  private subscriptionId;
10
- private wsEndpoint;
11
- private token;
12
- constructor(config?: {
13
- wsEndpoint?: string;
14
- });
15
- setAuthToken(token: string | null): void;
33
+ private subscribers;
34
+ private nextSubscriberId;
35
+ constructor(config: SubscriptionManagerConfig | undefined, authState: AuthState);
36
+ /**
37
+ * Register handlers for the udpNotifications stream. The first call opens
38
+ * the shared socket (provided we have a token); subsequent calls reuse it.
39
+ * The returned function detaches just this set of handlers and closes the
40
+ * socket once the last subscriber leaves.
41
+ */
42
+ subscribe(handlers: UdpNotificationHandlers): UnsubscribeFn;
16
43
  private ensureSubscription;
17
44
  private startSubscription;
18
- private handleNotification;
19
- onActorUpdate(handler: ActorUpdateHandler): UnsubscribeFn;
20
- onActorUpdateResponse(handler: ActorUpdateResponseHandler): UnsubscribeFn;
21
- onVoxelUpdate(handler: VoxelUpdateHandler): UnsubscribeFn;
22
- onVoxelUpdateResponse(handler: VoxelUpdateResponseHandler): UnsubscribeFn;
23
- onClientAudio(handler: ClientAudioHandler): UnsubscribeFn;
24
- onClientText(handler: ClientTextHandler): UnsubscribeFn;
25
- onClientEvent(handler: ClientEventHandler): UnsubscribeFn;
26
- onServerEvent(handler: ServerEventHandler): UnsubscribeFn;
27
- onGenericError(handler: GenericErrorHandler): UnsubscribeFn;
45
+ private dispatch;
28
46
  private checkIfShouldUnsubscribe;
29
47
  close(): void;
30
48
  }
@@ -1 +1 @@
1
- {"version":3,"file":"subscriptions.d.ts","sourceRoot":"","sources":["../src/subscriptions.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAEV,kBAAkB,EAClB,0BAA0B,EAC1B,kBAAkB,EAClB,0BAA0B,EAC1B,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,EAClB,mBAAmB,EACnB,aAAa,EACd,MAAM,YAAY,CAAC;AAcpB,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAUd;IAEF,OAAO,CAAC,QAAQ,CAA0B;IAC1C,OAAO,CAAC,aAAa,CAA6B;IAClD,OAAO,CAAC,cAAc,CAAuB;IAC7C,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,KAAK,CAAuB;gBAExB,MAAM,GAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAA;KAAO;IAIhD,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAIxC,OAAO,CAAC,kBAAkB;IAU1B,OAAO,CAAC,iBAAiB;IAyHzB,OAAO,CAAC,kBAAkB;IAmB1B,aAAa,CAAC,OAAO,EAAE,kBAAkB,GAAG,aAAa;IAYzD,qBAAqB,CAAC,OAAO,EAAE,0BAA0B,GAAG,aAAa;IAYzE,aAAa,CAAC,OAAO,EAAE,kBAAkB,GAAG,aAAa;IAYzD,qBAAqB,CAAC,OAAO,EAAE,0BAA0B,GAAG,aAAa;IAYzE,aAAa,CAAC,OAAO,EAAE,kBAAkB,GAAG,aAAa;IAYzD,YAAY,CAAC,OAAO,EAAE,iBAAiB,GAAG,aAAa;IAYvD,aAAa,CAAC,OAAO,EAAE,kBAAkB,GAAG,aAAa;IAYzD,aAAa,CAAC,OAAO,EAAE,kBAAkB,GAAG,aAAa;IAYzD,cAAc,CAAC,OAAO,EAAE,mBAAmB,GAAG,aAAa;IAY3D,OAAO,CAAC,wBAAwB;IAShC,KAAK,IAAI,IAAI;CAWd"}
1
+ {"version":3,"file":"subscriptions.d.ts","sourceRoot":"","sources":["../src/subscriptions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,KAAK,EAEV,kBAAkB,EAClB,0BAA0B,EAC1B,kBAAkB,EAClB,0BAA0B,EAC1B,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,EAClB,mBAAmB,EACnB,aAAa,EACd,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,uBAAuB;IACtC,aAAa,CAAC,EAAE,kBAAkB,CAAC;IACnC,qBAAqB,CAAC,EAAE,0BAA0B,CAAC;IACnD,aAAa,CAAC,EAAE,kBAAkB,CAAC;IACnC,qBAAqB,CAAC,EAAE,0BAA0B,CAAC;IACnD,aAAa,CAAC,EAAE,kBAAkB,CAAC;IACnC,YAAY,CAAC,EAAE,iBAAiB,CAAC;IACjC,aAAa,CAAC,EAAE,kBAAkB,CAAC;IACnC,aAAa,CAAC,EAAE,kBAAkB,CAAC;IACnC,cAAc,CAAC,EAAE,mBAAmB,CAAC;CACtC;AAOD,MAAM,WAAW,yBAAyB;IACxC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;IACtC,OAAO,CAAC,QAAQ,CAA0B;IAC1C,OAAO,CAAC,aAAa,CAA6B;IAClD,OAAO,CAAC,cAAc,CAAuB;IAC7C,OAAO,CAAC,WAAW,CAA6C;IAChE,OAAO,CAAC,gBAAgB,CAAK;gBAEjB,MAAM,EAAE,yBAAyB,YAAK,EAAE,SAAS,EAAE,SAAS;IAKxE;;;;;OAKG;IACH,SAAS,CAAC,QAAQ,EAAE,uBAAuB,GAAG,aAAa;IAU3D,OAAO,CAAC,kBAAkB;IAU1B,OAAO,CAAC,iBAAiB;IAsFzB,OAAO,CAAC,QAAQ;IA6ChB,OAAO,CAAC,wBAAwB;IAOhC,KAAK,IAAI,IAAI;CAMd"}
@@ -1,27 +1,37 @@
1
1
  /**
2
- * WebSocket subscription manager with type-specific handlers
2
+ * WebSocket subscription manager for the udpNotifications stream.
3
+ *
4
+ * Owns one shared `graphql-transport-ws` socket and dispatches incoming
5
+ * `udpNotifications` payloads to per-typename handler arrays. Reads its
6
+ * bearer token from `AuthState` so HTTP and WS auth can never drift.
7
+ *
8
+ * Public API is now `subscribe(handlers)` which returns an unsubscribe
9
+ * function; the per-handler `onActorUpdate` etc. shims are gone.
3
10
  */
4
11
  export class SubscriptionManager {
5
- constructor(config = {}) {
6
- this.handlers = {
7
- ActorUpdateNotification: [],
8
- ActorUpdateResponse: [],
9
- VoxelUpdateNotification: [],
10
- VoxelUpdateResponse: [],
11
- ClientAudioNotification: [],
12
- ClientTextNotification: [],
13
- ClientEventNotification: [],
14
- ServerEventNotification: [],
15
- GenericErrorResponse: [],
16
- };
12
+ constructor(config = {}, authState) {
17
13
  this.wsClient = null;
18
14
  this.wsUnsubscribe = null;
19
15
  this.subscriptionId = null;
20
- this.token = null;
16
+ this.subscribers = new Map();
17
+ this.nextSubscriberId = 1;
21
18
  this.wsEndpoint = config.wsEndpoint || 'ws://localhost:3000/graphql';
22
- }
23
- setAuthToken(token) {
24
- this.token = token;
19
+ this.authState = authState;
20
+ }
21
+ /**
22
+ * Register handlers for the udpNotifications stream. The first call opens
23
+ * the shared socket (provided we have a token); subsequent calls reuse it.
24
+ * The returned function detaches just this set of handlers and closes the
25
+ * socket once the last subscriber leaves.
26
+ */
27
+ subscribe(handlers) {
28
+ const id = `s${this.nextSubscriberId++}`;
29
+ this.subscribers.set(id, { id, handlers });
30
+ this.ensureSubscription();
31
+ return () => {
32
+ this.subscribers.delete(id);
33
+ this.checkIfShouldUnsubscribe();
34
+ };
25
35
  }
26
36
  ensureSubscription() {
27
37
  if (!this.wsClient ||
@@ -31,23 +41,18 @@ export class SubscriptionManager {
31
41
  }
32
42
  }
33
43
  startSubscription() {
34
- if (!this.token) {
44
+ const token = this.authState.getToken();
45
+ if (!token) {
35
46
  throw new Error('Must be authenticated to subscribe');
36
47
  }
37
- if (this.wsClient &&
38
- (this.wsClient.readyState === WebSocket.OPEN ||
39
- this.wsClient.readyState === WebSocket.CONNECTING)) {
40
- return; // Already connected or connecting
41
- }
42
- const wsUrl = this.wsEndpoint;
43
- this.subscriptionId = 'udp-notifications-' + Date.now();
44
- const ws = new WebSocket(wsUrl, 'graphql-transport-ws');
48
+ this.subscriptionId = `udp-notifications-${Date.now()}`;
49
+ const ws = new WebSocket(this.wsEndpoint, 'graphql-transport-ws');
45
50
  this.wsClient = ws;
46
51
  ws.onopen = () => {
47
52
  ws.send(JSON.stringify({
48
53
  type: 'connection_init',
49
54
  payload: {
50
- Authorization: `Bearer ${this.token}`,
55
+ Authorization: `Bearer ${token}`,
51
56
  },
52
57
  }));
53
58
  };
@@ -55,55 +60,30 @@ export class SubscriptionManager {
55
60
  try {
56
61
  const message = JSON.parse(typeof event.data === 'string' ? event.data : '');
57
62
  if (message.type === 'connection_ack') {
58
- const subscribeMessage = {
63
+ ws.send(JSON.stringify({
59
64
  id: this.subscriptionId,
60
65
  type: 'subscribe',
61
- payload: {
62
- query: `subscription {
63
- udpNotifications {
64
- __typename
65
- ... on ActorUpdateNotification { appId chunkX chunkY chunkZ distance decayRate uuid state sequenceNumber epochMillis }
66
- ... on VoxelUpdateNotification { appId chunkX chunkY chunkZ distance decayRate uuid voxelX voxelY voxelZ voxelType voxelState sequenceNumber epochMillis }
67
- ... on GenericErrorResponse { sequenceNumber errorCode }
68
- ... on ActorUpdateResponse { appId chunkX chunkY chunkZ distance decayRate uuid sequenceNumber epochMillis }
69
- ... on VoxelUpdateResponse { appId chunkX chunkY chunkZ distance decayRate uuid sequenceNumber epochMillis }
70
- ... on ClientAudioNotification { appId chunkX chunkY chunkZ distance decayRate uuid audioData sequenceNumber epochMillis }
71
- ... on ClientTextNotification { appId chunkX chunkY chunkZ distance decayRate uuid text sequenceNumber epochMillis }
72
- ... on ClientEventNotification { appId chunkX chunkY chunkZ distance decayRate uuid eventType state sequenceNumber epochMillis }
73
- ... on ServerEventNotification { appId chunkX chunkY chunkZ distance decayRate uuid eventType state sequenceNumber epochMillis }
74
- }
75
- }`,
76
- },
77
- };
78
- ws.send(JSON.stringify(subscribeMessage));
66
+ payload: { query: UDP_NOTIFICATIONS_QUERY },
67
+ }));
79
68
  }
80
69
  else if (message.type === 'next') {
81
- if (message.payload?.data?.udpNotifications === null) {
70
+ if (message.payload?.data?.udpNotifications === null)
82
71
  return;
83
- }
84
- if (message.payload?.data?.udpNotifications) {
85
- const notification = message.payload.data.udpNotifications;
86
- this.handleNotification(notification);
72
+ const notification = message.payload?.data
73
+ ?.udpNotifications;
74
+ if (notification) {
75
+ this.dispatch(notification);
87
76
  }
88
77
  else if (message.payload?.errors) {
89
78
  console.error('Subscription errors:', message.payload.errors);
90
- const firstError = message.payload.errors[0];
91
- const errorMsg = firstError?.message
92
- ? Array.isArray(firstError.message)
93
- ? firstError.message.join(', ')
94
- : String(firstError.message)
95
- : firstError?.extensions?.message || 'Unknown subscription error';
96
- console.error('Subscription error:', errorMsg);
97
79
  }
98
80
  }
99
81
  else if (message.type === 'error') {
100
82
  console.error('Subscription error:', message.payload);
101
83
  }
102
84
  else if (message.type === 'complete') {
103
- // Server ended the subscription
104
- if (this.wsClient === ws) {
85
+ if (this.wsClient === ws)
105
86
  this.wsClient = null;
106
- }
107
87
  }
108
88
  else if (message.type === 'ping') {
109
89
  ws.send(JSON.stringify({ type: 'pong' }));
@@ -120,9 +100,8 @@ export class SubscriptionManager {
120
100
  if (event.code !== 1000) {
121
101
  console.warn(`WebSocket closed unexpectedly: ${event.reason || event.code}`);
122
102
  }
123
- if (this.wsClient === ws) {
103
+ if (this.wsClient === ws)
124
104
  this.wsClient = null;
125
- }
126
105
  };
127
106
  this.wsUnsubscribe = () => {
128
107
  if (ws.readyState === WebSocket.OPEN) {
@@ -134,145 +113,80 @@ export class SubscriptionManager {
134
113
  }
135
114
  ws.close();
136
115
  }
137
- if (this.wsClient === ws) {
116
+ if (this.wsClient === ws)
138
117
  this.wsClient = null;
139
- }
140
118
  this.wsUnsubscribe = null;
141
119
  };
142
120
  }
143
- handleNotification(notification) {
121
+ dispatch(notification) {
144
122
  const type = notification.__typename;
145
123
  if (!type) {
146
124
  console.warn('Received notification without __typename:', notification);
147
125
  return;
148
126
  }
149
- const handlers = this.handlers[type];
150
- if (handlers && handlers.length > 0) {
151
- handlers.forEach((handler) => {
152
- try {
153
- handler(notification);
154
- }
155
- catch (error) {
156
- console.error(`Error in handler for ${type}:`, error);
127
+ // Snapshot the subscribers list so handlers that mutate the registry
128
+ // (e.g. by calling close()) don't affect this dispatch loop.
129
+ for (const sub of [...this.subscribers.values()]) {
130
+ try {
131
+ switch (type) {
132
+ case 'ActorUpdateNotification':
133
+ sub.handlers.onActorUpdate?.(notification);
134
+ break;
135
+ case 'ActorUpdateResponse':
136
+ sub.handlers.onActorUpdateResponse?.(notification);
137
+ break;
138
+ case 'VoxelUpdateNotification':
139
+ sub.handlers.onVoxelUpdate?.(notification);
140
+ break;
141
+ case 'VoxelUpdateResponse':
142
+ sub.handlers.onVoxelUpdateResponse?.(notification);
143
+ break;
144
+ case 'ClientAudioNotification':
145
+ sub.handlers.onClientAudio?.(notification);
146
+ break;
147
+ case 'ClientTextNotification':
148
+ sub.handlers.onClientText?.(notification);
149
+ break;
150
+ case 'ClientEventNotification':
151
+ sub.handlers.onClientEvent?.(notification);
152
+ break;
153
+ case 'ServerEventNotification':
154
+ sub.handlers.onServerEvent?.(notification);
155
+ break;
156
+ case 'GenericErrorResponse':
157
+ sub.handlers.onGenericError?.(notification);
158
+ break;
157
159
  }
158
- });
159
- }
160
- }
161
- onActorUpdate(handler) {
162
- this.handlers.ActorUpdateNotification.push(handler);
163
- this.ensureSubscription();
164
- return () => {
165
- const index = this.handlers.ActorUpdateNotification.indexOf(handler);
166
- if (index > -1) {
167
- this.handlers.ActorUpdateNotification.splice(index, 1);
168
- }
169
- this.checkIfShouldUnsubscribe();
170
- };
171
- }
172
- onActorUpdateResponse(handler) {
173
- this.handlers.ActorUpdateResponse.push(handler);
174
- this.ensureSubscription();
175
- return () => {
176
- const index = this.handlers.ActorUpdateResponse.indexOf(handler);
177
- if (index > -1) {
178
- this.handlers.ActorUpdateResponse.splice(index, 1);
179
- }
180
- this.checkIfShouldUnsubscribe();
181
- };
182
- }
183
- onVoxelUpdate(handler) {
184
- this.handlers.VoxelUpdateNotification.push(handler);
185
- this.ensureSubscription();
186
- return () => {
187
- const index = this.handlers.VoxelUpdateNotification.indexOf(handler);
188
- if (index > -1) {
189
- this.handlers.VoxelUpdateNotification.splice(index, 1);
190
160
  }
191
- this.checkIfShouldUnsubscribe();
192
- };
193
- }
194
- onVoxelUpdateResponse(handler) {
195
- this.handlers.VoxelUpdateResponse.push(handler);
196
- this.ensureSubscription();
197
- return () => {
198
- const index = this.handlers.VoxelUpdateResponse.indexOf(handler);
199
- if (index > -1) {
200
- this.handlers.VoxelUpdateResponse.splice(index, 1);
201
- }
202
- this.checkIfShouldUnsubscribe();
203
- };
204
- }
205
- onClientAudio(handler) {
206
- this.handlers.ClientAudioNotification.push(handler);
207
- this.ensureSubscription();
208
- return () => {
209
- const index = this.handlers.ClientAudioNotification.indexOf(handler);
210
- if (index > -1) {
211
- this.handlers.ClientAudioNotification.splice(index, 1);
212
- }
213
- this.checkIfShouldUnsubscribe();
214
- };
215
- }
216
- onClientText(handler) {
217
- this.handlers.ClientTextNotification.push(handler);
218
- this.ensureSubscription();
219
- return () => {
220
- const index = this.handlers.ClientTextNotification.indexOf(handler);
221
- if (index > -1) {
222
- this.handlers.ClientTextNotification.splice(index, 1);
223
- }
224
- this.checkIfShouldUnsubscribe();
225
- };
226
- }
227
- onClientEvent(handler) {
228
- this.handlers.ClientEventNotification.push(handler);
229
- this.ensureSubscription();
230
- return () => {
231
- const index = this.handlers.ClientEventNotification.indexOf(handler);
232
- if (index > -1) {
233
- this.handlers.ClientEventNotification.splice(index, 1);
234
- }
235
- this.checkIfShouldUnsubscribe();
236
- };
237
- }
238
- onServerEvent(handler) {
239
- this.handlers.ServerEventNotification.push(handler);
240
- this.ensureSubscription();
241
- return () => {
242
- const index = this.handlers.ServerEventNotification.indexOf(handler);
243
- if (index > -1) {
244
- this.handlers.ServerEventNotification.splice(index, 1);
245
- }
246
- this.checkIfShouldUnsubscribe();
247
- };
248
- }
249
- onGenericError(handler) {
250
- this.handlers.GenericErrorResponse.push(handler);
251
- this.ensureSubscription();
252
- return () => {
253
- const index = this.handlers.GenericErrorResponse.indexOf(handler);
254
- if (index > -1) {
255
- this.handlers.GenericErrorResponse.splice(index, 1);
161
+ catch (error) {
162
+ console.error(`Handler for ${type} threw:`, error);
256
163
  }
257
- this.checkIfShouldUnsubscribe();
258
- };
164
+ }
259
165
  }
260
166
  checkIfShouldUnsubscribe() {
261
- // Check if any handlers are still registered
262
- const hasHandlers = Object.values(this.handlers).some((handlers) => handlers.length > 0);
263
- if (!hasHandlers && this.wsUnsubscribe) {
167
+ if (this.subscribers.size === 0 && this.wsUnsubscribe) {
264
168
  this.wsUnsubscribe();
265
169
  this.wsUnsubscribe = null;
266
170
  }
267
171
  }
268
172
  close() {
269
- // Clear all handlers
270
- Object.keys(this.handlers).forEach((key) => {
271
- this.handlers[key] = [];
272
- });
273
- // Close WebSocket if open
173
+ this.subscribers.clear();
274
174
  if (this.wsUnsubscribe) {
275
175
  this.wsUnsubscribe();
276
176
  }
277
177
  }
278
178
  }
179
+ const UDP_NOTIFICATIONS_QUERY = `subscription {
180
+ udpNotifications {
181
+ __typename
182
+ ... on ActorUpdateNotification { appId chunkX chunkY chunkZ distance decayRate uuid state sequenceNumber epochMillis }
183
+ ... on VoxelUpdateNotification { appId chunkX chunkY chunkZ distance decayRate uuid voxelX voxelY voxelZ voxelType voxelState sequenceNumber epochMillis }
184
+ ... on GenericErrorResponse { sequenceNumber errorCode }
185
+ ... on ActorUpdateResponse { appId chunkX chunkY chunkZ distance decayRate uuid sequenceNumber epochMillis }
186
+ ... on VoxelUpdateResponse { appId chunkX chunkY chunkZ distance decayRate uuid sequenceNumber epochMillis }
187
+ ... on ClientAudioNotification { appId chunkX chunkY chunkZ distance decayRate uuid audioData sequenceNumber epochMillis }
188
+ ... on ClientTextNotification { appId chunkX chunkY chunkZ distance decayRate uuid text sequenceNumber epochMillis }
189
+ ... on ClientEventNotification { appId chunkX chunkY chunkZ distance decayRate uuid eventType state sequenceNumber epochMillis }
190
+ ... on ServerEventNotification { appId chunkX chunkY chunkZ distance decayRate uuid eventType state sequenceNumber epochMillis }
191
+ }
192
+ }`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crowdedkingdomstudios/crowdyjs",
3
- "version": "2.0.0",
3
+ "version": "2.1.1",
4
4
  "description": "Client SDK for Crowded Kingdoms GraphQL API with UDP proxy support",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -16,6 +16,9 @@
16
16
  "README.md"
17
17
  ],
18
18
  "scripts": {
19
+ "codegen": "graphql-codegen --config codegen.ts",
20
+ "codegen:watch": "graphql-codegen --config codegen.ts --watch",
21
+ "prebuild": "npm run codegen",
19
22
  "build": "tsc",
20
23
  "watch": "tsc --watch",
21
24
  "clean": "rm -rf dist",
@@ -33,9 +36,25 @@
33
36
  ],
34
37
  "author": "",
35
38
  "license": "MIT",
39
+ "repository": {
40
+ "type": "git",
41
+ "url": "git+https://github.com/CrowdedKingdoms/CrowdyJS.git"
42
+ },
43
+ "bugs": {
44
+ "url": "https://github.com/CrowdedKingdoms/CrowdyJS/issues"
45
+ },
46
+ "homepage": "https://github.com/CrowdedKingdoms/CrowdyJS#readme",
36
47
  "devDependencies": {
48
+ "@graphql-codegen/cli": "^6.3.0",
49
+ "@graphql-codegen/typed-document-node": "^6.1.8",
50
+ "@graphql-codegen/typescript": "^5.0.10",
51
+ "@graphql-codegen/typescript-operations": "^5.1.0",
37
52
  "@types/node": "^22.10.7",
38
53
  "typescript": "^5.7.3",
39
54
  "ws": "^8.20.0"
55
+ },
56
+ "dependencies": {
57
+ "@graphql-typed-document-node/core": "^3.2.0",
58
+ "graphql": "^16.13.2"
40
59
  }
41
60
  }