@kokimoki/app 2.1.1 → 3.1.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.
- package/README.md +72 -0
- package/dist/core/kokimoki-client.d.ts +127 -15
- package/dist/core/kokimoki-client.js +174 -22
- package/dist/core/room-subscription-mode.d.ts +1 -5
- package/dist/core/room-subscription-mode.js +3 -6
- package/dist/index.d.ts +8 -1
- package/dist/index.js +5 -0
- package/dist/kokimoki.min.d.ts +320 -2
- package/dist/kokimoki.min.js +1916 -115
- package/dist/kokimoki.min.js.map +1 -1
- package/dist/protocol/ws-message/reader.d.ts +1 -1
- package/dist/services/index.d.ts +1 -0
- package/dist/services/index.js +1 -0
- package/dist/services/kokimoki-ai.d.ts +185 -124
- package/dist/services/kokimoki-ai.js +201 -111
- package/dist/services/kokimoki-i18n.d.ts +262 -0
- package/dist/services/kokimoki-i18n.js +330 -0
- package/dist/stores/kokimoki-local-store.d.ts +1 -1
- package/dist/stores/kokimoki-store.d.ts +10 -1
- package/dist/stores/kokimoki-store.js +10 -1
- package/dist/types/common.d.ts +9 -0
- package/dist/types/env.d.ts +46 -0
- package/dist/types/env.js +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js +1 -0
- package/dist/utils/kokimoki-client.d.ts +35 -0
- package/dist/utils/kokimoki-client.js +42 -0
- package/dist/utils/kokimoki-dev.d.ts +30 -0
- package/dist/utils/kokimoki-dev.js +75 -0
- package/dist/utils/kokimoki-env.d.ts +20 -0
- package/dist/utils/kokimoki-env.js +30 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/docs/kokimoki-ai.instructions.md +316 -0
- package/docs/kokimoki-dynamic-stores.instructions.md +439 -0
- package/docs/kokimoki-i18n.instructions.md +285 -0
- package/docs/kokimoki-leaderboard.instructions.md +189 -0
- package/docs/kokimoki-sdk.instructions.md +260 -0
- package/docs/kokimoki-storage.instructions.md +162 -0
- package/llms.txt +43 -0
- package/package.json +9 -13
package/README.md
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# @kokimoki/app
|
|
2
|
+
|
|
3
|
+
The core SDK for building real-time multiplayer games with Kokimoki.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @kokimoki/app
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { getKmClient } from "@kokimoki/app";
|
|
15
|
+
|
|
16
|
+
const kmClient = getKmClient();
|
|
17
|
+
|
|
18
|
+
// Create a synchronized store
|
|
19
|
+
const gameStore = kmClient.store("game", {
|
|
20
|
+
players: {},
|
|
21
|
+
status: "waiting",
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// Update state with transactions
|
|
25
|
+
await kmClient.transact([gameStore], ([state]) => {
|
|
26
|
+
state.players[kmClient.id] = { name: "Player 1", score: 0 };
|
|
27
|
+
});
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Features
|
|
31
|
+
|
|
32
|
+
- **Real-time State Sync** - Synchronized stores across all connected clients
|
|
33
|
+
- **Dynamic Stores** - Room-based state isolation for teams, chat, breakout rooms
|
|
34
|
+
- **AI Services** - Text and image generation
|
|
35
|
+
- **Storage** - File uploads to CDN
|
|
36
|
+
- **i18n** - Internationalization with AI-powered translation
|
|
37
|
+
- **Leaderboards** - Player rankings and scores
|
|
38
|
+
|
|
39
|
+
## Documentation
|
|
40
|
+
|
|
41
|
+
See the [docs](./docs/) folder for detailed instructions:
|
|
42
|
+
|
|
43
|
+
| File | Description |
|
|
44
|
+
| ----------------------------------------------------------------------------------------- | --------------------------------------------- |
|
|
45
|
+
| [kokimoki-sdk.instructions.md](./docs/kokimoki-sdk.instructions.md) | Core SDK usage (client, stores, transactions) |
|
|
46
|
+
| [kokimoki-dynamic-stores.instructions.md](./docs/kokimoki-dynamic-stores.instructions.md) | Room-based state isolation |
|
|
47
|
+
| [kokimoki-ai.instructions.md](./docs/kokimoki-ai.instructions.md) | AI text and image generation |
|
|
48
|
+
| [kokimoki-storage.instructions.md](./docs/kokimoki-storage.instructions.md) | File storage and CDN uploads |
|
|
49
|
+
| [kokimoki-i18n.instructions.md](./docs/kokimoki-i18n.instructions.md) | Internationalization |
|
|
50
|
+
| [kokimoki-leaderboard.instructions.md](./docs/kokimoki-leaderboard.instructions.md) | Player rankings |
|
|
51
|
+
|
|
52
|
+
## Usage with React
|
|
53
|
+
|
|
54
|
+
```tsx
|
|
55
|
+
import { useSnapshot } from "valtio";
|
|
56
|
+
import { getKmClient } from "@kokimoki/app";
|
|
57
|
+
|
|
58
|
+
const kmClient = getKmClient();
|
|
59
|
+
const gameStore = kmClient.store("game", { count: 0 });
|
|
60
|
+
|
|
61
|
+
function Counter() {
|
|
62
|
+
const { count } = useSnapshot(gameStore.proxy);
|
|
63
|
+
|
|
64
|
+
const increment = () => {
|
|
65
|
+
kmClient.transact([gameStore], ([state]) => {
|
|
66
|
+
state.count++;
|
|
67
|
+
});
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
return <button onClick={increment}>Count: {count}</button>;
|
|
71
|
+
}
|
|
72
|
+
```
|
|
@@ -1,14 +1,39 @@
|
|
|
1
1
|
import type TypedEmitter from "typed-emitter";
|
|
2
|
-
import { KokimokiAiService, KokimokiLeaderboardService, KokimokiStorageService } from "../services";
|
|
2
|
+
import { KokimokiAiService, KokimokiI18nService, KokimokiLeaderboardService, KokimokiStorageService } from "../services";
|
|
3
3
|
import { KokimokiLocalStore, KokimokiStore } from "../stores";
|
|
4
|
-
import type { KokimokiClientEvents } from "../types";
|
|
4
|
+
import type { KokimokiClientEvents, KokimokiEnv } from "../types";
|
|
5
|
+
/**
|
|
6
|
+
* Reserved store name for the App Meta store.
|
|
7
|
+
* Stores with the `__km/` prefix are reserved for SDK internal use.
|
|
8
|
+
*/
|
|
9
|
+
export declare const APP_META_STORE_NAME = "__km/app-meta";
|
|
10
|
+
/**
|
|
11
|
+
* State type for the App Meta store.
|
|
12
|
+
* Used by the server to inject meta tags into the HTML response.
|
|
13
|
+
*
|
|
14
|
+
* All fields are optional - missing fields will fall back to defaults in index.html.
|
|
15
|
+
*/
|
|
16
|
+
export interface AppMetaState {
|
|
17
|
+
/** HTML lang attribute (e.g., 'en', 'et', 'de') */
|
|
18
|
+
lang?: string;
|
|
19
|
+
/** Document title (browser tab) */
|
|
20
|
+
title?: string;
|
|
21
|
+
/** Meta description */
|
|
22
|
+
description?: string;
|
|
23
|
+
/** Open Graph title (defaults to title if not set) */
|
|
24
|
+
ogTitle?: string;
|
|
25
|
+
/** Open Graph description (defaults to description if not set) */
|
|
26
|
+
ogDescription?: string;
|
|
27
|
+
/** Open Graph image URL */
|
|
28
|
+
ogImage?: string;
|
|
29
|
+
/** Favicon URL */
|
|
30
|
+
favicon?: string;
|
|
31
|
+
}
|
|
5
32
|
type Mutable<T> = {
|
|
6
33
|
-readonly [K in keyof T]: T[K] extends object ? Mutable<T[K]> : T[K];
|
|
7
34
|
};
|
|
8
35
|
type StoreValue<S> = S extends KokimokiStore<infer U> ? Mutable<U> : never;
|
|
9
|
-
declare const KokimokiClient_base:
|
|
10
|
-
new (): TypedEmitter<KokimokiClientEvents>;
|
|
11
|
-
};
|
|
36
|
+
declare const KokimokiClient_base: new () => TypedEmitter<KokimokiClientEvents>;
|
|
12
37
|
/**
|
|
13
38
|
* Kokimoki Client - Real-time Collaborative Game Development SDK
|
|
14
39
|
*
|
|
@@ -30,12 +55,8 @@ declare const KokimokiClient_base: {
|
|
|
30
55
|
* ```typescript
|
|
31
56
|
* import { KokimokiClient } from '@kokimoki/app';
|
|
32
57
|
*
|
|
33
|
-
* // Initialize the client
|
|
34
|
-
* const kmClient = new KokimokiClient(
|
|
35
|
-
* 'your-host.kokimoki.com',
|
|
36
|
-
* 'your-app-id',
|
|
37
|
-
* 'optional-access-code'
|
|
38
|
-
* );
|
|
58
|
+
* // Initialize the client (uses environment config from @kokimoki/kit)
|
|
59
|
+
* const kmClient = new KokimokiClient();
|
|
39
60
|
*
|
|
40
61
|
* // Connect to the server
|
|
41
62
|
* await kmClient.connect();
|
|
@@ -163,9 +184,6 @@ declare const KokimokiClient_base: {
|
|
|
163
184
|
* ```
|
|
164
185
|
*/
|
|
165
186
|
export declare class KokimokiClient<ClientContextT = any> extends KokimokiClient_base {
|
|
166
|
-
readonly host: string;
|
|
167
|
-
readonly appId: string;
|
|
168
|
-
readonly code: string;
|
|
169
187
|
private _wsUrl;
|
|
170
188
|
private _apiUrl;
|
|
171
189
|
private _id?;
|
|
@@ -189,9 +207,55 @@ export declare class KokimokiClient<ClientContextT = any> extends KokimokiClient
|
|
|
189
207
|
private _clientTokenKey;
|
|
190
208
|
private _editorContext;
|
|
191
209
|
private _ai?;
|
|
210
|
+
private _i18n?;
|
|
192
211
|
private _storage?;
|
|
193
212
|
private _leaderboard?;
|
|
194
|
-
|
|
213
|
+
private _metaStore?;
|
|
214
|
+
/**
|
|
215
|
+
* Dev mode config - set when running in dev frame with ?key= param
|
|
216
|
+
*/
|
|
217
|
+
private _devModeConfig?;
|
|
218
|
+
/**
|
|
219
|
+
* Environment configuration
|
|
220
|
+
*/
|
|
221
|
+
private _env;
|
|
222
|
+
/**
|
|
223
|
+
* The WebSocket server host.
|
|
224
|
+
*/
|
|
225
|
+
readonly host: string;
|
|
226
|
+
/**
|
|
227
|
+
* The application identifier.
|
|
228
|
+
*/
|
|
229
|
+
readonly appId: string;
|
|
230
|
+
/**
|
|
231
|
+
* The deploy code slug.
|
|
232
|
+
*/
|
|
233
|
+
readonly code: string;
|
|
234
|
+
/**
|
|
235
|
+
* Built-in App Meta store for managing HTML meta tags.
|
|
236
|
+
*
|
|
237
|
+
* This store is automatically joined when the client connects.
|
|
238
|
+
* The server uses this store to inject meta tags into the HTML response
|
|
239
|
+
* for proper SEO and social sharing previews.
|
|
240
|
+
*
|
|
241
|
+
* @example
|
|
242
|
+
* ```typescript
|
|
243
|
+
* import { useSnapshot } from 'valtio';
|
|
244
|
+
*
|
|
245
|
+
* const kmClient = getKmClient();
|
|
246
|
+
*
|
|
247
|
+
* // Read meta state
|
|
248
|
+
* const { title, description } = useSnapshot(kmClient.metaStore.proxy);
|
|
249
|
+
*
|
|
250
|
+
* // Update meta via transaction
|
|
251
|
+
* await kmClient.transact([kmClient.metaStore], ([meta]) => {
|
|
252
|
+
* meta.title = 'My Game';
|
|
253
|
+
* meta.description = 'An awesome multiplayer game';
|
|
254
|
+
* });
|
|
255
|
+
* ```
|
|
256
|
+
*/
|
|
257
|
+
get metaStore(): KokimokiStore<AppMetaState>;
|
|
258
|
+
constructor(env?: KokimokiEnv);
|
|
195
259
|
get id(): string;
|
|
196
260
|
get connectionId(): string;
|
|
197
261
|
get token(): string;
|
|
@@ -207,6 +271,32 @@ export declare class KokimokiClient<ClientContextT = any> extends KokimokiClient
|
|
|
207
271
|
* Indicates whether the client is running in editor/development mode.
|
|
208
272
|
*/
|
|
209
273
|
get isEditor(): boolean;
|
|
274
|
+
/**
|
|
275
|
+
* Generates a link for joining the app via URL or QR code.
|
|
276
|
+
*
|
|
277
|
+
* In dev mode, the link includes the code and context as URL parameters
|
|
278
|
+
* for local testing. In production, it generates a clean path-based URL.
|
|
279
|
+
*
|
|
280
|
+
* @param code - The join code (e.g., playerCode or presenterCode from clientContext)
|
|
281
|
+
* @param context - The client context to embed in the link (used in dev mode only)
|
|
282
|
+
* @returns The generated URL string
|
|
283
|
+
*
|
|
284
|
+
* @example
|
|
285
|
+
* ```typescript
|
|
286
|
+
* // Generate a player join link
|
|
287
|
+
* const playerLink = kmClient.generateLink(
|
|
288
|
+
* kmClient.clientContext.playerCode,
|
|
289
|
+
* { mode: 'player' }
|
|
290
|
+
* );
|
|
291
|
+
*
|
|
292
|
+
* // Generate a presenter link with player code
|
|
293
|
+
* const presenterLink = kmClient.generateLink(
|
|
294
|
+
* kmClient.clientContext.presenterCode,
|
|
295
|
+
* { mode: 'presenter', playerCode: kmClient.clientContext.playerCode }
|
|
296
|
+
* );
|
|
297
|
+
* ```
|
|
298
|
+
*/
|
|
299
|
+
generateLink(code: string, context: object): string;
|
|
210
300
|
/**
|
|
211
301
|
* Establishes a connection to the Kokimoki server.
|
|
212
302
|
*
|
|
@@ -288,6 +378,24 @@ export declare class KokimokiClient<ClientContextT = any> extends KokimokiClient
|
|
|
288
378
|
* Disables automatic reconnection, closes the WebSocket, and clears all intervals.
|
|
289
379
|
*/
|
|
290
380
|
close(): Promise<void>;
|
|
381
|
+
/**
|
|
382
|
+
* Waits for all subscriptions to be fully joined and notifies the loading screen.
|
|
383
|
+
*
|
|
384
|
+
* This should be called before rendering the app. It will:
|
|
385
|
+
* 1. Wait for all stores to complete initial synchronization
|
|
386
|
+
* 2. Post a 'km:ready' message to trigger loading screen fade
|
|
387
|
+
* 3. Post a 'km:ready' message to the parent window (for dev frame coordination)
|
|
388
|
+
*
|
|
389
|
+
* @param timeout - Maximum time to wait for subscriptions in milliseconds (default: 5000ms).
|
|
390
|
+
* @returns A promise that resolves when ready to render.
|
|
391
|
+
*
|
|
392
|
+
* @example
|
|
393
|
+
* ```ts
|
|
394
|
+
* await kmClient.waitForReady();
|
|
395
|
+
* renderApp(<App />);
|
|
396
|
+
* ```
|
|
397
|
+
*/
|
|
398
|
+
waitForReady(timeout?: number): Promise<void>;
|
|
291
399
|
/**
|
|
292
400
|
* Gets the internal room hash identifier for a store.
|
|
293
401
|
*
|
|
@@ -349,6 +457,10 @@ export declare class KokimokiClient<ClientContextT = any> extends KokimokiClient
|
|
|
349
457
|
* Access AI capabilities including text generation, structured JSON output, and image modification.
|
|
350
458
|
*/
|
|
351
459
|
get ai(): KokimokiAiService;
|
|
460
|
+
/**
|
|
461
|
+
* Access i18n URL resolution and translation loading utilities.
|
|
462
|
+
*/
|
|
463
|
+
get i18n(): KokimokiI18nService;
|
|
352
464
|
/**
|
|
353
465
|
* Access file upload and management for media files, images, and user-generated content.
|
|
354
466
|
*/
|
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
import EventEmitter from "events";
|
|
2
2
|
import * as Y from "yjs";
|
|
3
3
|
import { WsMessageReader, WsMessageType, WsMessageWriter, } from "../protocol/ws-message";
|
|
4
|
-
import { KokimokiAiService, KokimokiLeaderboardService, KokimokiStorageService, } from "../services";
|
|
4
|
+
import { KokimokiAiService, KokimokiI18nService, KokimokiLeaderboardService, KokimokiStorageService, } from "../services";
|
|
5
5
|
import { KokimokiLocalStore, KokimokiStore, KokimokiTransaction, } from "../stores";
|
|
6
|
+
import { initDevMode } from "../utils/kokimoki-dev";
|
|
7
|
+
import { getKmEnv } from "../utils/kokimoki-env";
|
|
6
8
|
import { KOKIMOKI_APP_VERSION } from "../version";
|
|
7
9
|
import { RoomSubscription } from "./room-subscription";
|
|
10
|
+
/**
|
|
11
|
+
* Reserved store name for the App Meta store.
|
|
12
|
+
* Stores with the `__km/` prefix are reserved for SDK internal use.
|
|
13
|
+
*/
|
|
14
|
+
export const APP_META_STORE_NAME = "__km/app-meta";
|
|
8
15
|
/**
|
|
9
16
|
* Kokimoki Client - Real-time Collaborative Game Development SDK
|
|
10
17
|
*
|
|
@@ -26,12 +33,8 @@ import { RoomSubscription } from "./room-subscription";
|
|
|
26
33
|
* ```typescript
|
|
27
34
|
* import { KokimokiClient } from '@kokimoki/app';
|
|
28
35
|
*
|
|
29
|
-
* // Initialize the client
|
|
30
|
-
* const kmClient = new KokimokiClient(
|
|
31
|
-
* 'your-host.kokimoki.com',
|
|
32
|
-
* 'your-app-id',
|
|
33
|
-
* 'optional-access-code'
|
|
34
|
-
* );
|
|
36
|
+
* // Initialize the client (uses environment config from @kokimoki/kit)
|
|
37
|
+
* const kmClient = new KokimokiClient();
|
|
35
38
|
*
|
|
36
39
|
* // Connect to the server
|
|
37
40
|
* await kmClient.connect();
|
|
@@ -159,9 +162,6 @@ import { RoomSubscription } from "./room-subscription";
|
|
|
159
162
|
* ```
|
|
160
163
|
*/
|
|
161
164
|
export class KokimokiClient extends EventEmitter {
|
|
162
|
-
host;
|
|
163
|
-
appId;
|
|
164
|
-
code;
|
|
165
165
|
_wsUrl;
|
|
166
166
|
_apiUrl;
|
|
167
167
|
_id;
|
|
@@ -185,19 +185,79 @@ export class KokimokiClient extends EventEmitter {
|
|
|
185
185
|
_clientTokenKey = "KM_TOKEN";
|
|
186
186
|
_editorContext;
|
|
187
187
|
_ai;
|
|
188
|
+
_i18n;
|
|
188
189
|
_storage;
|
|
189
190
|
_leaderboard;
|
|
190
|
-
|
|
191
|
+
_metaStore;
|
|
192
|
+
/**
|
|
193
|
+
* Dev mode config - set when running in dev frame with ?key= param
|
|
194
|
+
*/
|
|
195
|
+
_devModeConfig;
|
|
196
|
+
/**
|
|
197
|
+
* Environment configuration
|
|
198
|
+
*/
|
|
199
|
+
_env;
|
|
200
|
+
/**
|
|
201
|
+
* The WebSocket server host.
|
|
202
|
+
*/
|
|
203
|
+
host;
|
|
204
|
+
/**
|
|
205
|
+
* The application identifier.
|
|
206
|
+
*/
|
|
207
|
+
appId;
|
|
208
|
+
/**
|
|
209
|
+
* The deploy code slug.
|
|
210
|
+
*/
|
|
211
|
+
code;
|
|
212
|
+
/**
|
|
213
|
+
* Built-in App Meta store for managing HTML meta tags.
|
|
214
|
+
*
|
|
215
|
+
* This store is automatically joined when the client connects.
|
|
216
|
+
* The server uses this store to inject meta tags into the HTML response
|
|
217
|
+
* for proper SEO and social sharing previews.
|
|
218
|
+
*
|
|
219
|
+
* @example
|
|
220
|
+
* ```typescript
|
|
221
|
+
* import { useSnapshot } from 'valtio';
|
|
222
|
+
*
|
|
223
|
+
* const kmClient = getKmClient();
|
|
224
|
+
*
|
|
225
|
+
* // Read meta state
|
|
226
|
+
* const { title, description } = useSnapshot(kmClient.metaStore.proxy);
|
|
227
|
+
*
|
|
228
|
+
* // Update meta via transaction
|
|
229
|
+
* await kmClient.transact([kmClient.metaStore], ([meta]) => {
|
|
230
|
+
* meta.title = 'My Game';
|
|
231
|
+
* meta.description = 'An awesome multiplayer game';
|
|
232
|
+
* });
|
|
233
|
+
* ```
|
|
234
|
+
*/
|
|
235
|
+
get metaStore() {
|
|
236
|
+
if (!this._metaStore) {
|
|
237
|
+
throw new Error("Client not connected");
|
|
238
|
+
}
|
|
239
|
+
return this._metaStore;
|
|
240
|
+
}
|
|
241
|
+
constructor(env = getKmEnv()) {
|
|
191
242
|
super();
|
|
192
|
-
this.
|
|
193
|
-
this.
|
|
194
|
-
this.
|
|
243
|
+
this._env = env;
|
|
244
|
+
this.host = env.host;
|
|
245
|
+
this.appId = env.appId;
|
|
246
|
+
this.code = env.code ?? "";
|
|
247
|
+
// Initialize dev mode from URL params if running in dev environment
|
|
248
|
+
if (env.dev) {
|
|
249
|
+
this._devModeConfig = initDevMode();
|
|
250
|
+
if (this._devModeConfig) {
|
|
251
|
+
this._clientTokenKey = this._devModeConfig.tokenKey;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
195
254
|
// Set up the URLs
|
|
196
255
|
const secure = this.host.indexOf(":") === -1;
|
|
197
256
|
this._wsUrl = `ws${secure ? "s" : ""}://${this.host}`;
|
|
198
257
|
this._apiUrl = `http${secure ? "s" : ""}://${this.host}`;
|
|
199
258
|
// Initialize modules
|
|
200
259
|
this._ai = new KokimokiAiService(this);
|
|
260
|
+
this._i18n = new KokimokiI18nService(this);
|
|
201
261
|
this._storage = new KokimokiStorageService(this);
|
|
202
262
|
this._leaderboard = new KokimokiLeaderboardService(this);
|
|
203
263
|
// Set up ping interval
|
|
@@ -278,6 +338,37 @@ export class KokimokiClient extends EventEmitter {
|
|
|
278
338
|
get isEditor() {
|
|
279
339
|
return !!this._editorContext;
|
|
280
340
|
}
|
|
341
|
+
/**
|
|
342
|
+
* Generates a link for joining the app via URL or QR code.
|
|
343
|
+
*
|
|
344
|
+
* In dev mode, the link includes the code and context as URL parameters
|
|
345
|
+
* for local testing. In production, it generates a clean path-based URL.
|
|
346
|
+
*
|
|
347
|
+
* @param code - The join code (e.g., playerCode or presenterCode from clientContext)
|
|
348
|
+
* @param context - The client context to embed in the link (used in dev mode only)
|
|
349
|
+
* @returns The generated URL string
|
|
350
|
+
*
|
|
351
|
+
* @example
|
|
352
|
+
* ```typescript
|
|
353
|
+
* // Generate a player join link
|
|
354
|
+
* const playerLink = kmClient.generateLink(
|
|
355
|
+
* kmClient.clientContext.playerCode,
|
|
356
|
+
* { mode: 'player' }
|
|
357
|
+
* );
|
|
358
|
+
*
|
|
359
|
+
* // Generate a presenter link with player code
|
|
360
|
+
* const presenterLink = kmClient.generateLink(
|
|
361
|
+
* kmClient.clientContext.presenterCode,
|
|
362
|
+
* { mode: 'presenter', playerCode: kmClient.clientContext.playerCode }
|
|
363
|
+
* );
|
|
364
|
+
* ```
|
|
365
|
+
*/
|
|
366
|
+
generateLink(code, context) {
|
|
367
|
+
if (this._env.dev) {
|
|
368
|
+
return `${window.location.origin}?key=${code}&context=${btoa(JSON.stringify(context))}`;
|
|
369
|
+
}
|
|
370
|
+
return `${window.location.origin}/${code}`;
|
|
371
|
+
}
|
|
281
372
|
/**
|
|
282
373
|
* Establishes a connection to the Kokimoki server.
|
|
283
374
|
*
|
|
@@ -291,14 +382,11 @@ export class KokimokiClient extends EventEmitter {
|
|
|
291
382
|
if (this._connectPromise) {
|
|
292
383
|
return await this._connectPromise;
|
|
293
384
|
}
|
|
294
|
-
// Detect devtools
|
|
295
|
-
if (
|
|
385
|
+
// Detect devtools (editor)
|
|
386
|
+
if (!this._devModeConfig &&
|
|
387
|
+
window.parent &&
|
|
388
|
+
window.self !== window.parent) {
|
|
296
389
|
await new Promise((resolve) => {
|
|
297
|
-
/* // Wait up to 500ms for parent to respond
|
|
298
|
-
const timeout = setTimeout(() => {
|
|
299
|
-
window.removeEventListener("message", onMessage);
|
|
300
|
-
resolve();
|
|
301
|
-
}, 500); */
|
|
302
390
|
// Listen for parent response
|
|
303
391
|
const onMessage = (e) => {
|
|
304
392
|
// clearTimeout(timeout);
|
|
@@ -390,6 +478,8 @@ export class KokimokiClient extends EventEmitter {
|
|
|
390
478
|
console.error(`[Kokimoki] Failed to restore subscription for "${subscription.roomName}":`, err);
|
|
391
479
|
}
|
|
392
480
|
}
|
|
481
|
+
// Auto-join the built-in meta store with defaults from config
|
|
482
|
+
this._metaStore = this.store(APP_META_STORE_NAME, this._env.defaultAppMeta ?? {});
|
|
393
483
|
// Emit connected event
|
|
394
484
|
this._reconnectTimeout = 0;
|
|
395
485
|
this.emit("connected");
|
|
@@ -399,7 +489,20 @@ export class KokimokiClient extends EventEmitter {
|
|
|
399
489
|
this._id = message.clientId;
|
|
400
490
|
this._connectionId = message.id;
|
|
401
491
|
this._token = message.appToken;
|
|
402
|
-
|
|
492
|
+
// Context priority: dev mode URL params > test mode env > server context
|
|
493
|
+
if (this._devModeConfig?.context !== undefined) {
|
|
494
|
+
this._clientContext = this._devModeConfig.context;
|
|
495
|
+
}
|
|
496
|
+
else if (this._env.test && this._env.clientContext) {
|
|
497
|
+
this._clientContext = JSON.parse(this._env.clientContext);
|
|
498
|
+
}
|
|
499
|
+
else {
|
|
500
|
+
this._clientContext = message.clientContext;
|
|
501
|
+
}
|
|
502
|
+
// Notify parent window in test mode (editor)
|
|
503
|
+
if (this._env.test) {
|
|
504
|
+
setTimeout(() => window.postMessage({ clientKey: "test" }, "*"));
|
|
505
|
+
}
|
|
403
506
|
// Set up the auth headers
|
|
404
507
|
this._apiHeaders = new Headers({
|
|
405
508
|
Authorization: `Bearer ${this.token}`,
|
|
@@ -707,6 +810,46 @@ export class KokimokiClient extends EventEmitter {
|
|
|
707
810
|
clearInterval(this._pingInterval);
|
|
708
811
|
}
|
|
709
812
|
}
|
|
813
|
+
/**
|
|
814
|
+
* Waits for all subscriptions to be fully joined and notifies the loading screen.
|
|
815
|
+
*
|
|
816
|
+
* This should be called before rendering the app. It will:
|
|
817
|
+
* 1. Wait for all stores to complete initial synchronization
|
|
818
|
+
* 2. Post a 'km:ready' message to trigger loading screen fade
|
|
819
|
+
* 3. Post a 'km:ready' message to the parent window (for dev frame coordination)
|
|
820
|
+
*
|
|
821
|
+
* @param timeout - Maximum time to wait for subscriptions in milliseconds (default: 5000ms).
|
|
822
|
+
* @returns A promise that resolves when ready to render.
|
|
823
|
+
*
|
|
824
|
+
* @example
|
|
825
|
+
* ```ts
|
|
826
|
+
* await kmClient.waitForReady();
|
|
827
|
+
* renderApp(<App />);
|
|
828
|
+
* ```
|
|
829
|
+
*/
|
|
830
|
+
async waitForReady(timeout = 5000) {
|
|
831
|
+
const checkInterval = 10;
|
|
832
|
+
const maxChecks = Math.ceil(timeout / checkInterval);
|
|
833
|
+
await new Promise((resolve) => {
|
|
834
|
+
let checks = 0;
|
|
835
|
+
const intervalId = setInterval(() => {
|
|
836
|
+
let allJoined = true;
|
|
837
|
+
for (const subscription of this._subscriptionsByName.values()) {
|
|
838
|
+
if (!subscription.joined) {
|
|
839
|
+
allJoined = false;
|
|
840
|
+
break;
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
if (allJoined || ++checks >= maxChecks) {
|
|
844
|
+
clearInterval(intervalId);
|
|
845
|
+
resolve();
|
|
846
|
+
}
|
|
847
|
+
}, checkInterval);
|
|
848
|
+
});
|
|
849
|
+
// Post to both parent (for dev frame) and self (for loading screen)
|
|
850
|
+
window.parent.postMessage("km:ready", "*");
|
|
851
|
+
window.postMessage("km:ready", "*");
|
|
852
|
+
}
|
|
710
853
|
/**
|
|
711
854
|
* Gets the internal room hash identifier for a store.
|
|
712
855
|
*
|
|
@@ -798,6 +941,15 @@ export class KokimokiClient extends EventEmitter {
|
|
|
798
941
|
}
|
|
799
942
|
return this._ai;
|
|
800
943
|
}
|
|
944
|
+
/**
|
|
945
|
+
* Access i18n URL resolution and translation loading utilities.
|
|
946
|
+
*/
|
|
947
|
+
get i18n() {
|
|
948
|
+
if (!this._i18n) {
|
|
949
|
+
throw new Error("I18n client not initialized");
|
|
950
|
+
}
|
|
951
|
+
return this._i18n;
|
|
952
|
+
}
|
|
801
953
|
/**
|
|
802
954
|
* Access file upload and management for media files, images, and user-generated content.
|
|
803
955
|
*/
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
export
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
RoomSubscriptionMode["Write"] = "w";
|
|
5
|
-
RoomSubscriptionMode["ReadWrite"] = "b";
|
|
6
|
-
})(RoomSubscriptionMode || (RoomSubscriptionMode = {}));
|
|
1
|
+
// Re-export from stores to maintain backwards compatibility
|
|
2
|
+
// The enum is defined in kokimoki-store.ts to avoid circular dependencies
|
|
3
|
+
export { RoomSubscriptionMode } from "../stores/kokimoki-store";
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
export { KokimokiClient } from "./core";
|
|
2
|
+
export { APP_META_STORE_NAME } from "./core/kokimoki-client";
|
|
3
|
+
export type { AppMetaState } from "./core/kokimoki-client";
|
|
2
4
|
export { KokimokiStore } from "./stores";
|
|
3
|
-
export type { KokimokiClientEvents, Paginated, Upload } from "./types";
|
|
5
|
+
export type { KokimokiClientEvents, KokimokiEnv, Paginated, PollOptions, Upload, } from "./types";
|
|
6
|
+
export type { AllLanguagesStatus, I18nOptions, LanguageStatus, NamespaceStatus, RequestTranslationResult, TranslationStatus, } from "./services/kokimoki-i18n";
|
|
7
|
+
export type { AiJob, AiJobStatus, AiJobType } from "./services/kokimoki-ai";
|
|
8
|
+
export { getKmClient } from "./utils/kokimoki-client";
|
|
9
|
+
export { getKmEnv } from "./utils/kokimoki-env";
|
|
4
10
|
export * from "./utils/valtio";
|
|
11
|
+
export { z, type ZodType } from "zod/v4";
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
export { KokimokiClient } from "./core";
|
|
2
|
+
export { APP_META_STORE_NAME } from "./core/kokimoki-client";
|
|
2
3
|
export { KokimokiStore } from "./stores";
|
|
4
|
+
export { getKmClient } from "./utils/kokimoki-client";
|
|
5
|
+
export { getKmEnv } from "./utils/kokimoki-env";
|
|
3
6
|
// Re-export Valtio utilities
|
|
4
7
|
export * from "./utils/valtio";
|
|
8
|
+
// Re-export Zod for schema definitions
|
|
9
|
+
export { z } from "zod/v4";
|