@kokimoki/app 1.17.0 → 2.0.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 (71) hide show
  1. package/dist/core/index.d.ts +3 -0
  2. package/dist/core/index.js +3 -0
  3. package/dist/core/kokimoki-client.d.ts +361 -0
  4. package/dist/core/kokimoki-client.js +819 -0
  5. package/dist/core/room-subscription-mode.d.ts +5 -0
  6. package/dist/core/room-subscription-mode.js +6 -0
  7. package/dist/core/room-subscription.d.ts +15 -0
  8. package/dist/core/room-subscription.js +53 -0
  9. package/dist/fields.d.ts +110 -0
  10. package/dist/fields.js +158 -0
  11. package/dist/index.d.ts +4 -8
  12. package/dist/index.js +4 -9
  13. package/dist/kokimoki-ai.d.ts +153 -0
  14. package/dist/kokimoki-ai.js +164 -0
  15. package/dist/kokimoki-awareness.d.ts +14 -13
  16. package/dist/kokimoki-awareness.js +41 -33
  17. package/dist/kokimoki-client-refactored.d.ts +80 -0
  18. package/dist/kokimoki-client-refactored.js +400 -0
  19. package/dist/kokimoki-client.d.ts +282 -76
  20. package/dist/kokimoki-client.js +295 -232
  21. package/dist/kokimoki-leaderboard.d.ts +175 -0
  22. package/dist/kokimoki-leaderboard.js +203 -0
  23. package/dist/kokimoki-schema.d.ts +113 -0
  24. package/dist/kokimoki-schema.js +162 -0
  25. package/dist/kokimoki-storage.d.ts +156 -0
  26. package/dist/kokimoki-storage.js +208 -0
  27. package/dist/kokimoki.min.d.ts +775 -110
  28. package/dist/kokimoki.min.js +4088 -2408
  29. package/dist/kokimoki.min.js.map +1 -1
  30. package/dist/llms.txt +657 -0
  31. package/dist/message-queue.d.ts +8 -0
  32. package/dist/message-queue.js +19 -0
  33. package/dist/protocol/ws-message/index.d.ts +3 -0
  34. package/dist/protocol/ws-message/index.js +3 -0
  35. package/dist/protocol/ws-message/reader.d.ts +11 -0
  36. package/dist/protocol/ws-message/reader.js +36 -0
  37. package/dist/protocol/ws-message/type.d.ts +11 -0
  38. package/dist/protocol/ws-message/type.js +12 -0
  39. package/dist/protocol/ws-message/writer.d.ts +9 -0
  40. package/dist/protocol/ws-message/writer.js +45 -0
  41. package/dist/services/index.d.ts +3 -0
  42. package/dist/services/index.js +3 -0
  43. package/dist/services/kokimoki-ai.d.ts +153 -0
  44. package/dist/services/kokimoki-ai.js +164 -0
  45. package/dist/services/kokimoki-leaderboard.d.ts +175 -0
  46. package/dist/services/kokimoki-leaderboard.js +203 -0
  47. package/dist/services/kokimoki-storage.d.ts +155 -0
  48. package/dist/services/kokimoki-storage.js +208 -0
  49. package/dist/stores/index.d.ts +3 -0
  50. package/dist/stores/index.js +3 -0
  51. package/dist/stores/kokimoki-local-store.d.ts +11 -0
  52. package/dist/stores/kokimoki-local-store.js +40 -0
  53. package/dist/stores/kokimoki-store.d.ts +22 -0
  54. package/dist/stores/kokimoki-store.js +117 -0
  55. package/dist/stores/kokimoki-transaction.d.ts +18 -0
  56. package/dist/stores/kokimoki-transaction.js +143 -0
  57. package/dist/synced-schema.d.ts +74 -0
  58. package/dist/synced-schema.js +83 -0
  59. package/dist/synced-store.d.ts +10 -0
  60. package/dist/synced-store.js +9 -0
  61. package/dist/synced-types.d.ts +47 -0
  62. package/dist/synced-types.js +67 -0
  63. package/dist/types/index.d.ts +3 -0
  64. package/dist/types/index.js +3 -0
  65. package/dist/utils/valtio.d.ts +7 -0
  66. package/dist/utils/valtio.js +6 -0
  67. package/dist/version.d.ts +1 -1
  68. package/dist/version.js +2 -1
  69. package/dist/ws-message-type copy.d.ts +6 -0
  70. package/dist/ws-message-type copy.js +7 -0
  71. package/package.json +4 -3
