@kokimoki/app 3.1.2 → 3.1.4

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 (66) hide show
  1. package/README.md +1 -2
  2. package/dist/core/kokimoki-client.d.ts +4 -4
  3. package/dist/core/kokimoki-client.js +4 -4
  4. package/dist/index.d.ts +1 -1
  5. package/dist/kokimoki.min.d.ts +3 -321
  6. package/dist/kokimoki.min.js +763 -2014
  7. package/dist/kokimoki.min.js.map +1 -1
  8. package/dist/services/kokimoki-ai.d.ts +15 -53
  9. package/dist/services/kokimoki-ai.js +11 -82
  10. package/dist/services/kokimoki-i18n.d.ts +25 -97
  11. package/dist/services/kokimoki-i18n.js +30 -99
  12. package/dist/utils/valtio.d.ts +2 -5
  13. package/dist/utils/valtio.js +4 -2
  14. package/dist/version.d.ts +1 -1
  15. package/dist/version.js +1 -1
  16. package/docs/kokimoki-ai.instructions.md +33 -88
  17. package/docs/kokimoki-dynamic-stores.instructions.md +3 -3
  18. package/docs/kokimoki-i18n.instructions.md +8 -115
  19. package/docs/kokimoki-sdk.instructions.md +46 -7
  20. package/package.json +8 -2
  21. package/dist/fields.d.ts +0 -110
  22. package/dist/fields.js +0 -158
  23. package/dist/kokimoki-ai.d.ts +0 -153
  24. package/dist/kokimoki-ai.js +0 -164
  25. package/dist/kokimoki-awareness.d.ts +0 -21
  26. package/dist/kokimoki-awareness.js +0 -48
  27. package/dist/kokimoki-client-refactored.d.ts +0 -80
  28. package/dist/kokimoki-client-refactored.js +0 -400
  29. package/dist/kokimoki-client.d.ts +0 -362
  30. package/dist/kokimoki-client.js +0 -823
  31. package/dist/kokimoki-leaderboard.d.ts +0 -175
  32. package/dist/kokimoki-leaderboard.js +0 -203
  33. package/dist/kokimoki-local-store.d.ts +0 -11
  34. package/dist/kokimoki-local-store.js +0 -40
  35. package/dist/kokimoki-queue.d.ts +0 -0
  36. package/dist/kokimoki-queue.js +0 -38
  37. package/dist/kokimoki-req-res.d.ts +0 -0
  38. package/dist/kokimoki-req-res.js +0 -198
  39. package/dist/kokimoki-schema.d.ts +0 -113
  40. package/dist/kokimoki-schema.js +0 -162
  41. package/dist/kokimoki-storage.d.ts +0 -156
  42. package/dist/kokimoki-storage.js +0 -208
  43. package/dist/kokimoki-store.d.ts +0 -23
  44. package/dist/kokimoki-store.js +0 -117
  45. package/dist/kokimoki-transaction.d.ts +0 -18
  46. package/dist/kokimoki-transaction.js +0 -143
  47. package/dist/message-queue.d.ts +0 -8
  48. package/dist/message-queue.js +0 -19
  49. package/dist/room-subscription-mode.d.ts +0 -5
  50. package/dist/room-subscription-mode.js +0 -6
  51. package/dist/room-subscription.d.ts +0 -15
  52. package/dist/room-subscription.js +0 -52
  53. package/dist/synced-schema.d.ts +0 -74
  54. package/dist/synced-schema.js +0 -83
  55. package/dist/synced-store.d.ts +0 -10
  56. package/dist/synced-store.js +0 -9
  57. package/dist/synced-types.d.ts +0 -47
  58. package/dist/synced-types.js +0 -67
  59. package/dist/ws-message-reader.d.ts +0 -11
  60. package/dist/ws-message-reader.js +0 -36
  61. package/dist/ws-message-type copy.d.ts +0 -6
  62. package/dist/ws-message-type copy.js +0 -7
  63. package/dist/ws-message-type.d.ts +0 -11
  64. package/dist/ws-message-type.js +0 -12
  65. package/dist/ws-message-writer.d.ts +0 -9
  66. package/dist/ws-message-writer.js +0 -45
