@kokimoki/app 2.0.0 → 2.0.2

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 (45) 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/index.d.ts +4 -7
  10. package/dist/index.js +4 -7
  11. package/dist/kokimoki.min.d.ts +55 -59
  12. package/dist/kokimoki.min.js +3076 -1790
  13. package/dist/kokimoki.min.js.map +1 -1
  14. package/dist/llms.txt +75 -67
  15. package/dist/protocol/ws-message/index.d.ts +3 -0
  16. package/dist/protocol/ws-message/index.js +3 -0
  17. package/dist/protocol/ws-message/reader.d.ts +11 -0
  18. package/dist/protocol/ws-message/reader.js +36 -0
  19. package/dist/protocol/ws-message/type.d.ts +11 -0
  20. package/dist/protocol/ws-message/type.js +12 -0
  21. package/dist/protocol/ws-message/writer.d.ts +9 -0
  22. package/dist/protocol/ws-message/writer.js +45 -0
  23. package/dist/services/index.d.ts +3 -0
  24. package/dist/services/index.js +3 -0
  25. package/dist/services/kokimoki-ai.d.ts +153 -0
  26. package/dist/services/kokimoki-ai.js +164 -0
  27. package/dist/services/kokimoki-leaderboard.d.ts +175 -0
  28. package/dist/services/kokimoki-leaderboard.js +203 -0
  29. package/dist/services/kokimoki-storage.d.ts +155 -0
  30. package/dist/services/kokimoki-storage.js +208 -0
  31. package/dist/stores/index.d.ts +3 -0
  32. package/dist/stores/index.js +3 -0
  33. package/dist/stores/kokimoki-local-store.d.ts +11 -0
  34. package/dist/stores/kokimoki-local-store.js +40 -0
  35. package/dist/stores/kokimoki-store.d.ts +22 -0
  36. package/dist/stores/kokimoki-store.js +117 -0
  37. package/dist/stores/kokimoki-transaction.d.ts +18 -0
  38. package/dist/stores/kokimoki-transaction.js +143 -0
  39. package/dist/types/index.d.ts +3 -0
  40. package/dist/types/index.js +3 -0
  41. package/dist/utils/valtio.d.ts +7 -0
  42. package/dist/utils/valtio.js +6 -0
  43. package/dist/version.d.ts +1 -1
  44. package/dist/version.js +2 -1
  45. package/package.json +4 -3
