@baasix/sdk 0.1.6 → 0.1.8

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
@@ -807,24 +807,198 @@ const categories = await baasix.reports.distinct('products', 'category');
807
807
 
808
808
  ## Workflows
809
809
 
810
+ ### Basic Operations
811
+
812
+ ```typescript
813
+ // List workflows
814
+ const { data: workflows } = await baasix.workflows.find();
815
+
816
+ // Get workflow by ID
817
+ const workflow = await baasix.workflows.findOne('workflow-uuid');
818
+
819
+ // Create workflow
820
+ const newWorkflow = await baasix.workflows.create({
821
+ name: 'Order Processing',
822
+ trigger: { type: 'hook', config: { collection: 'orders', event: 'items.create.after' } },
823
+ nodes: [...],
824
+ edges: [...],
825
+ isActive: true
826
+ });
827
+
828
+ // Update workflow
829
+ await baasix.workflows.update('workflow-uuid', { name: 'Updated Name' });
830
+
831
+ // Delete workflow
832
+ await baasix.workflows.delete('workflow-uuid');
833
+
834
+ // Enable/Disable workflow
835
+ await baasix.workflows.enable('workflow-uuid');
836
+ await baasix.workflows.disable('workflow-uuid');
837
+
838
+ // Duplicate workflow
839
+ const copy = await baasix.workflows.duplicate('workflow-uuid', { name: 'Copy' });
840
+ ```
841
+
842
+ ### Execution
843
+
810
844
  ```typescript
811
845
  // Execute workflow
812
846
  const result = await baasix.workflows.execute('workflow-uuid', {
813
847
  orderId: 'order-123',
814
848
  });
815
849
 
850
+ // Execute a specific node
851
+ const nodeResult = await baasix.workflows.executeNode('workflow-uuid', 'node-id', {
852
+ inputData: 'value'
853
+ });
854
+
855
+ // Test workflow (without persisting)
856
+ const testResult = await baasix.workflows.test('workflow-uuid', { testData: {} });
857
+
816
858
  // Get execution history
817
- const { data: executions } = await baasix.workflows.getExecutions('workflow-uuid');
859
+ const { data: executions } = await baasix.workflows.getExecutions('workflow-uuid', {
860
+ limit: 50,
861
+ status: 'completed'
862
+ });
863
+
864
+ // Get specific execution
865
+ const execution = await baasix.workflows.getExecution('workflow-uuid', 'execution-uuid');
866
+
867
+ // Get execution logs
868
+ const logs = await baasix.workflows.getExecutionLogs('workflow-uuid', 'execution-uuid');
869
+
870
+ // Cancel running execution
871
+ await baasix.workflows.cancelExecution('workflow-uuid', 'execution-uuid');
818
872
 
819
873
  // Subscribe to execution updates (requires realtime)
820
874
  const unsubscribe = baasix.realtime.subscribeToExecution(executionId, (update) => {
821
- console.log('Execution progress:', update.progress, '%');
875
+ console.log('Progress:', update.progress, '%');
822
876
  if (update.status === 'complete') {
823
877
  console.log('Workflow finished!', update.result);
824
878
  }
825
879
  });
826
880
  ```
827
881
 
