@kokimoki/app 2.1.0 → 3.0.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.
Files changed (38) hide show
  1. package/README.md +72 -0
  2. package/dist/core/kokimoki-client.d.ts +75 -15
  3. package/dist/core/kokimoki-client.js +137 -22
  4. package/dist/index.d.ts +6 -1
  5. package/dist/index.js +4 -0
  6. package/dist/kokimoki.min.d.ts +322 -2
  7. package/dist/kokimoki.min.js +1884 -72
  8. package/dist/kokimoki.min.js.map +1 -1
  9. package/dist/llms.txt +6 -0
  10. package/dist/protocol/ws-message/reader.d.ts +1 -1
  11. package/dist/services/index.d.ts +1 -0
  12. package/dist/services/index.js +1 -0
  13. package/dist/services/kokimoki-ai.d.ts +185 -122
  14. package/dist/services/kokimoki-ai.js +201 -109
  15. package/dist/services/kokimoki-i18n.d.ts +259 -0
  16. package/dist/services/kokimoki-i18n.js +325 -0
  17. package/dist/stores/kokimoki-local-store.d.ts +1 -1
  18. package/dist/types/common.d.ts +9 -0
  19. package/dist/types/env.d.ts +36 -0
  20. package/dist/types/env.js +1 -0
  21. package/dist/types/index.d.ts +1 -0
  22. package/dist/types/index.js +1 -0
  23. package/dist/utils/kokimoki-client.d.ts +31 -0
  24. package/dist/utils/kokimoki-client.js +38 -0
  25. package/dist/utils/kokimoki-dev.d.ts +30 -0
  26. package/dist/utils/kokimoki-dev.js +75 -0
  27. package/dist/utils/kokimoki-env.d.ts +20 -0
  28. package/dist/utils/kokimoki-env.js +30 -0
  29. package/dist/version.d.ts +1 -1
  30. package/dist/version.js +1 -1
  31. package/docs/kokimoki-ai.instructions.md +316 -0
  32. package/docs/kokimoki-dynamic-stores.instructions.md +439 -0
  33. package/docs/kokimoki-i18n.instructions.md +285 -0
  34. package/docs/kokimoki-leaderboard.instructions.md +189 -0
  35. package/docs/kokimoki-sdk.instructions.md +221 -0
  36. package/docs/kokimoki-storage.instructions.md +162 -0
  37. package/llms.txt +43 -0
  38. package/package.json +9 -13