@@ -6,8 +6,7 @@ applyTo: "**/*.ts,**/*.tsx"
6
6
  # AI Integration
7
7
 
8
8
  Built-in methods for AI text generation and image transformation. No API keys required.
9
- All generation methods are **async job-based** - they submit a job and return immediately with a `jobId`.
10
- Use the corresponding poll method to wait for the result.
9
+ All generation methods are **async job-based** - they submit a job and return immediately with a `jobId`. Use `getJob()` to poll for the result.
11
10
 
12
11
  For core SDK concepts, see [Kokimoki SDK](./kokimoki-sdk.instructions.md).
13
12
 
@@ -19,7 +18,7 @@ Access AI API via `kmClient.ai`.
19
18
  - Text generation with configurable creativity (temperature)
20
19
  - Structured JSON output with Zod schema validation
21
20
  - AI-powered image generation and modification
22
- - Job-based async processing with polling support
21
+ - Job-based async processing
23
22
  - Resumable after page reload (persist jobId)
24
23
 
25
24
  ## Available Models
@@ -45,7 +44,7 @@ Access AI API via `kmClient.ai`.
45
44
 
46
45
  ### ai.generateText(req): Promise<{ jobId: string }>
47
46
 
48
- Submits a text generation job. Use `pollText()` to wait for the result.
47
+ Submits a text generation job. Use `getJob()` to poll the result.
49
48
 
50
49
  **Parameters:**
51
50
 
@@ -67,12 +66,13 @@ const { jobId } = await kmClient.ai.generateText({
67
66
  });
68
67
 
69
68
  // Poll for result
70
- const text = await kmClient.ai.pollText(jobId);
69
+ const job = await kmClient.ai.getJob<string>(jobId);
70
+ console.log("Generated quest:", job.result);
71
71
  ```
72
72
 
73
73
  ### ai.generateJson<T>(req): Promise<{ jobId: string }>
74
74
 
75
- Submits a JSON generation job with Zod schema validation. Use `pollJson()` to wait for the result with type inference.
75
+ Submits a JSON generation job with Zod schema validation. Use `getJob<T>()` to wait for the result with type inference.
76
76
 
77
77
  **Parameters:**
78
78
 
@@ -86,7 +86,7 @@ Submits a JSON generation job with Zod schema validation. Use `pollJson()` to wa
86
86
  **Example:**
87
87
 
88
88
  ```typescript
89
- import { z } from "zod/v4";
89
+ import { z } from "@kokimoki/app";
90
90
 
91
91
  // Define schema with Zod