package/dist/llms.txt ADDED
@@ -0,0 +1,657 @@
1
+ # Kokimoki SDK
2
+
3
+ The Kokimoki SDK is a comprehensive development toolkit for building real-time collaborative game applications
4
+
5
+ ## General guidelines
6
+
7
+ - **IMPORTANT** `kmClient` is the main entry point for Kokimoki SDK
8
+ - Use `kmClient.id` as a unique identifier for each client (player)
9
+ - Use `kmClient.store` for global stores and `kmClient.localStore` for local stores
10
+ - Use `kmClient.transact` for atomic state updates across single store or multiple stores
11
+ - Use `kmClient.storage.upload` and related API methods to handle file uploads (media, JSON, etc.) in application
12
+ - Use `kmClient.serverTimestamp()` for time-related matters as this will be synced among players
13
+ - Use `useSnapshot` hook from `valtio` to get reactive state inside React components
14
+ - Use AI integration API methods: `kmClient.ai.chat`, `kmClient.ai.generateJson`, and `kmClient.ai.modifyImage` to add AI capabilities to application
15
+ - Use leaderboard API methods: `kmClient.leaderboard.insertEntry`, `kmClient.leaderboard.upsertEntry`, `kmClient.leaderboard.listEntries`, and `kmClient.leaderboard.getBestEntry` to add leaderboard capabilities to application
16
+
17
+ ## Kokimoki Client
18
+
19
+ - The `kmClient` instance is created in [km-client.ts](../../src/services/km-client.ts)
20
+ - The `kmClient` provides the following key functionalities:
21
+ - `kmClient.store` and `kmClient.localStore` for creating stores
22
+ - `kmClient.transact` for atomic state updates
23
+ - `kmClient.serverTimestamp()` for synchronized timestamps
24
+ - `kmClient.id` for unique player identification
25
+
26
+ ## Client ID
27
+
28
+ - Each client (player) has a unique `kmClient.id` (aka `clientId`), stable identifier that represents a player across multiple connections
29
+ (client sessions)
30
+ - The `kmClient.id` is persistent across client connections
31
+ - The `kmClient.id` remains consistent after user reconnect or page reload
32
+ - Use `kmClient.id` to identify players in global stores
33
+ - Each client (player) can have multiple connections, but all connections share the same `kmClient.id`
34
+
35
+ ## Kokimoki Store
36
+
37
+ Kokimoki Store powered by `valtio` and `valtio-yjs` for real-time state management in Kokimoki game applications
38
+
39
+ ### Store initialization
40
+
41
+ - Stores should be defined in `src/state/stores/`
42
+ - Store can be created in two ways:
43
+ - `kmClient.localStore` is used for data stored on the player device (local)
44
+ - `kmClient.store` is used for data shared among all players in a game (global)
45
+
46
+ **Example: Creating a Store**
47
+
48
+ ```typescript
49
+ import { kmClient } from "@services/km-client";
50
+
51
+ interface State {
52
+ title: string;
53
+ count: number;
54
+ }
55
+
56
+ const initialState: State = {
57
+ title: "Store",
58
+ count: 0,
59
+ };
60
+
61
+ // Initialize global store with initial state
62
+ export const store = kmClient.store<PlayerState>("store-name", initialState);
63
+ ```
64
+
65
+ ### State management
66
+
67
+ - State actions are functions that modify the store state by performing transactions
68
+ - Actions should be defined in `/src/state/actions/`
69
+ - Use async/await for all state transactions by `kmClient.transact` function for global stores
70
+ - Transactions are atomic and ensure state consistency
71
+ - ALWAYS update store state inside `kmClient.transact()` within action function
72
+ - Prefer using records, not arrays: store collections as `Record<string, T>` with timestamp keys for automatic sorting and better sync performance
73
+
74
+ **Example: Updating State**
75
+
76
+ ```typescript
77
+ import { store } from "../store";
78
+
79
+ // Update state
80
+ await kmClient.transact([store], ([state]) => {
81
+ state.title = "New store";
82
+ state.count += 1;
83
+ });
84
+ ```
85
+
86
+ ### Combining Stores
87
+
88
+ - Multiple stores can be updated in a single transaction
89
+ - Prefer to update stores in a single transaction to ensure state consistency
90
+
91
+ **Example: Multiple Stores**
92
+
93
+ ```typescript
94
+ // Update multiple stores in a single transaction
95
+ await kmClient.transact([store1, store2], ([state1, state2]) => {
96
+ state1.name = "My Store1";
97
+ state2.name = "My Store2";
98
+ });
99
+ ```
100
+
101
+ ### Reactive State in Components
102
+
103
+ - Use `useSnapshot` hook from `valtio` to get reactive state inside React components
104
+ - The component will re-render when the store state changes
105
+
106
+ **Example: Using State in Components**
107
+
108
+ ```tsx
109
+ import { useSnapshot } from "valtio";
110
+ import { store } from "../store";
111
+
112
+ const Component = () => {
113
+ // Get reactive snapshot of the store state
114
+ const { title, count } = useSnapshot(store.proxy);
115
+
116
+ return (
117
+ <div>
118
+ <h1>Title: {title}</h1>
119
+ <p>Count: {count}</p>
120
+ </div>
121
+ );
122
+ };
123
+ ```
124
+
125
+ ## Server Time Synchronization
126
+
127
+ Kokimoki SDK implements a time synchronization system to ensure consistent timestamps across all connected clients, regardless of their local clock differences
128
+
129
+ - Use `kmClient.serverTimestamp()` to get the current server-synchronized timestamp
130
+ - The timestamp is a Epoch Unix Timestamp
131
+ - Use server timestamps for time-related matters like event scheduling, timeouts, timers, etc.
132
+
133
+ ## Storage
134
+
135
+ Kokimoki SDK provides a storage service for uploading and managing files. Commonly used for media files (images, videos, audio), but also supports other file types like JSON or text files that aren't suitable for real-time stores. No setup required.
136
+
137
+ Access storage API via `kmClient.storage`.
138
+
139
+ ### API Methods
140
+
141
+ #### storage.upload(name, blob, tags?): Promise<Upload>
142
+
143
+ Uploads a file to storage.
144
+
145
+ **Parameters:**
146
+
147
+ - **name**: `string` filename
148
+ - **blob**: `Blob` object to upload
149
+ - **tags**: `string[]` (optional, default: [])
150
+
151
+ **Example:**
152
+
153
+ ```typescript
154
+ const upload: Upload = await kmClient.storage.upload("filename.jpg", fileBlob, [
155
+ "tag1",
156
+ "tag2",
157
+ ]);
158
+ // Use upload.url to access the media file
159
+ ```
160
+
161
+ #### storage.listUploads(filter?, skip?, limit?): Promise<Paginated<Upload>>
162
+
163
+ Query uploaded files by filter and pagination
164
+
165
+ **Parameters:**
166
+
167
+ - **filter.clientId**: `string` Specific client (optional)
168
+ - **filter.mimeTypes**: `string[]` E.g., ['image/jpeg', 'image/png'] (optional)
169
+ - **filter.tags**: `string[]` All tags must match (optional)
170
+ - **skip**: `number` Pagination offset (default: 0)
171
+ - **limit**: `number` Max results (default: 100)
172
+
173
+ **Example:**
174
+
175
+ ```typescript
176
+ // Query uploads by tag and uploaded by this client
177
+ const { items, total } = await kmClient.storage.listUploads(
178
+ { clientId: kmClient.id, tags: ["tag1"] },
179
+ skip,
180
+ limit
181
+ );
182
+ ```
183
+
184
+ #### storage.updateUpload(id, update): Promise<Upload>
185
+
186
+ Replace uploaded file tags with new tags
187
+
188
+ **Parameters:**
189
+
190
+ - **id**: `string` Upload id
191
+ - **update.tags**: `string[]` (optional)
192
+
193
+ **Example:**
194
+
195
+ ```typescript
196
+ const updatedUpload: Upload = await kmClient.storage.updateUpload(upload.id, {
197
+ tags: ["new"],
198
+ });
199
+ ```
200
+
201
+ #### storage.deleteUpload(id): Promise<{ acknowledged: boolean; deletedCount: number }>
202
+
203
+ Permanently delete uploaded file
204
+
205
+ **Parameters:**
206
+
207
+ - **id**: `string` Upload id
208
+
209
+ **Example:**
210
+
211
+ ```typescript
212
+ await kmClient.storage.deleteUpload(upload.id);
213
+ ```
214
+
215
+ ### Types
216
+
217
+ ```typescript
218
+ interface Upload {
219
+ id: string; // unique id
220
+ url: string; // file url (CDN)
221
+ name: string; // original filename
222
+ size: number; // in bytes
223
+ mimeType: string;
224
+ clientId: string; // who uploaded
225
+ tags: string[]; // metadata for filtering and organization
226
+ completed: boolean; // upload status
227
+ createdAt: Date;
228
+ appId: string;
229
+ }
230
+
231
+ interface Paginated<T> {
232
+ items: T[];
233
+ total: number;
234
+ }
235
+ ```
236
+
237
+ ### Common Patterns
238
+
239
+ #### Example: User-specific uploads
240
+
241
+ ```typescript
242
+ // Get uploaded files by clientId
243
+ const clientUploads = await kmClient.storage.listUploads({
244
+ clientId: kmClient.id,
245
+ });
246
+ ```
247
+
248
+ #### Example: Filter by file type
249
+
250
+ ```typescript
251
+ // Get only uploaded images
252
+ const images = await kmClient.storage.listUploads({
253
+ mimeTypes: ["image/jpeg", "image/png"],
254
+ });
255
+ ```
256
+
257
+ #### Example: Tag-based uploads
258
+
259
+ ```typescript
260
+ // Upload file with tag
261
+ await kmClient.storage.upload("avatar.jpg", blob, ["profile"]);
262
+
263
+ // Query uploads by tag
264
+ const profileUploads = await kmClient.storage.listUploads({
265
+ tags: ["profile"],
266
+ });
267
+ ```
268
+
269
+ #### Example: Usage in Kokimoki Store
270
+
271
+ ```typescript
272
+ // Upload image from Blob
273
+ const upload = await kmClient.storage.upload("file.jpg", blob);
274
+
275
+ await kmClient.transact([store], (state) => {
276
+ // Add image to images array in the store
277
+ state.playerImages[upload.id] = { url: upload.url };
278
+ });
279
+ ```
280
+
281
+ ### Key Points
282
+
283
+ - **Use Cases**: Commonly used for media files (images, videos, audio), but also supports JSON, text files, or any data not suitable for real-time stores
284
+ - **CDN**: File `upload.url` is public and can be used directly
285
+ - **Tags**: Use tag system to organize uploads
286
+ - **Pagination**: Use skip/limit to paginate results
287
+ - **Filtering**: Combine clientId, mimeTypes, and tags to query uploads
288
+
289
+ ## AI Integration
290
+
291
+ Built-in methods for AI text generation and image transformation. No API keys required.
292
+
293
+ Access AI API via `kmClient.ai`.
294
+
295
+ ### API Methods
296
+
297
+ #### ai.chat(req): Promise<{ content: string }>
298
+
299
+ Used to generate text response with AI
300
+
301
+ **Parameters:**
302
+
303
+ - **req.model**: `string` AI model to use (optional). Available models:
304
+ - `gpt-4o`: OpenAI GPT-4 Optimized
305
+ - `gpt-4o-mini`: Smaller, faster GPT-4 variant
306
+ - `gpt-5`: OpenAI GPT-5 (latest)
307
+ - `gpt-5-mini`: Smaller GPT-5 variant
308
+ - `gpt-5-nano`: Smallest GPT-5 variant for lightweight tasks
309
+ - `gemini-2.5-flash-lite`: Google Gemini lite variant
310
+ - `gemini-2.5-flash`: Google Gemini fast variant
311
+ - **req.systemPrompt**: `string` AI role/behavior (optional)
312
+ - **req.userPrompt**: `string` User message (optional)
313
+ - **req.temperature**: `number` Creativity level from 0.0 = factual to 2.0 = creative (optional)
314
+ - **req.maxTokens**: `number` Response length limit (optional)
315
+
316
+ **Examples:**
317
+
318
+ ```typescript
319
+ // Generate text response
320
+ const { content } = await kmClient.ai.chat({
321
+ model: "gemini-2.5-flash-lite",
322
+ systemPrompt: "You are a sarcastic assistant",
323
+ userPrompt: "Write a story about dragons",
324
+ temperature: 0.7, // moderate creativity
325
+ maxTokens: 500, // limit to 500 tokens
326
+ });
327
+ ```
328
+
329
+ #### ai.generateJson<T>(req): Promise<T>
330
+
331
+ Used to generate structured JSON output with AI. Automatically ensures the response is valid JSON and parses it for you.
332
+
333
+ **Parameters:**
334
+
335
+ - **req.model**: `string` AI model to use (optional). Available models:
336
+ - `gpt-4o`: OpenAI GPT-4 Optimized
337
+ - `gpt-4o-mini`: Smaller, faster GPT-4 variant
338
+ - `gpt-5`: OpenAI GPT-5 (latest)
339
+ - `gpt-5-mini`: Smaller GPT-5 variant
340
+ - `gpt-5-nano`: Smallest GPT-5 variant for lightweight tasks
341
+ - `gemini-2.5-flash-lite`: Google Gemini lite variant
342
+ - `gemini-2.5-flash`: Google Gemini fast variant
343
+ - **req.systemPrompt**: `string` AI role/behavior (optional)
344
+ - **req.userPrompt**: `string` User message (optional)
345
+ - **req.temperature**: `number` Creativity level from 0.0 = factual to 2.0 = creative (optional)
346
+ - **req.maxTokens**: `number` Response length limit (optional)
347
+
348
+ **Returns:** Promise resolving to parsed JSON object of type T
349
+
350
+ **Examples:**
351
+
352
+ ```typescript
353
+ // Generate quiz questions
354
+ interface Question {
355
+ question: string;
356
+ options: string[];
357
+ correctAnswer: number;
358
+ }
359
+
360
+ const questions = await kmClient.ai.generateJson<Question[]>({
361
+ systemPrompt: "Generate quiz questions as JSON array",
362
+ userPrompt: "Create 5 history quiz questions with 4 options each",
363
+ temperature: 0.7,
364
+ });
365
+
366
+ // questions is already parsed and typed
367
+ questions.forEach((q) => console.log(q.question));
368
+ ```
369
+
370
+ ```typescript
371
+ // Generate game character data
372
+ interface Character {
373
+ name: string;
374
+ strength: number;
375
+ agility: number;
376
+ backstory: string;
377
+ }
378
+
379
+ const character = await kmClient.ai.generateJson<Character>({
380
+ userPrompt: "Create a fantasy warrior character with stats",
381
+ temperature: 0.8,
382
+ });
383
+
384
+ console.log(character.name, character.strength);
385
+ ```
386
+
387
+ #### ai.modifyImage(baseImageUrl, prompt, tags?): Promise<Upload>
388
+
389
+ Used to modify/transform image with AI. The result is stored as [`Upload`](#storage) object
390
+
391
+ **Parameters:**
392
+
393
+ - **baseImageUrl**: `string` Source image URL
394
+ - **prompt**: `string` Modification/transformation description
395
+ - **tags**: `string[]` Tags for the result in `Upload` format (optional, default: [])
396
+
397
+ **Example:**
398
+
399
+ ```typescript
400
+ // Modify image from url
401
+ const upload: Upload = await kmClient.ai.modifyImage(
402
+ "https://static.kokimoki.com/game/image.jpg",
403
+ "Make it look like a painting",
404
+ ["art", "ai-generated"]
405
+ );
406
+ ```
407
+
408
+ ## Store Connections
409
+
410
+ Each Kokimoki store has a `connections` property that provides real-time presence information of all clients connected to that store.
411
+
412
+ ### Accessing Connections
413
+
414
+ - Use `store.connections` to access the connections proxy for any Kokimoki store
415
+ - Use `store.connections.clientIds` to get a `Set` of online client IDs
416
+ - Use `useSnapshot` to get reactive updates when connections change
417
+ - **ALWAYS** use `useSnapshot(store.connections)` to get reactive updates when connections change
418
+
419
+ ### Example: Track Online Players
420
+
421
+ ```tsx
422
+ import { useSnapshot } from "valtio";
423
+ import { globalStore } from "@/state/stores/global-store";
424
+
425
+ const Component = () => {
426
+ // Get online client IDs from store connections
427
+ const onlineClientIds = useSnapshot(globalStore.connections).clientIds;
428
+
429
+ // Check if specific player is online
430
+ const isPlayerOnline = onlineClientIds.has(playerId);
431
+
432
+ // Get count of online players
433
+ const onlineCount = onlineClientIds.size;
434
+
435
+ return <div>Online players: {onlineCount}</div>;
436
+ };
437
+ ```
438
+
439
+ ### Example: Display Player List with Online Status
440
+
441
+ ```tsx
442
+ import { useSnapshot } from "valtio";
443
+ import { globalStore } from "@/state/stores/global-store";
444
+
445
+ const PlayerList = () => {
446
+ const players = useSnapshot(globalStore.proxy).players;
447
+ const onlineClientIds = useSnapshot(globalStore.connections).clientIds;
448
+
449
+ const playersList = Object.entries(players).map(([clientId, player]) => ({
450
+ clientId,
451
+ name: player.name,
452
+
453
+ isOnline: onlineClientIds.has(clientId),
454
+ }));
455
+
456
+ return (
457
+ <ul>
458
+ {playersList.map((player) => (
459
+ <li key={player.clientId}>
460
+ {player.name} - {player.isOnline ? "Online" : "Offline"}
461
+ </li>
462
+ ))}
463
+ </ul>
464
+ );
465
+ };
466
+ ```
467
+
468
+ ### Key Points
469
+
470
+ - Each store has its own `connections` property
471
+ - `connections.clientIds` is a `Set<string>` containing connected client IDs
472
+ - Use `useSnapshot(store.connections)` to get reactive updates
473
+ - Players can have multiple browser tabs open, but all share the same `clientId`
474
+ - A player is considered online if their `clientId` is in the `clientIds` set
475
+
476
+ ## Leaderboard
477
+
478
+ Kokimoki SDK provides a leaderboard system to track and display player rankings. No setup required.
479
+
480
+ Access leaderboard API via `kmClient.leaderboard`.
481
+
482
+ ### When to Use Leaderboard API vs Global Store
483
+
484
+ **Use the Leaderboard API when:**
485
+
486
+ - You have a large number of entries (hundreds to thousands of players)
487
+ - You need efficient ranking and sorting with database indexes
488
+ - You want pagination and optimized queries for top scores
489
+ - Memory and network efficiency are important
490
+
491
+ **Use a Global Store when:**
492
+
493
+ - You have a small number of players (typically under 100)
494
+ - You need real-time updates and live leaderboard changes
495
+ - You want to combine player scores with other game state
496
+ - The leaderboard is temporary (session-based or reset frequently)
497
+
498
+ The leaderboard API is optimized for scalability with database indexes and efficient queries, making it the better choice for games with many players. Global stores are ideal for smaller, real-time collaborative scenarios where you want immediate synchronization.
499
+
500
+ ### API Methods
501
+
502
+ #### leaderboard.insertEntry<MetadataT, PrivateMetadataT>(leaderboardName, sortDir, score, metadata, privateMetadata): Promise<{ rank: number }>
503
+
504
+ Add a new entry to a leaderboard. Creates a new entry each time it's called.
505
+
506
+ **Parameters:**
507
+
508
+ - **leaderboardName**: `string` Name of the leaderboard
509
+ - **sortDir**: `"asc" | "desc"` Sort direction (asc = lowest is best, desc = highest is best)
510
+ - **score**: `number` The score value
511
+ - **metadata**: `MetadataT` Public metadata visible to all players
512
+ - **privateMetadata**: `PrivateMetadataT` Private metadata only accessible via API
513
+
514
+ **Returns:** Promise resolving to an object with the entry's rank
515
+
516
+ **Example:**
517
+
518
+ ```typescript
519
+ const { rank } = await kmClient.leaderboard.insertEntry(
520
+ "high-scores",
521
+ "desc",
522
+ 1500,
523
+ { playerName: "Alice", level: 10 },
524
+ { sessionId: "abc123" }
525
+ );
526
+ console.log(`New rank: ${rank}`);
527
+ ```
528
+
529
+ #### leaderboard.upsertEntry<MetadataT, PrivateMetadataT>(leaderboardName, sortDir, score, metadata, privateMetadata): Promise<{ rank: number }>
530
+
531
+ Add or update the latest entry for the current client in a leaderboard. Replaces the previous entry if one exists.
532
+
533
+ **Parameters:**
534
+
535
+ - **leaderboardName**: `string` Name of the leaderboard
536
+ - **sortDir**: `"asc" | "desc"` Sort direction (asc = lowest is best, desc = highest is best)
537
+ - **score**: `number` The score value
538
+ - **metadata**: `MetadataT` Public metadata visible to all players
539
+ - **privateMetadata**: `PrivateMetadataT` Private metadata only accessible via API
540
+
541
+ **Returns:** Promise resolving to an object with the entry's rank
542
+
543
+ **Example:**
544
+
545
+ ```typescript
546
+ const { rank } = await kmClient.leaderboard.upsertEntry(
547
+ "daily-scores",
548
+ "desc",
549
+ 2000,
550
+ { playerName: "Bob", completionTime: 120 },
551
+ { deviceId: "xyz789" }
552
+ );
553
+ console.log(`Updated rank: ${rank}`);
554
+ ```
555
+
556
+ #### leaderboard.listEntries<MetadataT>(leaderboardName, sortDir, skip?, limit?): Promise<Paginated<{ rank: number; score: number; metadata: MetadataT }>>
557
+
558
+ List entries in a leaderboard with pagination.
559
+
560
+ **Parameters:**
561
+
562
+ - **leaderboardName**: `string` Name of the leaderboard
563
+ - **sortDir**: `"asc" | "desc"` Sort direction (asc = lowest is best, desc = highest is best)
564
+ - **skip**: `number` Number of entries to skip for pagination (default: 0)
565
+ - **limit**: `number` Maximum number of entries to return (default: 100)
566
+
567
+ **Returns:** Promise resolving to a paginated list of entries
568
+
569
+ **Example:**
570
+
571
+ ```typescript
572
+ const { items, total } = await kmClient.leaderboard.listEntries(
573
+ "weekly-scores",
574
+ "desc",
575
+ 0, // skip
576
+ 10 // limit - get top 10
577
+ );
578
+
579
+ items.forEach((entry) => {
580
+ console.log(
581
+ `Rank ${entry.rank}: ${entry.metadata.playerName} - ${entry.score}`
582
+ );
583
+ });
584
+ ```
585
+
586
+ #### leaderboard.getBestEntry<MetadataT>(leaderboardName, sortDir, clientId?): Promise<{ rank: number; score: number; metadata: MetadataT }>
587
+
588
+ Get the best entry for a specific client in a leaderboard.
589
+
590
+ **Parameters:**
591
+
592
+ - **leaderboardName**: `string` Name of the leaderboard
593
+ - **sortDir**: `"asc" | "desc"` Sort direction (asc = lowest is best, desc = highest is best)
594
+ - **clientId**: `string` (optional) Client ID to get entry for. Defaults to current client if not provided.
595
+
596
+ **Returns:** Promise resolving to the best entry for the client
597
+
598
+ **Example:**
599
+
600
+ ```typescript
601
+ // Get current client's best entry
602
+ const myBest = await kmClient.leaderboard.getBestEntry("all-time-high", "desc");
603
+ console.log(`My best: Rank ${myBest.rank}, Score ${myBest.score}`);
604
+
605
+ // Get another player's best entry
606
+ const otherBest = await kmClient.leaderboard.getBestEntry(
607
+ "all-time-high",
608
+ "desc",
609
+ "other-client-id"
610
+ );
611
+ ```
612
+
613
+ ### Common Patterns
614
+
615
+ #### Example: Track High Scores
616
+
617
+ ```typescript
618
+ // Submit a new high score
619
+ await kmClient.leaderboard.upsertEntry(
620
+ "high-scores",
621
+ "desc",
622
+ score,
623
+ { playerName: player.name },
624
+ { timestamp: Date.now() }
625
+ );
626
+
627
+ // Display top 10
628
+ const { items } = await kmClient.leaderboard.listEntries(
629
+ "high-scores",
630
+ "desc",
631
+ 0,
632
+ 10
633
+ );
634
+ ```
635
+
636
+ #### Example: Track Speed Run Times
637
+
638
+ ```typescript
639
+ // Submit completion time (lower is better)
640
+ await kmClient.leaderboard.upsertEntry(
641
+ "speed-run",
642
+ "asc",
643
+ completionTimeInSeconds,
644
+ { playerName: player.name, difficulty: "hard" },
645
+ {}
646
+ );
647
+
648
+ // Get personal best
649
+ const myBest = await kmClient.leaderboard.getBestEntry("speed-run", "asc");
650
+ ```
651
+
652
+ ### Key Points
653
+
654
+ - **Sort Direction**: Use `asc` when lower scores are better (e.g., completion time), `desc` when higher scores are better (e.g., points)
655
+ - **Insert vs Upsert**: Use `insertEntry` to keep all attempts, `upsertEntry` to keep only the latest/best
656
+ - **Metadata**: Public metadata is visible to all, private metadata is only accessible via API calls
657
+ - **Pagination**: Use skip/limit to implement leaderboard pages or "load more" functionality
@@ -0,0 +1,8 @@
1
+ import { SyncedStore } from "./synced-store";
2
+ export declare class MessageQueue<T> extends SyncedStore<{
3
+ messages: T[];
4
+ }> {
5
+ name: string;
6
+ constructor(name: string);
7
+ push(message: T): Promise<Uint8Array>;
8
+ }
@@ -0,0 +1,19 @@
1
+ import EventEmitter from "events";
2
+ import { SyncedStore } from "./synced-store";
3
+ import Y from "yjs";
4
+ export class MessageQueue extends SyncedStore {
5
+ name;
6
+ constructor(name) {
7
+ super({ messages: [] });
8
+ this.name = name;
9
+ }
10
+ async push(message) {
11
+ // Construct Y update to push the message to the queue
12
+ const id = crypto.randomUUID();
13
+ const ydoc = new Y.Doc();
14
+ const map = ydoc.getMap("messages");
15
+ map.set(id, message);
16
+ const update = Y.encodeStateAsUpdate(ydoc);
17
+ return update;
18
+ }
19
+ }
@@ -0,0 +1,3 @@
1
+ export { WsMessageReader } from "./reader";
2
+ export { WsMessageType } from "./type";
3
+ export { WsMessageWriter } from "./writer";
@@ -0,0 +1,3 @@
1
+ export { WsMessageReader } from "./reader";
2
+ export { WsMessageType } from "./type";
3
+ export { WsMessageWriter } from "./writer";
@@ -0,0 +1,11 @@
1
+ export declare class WsMessageReader {
2
+ private buffer;
3
+ private _view;
4
+ private _offset;
5
+ constructor(buffer: ArrayBuffer);
6
+ readInt32(): number;
7
+ readUint32(): number;
8
+ readString(): string;
9
+ readUint8Array(): Uint8Array<ArrayBuffer>;
10
+ get end(): boolean;
11
+ }