cocobase 1.3.5 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,228 @@
1
+ /**
2
+ * Player information in a game room
3
+ */
4
+ export interface Player {
5
+ id: string;
6
+ name?: string;
7
+ [key: string]: any;
8
+ }
9
+ /**
10
+ * Room information from room discovery
11
+ */
12
+ export interface RoomInfo {
13
+ room_id: string;
14
+ player_count: number;
15
+ max_players?: number;
16
+ metadata?: Record<string, any>;
17
+ [key: string]: any;
18
+ }
19
+ /**
20
+ * Room discovery response
21
+ */
22
+ export interface RoomListResponse {
23
+ rooms: RoomInfo[];
24
+ total: number;
25
+ }
26
+ /**
27
+ * Game event types
28
+ */
29
+ export type GameEventType = "connected" | "player_joined" | "player_left" | "state" | "message" | "error" | "disconnected" | string;
30
+ /**
31
+ * Game event callback
32
+ */
33
+ export type GameEventCallback<T = any> = (data: T) => void;
34
+ /**
35
+ * Options for connecting to a game
36
+ */
37
+ export interface GameConnectOptions {
38
+ /** Room ID to join */
39
+ roomId: string;
40
+ /** JWT token for authentication (optional) */
41
+ token?: string;
42
+ /** Additional data to send on connect */
43
+ metadata?: Record<string, any>;
44
+ }
45
+ /**
46
+ * GameClient - Easy-to-use multiplayer game client
47
+ *
48
+ * @example
49
+ * ```typescript
50
+ * const game = db.realtime.game('my-game-function');
51
+ *
52
+ * // Set up event handlers
53
+ * game.on('connected', (data) => {
54
+ * console.log('Connected! My player ID:', data.your_id);
55
+ * });
56
+ *
57
+ * game.on('player_joined', (data) => {
58
+ * console.log('Player joined:', data.player_id);
59
+ * });
60
+ *
61
+ * game.on('state', (data) => {
62
+ * // Update game state
63
+ * renderPlayers(data.players);
64
+ * });
65
+ *
66
+ * // Connect to a room
67
+ * await game.connect({ roomId: 'game-room-1' });
68
+ *
69
+ * // Send actions
70
+ * game.send({ action: 'move', x: 100, y: 200 });
71
+ *
72
+ * // Disconnect when done
73
+ * game.disconnect();
74
+ * ```
75
+ */
76
+ export declare class GameClient {
77
+ private projectId;
78
+ private functionName;
79
+ private baseURL;
80
+ private ws;
81
+ private token?;
82
+ private listeners;
83
+ private reconnectEnabled;
84
+ private reconnectDelay;
85
+ private maxReconnectDelay;
86
+ private currentReconnectDelay;
87
+ private reconnectTimeout?;
88
+ private lastConnectOptions?;
89
+ private pingInterval?;
90
+ private _isConnected;
91
+ private _playerId?;
92
+ constructor(projectId: string, functionName: string, token?: string);
93
+ /**
94
+ * Whether the client is currently connected
95
+ */
96
+ get isConnected(): boolean;
97
+ /**
98
+ * The current player's ID (available after connection)
99
+ */
100
+ get playerId(): string | undefined;
101
+ /**
102
+ * Connect to a game room
103
+ *
104
+ * @param options - Connection options
105
+ * @returns Promise that resolves when connected
106
+ *
107
+ * @example
108
+ * ```typescript
109
+ * await game.connect({ roomId: 'lobby' });
110
+ *
111
+ * // With authentication
112
+ * await game.connect({
113
+ * roomId: 'private-game',
114
+ * token: userJwtToken
115
+ * });
116
+ *
117
+ * // With metadata
118
+ * await game.connect({
119
+ * roomId: 'game-1',
120
+ * metadata: { playerName: 'John', team: 'red' }
121
+ * });
122
+ * ```
123
+ */
124
+ connect(options: GameConnectOptions): Promise<void>;
125
+ /**
126
+ * Send a message/action to the game server
127
+ *
128
+ * @param data - Data to send (will be JSON serialized)
129
+ *
130
+ * @example
131
+ * ```typescript
132
+ * // Send a move action
133
+ * game.send({ action: 'move', x: 100, y: 200 });
134
+ *
135
+ * // Send a chat message
136
+ * game.send({ action: 'chat', message: 'Hello!' });
137
+ *
138
+ * // Send any game action
139
+ * game.send({ action: 'shoot', angle: 45, power: 80 });
140
+ * ```
141
+ */
142
+ send(data: Record<string, any>): void;
143
+ /**
144
+ * Register an event listener
145
+ *
146
+ * @param event - Event type to listen for
147
+ * @param callback - Callback function
148
+ * @returns Unsubscribe function
149
+ *
150
+ * @example
151
+ * ```typescript
152
+ * // Listen for player joining
153
+ * const unsub = game.on('player_joined', (data) => {
154
+ * console.log('Player joined:', data.player_id);
155
+ * });
156
+ *
157
+ * // Later, unsubscribe
158
+ * unsub();
159
+ *
160
+ * // Common events:
161
+ * game.on('connected', (data) => { ... });
162
+ * game.on('player_joined', (data) => { ... });
163
+ * game.on('player_left', (data) => { ... });
164
+ * game.on('state', (data) => { ... });
165
+ * game.on('tick', (data) => { ... });
166
+ * game.on('error', (data) => { ... });
167
+ * game.on('disconnected', (data) => { ... });
168
+ * ```
169
+ */
170
+ on<T = any>(event: GameEventType, callback: GameEventCallback<T>): () => void;
171
+ /**
172
+ * Register a one-time event listener
173
+ *
174
+ * @param event - Event type to listen for
175
+ * @param callback - Callback function (called once then removed)
176
+ *
177
+ * @example
178
+ * ```typescript
179
+ * game.once('connected', (data) => {
180
+ * console.log('Connected with ID:', data.your_id);
181
+ * });
182
+ * ```
183
+ */
184
+ once<T = any>(event: GameEventType, callback: GameEventCallback<T>): void;
185
+ /**
186
+ * Remove an event listener
187
+ *
188
+ * @param event - Event type
189
+ * @param callback - Callback to remove
190
+ */
191
+ off(event: GameEventType, callback: GameEventCallback): void;
192
+ /**
193
+ * Remove all listeners for an event (or all events)
194
+ *
195
+ * @param event - Optional event type. If not provided, removes all listeners.
196
+ */
197
+ removeAllListeners(event?: GameEventType): void;
198
+ /**
199
+ * Disconnect from the game server
200
+ *
201
+ * @param disableReconnect - If true, prevents auto-reconnect
202
+ */
203
+ disconnect(disableReconnect?: boolean): void;
204
+ /**
205
+ * Enable or disable auto-reconnect
206
+ */
207
+ setAutoReconnect(enabled: boolean): void;
208
+ private emit;
209
+ private scheduleReconnect;
210
+ }
211
+ /**
212
+ * List available game rooms for a function
213
+ *
214
+ * @param projectId - Project ID
215
+ * @param apiKey - API key (optional)
216
+ * @param publicOnly - Only list public rooms (default: true)
217
+ * @returns Promise resolving to room list
218
+ *
219
+ * @example
220
+ * ```typescript
221
+ * const { rooms, total } = await listGameRooms('my-project');
222
+ * rooms.forEach(room => {
223
+ * console.log(`${room.room_id}: ${room.player_count}/${room.max_players} players`);
224
+ * });
225
+ * ```
226
+ */
227
+ export declare function listGameRooms(projectId: string, apiKey?: string, publicOnly?: boolean): Promise<RoomListResponse>;
228
+ //# sourceMappingURL=multiplayer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"multiplayer.d.ts","sourceRoot":"","sources":["../../src/realtime/multiplayer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,399 @@
1
+ import { BASEURL } from "../utils/utils.js";
2
+ /**
3
+ * GameClient - Easy-to-use multiplayer game client
4
+ *
5
+ * @example
6
+ * ```typescript
7
+ * const game = db.realtime.game('my-game-function');
8
+ *
9
+ * // Set up event handlers
10
+ * game.on('connected', (data) => {
11
+ * console.log('Connected! My player ID:', data.your_id);
12
+ * });
13
+ *
14
+ * game.on('player_joined', (data) => {
15
+ * console.log('Player joined:', data.player_id);
16
+ * });
17
+ *
18
+ * game.on('state', (data) => {
19
+ * // Update game state
20
+ * renderPlayers(data.players);
21
+ * });
22
+ *
23
+ * // Connect to a room
24
+ * await game.connect({ roomId: 'game-room-1' });
25
+ *
26
+ * // Send actions
27
+ * game.send({ action: 'move', x: 100, y: 200 });
28
+ *
29
+ * // Disconnect when done
30
+ * game.disconnect();
31
+ * ```
32
+ */
33
+ export class GameClient {
34
+ constructor(projectId, functionName, token) {
35
+ this.ws = null;
36
+ this.listeners = new Map();
37
+ this.reconnectEnabled = true;
38
+ this.reconnectDelay = 1000;
39
+ this.maxReconnectDelay = 30000;
40
+ this.currentReconnectDelay = 1000;
41
+ this._isConnected = false;
42
+ this.projectId = projectId;
43
+ this.functionName = functionName;
44
+ this.token = token;
45
+ this.baseURL = BASEURL.replace(/^http/, "ws");
46
+ }
47
+ /**
48
+ * Whether the client is currently connected
49
+ */
50
+ get isConnected() {
51
+ return this._isConnected;
52
+ }
53
+ /**
54
+ * The current player's ID (available after connection)
55
+ */
56
+ get playerId() {
57
+ return this._playerId;
58
+ }
59
+ /**
60
+ * Connect to a game room
61
+ *
62
+ * @param options - Connection options
63
+ * @returns Promise that resolves when connected
64
+ *
65
+ * @example
66
+ * ```typescript
67
+ * await game.connect({ roomId: 'lobby' });
68
+ *
69
+ * // With authentication
70
+ * await game.connect({
71
+ * roomId: 'private-game',
72
+ * token: userJwtToken
73
+ * });
74
+ *
75
+ * // With metadata
76
+ * await game.connect({
77
+ * roomId: 'game-1',
78
+ * metadata: { playerName: 'John', team: 'red' }
79
+ * });
80
+ * ```
81
+ */
82
+ connect(options) {
83
+ return new Promise((resolve, reject) => {
84
+ this.lastConnectOptions = options;
85
+ const authToken = options.token || this.token;
86
+ // Build WebSocket URL
87
+ let wsUrl = `${this.baseURL}/ws/${this.projectId}/${this.functionName}`;
88
+ if (authToken) {
89
+ wsUrl += `?token=${encodeURIComponent(authToken)}`;
90
+ }
91
+ try {
92
+ this.ws = new WebSocket(wsUrl);
93
+ }
94
+ catch (err) {
95
+ reject(err);
96
+ return;
97
+ }
98
+ this.ws.onopen = () => {
99
+ this.currentReconnectDelay = this.reconnectDelay;
100
+ // Send initial message with room_id and metadata
101
+ const initMessage = {
102
+ room_id: options.roomId,
103
+ };
104
+ if (options.metadata) {
105
+ Object.assign(initMessage, options.metadata);
106
+ }
107
+ this.ws?.send(JSON.stringify(initMessage));
108
+ // Start ping interval
109
+ this.pingInterval = globalThis.setInterval(() => {
110
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
111
+ this.ws.send(JSON.stringify({ type: "ping" }));
112
+ }
113
+ }, 30000);
114
+ };
115
+ this.ws.onmessage = (event) => {
116
+ try {
117
+ const data = JSON.parse(event.data);
118
+ // Handle pong silently
119
+ if (data.type === "pong")
120
+ return;
121
+ // Extract event type from various possible fields
122
+ const eventType = data.type || data.event || "message";
123
+ // Handle connection confirmation
124
+ if (eventType === "connected" ||
125
+ eventType === "welcome" ||
126
+ data.your_id) {
127
+ this._isConnected = true;
128
+ if (data.your_id) {
129
+ this._playerId = data.your_id;
130
+ }
131
+ this.emit("connected", data);
132
+ resolve();
133
+ return;
134
+ }
135
+ // Handle errors
136
+ if (eventType === "error" || data.error) {
137
+ this.emit("error", data);
138
+ if (!this._isConnected) {
139
+ reject(new Error(data.error || data.message || "Connection error"));
140
+ }
141
+ return;
142
+ }
143
+ // Emit the event
144
+ this.emit(eventType, data);
145
+ }
146
+ catch (err) {
147
+ this.emit("error", { error: err });
148
+ }
149
+ };
150
+ this.ws.onclose = (event) => {
151
+ this._isConnected = false;
152
+ if (this.pingInterval) {
153
+ clearInterval(this.pingInterval);
154
+ this.pingInterval = undefined;
155
+ }
156
+ this.emit("disconnected", {
157
+ code: event.code,
158
+ reason: event.reason,
159
+ wasClean: event.wasClean,
160
+ });
161
+ // Auto-reconnect if enabled and not a clean close
162
+ if (this.reconnectEnabled && !event.wasClean && this.lastConnectOptions) {
163
+ this.scheduleReconnect();
164
+ }
165
+ };
166
+ this.ws.onerror = (err) => {
167
+ this.emit("error", { error: err });
168
+ if (!this._isConnected) {
169
+ reject(new Error("WebSocket connection failed"));
170
+ }
171
+ };
172
+ // Timeout for initial connection
173
+ setTimeout(() => {
174
+ if (!this._isConnected) {
175
+ this.ws?.close();
176
+ reject(new Error("Connection timeout"));
177
+ }
178
+ }, 10000);
179
+ });
180
+ }
181
+ /**
182
+ * Send a message/action to the game server
183
+ *
184
+ * @param data - Data to send (will be JSON serialized)
185
+ *
186
+ * @example
187
+ * ```typescript
188
+ * // Send a move action
189
+ * game.send({ action: 'move', x: 100, y: 200 });
190
+ *
191
+ * // Send a chat message
192
+ * game.send({ action: 'chat', message: 'Hello!' });
193
+ *
194
+ * // Send any game action
195
+ * game.send({ action: 'shoot', angle: 45, power: 80 });
196
+ * ```
197
+ */
198
+ send(data) {
199
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
200
+ throw new Error("Not connected to game server");
201
+ }
202
+ this.ws.send(JSON.stringify(data));
203
+ }
204
+ /**
205
+ * Register an event listener
206
+ *
207
+ * @param event - Event type to listen for
208
+ * @param callback - Callback function
209
+ * @returns Unsubscribe function
210
+ *
211
+ * @example
212
+ * ```typescript
213
+ * // Listen for player joining
214
+ * const unsub = game.on('player_joined', (data) => {
215
+ * console.log('Player joined:', data.player_id);
216
+ * });
217
+ *
218
+ * // Later, unsubscribe
219
+ * unsub();
220
+ *
221
+ * // Common events:
222
+ * game.on('connected', (data) => { ... });
223
+ * game.on('player_joined', (data) => { ... });
224
+ * game.on('player_left', (data) => { ... });
225
+ * game.on('state', (data) => { ... });
226
+ * game.on('tick', (data) => { ... });
227
+ * game.on('error', (data) => { ... });
228
+ * game.on('disconnected', (data) => { ... });
229
+ * ```
230
+ */
231
+ on(event, callback) {
232
+ if (!this.listeners.has(event)) {
233
+ this.listeners.set(event, []);
234
+ }
235
+ this.listeners.get(event).push(callback);
236
+ // Return unsubscribe function
237
+ return () => {
238
+ const callbacks = this.listeners.get(event);
239
+ if (callbacks) {
240
+ const index = callbacks.indexOf(callback);
241
+ if (index !== -1) {
242
+ callbacks.splice(index, 1);
243
+ }
244
+ }
245
+ };
246
+ }
247
+ /**
248
+ * Register a one-time event listener
249
+ *
250
+ * @param event - Event type to listen for
251
+ * @param callback - Callback function (called once then removed)
252
+ *
253
+ * @example
254
+ * ```typescript
255
+ * game.once('connected', (data) => {
256
+ * console.log('Connected with ID:', data.your_id);
257
+ * });
258
+ * ```
259
+ */
260
+ once(event, callback) {
261
+ const wrapper = (data) => {
262
+ this.off(event, wrapper);
263
+ callback(data);
264
+ };
265
+ this.on(event, wrapper);
266
+ }
267
+ /**
268
+ * Remove an event listener
269
+ *
270
+ * @param event - Event type
271
+ * @param callback - Callback to remove
272
+ */
273
+ off(event, callback) {
274
+ const callbacks = this.listeners.get(event);
275
+ if (callbacks) {
276
+ const index = callbacks.indexOf(callback);
277
+ if (index !== -1) {
278
+ callbacks.splice(index, 1);
279
+ }
280
+ }
281
+ }
282
+ /**
283
+ * Remove all listeners for an event (or all events)
284
+ *
285
+ * @param event - Optional event type. If not provided, removes all listeners.
286
+ */
287
+ removeAllListeners(event) {
288
+ if (event) {
289
+ this.listeners.delete(event);
290
+ }
291
+ else {
292
+ this.listeners.clear();
293
+ }
294
+ }
295
+ /**
296
+ * Disconnect from the game server
297
+ *
298
+ * @param disableReconnect - If true, prevents auto-reconnect
299
+ */
300
+ disconnect(disableReconnect = true) {
301
+ if (disableReconnect) {
302
+ this.reconnectEnabled = false;
303
+ }
304
+ if (this.reconnectTimeout) {
305
+ clearTimeout(this.reconnectTimeout);
306
+ this.reconnectTimeout = undefined;
307
+ }
308
+ if (this.pingInterval) {
309
+ clearInterval(this.pingInterval);
310
+ this.pingInterval = undefined;
311
+ }
312
+ if (this.ws) {
313
+ this.ws.close(1000, "Client disconnect");
314
+ this.ws = null;
315
+ }
316
+ this._isConnected = false;
317
+ this._playerId = undefined;
318
+ }
319
+ /**
320
+ * Enable or disable auto-reconnect
321
+ */
322
+ setAutoReconnect(enabled) {
323
+ this.reconnectEnabled = enabled;
324
+ if (!enabled && this.reconnectTimeout) {
325
+ clearTimeout(this.reconnectTimeout);
326
+ this.reconnectTimeout = undefined;
327
+ }
328
+ }
329
+ emit(event, data) {
330
+ const callbacks = this.listeners.get(event);
331
+ if (callbacks) {
332
+ callbacks.forEach((cb) => {
333
+ try {
334
+ cb(data);
335
+ }
336
+ catch (err) {
337
+ console.error(`Error in ${event} handler:`, err);
338
+ }
339
+ });
340
+ }
341
+ // Also emit to wildcard listeners
342
+ const wildcardCallbacks = this.listeners.get("*");
343
+ if (wildcardCallbacks) {
344
+ wildcardCallbacks.forEach((cb) => {
345
+ try {
346
+ cb({ event, data });
347
+ }
348
+ catch (err) {
349
+ console.error("Error in wildcard handler:", err);
350
+ }
351
+ });
352
+ }
353
+ }
354
+ scheduleReconnect() {
355
+ if (this.reconnectTimeout)
356
+ return;
357
+ this.reconnectTimeout = setTimeout(async () => {
358
+ this.reconnectTimeout = undefined;
359
+ if (this.lastConnectOptions && this.reconnectEnabled) {
360
+ try {
361
+ await this.connect(this.lastConnectOptions);
362
+ }
363
+ catch {
364
+ // Exponential backoff
365
+ this.currentReconnectDelay = Math.min(this.currentReconnectDelay * 2, this.maxReconnectDelay);
366
+ this.scheduleReconnect();
367
+ }
368
+ }
369
+ }, this.currentReconnectDelay);
370
+ }
371
+ }
372
+ /**
373
+ * List available game rooms for a function
374
+ *
375
+ * @param projectId - Project ID
376
+ * @param apiKey - API key (optional)
377
+ * @param publicOnly - Only list public rooms (default: true)
378
+ * @returns Promise resolving to room list
379
+ *
380
+ * @example
381
+ * ```typescript
382
+ * const { rooms, total } = await listGameRooms('my-project');
383
+ * rooms.forEach(room => {
384
+ * console.log(`${room.room_id}: ${room.player_count}/${room.max_players} players`);
385
+ * });
386
+ * ```
387
+ */
388
+ export async function listGameRooms(projectId, apiKey, publicOnly = true) {
389
+ const url = `${BASEURL}/ws/rooms/${projectId}?public_only=${publicOnly}`;
390
+ const res = await fetch(url, {
391
+ headers: apiKey ? { "X-API-Key": apiKey } : {},
392
+ });
393
+ if (!res.ok) {
394
+ const text = await res.text();
395
+ throw new Error(`Failed to list game rooms: ${text}`);
396
+ }
397
+ return res.json();
398
+ }
399
+ //# sourceMappingURL=multiplayer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"multiplayer.js","sourceRoot":"","sources":["../../src/realtime/multiplayer.ts"],"names":[],"mappings":"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"}
@@ -9,6 +9,9 @@ export interface CocobaseConfig {
9
9
  /** Your Cocobase project ID (required for cloud functions) */
10
10
  projectId?: string;
11
11
  }