882
+ ### Statistics & Validation
883
+
884
+ ```typescript
885
+ // Get workflow statistics
886
+ const stats = await baasix.workflows.getStats('workflow-uuid');
887
+ console.log(`Total: ${stats.totalExecutions}, Success Rate: ${stats.successRate}%`);
888
+
889
+ // Validate workflow definition
890
+ const validation = await baasix.workflows.validate({
891
+ name: 'My Workflow',
892
+ nodes: [...],
893
+ edges: [...]
894
+ });
895
+ if (!validation.valid) {
896
+ console.log('Errors:', validation.errors);
897
+ }
898
+ ```
899
+
900
+ ### Export/Import
901
+
902
+ ```typescript
903
+ // Export single workflow
904
+ const exported = await baasix.workflows.export('workflow-uuid');
905
+
906
+ // Export all workflows
907
+ const allExported = await baasix.workflows.exportAll({
908
+ ids: ['wf-1', 'wf-2'], // Optional: specific workflows
909
+ includeInactive: true
910
+ });
911
+
912
+ // Preview import
913
+ const preview = await baasix.workflows.importPreview(file);
914
+ console.log('Will import:', preview.workflows.length);
915
+ console.log('Conflicts:', preview.conflicts);
916
+
917
+ // Import workflows
918
+ const result = await baasix.workflows.import(file, { overwrite: true });
919
+ console.log(`Imported: ${result.imported}, Skipped: ${result.skipped}`);
920
+ ```
921
+
922
+ ## Settings
923
+
924
+ ```typescript
925
+ // Get all settings
926
+ const settings = await baasix.settings.get();
927
+
928
+ // Get a specific setting
929
+ const appName = await baasix.settings.getKey('appName');
930
+
931
+ // Update settings
932
+ await baasix.settings.update({
933
+ appName: 'My Application',
934
+ theme: 'dark'
935
+ });
936
+
937
+ // Set a specific setting
938
+ await baasix.settings.set('appName', 'New App Name');
939
+
940
+ // Get settings by app URL (multi-tenant)
941
+ const tenantSettings = await baasix.settings.getByAppUrl('https://myapp.example.com');
942
+
943
+ // Get email branding
944
+ const branding = await baasix.settings.getBranding();
945
+
946
+ // Test email configuration (admin only)
947
+ await baasix.settings.testEmail('admin@example.com');
948
+
949
+ // Reload settings cache (admin only)
950
+ await baasix.settings.reload();
951
+
952
+ // Delete tenant settings (admin only)
953
+ await baasix.settings.deleteTenant();
954
+ ```
955
+
956
+ ## Permissions
957
+
958
+ ```typescript
959
+ // List all permissions
960
+ const { data: permissions } = await baasix.permissions.find();
961
+
962
+ // Get permissions for a role
963
+ const { data: rolePerms } = await baasix.permissions.findByRole('role-uuid');
964
+
965
+ // Get permissions for a collection
966
+ const { data: collectionPerms } = await baasix.permissions.findByCollection('products');
967
+
968
+ // Create permission
969
+ const permission = await baasix.permissions.create({
970
+ role_Id: 'editor-role-uuid',
971
+ collection: 'posts',
972
+ action: 'update',
973
+ fields: ['title', 'content'],
974
+ conditions: { author_Id: { eq: '$CURRENT_USER' } }
975
+ });
976
+
977
+ // Create CRUD permissions for a collection
978
+ await baasix.permissions.createCrudPermissions('role-uuid', 'products', {
979
+ create: { fields: ['name', 'price'] },
980
+ read: { fields: ['*'] },
981
+ update: { fields: ['name', 'price'] },
982
+ delete: false
983
+ });
984
+
985
+ // Update permission
986
+ await baasix.permissions.update('permission-uuid', { fields: ['*'] });
987
+
988
+ // Delete permission
989
+ await baasix.permissions.delete('permission-uuid');
990
+
991
+ // Reload permissions cache (admin only)
992
+ await baasix.permissions.reloadCache();
993
+
994
+ // Export all permissions
995
+ const exported = await baasix.permissions.export();
996
+
997
+ // Import permissions
998
+ const result = await baasix.permissions.import(exportedData, { overwrite: true });
999
+ console.log(`Imported: ${result.imported}`);
1000
+ ```
1001
+
828
1002
  ## Realtime Subscriptions
829
1003
 
830
1004
  The SDK supports real-time data updates via WebSocket connections.
@@ -879,6 +1053,52 @@ const channel = baasix.realtime
879
1053
  channel.unsubscribe();