92
92
  const enemySchema = z.object({
@@ -107,8 +107,8 @@ const { jobId } = await kmClient.ai.generateJson({
107
107
  await saveJobId(jobId);
108
108
 
109
109
  // Poll with schema for type inference and validation
110
- const enemy = await kmClient.ai.pollJson(jobId, { schema: enemySchema });
111
- console.log(enemy.name, enemy.health); // Fully typed!
110
+ const enemy = await kmClient.ai.getJob<Enemy>(jobId);
111
+ console.log(enemy.result?.name, enemy.result?.health); // Fully typed!
112
112
  ```
113
113
 
114
114
  ```typescript
@@ -127,13 +127,13 @@ const { jobId } = await kmClient.ai.generateJson({
127
127
  prompt: "Create 5 history quiz questions with 4 options each",
128
128
  });
129
129
 
130
- const questions = await kmClient.ai.pollJson(jobId, { schema: questionSchema });
131
- questions.forEach((q) => console.log(q.question));
130
+ const questions = await kmClient.ai.getJob<typeof questionSchema>(jobId);
131
+ questions.result?.forEach((q) => console.log(q.question));
132
132
  ```
133
133
 
134
134
  ### ai.generateImage(req): Promise<{ jobId: string }>
135
135
 
136
- Submits an image generation/modification job. Use `pollImage()` to wait for the result.
136
+ Submits an image generation/modification job. Use `getJob()` to wait for the result.
137
137
  The result is stored as `Upload` object (see [Storage](./kokimoki-storage.instructions.md)).
138
138
 
139
139
  **Parameters:**
@@ -152,8 +152,8 @@ const { jobId } = await kmClient.ai.generateImage({
152
152
  tags: ["background", "ai-generated"],
153
153
  });
154
154
 
155
- const upload = await kmClient.ai.pollImage(jobId);
156
- console.log(upload.url); // CDN URL to the generated image
155
+ const job = await kmClient.ai.getJob<string>(jobId);
156
+ console.log("Generated image:", job.result); // CDN URL to the generated image
157
157
  ```
158
158
 
159
159
  ```typescript
@@ -164,12 +164,13 @@ const { jobId } = await kmClient.ai.generateImage({
164
164
  tags: ["art", "ai-generated"],
165
165
  });
166
166
 
167
- const upload: Upload = await kmClient.ai.pollImage(jobId);
167
+ const job = await kmClient.ai.getJob<string>(jobId);
168
+ console.log("Generated image:", job.result); // CDN URL to the generated image
168
169
  ```
169
170
 
170
171
  ### ai.getJob(jobId): Promise<AiJob>
171
172
 
172
- Get the current status of an AI job without polling.
173
+ Get the current status of an AI job.
173
174
 
174
175
  **Parameters:**
175
176
 
@@ -199,73 +200,13 @@ if (job.status === "completed") {
199
200
  }
200
201
  ```
201
202
 
202
- ### ai.pollText(jobId, options?): Promise<string>
203
-
204
- Poll a text generation job until completion.
205
-
206
- **Parameters:**
207
-
208
- - **jobId**: `string` The job ID to poll
209
- - **options.pollInterval**: `number` Polling interval in ms (default: 2000, min: 1000)
210
- - **options.timeout**: `number` Timeout in ms (default: 120000)
211
- - **options.onProgress**: `(status: AiJobStatus) => void` Progress callback
212
-
213
- **Example:**
214
-
215
- ```typescript
216
- const text = await kmClient.ai.pollText(jobId, {
217
- timeout: 60000,
218
- onProgress: (status) => console.log("Status:", status),
219
- });
220
- ```
221
-
222
- ### ai.pollJson<T>(jobId, options): Promise<T>
223
-
224
- Poll a JSON generation job until completion with schema validation.
225
-
226
- **Parameters:**
227
-
228
- - **jobId**: `string` The job ID to poll
229
- - **options.schema**: `ZodType` Zod schema for validation and type inference (required)
230
- - **options.pollInterval**: `number` Polling interval in ms (default: 2000, min: 1000)
231
- - **options.timeout**: `number` Timeout in ms (default: 120000)
232
- - **options.onProgress**: `(status: AiJobStatus) => void` Progress callback
233
-
234
- **Example:**
235
-
236
- ```typescript
237
- const enemy = await kmClient.ai.pollJson(jobId, {
238
- schema: enemySchema,
239
- timeout: 60000,
240
- onProgress: (status) => console.log("Status:", status),
241
- });
242
- ```
243
-
244
- ### ai.pollImage(jobId, options?): Promise<Upload>
245
-
246
- Poll an image generation job until completion.
247
-
248
- **Parameters:**
249
-
250
- - **jobId**: `string` The job ID to poll
251
- - **options.pollInterval**: `number` Polling interval in ms (default: 2000, min: 1000)
252
- - **options.timeout**: `number` Timeout in ms (default: 120000)
253
- - **options.onProgress**: `(status: AiJobStatus) => void` Progress callback
254
-
255
- **Example:**
256
-
257
- ```typescript
258
- const upload = await kmClient.ai.pollImage(jobId, {
259
- timeout: 60000,
260
- onProgress: (status) => console.log("Status:", status),
261
- });
262
- ```
263
-
264
203
  ## Common Patterns
265
204
 
266
205
  ### Example: Persist Jobs Across Page Reloads
267
206
 
268
207
  ```typescript
208
+ import { snapshot } from "@kokimoki/app";
209
+
269
210
  // Store jobId in local store for persistence
270
211
  const { jobId } = await kmClient.ai.generateJson({
271
212
  schema: questSchema,
@@ -277,7 +218,7 @@ await kmClient.transact([localStore], ([state]) => {
277
218
  });
278
219
 
279
220
  // On page load, resume polling if job is pending
280
- const pendingJobId = localStore.proxy.pendingQuestJobId;
221
+ const pendingJobId = snapshot(localStore.proxy).pendingQuestJobId;
281
222
  if (pendingJobId) {
282
223
  const quest = await kmClient.ai.pollJson(pendingJobId, {
283
224
  schema: questSchema,
@@ -297,20 +238,24 @@ const { jobId } = await kmClient.ai.generateText({
297
238
  prompt: "Write a story",
298
239
  });
299
240
 
300
- const text = await kmClient.ai.pollText(jobId, {
301
- onProgress: (status) => {
302
- if (status === "queued") setLoadingText("Waiting in queue...");
303
- if (status === "processing") setLoadingText("Generating...");
304
- },
305
- });
306
- setLoading(false);
241
+ const checkInterval = setInterval(async () => {
242
+ const job = await kmClient.ai.getJob<string>(jobId);
243
+ if (job.status === "completed") {
244
+ console.log("Generated text:", job.result);
245
+ clearInterval(checkInterval);
246
+ setLoading(false);
247
+ } else if (job.status === "failed") {
248
+ console.error("AI generation failed:", job.error);
249
+ clearInterval(checkInterval);
250
+ setLoading(false);
251
+ }
252
+ }, 2000);
307
253
  ```
308
254
 
309
255
  ## Key Points
310
256
 
311
- - **Job-based**: All generation methods return `{ jobId }` immediately, use poll methods for results
257
+ - **Job-based**: All generation methods return `{ jobId }` immediately, use `getJob()` to poll for results
312
258
  - **Resumable**: Save `jobId` to store to resume polling after page reload
313
259
  - **Zod Schemas**: Use Zod schemas for `generateJson` to get automatic type inference and validation
314
260
  - **Temperature**: Range 0.0 (factual) to 1.0 (creative)
315
- - **Polling Options**: Configure `pollInterval`, `timeout`, and `onProgress` callback
316
261
  - **Image URLs**: Gemini models support multimodal input via `imageUrls` parameter
@@ -133,7 +133,7 @@ export function useDynamicStore<T extends object>(
133
133
  ## Basic Usage
134
134
 
135
135
  ```tsx
136
- import { useSnapshot } from "valtio";
136
+ import { useSnapshot } from "@kokimoki/app";
137
137
  import { useDynamicStore } from "@/hooks/useDynamicStore";
138
138
 
139
139
  interface RoomState {
@@ -350,7 +350,7 @@ const BreakoutRoom = ({ roomId }: { roomId: string }) => {
350
350
 
351
351
  ## File Organization
352
352
 
353
- **ALWAYS** organize dynamic store code using the standard store/actions pattern:
353
+ **ALWAYS** organize dynamic store code using the standard state/actions pattern:
354
354
 
355
355
  **State definition** (`src/state/chat-store.ts`):
356
356
 
@@ -408,7 +408,7 @@ export const chatActions = {
408
408
  **Usage in component** (`src/views/chat-view.tsx`):
409
409
 
410
410
  ```tsx
411
- import { useSnapshot } from "valtio";
411
+ import { useSnapshot } from "@kokimoki/app";
412
412
  import { useDynamicStore } from "@/hooks/useDynamicStore";
413
413
  import { createChatState, type ChatState } from "@/state/chat-store";
414
414
  import { chatActions } from "@/state/actions/chat-actions";
@@ -110,36 +110,12 @@ const url = kmClient.i18n.getNamespaceUrl("en", "game");
110
110
 
111
111
  ## AI Translation API
112
112
 
113
- ### i18n.getAllLanguagesStatus(): Promise<AllLanguagesStatus>
114
-
115
- Get the status of all languages that have been requested for AI translation.
116
-
117
- ```typescript
118
- interface AllLanguagesStatus {
119
- languages: { lng: string; status: LanguageStatus }[];
120
- }
121
-
122
- type LanguageStatus = "available" | "processing" | "failed" | "partial";
123
- ```
124
-
125
- **Example:**
126
-
127
- ```typescript
128
- const { languages } = await kmClient.i18n.getAllLanguagesStatus();
129
- // [{ lng: 'de', status: 'available' }, { lng: 'fr', status: 'processing' }]
130
- ```
131
-
132
- ### i18n.getTranslationStatus(lng): Promise<TranslationStatus>
113
+ ### i18n.getTranslationStatus(lng): Promise<{ status: TranslationStatus }>
133
114
 
134
115
  Get the translation status for a specific language.
135
116
 
136
117
  ```typescript
137
- interface TranslationStatus {
138
- status: "available" | "processing" | "not_available";
139
- namespaces: Record<string, NamespaceStatus>;
140
- }
141
-
142
- type NamespaceStatus = "available" | "processing" | "failed" | "not_available";
118
+ type TranslationStatus = "available" | "processing" | "failed";
143
119
  ```
144
120
 
145
121
  **Example:**
@@ -147,117 +123,34 @@ type NamespaceStatus = "available" | "processing" | "failed" | "not_available";
147
123
  ```typescript
148
124
  const status = await kmClient.i18n.getTranslationStatus("de");
149
125
 
150
- if (status.status === "available") {
126
+ if (status === "available") {
151
127
  i18next.changeLanguage("de");
152
- } else if (status.status === "processing") {
153
- // Show loading indicator
154
- } else {
155
- // Request translation
156
- await kmClient.i18n.requestTranslation("de");
157
128
  }
158
129
  ```
159
130
 
160
- ### i18n.requestTranslation(lng): Promise<RequestTranslationResult>
131
+ ### i18n.requestTranslation(lng): Promise<TranslationStatus>
161
132
 
162
133
  Request AI translation for a target language. Triggers background AI translation jobs for all namespaces.
163
134
 
164
135
  ```typescript
165
- interface RequestTranslationResult {
166
- lng: string;
167
- status: "started" | "already_processing" | "already_available";
168
- }
136
+ type TranslationStatus = "available" | "processing" | "failed";
169
137
  ```
170
138
 
171
139
  **Example:**
172
140
 
173
141
  ```typescript
174
- const result = await kmClient.i18n.requestTranslation("de");
142
+ const status = await kmClient.i18n.requestTranslation("de");
175
143
 
176
- if (result.status === "already_available") {
144
+ if (status === "available") {
177
145
  // Already translated, switch immediately
178
146
  i18next.changeLanguage("de");
179
147
  } else {
180
- // Poll until ready
181
- await kmClient.i18n.pollTranslation("de");
182
- i18next.changeLanguage("de");
148
+ // Poll until ready using getTranslationStatus
183
149
  }
184
150
  ```
185
151
 
186
- ### i18n.pollTranslation(lng, options?): Promise<void>
187
-
188
- Poll a translation request until all namespaces are available.
189
-
190
- **Parameters:**
191
-
192
- - **lng**: `string` Language code to poll for
193
- - **options.pollInterval**: `number` Polling interval in ms (default: 2000, min: 1000)
194
- - **options.timeout**: `number` Timeout in ms (default: 120000)
195
- - **options.onProgress**: `(status: TranslationStatus) => void` Progress callback
196
-
197
- **Example:**
198
-
199
- ```typescript
200
- await kmClient.i18n.pollTranslation("de", {
201
- timeout: 60000,
202
- onProgress: (status) => {
203
- console.log("Overall:", status.status);
204
- console.log("Namespaces:", status.namespaces);
205
- },
206
- });
207
-
208
- // Now safe to switch
209
- i18next.changeLanguage("de");
210
- ```
211
-
212
152
  ## Common Patterns
213
153
 
214
- ### Example: Language Switcher with AI Translation
215
-
216
- ```tsx
217
- import { useState } from "react";
218
- import { useTranslation } from "react-i18next";
219
- import { getKmClient } from "@kokimoki/app";
220
-
221
- const kmClient = getKmClient();
222
-
223
- const LanguageSwitcher = () => {
224
- const { i18n } = useTranslation();
225
- const [loading, setLoading] = useState(false);
226
-
227
- const switchLanguage = async (lng: string) => {
228
- // Check if translation is available
229
- const status = await kmClient.i18n.getTranslationStatus(lng);
230
-
231
- if (status.status === "available") {
232
- i18n.changeLanguage(lng);
233
- return;
234
- }
235
-
236
- // Request and wait for AI translation
237
- setLoading(true);
238
- await kmClient.i18n.requestTranslation(lng);
239
- await kmClient.i18n.pollTranslation(lng, {
240
- onProgress: (s) => console.log(`Translating: ${s.status}`),
241
- });
242
- setLoading(false);
243
-
244
- i18n.changeLanguage(lng);
245
- };
246
-
247
- return (
248
- <select
249
- value={i18n.language}
250
- onChange={(e) => switchLanguage(e.target.value)}
251
- disabled={loading}
252
- >
253
- <option value="en">English</option>
254
- <option value="de">Deutsch</option>
255
- <option value="fr">Français</option>
256
- </select>
257
- );
258
- };
259
- ```
260
-
261
154
  ### Example: Using Translations in Components
262
155
 
263
156
  ```tsx
@@ -16,7 +16,9 @@ The Kokimoki SDK is a comprehensive development toolkit for building real-time c
16
16
  - Use `kmClient.transact` for atomic state updates across single store or multiple stores
17
17
  - Use `kmClient.storage.upload` and related API methods to handle file uploads (media, JSON, etc.) in application
18
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
19
+ - Use `useSnapshot` hook to get reactive state inside React components — import from `@kokimoki/app` (re-exports valtio)
20
+ - Use `snapshot` function to read immutable state outside React components (in actions, event handlers, etc.) — import from `@kokimoki/app` — **NEVER** read directly from `store.proxy.field`
21
+ - Use `z` and `ZodType` from `@kokimoki/app` for schema definitions (re-exports zod)
20
22
  - Use AI integration API methods: `kmClient.ai.generateText`, `kmClient.ai.generateJson`, and `kmClient.ai.generateImage` with corresponding poll methods for AI capabilities
21
23
  - Use i18n API methods: `kmClient.i18n.createI18n`, `kmClient.i18n.init`, and `kmClient.i18n.requestTranslation` for internationalization with AI-powered translations
22
24
  - Use leaderboard API methods: `kmClient.leaderboard.insertEntry`, `kmClient.leaderboard.upsertEntry`, `kmClient.leaderboard.listEntries`, and `kmClient.leaderboard.getBestEntry` to add leaderboard capabilities to application
@@ -118,13 +120,13 @@ await kmClient.transact([store1, store2], ([state1, state2]) => {
118
120
 
119
121
  ### Reactive State in Components
120
122
 
121
- - Use `useSnapshot` hook from `valtio` to get reactive state inside React components
123
+ - Use `useSnapshot` hook to get reactive state inside React components (import from `@kokimoki/app`)
122
124
  - The component will re-render when the store state changes
123
125
 
124
126
  **Example: Using State in Components**
125
127
 
126
128
  ```tsx
127
- import { useSnapshot } from "valtio";
129
+ import { useSnapshot } from "@kokimoki/app";
128
130
  import { store } from "../store";
129
131
 
130
132
  const Component = () => {
@@ -140,6 +142,44 @@ const Component = () => {
140
142
  };
141
143
  ```
142
144
 
145
+ ### Reading State Outside Components
146
+
147
+ When reading state **outside React components** (in store actions, event handlers, initialization logic), use `snapshot()` from `@kokimoki/app` to get an immutable copy.
148
+ **NEVER** read directly from `store.proxy` — the proxy is mutable and values can change between read and use.
149
+
150
+ ```typescript
151
+ import { snapshot } from "@kokimoki/app";
152
+
153
+ // WRONG — mutable proxy, value can change unexpectedly
154
+ const value = store.proxy.someField;
155
+
156
+ // CORRECT — immutable snapshot, safe to use
157
+ const value = snapshot(store.proxy).someField;
158
+ ```
159
+
160
+ **Example: Reading state in an store actions**
161
+
162
+ ```typescript
163
+ import { snapshot } from "@kokimoki/app";
164
+
165
+ // Action function that reads state and updates it based on conditions
166
+ async function doSomething() {
167
+ const { count, completed } = snapshot(store.proxy);
168
+
169
+ if (count > 10 && !completed) {
170
+ await kmClient.transact([store], ([state]) => {
171
+ state.completed = true;
172
+ });
173
+ }
174
+ }
175
+ ```
176
+
177
+ ### Common Patterns
178
+
179
+ - **Inside React components** → `useSnapshot(store.proxy)` (reactive, triggers re-renders)
180
+ - **Outside React components** → `snapshot(store.proxy)` (immutable, read-only)
181
+ - **Writing state** → Always use `kmClient.transact()` (never assign to proxy directly)
182
+
143
183
  ## Dynamic Stores
144
184
 
145
185
  For room-based state isolation (teams, chat rooms, breakout rooms), see [Dynamic Stores](./kokimoki-dynamic-stores.instructions.md).
@@ -166,7 +206,7 @@ Each Kokimoki store has a `connections` property that provides real-time presenc
166
206
  ### Example: Track Online Players
167
207
 
168
208
  ```tsx
169
- import { useSnapshot } from "valtio";
209
+ import { useSnapshot } from "@kokimoki/app";
170
210
  import { globalStore } from "@/state/stores/global-store";
171
211
 
172
212
  const Component = () => {
@@ -186,7 +226,7 @@ const Component = () => {
186
226
  ### Example: Display Player List with Online Status
187
227
 
188
228
  ```tsx
189
- import { useSnapshot } from "valtio";
229
+ import { useSnapshot } from "@kokimoki/app";
190
230
  import { globalStore } from "@/state/stores/global-store";
191
231
 
192
232
  const PlayerList = () => {
@@ -231,8 +271,7 @@ Store names with the `__km/` prefix are reserved for SDK internal use. Do not cr
231
271
  ### Using App Meta
232
272
 
233
273
  ```tsx
234
- import { getKmClient } from "@kokimoki/app";
235
- import { useSnapshot } from "valtio";
274
+ import { useSnapshot, getKmClient } from "@kokimoki/app";
236
275
 
237
276
  const kmClient = getKmClient();
238
277
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kokimoki/app",
3
- "version": "3.1.2",
3
+ "version": "3.1.4",
4
4
  "type": "module",
5
5
  "description": "Kokimoki app",
6
6
  "main": "dist/index.js",
@@ -19,17 +19,23 @@
19
19
  "prebuild": "node scripts/generate-version.js",
20
20
  "build": "tsc",
21
21
  "dev": "tsc -w",
22
- "docs": "typedoc src/index.ts"
22
+ "docs": "typedoc src/index.ts",
23
+ "lint": "eslint .",
24
+ "typecheck": "tsc --noEmit"
23
25
  },
24
26
  "author": "Loquiz OÜ",
25
27
  "license": "Apache-2.0",
26
28
  "devDependencies": {
29
+ "eslint-config": "*",
27
30
  "@types/chai": "^4",
28
31
  "@types/mocha": "^10",
29
32
  "@types/node": "^18",
30
33
  "@types/ws": "^8.5.5",
31
34
  "bson-objectid": "^2.0.4",
32
35
  "chai": "^4",
36
+ "eslint": "^9.39.1",
37
+ "eslint-plugin-chai-friendly": "^1.1.0",
38
+ "eslint-plugin-valtio": "^0.8.0",
33
39
  "mocha": "^10",
34
40
  "ts-node": "^10",
35
41
  "tsx": "^4",
package/dist/fields.d.ts DELETED
@@ -1,110 +0,0 @@
1
- export interface FieldOptions {
2
- label?: string;
3
- }
4
- export declare abstract class Field<T> {
5
- readonly options: FieldOptions;
6
- constructor(options: FieldOptions);
7
- abstract get value(): T;
8
- abstract get schema(): any;
9
- }
10
- export declare class BooleanField extends Field<boolean> {
11
- value: boolean;
12
- constructor(value: boolean, options?: FieldOptions);
13
- get schema(): {
14
- type: string;
15
- default: boolean;
16
- };
17
- }
18
- export declare class ConstField<T extends string> extends Field<
19
- string extends T ? never : T
20
- > {
21
- value: string extends T ? never : T;
22
- constructor(value: string extends T ? never : T, options?: FieldOptions);
23
- get schema(): {
24
- const: string extends T ? never : T;
25
- };
26
- }
27
- export declare class ImageField extends Field<string> {
28
- value: string;
29
- constructor(value: string, options?: FieldOptions);
30
- get schema(): {
31
- type: string;
32
- default: string;
33
- };
34
- }
35
- export declare class TextField extends Field<string> {
36
- value: string;
37
- constructor(value: string, options?: FieldOptions);
38
- get schema(): {
39
- type: string;
40
- default: string;
41
- };
42
- }
43
- export declare class EnumField<T extends Record<string, string>> extends Field<
44
- keyof T
45
- > {
46
- enumValues: T;
47
- value: keyof T;
48
- constructor(enumValues: T, value: keyof T, options?: FieldOptions);
49
- get schema(): {
50
- enum: string[];
51
- };
52
- }
53
- export declare class IntegerField extends Field<number> {
54
- value: number;
55
- constructor(value: number, options?: FieldOptions);
56
- get schema(): {
57
- type: string;
58
- default: number;
59
- };
60
- }
61
- export declare class FloatField extends Field<number> {
62
- value: number;
63
- constructor(value: number, options?: FieldOptions);
64
- get schema(): {
65
- type: string;
66
- default: number;
67
- };
68
- }
69
- export declare class FormGroup<
70
- T extends Record<string, Field<any>>,
71
- O extends Record<string, Field<any>>,
72
- > extends Field<
73
- {
74
- [key in keyof T]: T[key]["value"];
75
- } & Partial<{
76
- [key in keyof O]: O[key]["value"];
77
- }>
78
- > {
79
- requiredFields: T;
80
- optionalFields: O;
81
- constructor(requiredFields: T, optionalFields?: O, options?: FieldOptions);
82
- get value(): {
83
- [key in keyof T]: T[key]["value"];
84
- } & Partial<{
85
- [key in keyof O]: O[key]["value"];
86
- }>;
87
- get schema(): {
88
- type: string;
89
- properties: any;
90
- required: string[];
91
- };
92
- }
93
- export declare class FormArray<T> extends Field<T[]> {
94
- private factory;
95
- value: Field<T>["value"][];
96
- constructor(
97
- factory: () => Field<T>,
98
- value: Field<T>["value"][],
99
- options?: FieldOptions,
100
- );
101
- get schema(): {
102
- type: string;
103
- items: any;
104
- default: T[];
105
- };
106
- }
107
- export declare class Form<
108
- R extends Record<string, Field<any>>,
109
- O extends Record<string, Field<any>>,
110
- > extends FormGroup<R, O> {}