@@ -0,0 +1,164 @@
1
+ /**
2
+ * Kokimoki AI Integration Service
3
+ *
4
+ * Provides built-in AI capabilities for game applications without requiring API keys or setup.
5
+ * Includes text generation, structured JSON output, and image modification.
6
+ *
7
+ * **Key Features:**
8
+ * - Multiple AI models (GPT-4, GPT-5, Gemini variants)
9
+ * - Text generation with configurable creativity (temperature)
10
+ * - Structured JSON output for game data
11
+ * - AI-powered image modifications
12
+ * - No API keys or configuration required
13
+ *
14
+ * **Common Use Cases:**
15
+ * - Generate dynamic game content (quests, dialogues, stories)
16
+ * - Create AI opponents or NPCs with personalities
17
+ * - Generate quiz questions or trivia
18
+ * - Create game assets (character stats, item descriptions)
19
+ * - Transform user-uploaded images
20
+ * - Generate procedural content
21
+ *
22
+ * Access via `kmClient.ai`
23
+ *
24
+ * @example
25
+ * ```typescript
26
+ * // Generate story text
27
+ * const story = await kmClient.ai.chat({
28
+ * systemPrompt: 'You are a fantasy story writer',
29
+ * userPrompt: 'Write a short quest description',
30
+ * temperature: 0.8
31
+ * });
32
+ *
33
+ * // Generate structured game data
34
+ * interface Enemy {
35
+ * name: string;
36
+ * health: number;
37
+ * attack: number;
38
+ * }
39
+ * const enemy = await kmClient.ai.generateJson<Enemy>({
40
+ * userPrompt: 'Create a level 5 goblin warrior'
41
+ * });
42
+ *
43
+ * // Transform image
44
+ * const modified = await kmClient.ai.modifyImage(
45
+ * imageUrl,
46
+ * 'Make it look like pixel art'
47
+ * );
48
+ * ```
49
+ */
50
+ export class KokimokiAiService {
51
+ client;
52
+ constructor(client) {
53
+ this.client = client;
54
+ }
55
+ /**
56
+ * Generate a chat response from the AI model.
57
+ *
58
+ * Sends a chat request to the AI service and returns the generated response.
59
+ * Supports multiple AI models including GPT and Gemini variants with configurable
60
+ * parameters for fine-tuning the response behavior.
61
+ *
62
+ * @param req The chat request parameters.
63
+ * @param req.model Optional. The AI model to use. Defaults to server-side default if not specified.
64
+ * Available models:
65
+ * - `gpt-4o`: OpenAI GPT-4 Optimized
66
+ * - `gpt-4o-mini`: Smaller, faster GPT-4 variant
67
+ * - `gpt-5`: OpenAI GPT-5 (latest)
68
+ * - `gpt-5-mini`: Smaller GPT-5 variant
69
+ * - `gpt-5-nano`: Smallest GPT-5 variant for lightweight tasks
70
+ * - `gemini-2.5-flash-lite`: Google Gemini lite variant
71
+ * - `gemini-2.5-flash`: Google Gemini fast variant
72
+ * @param req.systemPrompt Optional. The system message that sets the behavior and context for the AI.
73
+ * This helps define the AI's role, personality, and constraints.
74
+ * @param req.userPrompt The user's message or question to send to the AI.
75
+ * @param req.temperature Optional. Controls randomness in the response (0.0 to 1.0).
76
+ * Lower values make output more focused and deterministic,
77
+ * higher values make it more creative and varied.
78
+ * @param req.maxTokens Optional. The maximum number of tokens to generate in the response.
79
+ * Controls the length of the AI's output.
80
+ *
81
+ * @returns A promise that resolves to an object containing the AI-generated response.
82
+ * @returns {string} content The text content of the AI's response.
83
+ *
84
+ * @throws An error object if the API request fails.
85
+ *
86
+ * @example
87
+ * ```typescript
88
+ * const response = await client.ai.chat({
89
+ * model: "gpt-4o",
90
+ * systemPrompt: "You are a helpful coding assistant.",
91
+ * userPrompt: "Explain what TypeScript is in one sentence.",
92
+ * temperature: 0.7,
93
+ * maxTokens: 100
94
+ * });
95
+ * console.log(response.content);
96
+ * ```
97
+ */
98
+ async chat(req) {
99
+ const res = await fetch(`${this.client.apiUrl}/ai/chat`, {
100
+ method: "POST",
101
+ headers: this.client.apiHeaders,
102
+ body: JSON.stringify(req),
103
+ });
104
+ if (!res.ok) {
105
+ throw await res.json();
106
+ }
107
+ return await res.json();
108
+ }
109
+ /**
110
+ * Generate structured JSON output from the AI model.
111
+ *
112
+ * Sends a chat request to the AI service with the expectation of receiving
113
+ * a JSON-formatted response. This is useful for scenarios where the output
114
+ * needs to be parsed or processed programmatically.
115
+ *
116
+ * @param req The chat request parameters.
117
+ * @param req.model Optional. The AI model to use. Defaults to server-side default if not specified.
118
+ * Available models:
119
+ * - `gpt-4o`: OpenAI GPT-4 Optimized
120
+ * - `gpt-4o-mini`: Smaller, faster GPT-4 variant
121
+ * - `gpt-5`: OpenAI GPT-5 (latest)
122
+ * - `gpt-5-mini`: Smaller GPT-5 variant
123
+ * - `gpt-5-nano`: Smallest GPT-5 variant for lightweight tasks
124
+ * - `gemini-2.5-flash-lite`: Google Gemini lite variant
125
+ * - `gemini-2.5-flash`: Google Gemini fast variant
126
+ * @param req.systemPrompt Optional. The system message that sets the behavior and context for the AI.
127
+ * This helps define the AI's role, personality, and constraints.
128
+ * @param req.userPrompt The user's message or question to send to the AI.
129
+ * @param req.temperature Optional. Controls randomness in the response (0.0 to 1.0).
130
+ * Lower values make output more focused and deterministic,
131
+ * higher values make it more creative and varied.
132
+ * @param req.maxTokens Optional. The maximum number of tokens to generate in the response.
133
+ * Controls the length of the AI's output.
134
+ *
135
+ * @returns A promise that resolves to the parsed JSON object generated by the AI.
136
+ *
137
+ * @throws An error object if the API request fails or if the response is not valid JSON.
138
+ */
139
+ async generateJson(req) {
140
+ const { content } = await this.chat({
141
+ ...req,
142
+ responseMimeType: "application/json",
143
+ });
144
+ return JSON.parse(content);
145
+ }
146
+ /**
147
+ * Modify an image using the AI service.
148
+ * @param baseImageUrl The URL of the base image to modify.
149
+ * @param prompt The modification prompt to apply to the image.
150
+ * @param tags Optional. Tags to associate with the image.
151
+ * @returns A promise that resolves to the modified image upload information.
152
+ */
153
+ async modifyImage(baseImageUrl, prompt, tags = [], model = 'gemini-2.5-flash-image') {
154
+ const res = await fetch(`${this.client.apiUrl}/ai/modify-image`, {
155
+ method: "POST",
156
+ headers: this.client.apiHeaders,
157
+ body: JSON.stringify({ baseImageUrl, prompt, tags, model }),
158
+ });
159
+ if (!res.ok) {
160
+ throw await res.json();
161
+ }
162
+ return await res.json();
163
+ }
164
+ }
@@ -0,0 +1,175 @@
1
+ import { KokimokiClient } from "../core";
2
+ import type { Paginated } from "../types";
3
+ /**
4
+ * Kokimoki Leaderboard Service
5
+ *
6
+ * Provides efficient player ranking and score tracking with database indexes and optimized queries.
7
+ * Ideal for games with large numbers of players and competitive scoring.
8
+ *
9
+ * **Key Features:**
10
+ * - Efficient ranking with database indexes
11
+ * - Support for ascending (lowest-is-best) and descending (highest-is-best) sorting
12
+ * - Pagination for large leaderboards
13
+ * - Public and private metadata for entries
14
+ * - Insert (preserve all attempts) or upsert (keep latest only) modes
15
+ *
16
+ * **When to use Leaderboard API vs Global Store:**
17
+ *
18
+ * Use the **Leaderboard API** when:
19
+ * - You have a large number of entries (hundreds to thousands of players)
20
+ * - You need efficient ranking and sorting with database indexes
21
+ * - You want pagination and optimized queries for top scores
22
+ * - Memory and network efficiency are important
23
+ *
24
+ * Use a **Global Store** when:
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
+ * Access via `kmClient.leaderboard`
31
+ *
32
+ * @example
33
+ * ```typescript
34
+ * // Submit a high score
35
+ * const { rank } = await kmClient.leaderboard.upsertEntry(
36
+ * 'high-scores',
37
+ * 'desc',
38
+ * 1500,
39
+ * { playerName: 'Alice' },
40
+ * {}
41
+ * );
42
+ *
43
+ * // Get top 10
44
+ * const { items } = await kmClient.leaderboard.listEntries('high-scores', 'desc', 0, 10);
45
+ *
46
+ * // Get player's best
47
+ * const best = await kmClient.leaderboard.getBestEntry('high-scores', 'desc');
48
+ * ```
49
+ */
50
+ export declare class KokimokiLeaderboardService {
51
+ private readonly client;
52
+ constructor(client: KokimokiClient);
53
+ /**
54
+ * Add a new entry to a leaderboard.
55
+ *
56
+ * Creates a new entry each time it's called, preserving all attempts. Use this when you want
57
+ * to track every score submission (e.g., all game attempts).
58
+ *
59
+ * @param leaderboardName The name of the leaderboard to add the entry to
60
+ * @param sortDir Sort direction: "asc" for lowest-is-best (e.g., completion time),
61
+ * "desc" for highest-is-best (e.g., points)
62
+ * @param score The numeric score value
63
+ * @param metadata Public metadata visible to all players (e.g., player name, level)
64
+ * @param privateMetadata Private metadata only accessible via API calls (e.g., session ID)
65
+ * @returns A promise resolving to an object with the entry's rank
66
+ *
67
+ * @example
68
+ * ```typescript
69
+ * const { rank } = await kmClient.leaderboard.insertEntry(
70
+ * 'high-scores',
71
+ * 'desc',
72
+ * 1500,
73
+ * { playerName: 'Alice', level: 10 },
74
+ * { sessionId: 'abc123' }
75
+ * );
76
+ * console.log(`New rank: ${rank}`);
77
+ * ```
78
+ */
79
+ insertEntry<MetadataT, PrivateMetadataT>(leaderboardName: string, sortDir: "asc" | "desc", score: number, metadata: MetadataT, privateMetadata: PrivateMetadataT): Promise<{
80
+ rank: number;
81
+ }>;
82
+ /**
83
+ * Add or update the latest entry for the current client in a leaderboard.
84
+ *
85
+ * Replaces the previous entry if one exists for this client. Use this when you only want
86
+ * to keep the latest or best score per player (e.g., daily high score).
87
+ *
88
+ * @param leaderboardName The name of the leaderboard to upsert the entry in
89
+ * @param sortDir Sort direction: "asc" for lowest-is-best (e.g., completion time),
90
+ * "desc" for highest-is-best (e.g., points)
91
+ * @param score The numeric score value
92
+ * @param metadata Public metadata visible to all players (e.g., player name, completion time)
93
+ * @param privateMetadata Private metadata only accessible via API calls (e.g., device ID)
94
+ * @returns A promise resolving to an object with the entry's updated rank
95
+ *
96
+ * @example
97
+ * ```typescript
98
+ * const { rank } = await kmClient.leaderboard.upsertEntry(
99
+ * 'daily-scores',
100
+ * 'desc',
101
+ * 2000,
102
+ * { playerName: 'Bob', completionTime: 120 },
103
+ * { deviceId: 'xyz789' }
104
+ * );
105
+ * console.log(`Updated rank: ${rank}`);
106
+ * ```
107
+ */
108
+ upsertEntry<MetadataT, PrivateMetadataT>(leaderboardName: string, sortDir: "asc" | "desc", score: number, metadata: MetadataT, privateMetadata: PrivateMetadataT): Promise<{
109
+ rank: number;
110
+ }>;
111
+ /**
112
+ * List entries in a leaderboard with pagination.
113
+ *
114
+ * Retrieves a sorted list of leaderboard entries. Use skip and limit parameters for
115
+ * pagination (e.g., showing top 10, or implementing "load more" functionality).
116
+ *
117
+ * @param leaderboardName The name of the leaderboard to query
118
+ * @param sortDir Sort direction: "asc" for lowest-is-best (e.g., completion time),
119
+ * "desc" for highest-is-best (e.g., points)
120
+ * @param skip Number of entries to skip for pagination (default: 0)
121
+ * @param limit Maximum number of entries to return (default: 100)
122
+ * @returns A promise resolving to a paginated list of entries with rank, score, and metadata
123
+ *
124
+ * @example
125
+ * ```typescript
126
+ * // Get top 10 scores
127
+ * const { items, total } = await kmClient.leaderboard.listEntries(
128
+ * 'weekly-scores',
129
+ * 'desc',
130
+ * 0,
131
+ * 10
132
+ * );
133
+ *
134
+ * items.forEach(entry => {
135
+ * console.log(`Rank ${entry.rank}: ${entry.metadata.playerName} - ${entry.score}`);
136
+ * });
137
+ * ```
138
+ */
139
+ listEntries<MetadataT>(leaderboardName: string, sortDir: "asc" | "desc", skip?: number, limit?: number): Promise<Paginated<{
140
+ rank: number;
141
+ score: number;
142
+ metadata: MetadataT;
143
+ }>>;
144
+ /**
145
+ * Get the best entry for a specific client in a leaderboard.
146
+ *
147
+ * Retrieves the highest-ranked entry for a client based on the sort direction.
148
+ * Defaults to the current client if no clientId is provided.
149
+ *
150
+ * @param leaderboardName The name of the leaderboard to query
151
+ * @param sortDir Sort direction: "asc" for lowest-is-best (e.g., completion time),
152
+ * "desc" for highest-is-best (e.g., points)
153
+ * @param clientId The client ID to get the best entry for (optional, defaults to current client)
154
+ * @returns A promise resolving to the best entry with rank, score, and metadata
155
+ *
156
+ * @example
157
+ * ```typescript
158
+ * // Get current client's best entry
159
+ * const myBest = await kmClient.leaderboard.getBestEntry('all-time-high', 'desc');
160
+ * console.log(`My best: Rank ${myBest.rank}, Score ${myBest.score}`);
161
+ *
162
+ * // Get another player's best entry
163
+ * const otherBest = await kmClient.leaderboard.getBestEntry(
164
+ * 'all-time-high',
165
+ * 'desc',
166
+ * 'other-client-id'
167
+ * );
168
+ * ```
169
+ */
170
+ getBestEntry<MetadataT>(leaderboardName: string, sortDir: "asc" | "desc", clientId?: string): Promise<{
171
+ rank: number;
172
+ score: number;
173
+ metadata: MetadataT;
174
+ }>;
175
+ }
@@ -0,0 +1,203 @@
1
+ /**
2
+ * Kokimoki Leaderboard Service
3
+ *
4
+ * Provides efficient player ranking and score tracking with database indexes and optimized queries.
5
+ * Ideal for games with large numbers of players and competitive scoring.
6
+ *
7
+ * **Key Features:**
8
+ * - Efficient ranking with database indexes
9
+ * - Support for ascending (lowest-is-best) and descending (highest-is-best) sorting
10
+ * - Pagination for large leaderboards
11
+ * - Public and private metadata for entries
12
+ * - Insert (preserve all attempts) or upsert (keep latest only) modes
13
+ *
14
+ * **When to use Leaderboard API vs Global Store:**
15
+ *
16
+ * Use the **Leaderboard API** when:
17
+ * - You have a large number of entries (hundreds to thousands of players)
18
+ * - You need efficient ranking and sorting with database indexes
19
+ * - You want pagination and optimized queries for top scores
20
+ * - Memory and network efficiency are important
21
+ *
22
+ * Use a **Global Store** when:
23
+ * - You have a small number of players (typically under 100)
24
+ * - You need real-time updates and live leaderboard changes
25
+ * - You want to combine player scores with other game state
26
+ * - The leaderboard is temporary (session-based or reset frequently)
27
+ *
28
+ * Access via `kmClient.leaderboard`
29
+ *
30
+ * @example
31
+ * ```typescript
32
+ * // Submit a high score
33
+ * const { rank } = await kmClient.leaderboard.upsertEntry(
34
+ * 'high-scores',
35
+ * 'desc',
36
+ * 1500,
37
+ * { playerName: 'Alice' },
38
+ * {}
39
+ * );
40
+ *
41
+ * // Get top 10
42
+ * const { items } = await kmClient.leaderboard.listEntries('high-scores', 'desc', 0, 10);
43
+ *
44
+ * // Get player's best
45
+ * const best = await kmClient.leaderboard.getBestEntry('high-scores', 'desc');
46
+ * ```
47
+ */
48
+ export class KokimokiLeaderboardService {
49
+ client;
50
+ constructor(client) {
51
+ this.client = client;
52
+ }
53
+ /**
54
+ * Add a new entry to a leaderboard.
55
+ *
56
+ * Creates a new entry each time it's called, preserving all attempts. Use this when you want
57
+ * to track every score submission (e.g., all game attempts).
58
+ *
59
+ * @param leaderboardName The name of the leaderboard to add the entry to
60
+ * @param sortDir Sort direction: "asc" for lowest-is-best (e.g., completion time),
61
+ * "desc" for highest-is-best (e.g., points)
62
+ * @param score The numeric score value
63
+ * @param metadata Public metadata visible to all players (e.g., player name, level)
64
+ * @param privateMetadata Private metadata only accessible via API calls (e.g., session ID)
65
+ * @returns A promise resolving to an object with the entry's rank
66
+ *
67
+ * @example
68
+ * ```typescript
69
+ * const { rank } = await kmClient.leaderboard.insertEntry(
70
+ * 'high-scores',
71
+ * 'desc',
72
+ * 1500,
73
+ * { playerName: 'Alice', level: 10 },
74
+ * { sessionId: 'abc123' }
75
+ * );
76
+ * console.log(`New rank: ${rank}`);
77
+ * ```
78
+ */
79
+ async insertEntry(leaderboardName, sortDir, score, metadata, privateMetadata) {
80
+ const res = await fetch(`${this.client.apiUrl}/leaderboard-entries`, {
81
+ method: "POST",
82
+ headers: this.client.apiHeaders,
83
+ body: JSON.stringify({
84
+ leaderboardName,
85
+ sortDir,
86
+ score,
87
+ metadata,
88
+ privateMetadata,
89
+ upsert: false,
90
+ }),
91
+ });
92
+ return await res.json();
93
+ }
94
+ /**
95
+ * Add or update the latest entry for the current client in a leaderboard.
96
+ *
97
+ * Replaces the previous entry if one exists for this client. Use this when you only want
98
+ * to keep the latest or best score per player (e.g., daily high score).
99
+ *
100
+ * @param leaderboardName The name of the leaderboard to upsert the entry in
101
+ * @param sortDir Sort direction: "asc" for lowest-is-best (e.g., completion time),
102
+ * "desc" for highest-is-best (e.g., points)
103
+ * @param score The numeric score value
104
+ * @param metadata Public metadata visible to all players (e.g., player name, completion time)
105
+ * @param privateMetadata Private metadata only accessible via API calls (e.g., device ID)
106
+ * @returns A promise resolving to an object with the entry's updated rank
107
+ *
108
+ * @example
109
+ * ```typescript
110
+ * const { rank } = await kmClient.leaderboard.upsertEntry(
111
+ * 'daily-scores',
112
+ * 'desc',
113
+ * 2000,
114
+ * { playerName: 'Bob', completionTime: 120 },
115
+ * { deviceId: 'xyz789' }
116
+ * );
117
+ * console.log(`Updated rank: ${rank}`);
118
+ * ```
119
+ */
120
+ async upsertEntry(leaderboardName, sortDir, score, metadata, privateMetadata) {
121
+ const res = await fetch(`${this.client.apiUrl}/leaderboard-entries`, {
122
+ method: "POST",
123
+ headers: this.client.apiHeaders,
124
+ body: JSON.stringify({
125
+ leaderboardName,
126
+ sortDir,
127
+ score,
128
+ metadata,
129
+ privateMetadata,
130
+ upsert: true,
131
+ }),
132
+ });
133
+ return await res.json();
134
+ }
135
+ /**
136
+ * List entries in a leaderboard with pagination.
137
+ *
138
+ * Retrieves a sorted list of leaderboard entries. Use skip and limit parameters for
139
+ * pagination (e.g., showing top 10, or implementing "load more" functionality).
140
+ *
141
+ * @param leaderboardName The name of the leaderboard to query
142
+ * @param sortDir Sort direction: "asc" for lowest-is-best (e.g., completion time),
143
+ * "desc" for highest-is-best (e.g., points)
144
+ * @param skip Number of entries to skip for pagination (default: 0)
145
+ * @param limit Maximum number of entries to return (default: 100)
146
+ * @returns A promise resolving to a paginated list of entries with rank, score, and metadata
147
+ *
148
+ * @example
149
+ * ```typescript
150
+ * // Get top 10 scores
151
+ * const { items, total } = await kmClient.leaderboard.listEntries(
152
+ * 'weekly-scores',
153
+ * 'desc',
154
+ * 0,
155
+ * 10
156
+ * );
157
+ *
158
+ * items.forEach(entry => {
159
+ * console.log(`Rank ${entry.rank}: ${entry.metadata.playerName} - ${entry.score}`);
160
+ * });
161
+ * ```
162
+ */
163
+ async listEntries(leaderboardName, sortDir, skip = 0, limit = 100) {
164
+ const encodedLeaderboardName = encodeURIComponent(leaderboardName);
165
+ const res = await fetch(`${this.client.apiUrl}/leaderboard-entries?leaderboardName=${encodedLeaderboardName}&sortDir=${sortDir}&skip=${skip}&limit=${limit}`, {
166
+ headers: this.client.apiHeaders,
167
+ });
168
+ return await res.json();
169
+ }
170
+ /**
171
+ * Get the best entry for a specific client in a leaderboard.
172
+ *
173
+ * Retrieves the highest-ranked entry for a client based on the sort direction.
174
+ * Defaults to the current client if no clientId is provided.
175
+ *
176
+ * @param leaderboardName The name of the leaderboard to query
177
+ * @param sortDir Sort direction: "asc" for lowest-is-best (e.g., completion time),
178
+ * "desc" for highest-is-best (e.g., points)
179
+ * @param clientId The client ID to get the best entry for (optional, defaults to current client)
180
+ * @returns A promise resolving to the best entry with rank, score, and metadata
181
+ *
182
+ * @example
183
+ * ```typescript
184
+ * // Get current client's best entry
185
+ * const myBest = await kmClient.leaderboard.getBestEntry('all-time-high', 'desc');
186
+ * console.log(`My best: Rank ${myBest.rank}, Score ${myBest.score}`);
187
+ *
188
+ * // Get another player's best entry
189
+ * const otherBest = await kmClient.leaderboard.getBestEntry(
190
+ * 'all-time-high',
191
+ * 'desc',
192
+ * 'other-client-id'
193
+ * );
194
+ * ```
195
+ */
196
+ async getBestEntry(leaderboardName, sortDir, clientId) {
197
+ const encodedLeaderboardName = encodeURIComponent(leaderboardName);
198
+ const res = await fetch(`${this.client.apiUrl}/leaderboard-entries/best?leaderboardName=${encodedLeaderboardName}&sortDir=${sortDir}&clientId=${clientId || this.client.id}`, {
199
+ headers: this.client.apiHeaders,
200
+ });
201
+ return await res.json();
202
+ }
203
+ }
@@ -0,0 +1,155 @@
1
+ import { KokimokiClient } from "../core";
2
+ import type { Paginated, Upload } from "../types";
3
+ /**
4
+ * Kokimoki Storage Service
5
+ *
6
+ * Provides file upload and management capabilities for game applications. Ideal for media files
7
+ * (images, videos, audio) and other data not suitable for real-time stores (JSON, text files).
8
+ *
9
+ * **Key Features:**
10
+ * - Upload files to cloud storage with CDN delivery
11
+ * - Tag-based organization and filtering
12
+ * - Query uploads by client, MIME type, or tags
13
+ * - Pagination support for large collections
14
+ * - Public CDN URLs for direct access
15
+ *
16
+ * **Common Use Cases:**
17
+ * - Player avatars and profile images
18
+ * - Game screenshots and replays
19
+ * - User-generated content
20
+ * - Asset uploads (levels, maps, custom skins)
21
+ * - JSON configuration files
22
+ *
23
+ * Access via `kmClient.storage`
24
+ *
25
+ * @example
26
+ * ```typescript
27
+ * // Upload an image
28
+ * const upload = await kmClient.storage.upload('avatar.jpg', imageBlob, ['profile']);
29
+ *
30
+ * // Query user's uploads
31
+ * const myUploads = await kmClient.storage.listUploads({
32
+ * clientId: kmClient.id
33
+ * });
34
+ *
35
+ * // Use in store
36
+ * await kmClient.transact([store], (state) => {
37
+ * state.playerAvatar = upload.url;
38
+ * });
39
+ * ```
40
+ */
41
+ export declare class KokimokiStorageService {
42
+ private readonly client;
43
+ constructor(client: KokimokiClient);
44
+ private createUpload;
45
+ private uploadChunks;
46
+ private completeUpload;
47
+ /**
48
+ * Upload a file to cloud storage.
49
+ *
50
+ * Uploads a file (Blob) to Kokimoki storage and returns an Upload object with a public CDN URL.
51
+ * Files are automatically chunked for efficient upload. The returned URL can be used directly
52
+ * in your application (e.g., in img tags or store state).
53
+ *
54
+ * @param name The filename for the upload
55
+ * @param blob The Blob object containing the file data
56
+ * @param tags Optional array of tags for organizing and filtering uploads (default: [])
57
+ * @returns A promise resolving to the Upload object with CDN URL and metadata
58
+ *
59
+ * @example
60
+ * ```typescript
61
+ * // Upload image with tags
62
+ * const upload = await kmClient.storage.upload(
63
+ * 'avatar.jpg',
64
+ * imageBlob,
65
+ * ['profile', 'avatar']
66
+ * );
67
+ *
68
+ * // Use the CDN URL
69
+ * console.log(upload.url); // https://cdn.kokimoki.com/...
70
+ *
71
+ * // Store in game state
72
+ * await kmClient.transact([store], (state) => {
73
+ * state.players[kmClient.id].avatar = upload.url;
74
+ * });
75
+ * ```
76
+ */
77
+ upload(name: string, blob: Blob, tags?: string[]): Promise<Upload>;
78
+ /**
79
+ * Update metadata for an existing upload.
80
+ *
81
+ * Allows you to replace the tags associated with an upload. Useful for reorganizing
82
+ * or recategorizing uploaded files.
83
+ *
84
+ * @param id The upload ID to update
85
+ * @param update Object containing the new tags array
86
+ * @returns A promise resolving to the updated Upload object
87
+ *
88
+ * @example
89
+ * ```typescript
90
+ * // Update tags
91
+ * const updated = await kmClient.storage.updateUpload(upload.id, {
92
+ * tags: ['archived', 'old-profile']
93
+ * });
94
+ * ```
95
+ */
96
+ updateUpload(id: string, update: {
97
+ tags?: string[];
98
+ }): Promise<Upload>;
99
+ /**
100
+ * Query uploaded files with filtering and pagination.
101
+ *
102
+ * Retrieves a list of uploads matching the specified criteria. Supports filtering by
103
+ * client (uploader), MIME types, and tags. Use pagination for large collections.
104
+ *
105
+ * @param filter Optional filter criteria
106
+ * @param filter.clientId Filter by uploader's client ID
107
+ * @param filter.mimeTypes Filter by MIME types (e.g., ['image/jpeg', 'image/png'])
108
+ * @param filter.tags Filter by tags (all specified tags must match)
109
+ * @param skip Number of results to skip for pagination (default: 0)
110
+ * @param limit Maximum number of results to return (default: 100)
111
+ * @returns A promise resolving to paginated Upload results with total count
112
+ *
113
+ * @example
114
+ * ```typescript
115
+ * // Get current user's images
116
+ * const myImages = await kmClient.storage.listUploads({
117
+ * clientId: kmClient.id,
118
+ * mimeTypes: ['image/jpeg', 'image/png']
119
+ * });
120
+ *
121
+ * // Get all profile avatars
122
+ * const avatars = await kmClient.storage.listUploads({
123
+ * tags: ['profile', 'avatar']
124
+ * });
125
+ *
126
+ * // Pagination
127
+ * const page2 = await kmClient.storage.listUploads({}, 10, 10);
128
+ * ```
129
+ */
130
+ listUploads(filter?: {
131
+ clientId?: string;
132
+ mimeTypes?: string[];
133
+ tags?: string[];
134
+ }, skip?: number, limit?: number): Promise<Paginated<Upload>>;
135
+ /**
136
+ * Permanently delete an uploaded file.
137
+ *
138
+ * Removes the file from cloud storage and the CDN. The URL will no longer be accessible.
139
+ * This operation cannot be undone.
140
+ *
141
+ * @param id The upload ID to delete
142
+ * @returns A promise resolving to deletion confirmation
143
+ *
144
+ * @example
145
+ * ```typescript
146
+ * // Delete an upload
147
+ * const result = await kmClient.storage.deleteUpload(upload.id);
148
+ * console.log(`Deleted: ${result.deletedCount} file(s)`);
149
+ * ```
150
+ */
151
+ deleteUpload(id: string): Promise<{
152
+ acknowledged: boolean;
153
+ deletedCount: number;
154
+ }>;
155
+ }