880
1054
  ```
881
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
1062
+ await baasix.realtime.joinRoom('game:lobby');
1063
+
1064
+ // Get current members (you must be in the room)
1065
+ const members = await baasix.realtime.getRoomMembers('game:lobby');
1066
+ // [{ socketId: string, userId: string|number, isCreator: boolean }, ...]
1067
+
1068
+ // Send a message to all room members
1069
+ await baasix.realtime.sendToRoom('game:lobby', 'chat', { text: 'Hello!' });
1070
+
1071
+ // Listen for room messages
1072
+ const unsubscribe = baasix.realtime.onRoomMessage('game:lobby', 'chat', (data) => {
1073
+ console.log(`${data.sender.userId}: ${data.payload.text}`);
1074
+ });
1075
+
1076
+ // Listen for users joining / leaving
1077
+ baasix.realtime.onRoomUserJoined('game:lobby', (data) => {
1078
+ console.log(`${data.userId} joined`);
1079
+ });
1080
+ baasix.realtime.onRoomUserLeft('game:lobby', (data) => {
1081
+ console.log(`${data.userId} left`);
1082
+ });
1083
+
1084
+ // Kick a user — only the room creator can do this
1085
+ await baasix.realtime.kickFromRoom('game:lobby', 'target-user-id');
1086
+
1087
+ // Listen for being kicked out (fires only on the kicked user's socket)
1088
+ baasix.realtime.onKicked('game:lobby', ({ kickedBy }) => {
1089
+ console.log(`You were kicked by user ${kickedBy}`);
1090
+ // Room listeners are automatically cleaned up after a kick
1091
+ });
1092
+
1093
+ // Listen for ownership changes (fires for all members when creator changes)
1094
+ baasix.realtime.onRoomCreatorChanged('game:lobby', ({ newCreatorUserId }) => {
1095
+ console.log(`New room owner: ${newCreatorUserId}`);
1096
+ });
1097
+
1098
+ // Leave the room
1099
+ await baasix.realtime.leaveRoom('game:lobby');
1100
+ ```
1101
+
882
1102
  ### Connection Management
883
1103
 