12
+ export interface Response {
13
+ message: string;
14
+ }
12
15
  /**
13
16
  * Represents a collection in the database.
14
17
  */
@@ -91,9 +94,36 @@ export interface Query {
91
94
  * Response from authentication operations containing the access token.
92
95
  */
93
96
  export interface TokenResponse {
94
- /** JWT access token for authenticated requests */
97
+ /** JWT access token for authenticated requests (undefined if 2FA required) */
98
+ access_token?: string;
99
+ /** User object (undefined if 2FA required) */
100
+ user?: AppUser;
101
+ /** Whether 2FA verification is required to complete login */
102
+ requires_2fa?: boolean;
103
+ /** Message from the server (e.g., "2FA code sent to your email") */
104
+ message?: string;
105
+ }
106
+ /**
107
+ * Response from 2FA verification endpoint.
108
+ */
109
+ export interface TwoFAVerifyResponse {
110
+ /** JWT access token after successful 2FA verification */
95
111
  access_token: string;
112
+ /** User object */
96
113
  user: AppUser;
114
+ /** Success message */
115
+ message: string;
116
+ }
117
+ /**
118
+ * Login result that clearly indicates whether 2FA is required.
119
+ */
120
+ export interface LoginResult {
121
+ /** Whether 2FA verification is required */
122
+ requires_2fa: boolean;
123
+ /** User object (only present if login succeeded without 2FA) */
124
+ user?: AppUser;
125
+ /** Message from server (present when 2FA is required) */
126
+ message?: string;
97
127
  }
98
128
  /**
99
129
  * Paginated list of users with metadata.