@@ -0,0 +1,189 @@
1
+ ---
2
+ description: "Leaderboard and player rankings with Kokimoki SDK"
3
+ applyTo: "**/*.ts,**/*.tsx"
4
+ ---
5
+
6
+ # Leaderboard
7
+
8
+ Kokimoki SDK provides a leaderboard system to track and display player rankings. No setup required.
9
+
10
+ For core SDK concepts, see [Kokimoki SDK](./kokimoki-sdk.instructions.md).
11
+
12
+ Access leaderboard API via `kmClient.leaderboard`.
13
+
14
+ ## When to Use Leaderboard API vs Global Store
15
+
16
+ **Use the Leaderboard API when:**
17
+
18
+ - You have a large number of entries (hundreds to thousands of players)
19
+ - You need efficient ranking and sorting with database indexes
20
+ - You want pagination and optimized queries for top scores
21
+ - Memory and network efficiency are important
22
+
23
+ **Use a Global Store when:**
24
+
25
+ - You have a small number of players (typically under 100)
26
+ - You need real-time updates and live leaderboard changes
27
+ - You want to combine player scores with other game state
28
+ - The leaderboard is temporary (session-based or reset frequently)
29
+
30
+ 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.
31
+
32
+ ## API Methods
33
+
34
+ ### leaderboard.insertEntry<MetadataT, PrivateMetadataT>(leaderboardName, sortDir, score, metadata, privateMetadata): Promise<{ rank: number }>
35
+
36
+ Add a new entry to a leaderboard. Creates a new entry each time it's called.
37
+
38
+ **Parameters:**
39
+
40
+ - **leaderboardName**: `string` Name of the leaderboard
41
+ - **sortDir**: `"asc" | "desc"` Sort direction (asc = lowest is best, desc = highest is best)
42
+ - **score**: `number` The score value
43
+ - **metadata**: `MetadataT` Public metadata visible to all players
44
+ - **privateMetadata**: `PrivateMetadataT` Private metadata only accessible via API
45
+
46
+ **Returns:** Promise resolving to an object with the entry's rank
47
+
48
+ **Example:**
49
+
50
+ ```typescript
51
+ const { rank } = await kmClient.leaderboard.insertEntry(
52
+ "high-scores",
53
+ "desc",
54
+ 1500,
55
+ { playerName: "Alice", level: 10 },
56
+ { sessionId: "abc123" }
57
+ );
58
+ console.log(`New rank: ${rank}`);
59
+ ```
60
+
61
+ ### leaderboard.upsertEntry<MetadataT, PrivateMetadataT>(leaderboardName, sortDir, score, metadata, privateMetadata): Promise<{ rank: number }>
62
+
63
+ Add or update the latest entry for the current client in a leaderboard. Replaces the previous entry if one exists.
64
+
65
+ **Parameters:**
66
+
67
+ - **leaderboardName**: `string` Name of the leaderboard
68
+ - **sortDir**: `"asc" | "desc"` Sort direction (asc = lowest is best, desc = highest is best)
69
+ - **score**: `number` The score value
70
+ - **metadata**: `MetadataT` Public metadata visible to all players
71
+ - **privateMetadata**: `PrivateMetadataT` Private metadata only accessible via API
72
+
73
+ **Returns:** Promise resolving to an object with the entry's rank
74
+
75
+ **Example:**
76
+
77
+ ```typescript
78
+ const { rank } = await kmClient.leaderboard.upsertEntry(
79
+ "daily-scores",
80
+ "desc",
81
+ 2000,
82
+ { playerName: "Bob", completionTime: 120 },
83
+ { deviceId: "xyz789" }
84
+ );
85
+ console.log(`Updated rank: ${rank}`);
86
+ ```
87
+
88
+ ### leaderboard.listEntries<MetadataT>(leaderboardName, sortDir, skip?, limit?): Promise<Paginated<{ rank: number; score: number; metadata: MetadataT }>>
89
+
90
+ List entries in a leaderboard with pagination.
91
+
92
+ **Parameters:**
93
+
94
+ - **leaderboardName**: `string` Name of the leaderboard
95
+ - **sortDir**: `"asc" | "desc"` Sort direction (asc = lowest is best, desc = highest is best)
96
+ - **skip**: `number` Number of entries to skip for pagination (default: 0)
97
+ - **limit**: `number` Maximum number of entries to return (default: 100)
98
+
99
+ **Returns:** Promise resolving to a paginated list of entries
100
+
101
+ **Example:**
102
+
103
+ ```typescript
104
+ const { items, total } = await kmClient.leaderboard.listEntries(
105
+ "weekly-scores",
106
+ "desc",
107
+ 0, // skip
108
+ 10 // limit - get top 10
109
+ );
110
+
111
+ items.forEach((entry) => {
112
+ console.log(
113
+ `Rank ${entry.rank}: ${entry.metadata.playerName} - ${entry.score}`
114
+ );
115
+ });
116
+ ```
117
+
118
+ ### leaderboard.getBestEntry<MetadataT>(leaderboardName, sortDir, clientId?): Promise<{ rank: number; score: number; metadata: MetadataT }>
119
+
120
+ Get the best entry for a specific client in a leaderboard.
121
+
122
+ **Parameters:**
123
+
124
+ - **leaderboardName**: `string` Name of the leaderboard
125
+ - **sortDir**: `"asc" | "desc"` Sort direction (asc = lowest is best, desc = highest is best)
126
+ - **clientId**: `string` (optional) Client ID to get entry for. Defaults to current client if not provided.
127
+
128
+ **Returns:** Promise resolving to the best entry for the client
129
+
130
+ **Example:**
131
+
132
+ ```typescript
133
+ // Get current client's best entry
134
+ const myBest = await kmClient.leaderboard.getBestEntry("all-time-high", "desc");
135
+ console.log(`My best: Rank ${myBest.rank}, Score ${myBest.score}`);
136
+
137
+ // Get another player's best entry
138
+ const otherBest = await kmClient.leaderboard.getBestEntry(
139
+ "all-time-high",
140
+ "desc",
141
+ "other-client-id"
142
+ );
143
+ ```
144
+
145
+ ## Common Patterns
146
+
147
+ ### Example: Track High Scores
148
+
149
+ ```typescript
150
+ // Submit a new high score
151
+ await kmClient.leaderboard.upsertEntry(
152
+ "high-scores",
153
+ "desc",
154
+ score,
155
+ { playerName: player.name },
156
+ { timestamp: Date.now() }
157
+ );
158
+
159
+ // Display top 10
160
+ const { items } = await kmClient.leaderboard.listEntries(
161
+ "high-scores",
162
+ "desc",
163
+ 0,
164
+ 10
165
+ );
166
+ ```
167
+
168
+ ### Example: Track Speed Run Times
169
+
170
+ ```typescript
171
+ // Submit completion time (lower is better)
172
+ await kmClient.leaderboard.upsertEntry(
173
+ "speed-run",
174
+ "asc",
175
+ completionTimeInSeconds,
176
+ { playerName: player.name, difficulty: "hard" },
177
+ {}
178
+ );
179
+
180
+ // Get personal best
181
+ const myBest = await kmClient.leaderboard.getBestEntry("speed-run", "asc");
182
+ ```
183
+
184
+ ## Key Points
185
+
186
+ - **Sort Direction**: Use `asc` when lower scores are better (e.g., completion time), `desc` when higher scores are better (e.g., points)
187
+ - **Insert vs Upsert**: Use `insertEntry` to keep all attempts, `upsertEntry` to keep only the latest/best
188
+ - **Metadata**: Public metadata is visible to all, private metadata is only accessible via API calls
189
+ - **Pagination**: Use skip/limit to implement leaderboard pages or "load more" functionality
@@ -0,0 +1,221 @@
1
+ ---
2
+ description: "Core Kokimoki SDK for real-time collaborative game applications"
3
+ applyTo: "**/*.ts,**/*.tsx"
4
+ ---
5
+
6
+ # Kokimoki SDK
7
+
8
+ The Kokimoki SDK is a comprehensive development toolkit for building real-time collaborative game applications
9
+
10
+ ## General guidelines
11
+
12
+ - **IMPORTANT** Use `getKmClient()` from `@kokimoki/app` to get the singleton client instance
13
+ - Use `kmClient.id` as a unique identifier for each client (player)
14
+ - Use `kmClient.store` for global stores and `kmClient.localStore` for local stores
15
+ - Use dynamic stores with `kmClient.join()` and `kmClient.leave()` for room-based isolation (teams, chat rooms, breakout rooms)
16
+ - Use `kmClient.transact` for atomic state updates across single store or multiple stores
17
+ - Use `kmClient.storage.upload` and related API methods to handle file uploads (media, JSON, etc.) in application
18
+ - Use `kmClient.serverTimestamp()` for time-related matters as this will be synced among players
19
+ - Use `useSnapshot` hook from `valtio` to get reactive state inside React components
20
+ - Use AI integration API methods: `kmClient.ai.generateText`, `kmClient.ai.generateJson`, and `kmClient.ai.generateImage` with corresponding poll methods for AI capabilities
21
+ - Use i18n API methods: `kmClient.i18n.createI18n`, `kmClient.i18n.init`, and `kmClient.i18n.requestTranslation` for internationalization with AI-powered translations
22
+ - Use leaderboard API methods: `kmClient.leaderboard.insertEntry`, `kmClient.leaderboard.upsertEntry`, `kmClient.leaderboard.listEntries`, and `kmClient.leaderboard.getBestEntry` to add leaderboard capabilities to application
23
+
24
+ ## Kokimoki Client
25
+
26
+ - Use `getKmClient()` to get the singleton Kokimoki client instance
27
+ - The `kmClient` provides the following key functionalities:
28
+ - `kmClient.store` and `kmClient.localStore` for creating stores
29
+ - `kmClient.join` and `kmClient.leave` for dynamic store lifecycle management
30
+ - `kmClient.transact` for atomic state updates
31
+ - `kmClient.serverTimestamp()` for synchronized timestamps
32
+ - `kmClient.id` for unique player identification
33
+
34
+ **Example: Getting the Client**
35
+
36
+ ```typescript
37
+ import { getKmClient } from "@kokimoki/app";
38
+
39
+ const kmClient = getKmClient();
40
+ ```
41
+
42
+ ## Client ID
43
+
44
+ - Each client (player) has a unique `kmClient.id` (aka `clientId`), stable identifier that represents a player across multiple connections
45
+ (client sessions)
46
+ - The `kmClient.id` is persistent across client connections
47
+ - The `kmClient.id` remains consistent after user reconnect or page reload
48
+ - Use `kmClient.id` to identify players in global stores
49
+ - Each client (player) can have multiple connections, but all connections share the same `kmClient.id`
50
+
51
+ ## Kokimoki Store
52
+
53
+ Kokimoki Store powered by `valtio` and `valtio-yjs` for real-time state management in Kokimoki game applications
54
+
55
+ ### Store initialization
56
+
57
+ - Stores should be defined in `src/state/stores/`
58
+ - Store can be created in two ways:
59
+ - `kmClient.localStore` is used for data stored on the player device (local)
60
+ - `kmClient.store` is used for data shared among all players in a game (global)
61
+
62
+ **Example: Creating a Store**
63
+
64
+ ```typescript
65
+ import { getKmClient } from "@kokimoki/app";
66
+
67
+ const kmClient = getKmClient();
68
+
69
+ interface State {
70
+ title: string;
71
+ count: number;
72
+ }
73
+
74
+ const initialState: State = {
75
+ title: "Store",
76
+ count: 0,
77
+ };
78
+
79
+ // Initialize global store with initial state
80
+ export const store = kmClient.store<State>("store-name", initialState);
81
+ ```
82
+
83
+ ### State management
84
+
85
+ - State actions are functions that modify the store state by performing transactions
86
+ - Actions should be defined in `/src/state/actions/`
87
+ - Use async/await for all state transactions by `kmClient.transact` function for global stores
88
+ - Transactions are atomic and ensure state consistency
89
+ - ALWAYS update store state inside `kmClient.transact()` within action function
90
+ - Prefer using records, not arrays: store collections as `Record<string, T>` with timestamp keys for automatic sorting and better sync performance
91
+
92
+ **Example: Updating State**
93
+
94
+ ```typescript
95
+ import { store } from "../store";
96
+
97
+ // Update state
98
+ await kmClient.transact([store], ([state]) => {
99
+ state.title = "New store";
100
+ state.count += 1;
101
+ });
102
+ ```
103
+
104
+ ### Combining Stores
105
+
106
+ - Multiple stores can be updated in a single transaction
107
+ - Prefer to update stores in a single transaction to ensure state consistency
108
+
109
+ **Example: Multiple Stores**
110
+
111
+ ```typescript
112
+ // Update multiple stores in a single transaction
113
+ await kmClient.transact([store1, store2], ([state1, state2]) => {
114
+ state1.name = "My Store1";
115
+ state2.name = "My Store2";
116
+ });
117
+ ```
118
+
119
+ ### Reactive State in Components
120
+
121
+ - Use `useSnapshot` hook from `valtio` to get reactive state inside React components
122
+ - The component will re-render when the store state changes
123
+
124
+ **Example: Using State in Components**
125
+
126
+ ```tsx
127
+ import { useSnapshot } from "valtio";
128
+ import { store } from "../store";
129
+
130
+ const Component = () => {
131
+ // Get reactive snapshot of the store state
132
+ const { title, count } = useSnapshot(store.proxy);
133
+
134
+ return (
135
+ <div>
136
+ <h1>Title: {title}</h1>
137
+ <p>Count: {count}</p>
138
+ </div>
139
+ );
140
+ };
141
+ ```
142
+
143
+ ## Dynamic Stores
144
+
145
+ For room-based state isolation (teams, chat rooms, breakout rooms), see [Dynamic Stores](./kokimoki-dynamic-stores.instructions.md).
146
+
147
+ ## Server Time Synchronization
148
+
149
+ Kokimoki SDK implements a time synchronization system to ensure consistent timestamps across all connected clients, regardless of their local clock differences
150
+
151
+ - Use `kmClient.serverTimestamp()` to get the current server-synchronized timestamp
152
+ - The timestamp is a Epoch Unix Timestamp
153
+ - Use server timestamps for time-related matters like event scheduling, timeouts, timers, etc.
154
+
155
+ ## Store Connections
156
+
157
+ Each Kokimoki store has a `connections` property that provides real-time presence information of all clients connected to that store.
158
+
159
+ ### Accessing Connections
160
+
161
+ - Use `store.connections` to access the connections proxy for any Kokimoki store
162
+ - Use `store.connections.clientIds` to get a `Set` of online client IDs
163
+ - Use `useSnapshot` to get reactive updates when connections change
164
+ - **ALWAYS** use `useSnapshot(store.connections)` to get reactive updates when connections change
165
+
166
+ ### Example: Track Online Players
167
+
168
+ ```tsx
169
+ import { useSnapshot } from "valtio";
170
+ import { globalStore } from "@/state/stores/global-store";
171
+
172
+ const Component = () => {
173
+ // Get online client IDs from store connections
174
+ const onlineClientIds = useSnapshot(globalStore.connections).clientIds;
175
+
176
+ // Check if specific player is online
177
+ const isPlayerOnline = onlineClientIds.has(playerId);
178
+
179
+ // Get count of online players
180
+ const onlineCount = onlineClientIds.size;
181
+
182
+ return <div>Online players: {onlineCount}</div>;
183
+ };
184
+ ```
185
+
186
+ ### Example: Display Player List with Online Status
187
+
188
+ ```tsx
189
+ import { useSnapshot } from "valtio";
190
+ import { globalStore } from "@/state/stores/global-store";
191
+
192
+ const PlayerList = () => {
193
+ const players = useSnapshot(globalStore.proxy).players;
194
+ const onlineClientIds = useSnapshot(globalStore.connections).clientIds;
195
+
196
+ const playersList = Object.entries(players).map(([clientId, player]) => ({
197
+ clientId,
198
+ name: player.name,
199
+
200
+ isOnline: onlineClientIds.has(clientId),
201
+ }));
202
+
203
+ return (
204
+ <ul>
205
+ {playersList.map((player) => (
206
+ <li key={player.clientId}>
207
+ {player.name} - {player.isOnline ? "Online" : "Offline"}
208
+ </li>
209
+ ))}
210
+ </ul>
211
+ );
212
+ };
213
+ ```
214
+
215
+ ### Key Points
216
+
217
+ - Each store has its own `connections` property
218
+ - `connections.clientIds` is a `Set<string>` containing connected client IDs
219
+ - Use `useSnapshot(store.connections)` to get reactive updates
220
+ - Players can have multiple browser tabs open, but all share the same `clientId`
221
+ - A player is considered online if their `clientId` is in the `clientIds` set
@@ -0,0 +1,162 @@
1
+ ---
2
+ description: "File storage and CDN uploads with Kokimoki SDK"
3
+ applyTo: "**/*.ts,**/*.tsx"
4
+ ---
5
+
6
+ # Storage
7
+
8
+ 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.
9
+
10
+ For core SDK concepts, see [Kokimoki SDK](./kokimoki-sdk.instructions.md).
11
+
12
+ Access storage API via `kmClient.storage`.
13
+
14
+ ## API Methods
15
+
16
+ ### storage.upload(name, blob, tags?): Promise<Upload>
17
+
18
+ Uploads a file to storage.
19
+
20
+ **Parameters:**
21
+
22
+ - **name**: `string` filename
23
+ - **blob**: `Blob` object to upload
24
+ - **tags**: `string[]` (optional, default: [])
25
+
26
+ **Example:**
27
+
28
+ ```typescript
29
+ const upload: Upload = await kmClient.storage.upload("filename.jpg", fileBlob, [
30
+ "tag1",
31
+ "tag2",
32
+ ]);
33
+ // Use upload.url to access the media file
34
+ ```
35
+
36
+ ### storage.listUploads(filter?, skip?, limit?): Promise<Paginated<Upload>>
37
+
38
+ Query uploaded files by filter and pagination
39
+
40
+ **Parameters:**
41
+
42
+ - **filter.clientId**: `string` Specific client (optional)
43
+ - **filter.mimeTypes**: `string[]` E.g., ['image/jpeg', 'image/png'] (optional)
44
+ - **filter.tags**: `string[]` All tags must match (optional)
45
+ - **skip**: `number` Pagination offset (default: 0)
46
+ - **limit**: `number` Max results (default: 100)
47
+
48
+ **Example:**
49
+
50
+ ```typescript
51
+ // Query uploads by tag and uploaded by this client
52
+ const { items, total } = await kmClient.storage.listUploads(
53
+ { clientId: kmClient.id, tags: ["tag1"] },
54
+ skip,
55
+ limit
56
+ );
57
+ ```
58
+
59
+ ### storage.updateUpload(id, update): Promise<Upload>
60
+
61
+ Replace uploaded file tags with new tags
62
+
63
+ **Parameters:**
64
+
65
+ - **id**: `string` Upload id
66
+ - **update.tags**: `string[]` (optional)
67
+
68
+ **Example:**
69
+
70
+ ```typescript
71
+ const updatedUpload: Upload = await kmClient.storage.updateUpload(upload.id, {
72
+ tags: ["new"],
73
+ });
74
+ ```
75
+
76
+ ### storage.deleteUpload(id): Promise<{ acknowledged: boolean; deletedCount: number }>
77
+
78
+ Permanently delete uploaded file
79
+
80
+ **Parameters:**
81
+
82
+ - **id**: `string` Upload id
83
+
84
+ **Example:**
85
+
86
+ ```typescript
87
+ await kmClient.storage.deleteUpload(upload.id);
88
+ ```
89
+
90
+ ## Types
91
+
92
+ ```typescript
93
+ interface Upload {
94
+ id: string; // unique id
95
+ url: string; // file url (CDN)
96
+ name: string; // original filename
97
+ size: number; // in bytes
98
+ mimeType: string;
99
+ clientId: string; // who uploaded
100
+ tags: string[]; // metadata for filtering and organization
101
+ completed: boolean; // upload status
102
+ createdAt: Date;
103
+ appId: string;
104
+ }
105
+
106
+ interface Paginated<T> {
107
+ items: T[];
108
+ total: number;
109
+ }
110
+ ```
111
+
112
+ ## Common Patterns
113
+
114
+ ### Example: User-specific uploads
115
+
116
+ ```typescript
117
+ // Get uploaded files by clientId
118
+ const clientUploads = await kmClient.storage.listUploads({
119
+ clientId: kmClient.id,
120
+ });
121
+ ```
122
+
123
+ ### Example: Filter by file type
124
+
125
+ ```typescript
126
+ // Get only uploaded images
127
+ const images = await kmClient.storage.listUploads({
128
+ mimeTypes: ["image/jpeg", "image/png"],
129
+ });
130
+ ```
131
+
132
+ ### Example: Tag-based uploads
133
+
134
+ ```typescript
135
+ // Upload file with tag
136
+ await kmClient.storage.upload("avatar.jpg", blob, ["profile"]);
137
+
138
+ // Query uploads by tag
139
+ const profileUploads = await kmClient.storage.listUploads({
140
+ tags: ["profile"],
141
+ });
142
+ ```
143
+
144
+ ### Example: Usage in Kokimoki Store
145
+
146
+ ```typescript
147
+ // Upload image from Blob
148
+ const upload = await kmClient.storage.upload("file.jpg", blob);
149
+
150
+ await kmClient.transact([store], (state) => {
151
+ // Add image to images array in the store
152
+ state.playerImages[upload.id] = { url: upload.url };
153
+ });
154
+ ```
155
+
156
+ ## Key Points
157
+
158
+ - **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
159
+ - **CDN**: File `upload.url` is public and can be used directly
160
+ - **Tags**: Use tag system to organize uploads
161
+ - **Pagination**: Use skip/limit to paginate results
162
+ - **Filtering**: Combine clientId, mimeTypes, and tags to query uploads
package/llms.txt ADDED
@@ -0,0 +1,43 @@
1
+ # Kokimoki SDK Documentation
2
+
3
+ > Documentation for @kokimoki/app - the core SDK for building real-time multiplayer games.
4
+
5
+ ## Instruction Files
6
+
7
+ The following instruction files provide detailed guidance for AI assistants:
8
+
9
+ ### Core SDK
10
+ - docs/kokimoki-sdk.instructions.md: Core SDK usage including client initialization, stores, transactions, server time synchronization, and store connections. Start here for basic Kokimoki development.
11
+
12
+ ### Dynamic Stores
13
+ - docs/kokimoki-dynamic-stores.instructions.md: Room-based state isolation for teams, chat rooms, and breakout rooms. Includes the useDynamicStore hook pattern and lifecycle management.
14
+
15
+ ### Services
16
+
17
+ - docs/kokimoki-ai.instructions.md: AI-powered text and image generation using generateText() and generateImage() APIs.
18
+
19
+ - docs/kokimoki-storage.instructions.md: File storage and CDN uploads using the storage service.
20
+
21
+ - docs/kokimoki-i18n.instructions.md: Internationalization with AI-powered translation support.
22
+
23
+ - docs/kokimoki-leaderboard.instructions.md: Player rankings and score management.
24
+
25
+ ## Key Concepts
26
+
27
+ - **KmClient**: Central client instance obtained via getKmClient()
28
+ - **Stores**: Synchronized state containers using Valtio proxies
29
+ - **Transactions**: Atomic state updates via kmClient.transact()
30
+ - **Dynamic Stores**: Room-scoped stores with join/leave lifecycle
31
+ - **Server Time**: Synchronized timestamps via kmClient.serverTimestamp()
32
+
33
+ ## File Patterns
34
+
35
+ Instruction files use YAML frontmatter with:
36
+ - `description`: Brief description of the file's purpose
37
+ - `applyTo`: Glob pattern for when the instructions apply (e.g., "**/*.{ts,tsx}")
38
+
39
+ ## Recommended Reading Order
40
+
41
+ 1. docs/kokimoki-sdk.instructions.md (required - core concepts)
42
+ 2. docs/kokimoki-dynamic-stores.instructions.md (if using rooms/teams)
43
+ 3. Service-specific files as needed
package/package.json CHANGED
@@ -1,12 +1,14 @@
1
1
  {
2
2
  "name": "@kokimoki/app",
3
- "version": "2.1.0",
3
+ "version": "3.0.0",
4
4
  "type": "module",
5
5
  "description": "Kokimoki app",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
8
8
  "files": [
9
- "dist"
9
+ "dist",
10
+ "docs",
11
+ "llms.txt"
10
12
  ],
11
13
  "ts-node": {
12
14
  "esm": true,
@@ -16,19 +18,12 @@
16
18
  "test": "NODE_OPTIONS='--import tsx' mocha",
17
19
  "prebuild": "node scripts/generate-version.js",
18
20
  "build": "tsc",
19
- "postbuild": "rollup --config && cp docs/instructions.md dist/llms.txt",
20
21
  "dev": "tsc -w",
21
- "docs": "typedoc src/index.ts",
22
- "rollup": "rollup --config"
22
+ "docs": "typedoc src/index.ts"
23
23
  },
24
24
  "author": "Loquiz OÜ",
25
25
  "license": "Apache-2.0",
26
26
  "devDependencies": {
27
- "@rollup/plugin-commonjs": "^25.0.7",
28
- "@rollup/plugin-node-resolve": "^15.2.3",
29
- "@rollup/plugin-replace": "^5.0.5",
30
- "@rollup/plugin-terser": "^0.4.4",
31
- "@rollup/plugin-typescript": "^11.1.6",
32
27
  "@types/chai": "^4",
33
28
  "@types/mocha": "^10",
34
29
  "@types/node": "^18",
@@ -36,8 +31,6 @@
36
31
  "bson-objectid": "^2.0.4",
37
32
  "chai": "^4",
38
33
  "mocha": "^10",
39
- "rollup": "^4.13.0",
40
- "rollup-plugin-dts": "^6.1.0",
41
34
  "ts-node": "^10",
42
35
  "tsx": "^4",
43
36
  "typedoc": "^0.24.8",
@@ -47,9 +40,12 @@
47
40
  "derive-valtio": "^0.2.0",
48
41
  "events": "^3.3.0",
49
42
  "farmhash-modern": "^1.1.0",
43
+ "i18next": "^25.7.4",
44
+ "i18next-http-backend": "^3.0.2",
50
45
  "typed-emitter": "^2.1.0",
51
46
  "valtio": "^2.1.5",
52
47
  "valtio-yjs": "^0.6.0",
53
- "yjs": "^13.6.27"
48
+ "yjs": "^13.6.27",
49
+ "zod": "^4.3.5"
54
50
  }
55
51
  }