884
1104
  ```typescript
package/dist/index.cjs CHANGED
@@ -2584,6 +2584,37 @@ var PermissionsModule = class {
2584
2584
  async reloadCache() {
2585
2585
  await this.client.post("/permissions/reload");
2586
2586
  }
2587
+ /**
2588
+ * Export all permissions (admin only)
2589
+ *
2590
+ * @example
2591
+ * ```typescript
2592
+ * const exported = await baasix.permissions.export();
2593
+ * // Save to file or transfer
2594
+ * ```
2595
+ */
2596
+ async export() {
2597
+ const response = await this.client.post("/permissions-export", {});
2598
+ return response.data;
2599
+ }
2600
+ /**
2601
+ * Import permissions from exported data (admin only)
2602
+ *
2603
+ * @example
2604
+ * ```typescript
2605
+ * const result = await baasix.permissions.import(exportedData, {
2606
+ * overwrite: true
2607
+ * });
2608
+ * console.log('Imported:', result.imported, 'permissions');
2609
+ * ```
2610
+ */
2611
+ async import(data, options) {
2612
+ const response = await this.client.post("/permissions-import", {
2613
+ ...data,
2614
+ ...options
2615
+ });
2616
+ return response.data;
2617
+ }
2587
2618
  };
2588
2619
 
2589
2620
  // src/modules/settings.ts
@@ -2650,6 +2681,73 @@ var SettingsModule = class {
2650
2681
  async set(key, value) {
2651
2682
  return this.update({ [key]: value });
2652
2683
  }
2684
+ /**
2685
+ * Get settings by application URL (useful for multi-tenant apps)
2686
+ *
2687
+ * @example
2688
+ * ```typescript
2689
+ * const settings = await baasix.settings.getByAppUrl('https://myapp.example.com');
2690
+ * ```
2691
+ */
2692
+ async getByAppUrl(appUrl) {
2693
+ const response = await this.client.get(
2694
+ "/settings/by-app-url",
2695
+ { params: { appUrl } }
2696
+ );
2697
+ return response.data;
2698
+ }
2699
+ /**
2700
+ * Get email branding settings for the current tenant
2701
+ *
2702
+ * @example
2703
+ * ```typescript
2704
+ * const branding = await baasix.settings.getBranding();
2705
+ * console.log(branding.logo, branding.primaryColor);
2706
+ * ```
2707
+ */
2708
+ async getBranding() {
2709
+ const response = await this.client.get(
2710
+ "/settings/branding"
2711
+ );
2712
+ return response.data;
2713
+ }
2714
+ /**
2715
+ * Test email configuration by sending a test email
2716
+ *
2717
+ * @example
2718
+ * ```typescript
2719
+ * await baasix.settings.testEmail('admin@example.com');
2720
+ * ```
2721
+ */
2722
+ async testEmail(to) {
2723
+ const response = await this.client.post(
2724
+ "/settings/test-email",
2725
+ { to }
2726
+ );
2727
+ return response;
2728
+ }
2729
+ /**
2730
+ * Reload settings cache (admin only)
2731
+ *
2732
+ * @example
2733
+ * ```typescript
2734
+ * await baasix.settings.reload();
2735
+ * ```
2736
+ */
2737
+ async reload() {
2738
+ await this.client.post("/settings/reload");
2739
+ }
2740
+ /**
2741
+ * Delete tenant settings (admin only, multi-tenant)
2742
+ *
2743
+ * @example
2744
+ * ```typescript
2745
+ * await baasix.settings.deleteTenant();
2746
+ * ```
2747
+ */
2748
+ async deleteTenant() {
2749
+ await this.client.delete("/settings/tenant");
2750
+ }
2653
2751
  };
2654
2752
 
2655
2753
  // src/modules/reports.ts
@@ -3027,6 +3125,159 @@ var WorkflowsModule = class {
3027
3125
  ...overrides
3028
3126
  });
3029
3127
  }
3128
+ /**
3129
+ * Execute a specific node in a workflow
3130
+ *
3131
+ * @example
3132
+ * ```typescript
3133
+ * const result = await baasix.workflows.executeNode(
3134
+ * 'workflow-uuid',
3135
+ * 'node-id',
3136
+ * { inputData: 'value' }
3137
+ * );
3138
+ * ```
3139
+ */
3140
+ async executeNode(workflowId, nodeId, triggerData) {
3141
+ const response = await this.client.post(
3142
+ `/workflows/${workflowId}/nodes/${nodeId}/execute`,
3143
+ { triggerData }
3144
+ );
3145
+ return response.data;
3146
+ }
3147
+ /**
3148
+ * Get execution logs for a specific execution
3149
+ *
3150
+ * @example
3151
+ * ```typescript
3152
+ * const logs = await baasix.workflows.getExecutionLogs(
3153
+ * 'workflow-uuid',
3154
+ * 'execution-uuid'
3155
+ * );
3156
+ * ```
3157
+ */
3158
+ async getExecutionLogs(workflowId, executionId) {
3159
+ const response = await this.client.get(
3160
+ `/workflows/${workflowId}/executions/${executionId}/logs`
3161
+ );
3162
+ return response.data;
3163
+ }
3164
+ /**
3165
+ * Get workflow statistics
3166
+ *
3167
+ * @example
3168
+ * ```typescript
3169
+ * const stats = await baasix.workflows.getStats('workflow-uuid');
3170
+ * console.log(stats.totalExecutions, stats.successRate);
3171
+ * ```
3172
+ */
3173
+ async getStats(id) {
3174
+ const response = await this.client.get(`/workflows/${id}/stats`);
3175
+ return response.data;
3176
+ }
3177
+ /**
3178
+ * Validate a workflow definition
3179
+ *
3180
+ * @example
3181
+ * ```typescript
3182
+ * const result = await baasix.workflows.validate({
3183
+ * name: 'My Workflow',
3184
+ * nodes: [...],
3185
+ * edges: [...]
3186
+ * });
3187
+ * if (result.valid) {
3188
+ * console.log('Workflow is valid');
3189
+ * } else {
3190
+ * console.log('Errors:', result.errors);
3191
+ * }
3192
+ * ```
3193
+ */
3194
+ async validate(workflow) {
3195
+ const response = await this.client.post(
3196
+ "/workflows/validate",
3197
+ workflow
3198
+ );
3199
+ return response;
3200
+ }
3201
+ /**
3202
+ * Export a single workflow
3203
+ *
3204
+ * @example
3205
+ * ```typescript
3206
+ * const exported = await baasix.workflows.export('workflow-uuid');
3207
+ * // Save to file or transfer
3208
+ * ```
3209
+ */
3210
+ async export(id) {
3211
+ const response = await this.client.get(
3212
+ `/workflows/${id}/export`
3213
+ );
3214
+ return response.data;
3215
+ }
3216
+ /**
3217
+ * Export multiple workflows
3218
+ *
3219
+ * @example
3220
+ * ```typescript
3221
+ * // Export all workflows
3222
+ * const exported = await baasix.workflows.exportAll();
3223
+ *
3224
+ * // Export specific workflows
3225
+ * const exported = await baasix.workflows.exportAll({
3226
+ * ids: ['workflow-1', 'workflow-2']
3227
+ * });
3228
+ * ```
3229
+ */
3230
+ async exportAll(options) {
3231
+ const response = await this.client.post(
3232
+ "/workflows/export",
3233
+ options || {}
3234
+ );
3235
+ return response.data;
3236
+ }
3237
+ /**
3238
+ * Preview workflow import without applying changes
3239
+ *
3240
+ * @example
3241
+ * ```typescript
3242
+ * const preview = await baasix.workflows.importPreview(file);
3243
+ * console.log('Will import:', preview.workflows.length, 'workflows');
3244
+ * console.log('Conflicts:', preview.conflicts);
3245
+ * ```
3246
+ */
3247
+ async importPreview(file) {
3248
+ const formData = new FormData();
3249
+ if (file instanceof File) {
3250
+ formData.append("file", file);
3251
+ } else {
3252
+ formData.append("file", file);
3253
+ }
3254
+ const response = await this.client.post("/workflows/import/preview", formData);
3255
+ return response.data;
3256
+ }
3257
+ /**
3258
+ * Import workflows from a file
3259
+ *
3260
+ * @example
3261
+ * ```typescript
3262
+ * const result = await baasix.workflows.import(file, {
3263
+ * overwrite: true
3264
+ * });
3265
+ * console.log('Imported:', result.imported, 'workflows');
3266
+ * ```
3267
+ */
3268
+ async import(file, options) {
3269
+ const formData = new FormData();
3270
+ if (file instanceof File) {
3271
+ formData.append("file", file);
3272
+ } else {
3273
+ formData.append("file", file);
3274
+ }
3275
+ if (options?.overwrite !== void 0) {
3276
+ formData.append("overwrite", String(options.overwrite));
3277
+ }
3278
+ const response = await this.client.post("/workflows/import", formData);
3279
+ return response.data;
3280
+ }
3030
3281
  };
3031
3282
 
3032
3283
  // src/modules/realtime.ts
@@ -3042,6 +3293,8 @@ var RealtimeModule = class {
3042
3293
  roomCallbacks = /* @__PURE__ */ new Map();
3043
3294
  // room -> event -> callbacks
3044
3295
  roomUserCallbacks = /* @__PURE__ */ new Map();
3296
+ kickCallbacks = /* @__PURE__ */ new Map();
3297
+ creatorChangedCallbacks = /* @__PURE__ */ new Map();
3045
3298
  connectionCallbacks = /* @__PURE__ */ new Set();
3046
3299
  reconnecting = false;
3047
3300
  connectionPromise = null;
@@ -3171,6 +3424,27 @@ var RealtimeModule = class {
3171
3424
  }
3172
3425
  });
3173
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
+ });
3174
3448
  this.socket.connect();
3175
3449
  } catch (error) {
3176
3450
  this.connectionPromise = null;
@@ -3200,6 +3474,8 @@ var RealtimeModule = class {
3200
3474
  this.workflowCallbacks.clear();
3201
3475
  this.roomCallbacks.clear();
3202
3476
  this.roomUserCallbacks.clear();
3477
+ this.kickCallbacks.clear();
3478
+ this.creatorChangedCallbacks.clear();
3203
3479
  }
3204
3480
  /**
3205
3481
  * Check if connected to the realtime server
@@ -3470,6 +3746,110 @@ var RealtimeModule = class {
3470
3746
  });
3471
3747
  });
3472
3748
  }
3749
+ /**
3750
+ * Get the list of users currently in a room.
3751
+ * You must already be a member of the room to call this.
3752
+ *
3753
+ * @example
3754
+ * ```typescript
3755
+ * const members = await baasix.realtime.getRoomMembers('game:lobby');
3756
+ * members.forEach(m => {
3757
+ * console.log(m.userId, m.isCreator ? '(owner)' : '');
3758
+ * });
3759
+ * ```
3760
+ */
3761
+ async getRoomMembers(roomName) {
3762
+ if (!this.socket?.connected) {
3763
+ throw new Error("Not connected. Call connect() first.");
3764
+ }
3765
+ return new Promise((resolve, reject) => {
3766
+ this.socket.emit("room:members", { room: roomName }, (response) => {
3767
+ if (response.status === "success") {
3768
+ resolve(response.members);
3769
+ } else {
3770
+ reject(new Error(response.message || "Failed to get room members"));
3771
+ }
3772
+ });
3773
+ });
3774
+ }
3775
+ /**
3776
+ * Kick a user from a custom room. Only the room creator may call this.
3777
+ *
3778
+ * @example
3779
+ * ```typescript
3780
+ * await baasix.realtime.kickFromRoom('game:lobby', 'user-id-123');
3781
+ * ```
3782
+ */
3783
+ async kickFromRoom(roomName, targetUserId) {
3784
+ if (!this.socket?.connected) {
3785
+ throw new Error("Not connected. Call connect() first.");
3786
+ }
3787
+ return new Promise((resolve, reject) => {
3788
+ this.socket.emit(
3789
+ "room:kick",
3790
+ { room: roomName, userId: targetUserId },
3791
+ (response) => {
3792
+ if (response.status === "success") {
3793
+ resolve();
3794
+ } else {
3795
+ reject(new Error(response.message || "Failed to kick user"));
3796
+ }
3797
+ }
3798
+ );
3799
+ });
3800
+ }
3801
+ /**
3802
+ * Listen for being kicked from a room.
3803
+ * The callback fires when the current user is removed by the room creator.
3804
+ * Room listeners are automatically cleaned up after the kick.
3805
+ *
3806
+ * @example
3807
+ * ```typescript
3808
+ * baasix.realtime.onKicked('game:lobby', ({ kickedBy }) => {
3809
+ * console.log(`You were kicked by user ${kickedBy}`);
3810
+ * });
3811
+ * ```
3812
+ */
3813
+ onKicked(roomName, callback) {
3814
+ if (!this.kickCallbacks.has(roomName)) {
3815
+ this.kickCallbacks.set(roomName, /* @__PURE__ */ new Set());
3816
+ }
3817
+ this.kickCallbacks.get(roomName).add(callback);
3818
+ return () => {
3819
+ const callbacks = this.kickCallbacks.get(roomName);
3820
+ if (callbacks) {
3821
+ callbacks.delete(callback);
3822
+ if (callbacks.size === 0) {
3823
+ this.kickCallbacks.delete(roomName);
3824
+ }
3825
+ }
3826
+ };
3827
+ }
3828
+ /**
3829
+ * Listen for room ownership changes (e.g. when the creator leaves).
3830
+ *
3831
+ * @example
3832
+ * ```typescript
3833
+ * baasix.realtime.onRoomCreatorChanged('game:lobby', ({ newCreatorUserId }) => {
3834
+ * console.log(`New room owner: ${newCreatorUserId}`);
3835
+ * });
3836
+ * ```
3837
+ */
3838
+ onRoomCreatorChanged(roomName, callback) {
3839
+ if (!this.creatorChangedCallbacks.has(roomName)) {
3840
+ this.creatorChangedCallbacks.set(roomName, /* @__PURE__ */ new Set());
3841
+ }
3842
+ this.creatorChangedCallbacks.get(roomName).add(callback);
3843
+ return () => {
3844
+ const callbacks = this.creatorChangedCallbacks.get(roomName);
3845
+ if (callbacks) {
3846
+ callbacks.delete(callback);
3847
+ if (callbacks.size === 0) {
3848
+ this.creatorChangedCallbacks.delete(roomName);
3849
+ }
3850
+ }
3851
+ };
3852
+ }
3473
3853
  /**
3474
3854
  * Send a message to a room
3475
3855
  *
@@ -3622,6 +4002,8 @@ var RealtimeModule = class {
3622
4002
  cleanupRoomListeners(roomName) {
3623
4003
  this.roomCallbacks.delete(roomName);
3624
4004
  this.roomUserCallbacks.delete(roomName);
4005
+ this.kickCallbacks.delete(roomName);
4006
+ this.creatorChangedCallbacks.delete(roomName);
3625
4007
  }
3626
4008
  // ===================
3627
4009
  // Channel (Room) API - Supabase-style