@baasix/sdk 0.1.7 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1053,6 +1053,57 @@ const channel = baasix.realtime
1053
1053
  channel.unsubscribe();
1054
1054
  ```
1055
1055
 
1056
+ ### Custom Rooms
1057
+
1058
+ Custom rooms enable real-time communication between users for chat, games, or collaborative features. The **first user to join** a room becomes its creator. If the creator leaves temporarily, ownership transfers to the next member — but the **original creator automatically reclaims ownership** when they rejoin. If the room empties and is recreated, the next joiner becomes the new owner.
1059
+
1060
+ ```typescript
1061
+ // Join a room — pass optional metadata stored alongside your membership
1062
+ await baasix.realtime.joinRoom('game:lobby', {
1063
+ username: 'Alice',
1064
+ avatar: 'https://example.com/alice.png',
1065
+ team: 'blue',
1066
+ });
1067
+
1068
+ // Get current members (you must be in the room)
1069
+ // Each entry includes userId, socketId, isCreator, and metadata
1070
+ const members = await baasix.realtime.getRoomMembers('game:lobby');
1071
+ // [{ socketId: string, userId: string|number, isCreator: boolean, metadata: Record<string,any> }, ...]
1072
+
1073
+ // Send a message to all room members
1074
+ await baasix.realtime.sendToRoom('game:lobby', 'chat', { text: 'Hello!' });
1075
+
1076
+ // Listen for room messages
1077
+ const unsubscribe = baasix.realtime.onRoomMessage('game:lobby', 'chat', (data) => {
1078
+ console.log(`${data.sender.userId}: ${data.payload.text}`);
1079
+ });
1080
+
1081
+ // Listen for users joining / leaving (joined event includes their metadata)
1082
+ baasix.realtime.onRoomUserJoined('game:lobby', (data) => {
1083
+ console.log(`${data.metadata.username} joined`);
1084
+ });
1085
+ baasix.realtime.onRoomUserLeft('game:lobby', (data) => {
1086
+ console.log(`${data.userId} left`);
1087
+ });
1088
+
1089
+ // Kick a user — only the room creator can do this
1090
+ await baasix.realtime.kickFromRoom('game:lobby', 'target-user-id');
1091
+
1092
+ // Listen for being kicked out (fires only on the kicked user's socket)
1093
+ baasix.realtime.onKicked('game:lobby', ({ kickedBy }) => {
1094
+ console.log(`You were kicked by user ${kickedBy}`);
1095
+ // Room listeners are automatically cleaned up after a kick
1096
+ });
1097
+
1098
+ // Listen for ownership changes (fires for all members when creator changes)
1099
+ baasix.realtime.onRoomCreatorChanged('game:lobby', ({ newCreatorUserId }) => {
1100
+ console.log(`New room owner: ${newCreatorUserId}`);
1101
+ });
1102
+
1103
+ // Leave the room
1104
+ await baasix.realtime.leaveRoom('game:lobby');
1105
+ ```
1106
+
1056
1107
  ### Connection Management
1057
1108
 
1058
1109
  ```typescript
package/dist/index.cjs CHANGED
@@ -3293,6 +3293,8 @@ var RealtimeModule = class {
3293
3293
  roomCallbacks = /* @__PURE__ */ new Map();
3294
3294
  // room -> event -> callbacks
3295
3295
  roomUserCallbacks = /* @__PURE__ */ new Map();
3296
+ kickCallbacks = /* @__PURE__ */ new Map();
3297
+ creatorChangedCallbacks = /* @__PURE__ */ new Map();
3296
3298
  connectionCallbacks = /* @__PURE__ */ new Set();
3297
3299
  reconnecting = false;
3298
3300
  connectionPromise = null;
@@ -3422,6 +3424,27 @@ var RealtimeModule = class {
3422
3424
  }
3423
3425
  });
3424
3426
  });
