@kokimoki/app 2.0.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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 +54 -58
  12. package/dist/kokimoki.min.js +3080 -1794
  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,208 @@
1
+ /**
2
+ * Kokimoki Storage Service
3
+ *
4
+ * Provides file upload and management capabilities for game applications. Ideal for media files
5
+ * (images, videos, audio) and other data not suitable for real-time stores (JSON, text files).
6
+ *
7
+ * **Key Features:**
8
+ * - Upload files to cloud storage with CDN delivery
9
+ * - Tag-based organization and filtering
10
+ * - Query uploads by client, MIME type, or tags
11
+ * - Pagination support for large collections
12
+ * - Public CDN URLs for direct access
13
+ *
14
+ * **Common Use Cases:**
15
+ * - Player avatars and profile images
16
+ * - Game screenshots and replays
17
+ * - User-generated content
18
+ * - Asset uploads (levels, maps, custom skins)
19
+ * - JSON configuration files
20
+ *
21
+ * Access via `kmClient.storage`
22
+ *
23
+ * @example
24
+ * ```typescript
25
+ * // Upload an image
26
+ * const upload = await kmClient.storage.upload('avatar.jpg', imageBlob, ['profile']);
27
+ *
28
+ * // Query user's uploads
29
+ * const myUploads = await kmClient.storage.listUploads({
30
+ * clientId: kmClient.id
31
+ * });
32
+ *
33
+ * // Use in store
34
+ * await kmClient.transact([store], (state) => {
35
+ * state.playerAvatar = upload.url;
36
+ * });
37
+ * ```
38
+ */
39
+ export class KokimokiStorageService {
40
+ client;
41
+ constructor(client) {
42
+ this.client = client;
43
+ }
44
+ // Storage
45
+ async createUpload(name, blob, tags) {
46
+ const res = await fetch(`${this.client.apiUrl}/uploads`, {
47
+ method: "POST",
48
+ headers: this.client.apiHeaders,
49
+ body: JSON.stringify({
50
+ name,
51
+ size: blob.size,
52
+ mimeType: blob.type,
53
+ tags,
54
+ }),
55
+ });
56
+ return await res.json();
57
+ }
58
+ async uploadChunks(blob, chunkSize, signedUrls) {
59
+ return await Promise.all(signedUrls.map(async (url, index) => {
60
+ const start = index * chunkSize;
61
+ const end = Math.min(start + chunkSize, blob.size);
62
+ const chunk = blob.slice(start, end);
63
+ const res = await fetch(url, { method: "PUT", body: chunk });
64
+ return JSON.parse(res.headers.get("ETag") || '""');
65
+ }));
66
+ }
67
+ async completeUpload(id, etags) {
68
+ const res = await fetch(`${this.client.apiUrl}/uploads/${id}`, {
69
+ method: "PUT",
70
+ headers: this.client.apiHeaders,
71
+ body: JSON.stringify({ etags }),
72
+ });
73
+ return await res.json();
74
+ }
75
+ /**
76
+ * Upload a file to cloud storage.
77
+ *
78
+ * Uploads a file (Blob) to Kokimoki storage and returns an Upload object with a public CDN URL.
79
+ * Files are automatically chunked for efficient upload. The returned URL can be used directly
80
+ * in your application (e.g., in img tags or store state).
81
+ *
82
+ * @param name The filename for the upload
83
+ * @param blob The Blob object containing the file data
84
+ * @param tags Optional array of tags for organizing and filtering uploads (default: [])
85
+ * @returns A promise resolving to the Upload object with CDN URL and metadata
86
+ *
87
+ * @example
88
+ * ```typescript
89
+ * // Upload image with tags
90
+ * const upload = await kmClient.storage.upload(
91
+ * 'avatar.jpg',
92
+ * imageBlob,
93
+ * ['profile', 'avatar']
94
+ * );
95
+ *
96
+ * // Use the CDN URL
97
+ * console.log(upload.url); // https://cdn.kokimoki.com/...
98
+ *
99
+ * // Store in game state
100
+ * await kmClient.transact([store], (state) => {
101
+ * state.players[kmClient.id].avatar = upload.url;
102
+ * });
103
+ * ```
104
+ */
105
+ async upload(name, blob, tags = []) {
106
+ const { id, chunkSize, urls } = await this.createUpload(name, blob, tags);
107
+ const etags = await this.uploadChunks(blob, chunkSize, urls);
108
+ return await this.completeUpload(id, etags);
109
+ }
110
+ /**
111
+ * Update metadata for an existing upload.
112
+ *
113
+ * Allows you to replace the tags associated with an upload. Useful for reorganizing
114
+ * or recategorizing uploaded files.
115
+ *
116
+ * @param id The upload ID to update
117
+ * @param update Object containing the new tags array
118
+ * @returns A promise resolving to the updated Upload object
119
+ *
120
+ * @example
121
+ * ```typescript
122
+ * // Update tags
123
+ * const updated = await kmClient.storage.updateUpload(upload.id, {
124
+ * tags: ['archived', 'old-profile']
125
+ * });
126
+ * ```
127
+ */
128
+ async updateUpload(id, update) {
129
+ const res = await fetch(`${this.client.apiUrl}/uploads/${id}`, {
130
+ method: "PUT",
131
+ headers: this.client.apiHeaders,
132
+ body: JSON.stringify(update),
133
+ });
134
+ return await res.json();
135
+ }
136
+ /**
137
+ * Query uploaded files with filtering and pagination.
138
+ *
139
+ * Retrieves a list of uploads matching the specified criteria. Supports filtering by
140
+ * client (uploader), MIME types, and tags. Use pagination for large collections.
141
+ *
142
+ * @param filter Optional filter criteria
143
+ * @param filter.clientId Filter by uploader's client ID
144
+ * @param filter.mimeTypes Filter by MIME types (e.g., ['image/jpeg', 'image/png'])
145
+ * @param filter.tags Filter by tags (all specified tags must match)
146
+ * @param skip Number of results to skip for pagination (default: 0)
147
+ * @param limit Maximum number of results to return (default: 100)
148
+ * @returns A promise resolving to paginated Upload results with total count
149
+ *
150
+ * @example
151
+ * ```typescript
152
+ * // Get current user's images
153
+ * const myImages = await kmClient.storage.listUploads({
154
+ * clientId: kmClient.id,
155
+ * mimeTypes: ['image/jpeg', 'image/png']
156
+ * });
157
+ *
158
+ * // Get all profile avatars
159
+ * const avatars = await kmClient.storage.listUploads({
160
+ * tags: ['profile', 'avatar']
161
+ * });
162
+ *
163
+ * // Pagination
164
+ * const page2 = await kmClient.storage.listUploads({}, 10, 10);
165
+ * ```
166
+ */
167
+ async listUploads(filter = {}, skip = 0, limit = 100) {
168
+ const url = new URL("/uploads", this.client.apiUrl);
169
+ url.searchParams.set("skip", skip.toString());
170
+ url.searchParams.set("limit", limit.toString());
171
+ if (filter.clientId) {
172
+ url.searchParams.set("clientId", filter.clientId);
173
+ }
174
+ if (filter.mimeTypes) {
175
+ url.searchParams.set("mimeTypes", filter.mimeTypes.join());
176
+ }
177
+ if (filter.tags) {
178
+ url.searchParams.set("tags", filter.tags.join());
179
+ }
180
+ const res = await fetch(url.href, {
181
+ headers: this.client.apiHeaders,
182
+ });
183
+ return await res.json();
184
+ }
185
+ /**
186
+ * Permanently delete an uploaded file.
187
+ *
188
+ * Removes the file from cloud storage and the CDN. The URL will no longer be accessible.
189
+ * This operation cannot be undone.
190
+ *
191
+ * @param id The upload ID to delete
192
+ * @returns A promise resolving to deletion confirmation
193
+ *
194
+ * @example
195
+ * ```typescript
196
+ * // Delete an upload
197
+ * const result = await kmClient.storage.deleteUpload(upload.id);
198
+ * console.log(`Deleted: ${result.deletedCount} file(s)`);
199
+ * ```
200
+ */
201
+ async deleteUpload(id) {
202
+ const res = await fetch(`${this.client.apiUrl}/uploads/${id}`, {
203
+ method: "DELETE",
204
+ headers: this.client.apiHeaders,
205
+ });
206
+ return await res.json();
207
+ }
208
+ }
@@ -0,0 +1,3 @@
1
+ export * from "./kokimoki-local-store";
2
+ export * from "./kokimoki-store";
3
+ export * from "./kokimoki-transaction";
@@ -0,0 +1,3 @@
1
+ export * from "./kokimoki-local-store";
2
+ export * from "./kokimoki-store";
3
+ export * from "./kokimoki-transaction";
@@ -0,0 +1,11 @@
1
+ import { KokimokiStore } from "./kokimoki-store";
2
+ export declare class KokimokiLocalStore<T extends object> extends KokimokiStore<T> {
3
+ private readonly localRoomName;
4
+ private _stateKey?;
5
+ private get stateKey();
6
+ constructor(localRoomName: string, defaultState: T);
7
+ getInitialUpdate(appId: string, clientId: string): {
8
+ roomHash: number;
9
+ initialUpdate: Uint8Array<ArrayBufferLike> | undefined;
10
+ };
11
+ }
@@ -0,0 +1,40 @@
1
+ import { fingerprint32 } from "farmhash-modern";
2
+ import * as Y from "yjs";
3
+ import { RoomSubscriptionMode } from "../core";
4
+ import { KokimokiStore } from "./kokimoki-store";
5
+ export class KokimokiLocalStore extends KokimokiStore {
6
+ localRoomName;
7
+ _stateKey;
8
+ get stateKey() {
9
+ if (!this._stateKey) {
10
+ throw new Error("Not initialized");
11
+ }
12
+ return this._stateKey;
13
+ }
14
+ constructor(localRoomName, defaultState) {
15
+ super(`/l/${localRoomName}`, defaultState, RoomSubscriptionMode.ReadWrite);
16
+ this.localRoomName = localRoomName;
17
+ // Synchronize doc changes to local storage
18
+ // TODO: maybe do not serialize full state every time
19
+ this.doc.on("update", () => {
20
+ const value = Y.encodeStateAsUpdate(this.doc);
21
+ const valueString = String.fromCharCode(...value);
22
+ const valueBase64 = btoa(valueString);
23
+ localStorage.setItem(this.stateKey, valueBase64);
24
+ });
25
+ }
26
+ getInitialUpdate(appId, clientId) {
27
+ this._stateKey = `${appId}/${clientId}/${this.localRoomName}`;
28
+ // get initial update from local storage
29
+ let initialUpdate = undefined;
30
+ const state = localStorage.getItem(this.stateKey);
31
+ if (state) {
32
+ const valueString = atob(state);
33
+ initialUpdate = Uint8Array.from(valueString, (c) => c.charCodeAt(0));
34
+ }
35
+ return {
36
+ roomHash: fingerprint32(this.roomName),
37
+ initialUpdate,
38
+ };
39
+ }
40
+ }
@@ -0,0 +1,22 @@
1
+ import { Snapshot } from "valtio/vanilla";
2
+ import * as Y from "yjs";
3
+ import { type KokimokiClient, RoomSubscriptionMode } from "../core";
4
+ export declare class KokimokiStore<T extends object> {
5
+ readonly roomName: string;
6
+ readonly defaultValue: T;
7
+ readonly mode: RoomSubscriptionMode;
8
+ readonly doc: Y.Doc;
9
+ readonly proxy: T;
10
+ readonly docRoot: Y.Map<unknown>;
11
+ readonly connections: {
12
+ connectionIds: Set<string>;
13
+ clientIds: Set<string>;
14
+ };
15
+ private _unsubscribeConnectionsHandler;
16
+ constructor(roomName: string, defaultValue: T, mode?: RoomSubscriptionMode);
17
+ get(): Snapshot<T>;
18
+ subscribe(set: (value: Snapshot<T>) => void): () => void;
19
+ onJoin(client: KokimokiClient<any>): Promise<void>;
20
+ onBeforeLeave(_client: KokimokiClient): Promise<void>;
21
+ onLeave(_client: KokimokiClient): Promise<void>;
22
+ }
@@ -0,0 +1,117 @@
1
+ import { bind as yjsBind } from "valtio-yjs";
2
+ import { proxy, snapshot, subscribe } from "valtio/vanilla";
3
+ import * as Y from "yjs";
4
+ import { RoomSubscriptionMode } from "../core";
5
+ export class KokimokiStore {
6
+ roomName;
7
+ defaultValue;
8
+ mode;
9
+ doc;
10
+ proxy;
11
+ docRoot;
12
+ connections;
13
+ _unsubscribeConnectionsHandler = () => { };
14
+ constructor(roomName, defaultValue, mode = RoomSubscriptionMode.ReadWrite) {
15
+ this.roomName = roomName;
16
+ this.defaultValue = defaultValue;
17
+ this.mode = mode;
18
+ // "_connections" is a reserved key for tracking connections
19
+ if ("_connections" in defaultValue) {
20
+ throw new Error(`"_connections" is a reserved key in KokimokiStore`);
21
+ }
22
+ // Construct Y doc
23
+ this.doc = new Y.Doc();
24
+ this.docRoot = this.doc.getMap("root");
25
+ // Construct proxy object
26
+ this.proxy = proxy();
27
+ // @ts-ignore
28
+ yjsBind(this.proxy, this.docRoot);
29
+ // Construct connections proxy
30
+ this.connections = proxy({
31
+ connectionIds: new Set(),
32
+ clientIds: new Set(),
33
+ });
34
+ }
35
+ get() {
36
+ return snapshot(this.proxy);
37
+ }
38
+ subscribe(set) {
39
+ const handler = () => set(this.get());
40
+ this.doc.on("update", handler);
41
+ set(this.get());
42
+ return () => this.doc.off("update", handler);
43
+ }
44
+ async onJoin(client) {
45
+ // Update connections whenever _connections changes
46
+ let prevConnectionIds = new Set();
47
+ let prevClientIds = new Set();
48
+ this._unsubscribeConnectionsHandler = subscribe(this.proxy, () => {
49
+ // @ts-ignore
50
+ const newConnectionIds = new Set(
51
+ // @ts-ignore
52
+ Object.keys(this.proxy._connections || {}));
53
+ // Update only if there are changes
54
+ let connectionIdsChanged = false;
55
+ if (newConnectionIds.size !== prevConnectionIds.size) {
56
+ connectionIdsChanged = true;
57
+ }
58
+ if (!connectionIdsChanged) {
59
+ for (const id of newConnectionIds) {
60
+ if (!prevConnectionIds.has(id)) {
61
+ connectionIdsChanged = true;
62
+ break;
63
+ }
64
+ }
65
+ }
66
+ if (!connectionIdsChanged) {
67
+ for (const id of prevConnectionIds) {
68
+ if (!newConnectionIds.has(id)) {
69
+ connectionIdsChanged = true;
70
+ break;
71
+ }
72
+ }
73
+ }
74
+ if (connectionIdsChanged) {
75
+ this.connections.connectionIds = newConnectionIds;
76
+ prevConnectionIds = new Set(newConnectionIds);
77
+ }
78
+ // @ts-ignore
79
+ const newClientIds = new Set(
80
+ // @ts-ignore
81
+ Object.values(this.proxy._connections || {}));
82
+ // Update only if there are changes
83
+ let clientIdsChanged = false;
84
+ if (newClientIds.size !== prevClientIds.size) {
85
+ clientIdsChanged = true;
86
+ }
87
+ if (!clientIdsChanged) {
88
+ for (const id of newClientIds) {
89
+ if (!prevClientIds.has(id)) {
90
+ clientIdsChanged = true;
91
+ break;
92
+ }
93
+ }
94
+ }
95
+ if (!clientIdsChanged) {
96
+ for (const id of prevClientIds) {
97
+ if (!newClientIds.has(id)) {
98
+ clientIdsChanged = true;
99
+ break;
100
+ }
101
+ }
102
+ }
103
+ if (clientIdsChanged) {
104
+ this.connections.clientIds = newClientIds;
105
+ prevClientIds = new Set(newClientIds);
106
+ }
107
+ });
108
+ // Add client to _connections map
109
+ await client.transact([this], ([state]) => {
110
+ state._connections[client.connectionId] = client.id;
111
+ });
112
+ }
113
+ async onBeforeLeave(_client) { }
114
+ async onLeave(_client) {
115
+ this._unsubscribeConnectionsHandler();
116
+ }
117
+ }
@@ -0,0 +1,18 @@
1
+ import { KokimokiStore } from "./kokimoki-store";
2
+ export declare class KokimokiTransaction<T extends object[]> {
3
+ private _updates;
4
+ private _consumeMessagesInRooms;
5
+ private _proxies;
6
+ private _unbindProxies;
7
+ constructor(stores: {
8
+ [K in keyof T]: KokimokiStore<T[K]>;
9
+ });
10
+ getProxies(): T;
11
+ getUpdates(): Promise<{
12
+ updates: {
13
+ roomName: string;
14
+ update: Uint8Array;
15
+ }[];
16
+ consumedMessages: Set<string>;
17
+ }>;
18
+ }
@@ -0,0 +1,143 @@
1
+ import { bind as yjsBind } from "valtio-yjs";
2
+ import { proxy as yjsProxy } from "valtio/vanilla";
3
+ import * as Y from "yjs";
4
+ export class KokimokiTransaction {
5
+ // private _clones = new Map<
6
+ // string,
7
+ // { docClone: Y.Doc; proxyClone: any; unbindProxy: () => void }
8
+ // >();
9
+ _updates = new Map();
10
+ _consumeMessagesInRooms = new Set();
11
+ // private _queueMessageCounter = 0;
12
+ _proxies = [];
13
+ _unbindProxies = [];
14
+ constructor(stores) {
15
+ // Create a proxy for each store
16
+ for (const store of stores) {
17
+ // Clone initial state
18
+ const docClone = new Y.Doc();
19
+ const docRoot = docClone.getMap("root");
20
+ Y.applyUpdate(docClone, Y.encodeStateAsUpdate(store.doc));
21
+ // Set up proxy
22
+ const proxyClone = yjsProxy();
23
+ const unbindProxy = yjsBind(proxyClone, docRoot);
24
+ this._proxies.push(proxyClone);
25
+ this._unbindProxies.push(unbindProxy);
26
+ // Listen for updates
27
+ docClone.on("update", (update) => {
28
+ if (this._updates.has(store.roomName)) {
29
+ const prevUpdate = this._updates.get(store.roomName);
30
+ const nextUpdate = Y.mergeUpdates([prevUpdate, update]);
31
+ this._updates.set(store.roomName, nextUpdate);
32
+ }
33
+ else {
34
+ this._updates.set(store.roomName, update);
35
+ }
36
+ });
37
+ // this._clones.set(store.roomName, { docClone, proxyClone, unbindProxy });
38
+ }
39
+ }
40
+ getProxies() {
41
+ // @ts-ignore
42
+ return this._proxies;
43
+ }
44
+ // private _parseTarget(target: any): {
45
+ // roomName: string;
46
+ // doc: Y.Doc;
47
+ // obj: any;
48
+ // key: string;
49
+ // path: string[];
50
+ // } {
51
+ // return target();
52
+ // }
53
+ // private _parsePath(obj: any, path: string[]) {
54
+ // for (let i = 0; i < path.length - 1; i++) {
55
+ // if (!(path[i] in obj)) {
56
+ // obj[path[i]] = {};
57
+ // }
58
+ // obj = obj[path[i]];
59
+ // }
60
+ // return { obj, key: path[path.length - 1] };
61
+ // }
62
+ // private _getClone(roomName: string, doc: Y.Doc) {
63
+ // if (!this._clones.has(roomName)) {
64
+ // // Clone doc
65
+ // const docClone = new Y.Doc();
66
+ // const docRoot = docClone.getMap("root");
67
+ // Y.applyUpdate(docClone, Y.encodeStateAsUpdate(doc));
68
+ // // Set up proxy
69
+ // const proxyClone = yjsProxy() as any;
70
+ // const unbindProxy = yjsBind(proxyClone, docRoot);
71
+ // // Listen for updates
72
+ // docClone.on("update", (update) => {
73
+ // if (this._updates.has(roomName)) {
74
+ // const prevUpdate = this._updates.get(roomName)!;
75
+ // const nextUpdate = Y.mergeUpdates([prevUpdate, update]);
76
+ // this._updates.set(roomName, nextUpdate);
77
+ // } else {
78
+ // this._updates.set(roomName, update);
79
+ // }
80
+ // });
81
+ // this._clones.set(roomName, { docClone, proxyClone, unbindProxy });
82
+ // }
83
+ // return this._clones.get(roomName)!;
84
+ // }
85
+ // get<T>(target: T): T {
86
+ // const { roomName, doc, path } = this._parseTarget(target);
87
+ // const { proxyClone } = this._getClone(roomName, doc);
88
+ // const { obj, key } = this._parsePath(proxyClone, path);
89
+ // return obj[key];
90
+ // }
91
+ // set<T>(target: T, value: T) {
92
+ // const { roomName, doc, path } = this._parseTarget(target);
93
+ // const { proxyClone } = this._getClone(roomName, doc);
94
+ // const { obj, key } = this._parsePath(proxyClone, path);
95
+ // obj[key] = value;
96
+ // }
97
+ // delete<T>(target: T) {
98
+ // const { roomName, doc, path } = this._parseTarget(target);
99
+ // const { proxyClone } = this._getClone(roomName, doc);
100
+ // const { obj, key } = this._parsePath(proxyClone, path);
101
+ // delete obj[key];
102
+ // }
103
+ // push<T>(target: T[], value: T) {
104
+ // const { roomName, doc, path } = this._parseTarget(target);
105
+ // const { proxyClone } = this._getClone(roomName, doc);
106
+ // const { obj, key } = this._parsePath(proxyClone, path);
107
+ // if (!(key in obj)) {
108
+ // obj[key] = [];
109
+ // }
110
+ // obj[key].push(value);
111
+ // }
112
+ // queueMessage<T>(queue: KokimokiQueue<S.Generic<T>>, payload: T) {
113
+ // const messageId = `${this.kmClient.serverTimestamp()}/${this
114
+ // ._queueMessageCounter++}/${Math.random().toString(36).slice(2)}`;
115
+ // this.set(queue.root[messageId], payload);
116
+ // return messageId;
117
+ // }
118
+ // consumeMessage<T>(queue: KokimokiQueue<S.Generic<T>>, messageId: string) {
119
+ // if (!queue.proxy[messageId]) {
120
+ // throw new Error(
121
+ // `Message ${messageId} does not exist or has already been consumed`
122
+ // );
123
+ // }
124
+ // this.delete(queue.root[messageId]);
125
+ // this._consumeMessagesInRooms.add(queue.roomName);
126
+ // }
127
+ async getUpdates() {
128
+ // Wait for doc update handling to finish
129
+ return await new Promise((resolve) => setTimeout(() => {
130
+ // Clean up
131
+ for (const unbindProxy of this._unbindProxies) {
132
+ unbindProxy();
133
+ }
134
+ // Generates updates
135
+ const updates = Array.from(this._updates.entries()).map(([roomName, update]) => ({
136
+ roomName,
137
+ update,
138
+ }));
139
+ // Done
140
+ resolve({ updates, consumedMessages: this._consumeMessagesInRooms });
141
+ }));
142
+ }
143
+ }
@@ -0,0 +1,3 @@
1
+ export * from "./common";
2
+ export * from "./events";
3
+ export * from "./upload";
@@ -0,0 +1,3 @@
1
+ export * from "./common";
2
+ export * from "./events";
3
+ export * from "./upload";
@@ -0,0 +1,7 @@
1
+ import "valtio";
2
+ declare module "valtio" {
3
+ function useSnapshot<T extends object>(p: T): T;
4
+ }
5
+ export { proxy, ref, snapshot, subscribe, useSnapshot } from "valtio";
6
+ export { derive, underive } from "derive-valtio";
7
+ export { devtools, subscribeKey, useProxy, watch } from "valtio/utils";
@@ -0,0 +1,6 @@
1
+ import "valtio";
2
+ // Core Valtio exports
3
+ export { proxy, ref, snapshot, subscribe, useSnapshot } from "valtio";
4
+ // Valtio utilities
5
+ export { derive, underive } from "derive-valtio";
6
+ export { devtools, subscribeKey, useProxy, watch } from "valtio/utils";
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const KOKIMOKI_APP_VERSION = "2.0.0";
1
+ export declare const KOKIMOKI_APP_VERSION = "2.0.1";
package/dist/version.js CHANGED
@@ -1 +1,2 @@
1
- export const KOKIMOKI_APP_VERSION = "2.0.0";
1
+ // Auto-generated file. Do not edit manually.
2
+ export const KOKIMOKI_APP_VERSION = '2.0.1';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kokimoki/app",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
4
4
  "type": "module",
5
5
  "description": "Kokimoki app",
6
6
  "main": "dist/index.js",
@@ -14,9 +14,9 @@
14
14
  },
15
15
  "scripts": {
16
16
  "test": "NODE_OPTIONS='--import tsx' mocha",
17
- "prebuild": "node -p \"'export const KOKIMOKI_APP_VERSION = ' + JSON.stringify(require('./package.json').version) + ';'\" > src/version.ts",
17
+ "prebuild": "node scripts/generate-version.js",
18
18
  "build": "tsc",
19
- "postbuild": "rollup --config && cp llms.txt dist/",
19
+ "postbuild": "rollup --config && cp docs/instructions.md dist/llms.txt",
20
20
  "dev": "tsc -w",
21
21
  "docs": "typedoc src/index.ts",
22
22
  "rollup": "rollup --config"
@@ -44,6 +44,7 @@
44
44
  "typescript": "^5.1.6"
45
45
  },
46
46
  "dependencies": {
47
+ "derive-valtio": "^0.2.0",
47
48
  "events": "^3.3.0",
48
49
  "farmhash-modern": "^1.1.0",
49
50
  "typed-emitter": "^2.1.0",