3427
+ this.socket.on("room:kicked", (data) => {
3428
+ const callbacks = this.kickCallbacks.get(data.room);
3429
+ callbacks?.forEach((cb) => {
3430
+ try {
3431
+ cb(data);
3432
+ } catch (e) {
3433
+ console.error("[Baasix Realtime] Error in room kicked callback:", e);
3434
+ }
3435
+ });
3436
+ this.cleanupRoomListeners(data.room);
3437
+ });
3438
+ this.socket.on("room:creator:changed", (data) => {
3439
+ const callbacks = this.creatorChangedCallbacks.get(data.room);
3440
+ callbacks?.forEach((cb) => {
3441
+ try {
3442
+ cb(data);
3443
+ } catch (e) {
3444
+ console.error("[Baasix Realtime] Error in room creator changed callback:", e);
3445
+ }
3446
+ });
3447
+ });
3425
3448
  this.socket.connect();
3426
3449
  } catch (error) {
3427
3450
  this.connectionPromise = null;
@@ -3451,6 +3474,8 @@ var RealtimeModule = class {
3451
3474
  this.workflowCallbacks.clear();
3452
3475
  this.roomCallbacks.clear();
3453
3476
  this.roomUserCallbacks.clear();
3477
+ this.kickCallbacks.clear();
3478
+ this.creatorChangedCallbacks.clear();
3454
3479
  }
3455
3480
  /**
3456
3481
  * Check if connected to the realtime server
@@ -3670,25 +3695,36 @@ var RealtimeModule = class {
3670
3695
  // Custom Rooms API
3671
3696
  // ===================
3672
3697
  /**
3673
- * Join a custom room for real-time communication
3674
- *
3698
+ * Join a custom room for real-time communication.
3699
+ *
3700
+ * You can optionally attach metadata (e.g. display name, avatar, team) that
3701
+ * will be visible to all other members via {@link getRoomMembers} and in
3702
+ * `room:user:joined` events.
3703
+ *
3704
+ * @param roomName - The room to join.
3705
+ * @param metadata - Optional key/value pairs stored alongside this member.
3706
+ *
3675
3707
  * @example
3676
3708
  * ```typescript
3677
- * // Join a room
3678
- * await baasix.realtime.joinRoom('game:lobby');
3679
- *
3680
- * // Listen for messages
3681
- * baasix.realtime.onRoomMessage('game:lobby', 'chat', (data) => {
3682
- * console.log(`${data.sender.userId}: ${data.payload.text}`);
3709
+ * // Join a room with metadata
3710
+ * await baasix.realtime.joinRoom('game:lobby', {
3711
+ * username: 'Alice',
3712
+ * avatar: 'https://example.com/alice.png',
3713
+ * team: 'blue',
3714
+ * });
3715
+ *
3716
+ * // Listen for other users joining (includes their metadata)
3717
+ * baasix.realtime.onUserJoined('game:lobby', (data) => {
3718
+ * console.log(data.metadata.username, 'joined');
3683
3719
  * });
3684
3720
  * ```
3685
3721
  */
3686
- async joinRoom(roomName) {
3722
+ async joinRoom(roomName, metadata = {}) {
3687
3723
  if (!this.socket?.connected) {
3688
3724
  throw new Error("Not connected. Call connect() first.");
3689
3725
  }
3690
3726
  return new Promise((resolve, reject) => {
3691
- this.socket.emit("room:join", { room: roomName }, (response) => {
3727
+ this.socket.emit("room:join", { room: roomName, metadata }, (response) => {
3692
3728
  if (response.status === "success") {
3693
3729
  this.setupRoomListeners(roomName);
3694
3730
  resolve();
@@ -3721,6 +3757,113 @@ var RealtimeModule = class {
3721
3757
  });
3722
3758
  });
3723
3759
  }
3760
+ /**
3761
+ * Get the list of users currently in a room.
3762
+ * You must already be a member of the room to call this.
3763
+ *
3764
+ * Each entry includes `userId`, `socketId`, `isCreator`, and `metadata`
3765
+ * (the custom object the member passed to {@link joinRoom}).
3766
+ *
3767
+ * @example
3768
+ * ```typescript
3769
+ * const members = await baasix.realtime.getRoomMembers('game:lobby');
3770
+ * members.forEach(m => {
3771
+ * console.log(m.metadata.username, m.isCreator ? '(owner)' : '');
3772
+ * });
3773
+ * ```
3774
+ */
3775
+ async getRoomMembers(roomName) {
3776
+ if (!this.socket?.connected) {
3777
+ throw new Error("Not connected. Call connect() first.");
3778
+ }
3779
+ return new Promise((resolve, reject) => {
3780
+ this.socket.emit("room:members", { room: roomName }, (response) => {
3781
+ if (response.status === "success") {
3782
+ resolve(response.members);
3783
+ } else {
3784
+ reject(new Error(response.message || "Failed to get room members"));
3785
+ }
3786
+ });
3787
+ });
3788
+ }
3789
+ /**
3790
+ * Kick a user from a custom room. Only the room creator may call this.
3791
+ *
3792
+ * @example
3793
+ * ```typescript
3794
+ * await baasix.realtime.kickFromRoom('game:lobby', 'user-id-123');
3795
+ * ```
3796
+ */
3797
+ async kickFromRoom(roomName, targetUserId) {
3798
+ if (!this.socket?.connected) {
3799
+ throw new Error("Not connected. Call connect() first.");
3800
+ }
3801
+ return new Promise((resolve, reject) => {
3802
+ this.socket.emit(
3803
+ "room:kick",
3804
+ { room: roomName, userId: targetUserId },
3805
+ (response) => {
3806
+ if (response.status === "success") {
3807
+ resolve();
3808
+ } else {
3809
+ reject(new Error(response.message || "Failed to kick user"));
3810
+ }
3811
+ }
3812
+ );
3813
+ });
3814
+ }
3815
+ /**
3816
+ * Listen for being kicked from a room.
3817
+ * The callback fires when the current user is removed by the room creator.
3818
+ * Room listeners are automatically cleaned up after the kick.
3819
+ *
3820
+ * @example
3821
+ * ```typescript
3822
+ * baasix.realtime.onKicked('game:lobby', ({ kickedBy }) => {
3823
+ * console.log(`You were kicked by user ${kickedBy}`);
3824
+ * });
3825
+ * ```
3826
+ */
3827
+ onKicked(roomName, callback) {
3828
+ if (!this.kickCallbacks.has(roomName)) {
3829
+ this.kickCallbacks.set(roomName, /* @__PURE__ */ new Set());
3830
+ }
3831
+ this.kickCallbacks.get(roomName).add(callback);
3832
+ return () => {
3833
+ const callbacks = this.kickCallbacks.get(roomName);
3834
+ if (callbacks) {
3835
+ callbacks.delete(callback);
3836
+ if (callbacks.size === 0) {
3837
+ this.kickCallbacks.delete(roomName);
3838
+ }
3839
+ }
3840
+ };
3841
+ }
3842
+ /**
3843
+ * Listen for room ownership changes (e.g. when the creator leaves).
3844
+ *
3845
+ * @example
3846
+ * ```typescript
3847
+ * baasix.realtime.onRoomCreatorChanged('game:lobby', ({ newCreatorUserId }) => {
3848
+ * console.log(`New room owner: ${newCreatorUserId}`);
3849
+ * });
3850
+ * ```
3851
+ */
3852
+ onRoomCreatorChanged(roomName, callback) {
3853
+ if (!this.creatorChangedCallbacks.has(roomName)) {
3854
+ this.creatorChangedCallbacks.set(roomName, /* @__PURE__ */ new Set());
3855
+ }
3856
+ this.creatorChangedCallbacks.get(roomName).add(callback);
3857
+ return () => {
3858
+ const callbacks = this.creatorChangedCallbacks.get(roomName);
3859
+ if (callbacks) {
3860
+ callbacks.delete(callback);
3861
+ if (callbacks.size === 0) {
3862
+ this.creatorChangedCallbacks.delete(roomName);
3863
+ }
3864
+ }
3865
+ };
3866
+ }
3724
3867
  /**
3725
3868
  * Send a message to a room
3726
3869
  *
@@ -3873,6 +4016,8 @@ var RealtimeModule = class {
3873
4016
  cleanupRoomListeners(roomName) {
3874
4017
  this.roomCallbacks.delete(roomName);
3875
4018
  this.roomUserCallbacks.delete(roomName);
4019
+ this.kickCallbacks.delete(roomName);
4020
+ this.creatorChangedCallbacks.delete(roomName);
3876
4021
  }
3877
4022
  // ===================
3878
4023
  // Channel (Room) API - Supabase-style