@kokimoki/app 1.3.2 → 1.4.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.
@@ -3,8 +3,12 @@ import type { KokimokiClientEvents } from "./types/events";
3
3
  import type { Upload } from "./types/upload";
4
4
  import type { Paginated } from "./types/common";
5
5
  import { KokimokiTransaction } from "./kokimoki-transaction";
6
- import type { KokimokiStore } from "./kokimoki-store";
6
+ import { KokimokiStore } from "./kokimoki-store";
7
7
  import type { KokimokiSchema as S } from "./kokimoki-schema";
8
+ import type { RoomSubscriptionMode } from "./room-subscription-mode";
9
+ import { KokimokiQueue } from "./kokimoki-queue";
10
+ import { KokimokiAwareness } from "./kokimoki-awareness";
11
+ import { KokimokiReqRes } from "./kokimoki-req-res";
8
12
  declare const KokimokiClient_base: new () => TypedEmitter<KokimokiClientEvents>;
9
13
  export declare class KokimokiClient<ClientContextT = any> extends KokimokiClient_base {
10
14
  readonly host: string;
@@ -30,6 +34,7 @@ export declare class KokimokiClient<ClientContextT = any> extends KokimokiClient
30
34
  private _autoReconnect;
31
35
  private _reconnectTimeout;
32
36
  private _pingInterval;
37
+ private _clientTokenKey;
33
38
  constructor(host: string, appId: string, code?: string);
34
39
  get id(): string;
35
40
  get connectionId(): string;
@@ -69,7 +74,13 @@ export declare class KokimokiClient<ClientContextT = any> extends KokimokiClient
69
74
  private sendUnsubscribeReq;
70
75
  join<T extends S.Generic<unknown>>(store: KokimokiStore<T>): Promise<void>;
71
76
  leave<T extends S.Generic<unknown>>(store: KokimokiStore<T>): Promise<void>;
72
- transact(handler: (t: KokimokiTransaction) => void): Promise<void>;
77
+ transact(handler: (t: KokimokiTransaction) => void | Promise<void>): Promise<void>;
73
78
  close(): Promise<void>;
79
+ getRoomHash<T extends S.Generic<unknown>>(store: KokimokiStore<T>): number;
80
+ /** Initializers */
81
+ store<T extends S.Generic<unknown>>(name: string, schema: T, autoJoin?: boolean): KokimokiStore<T, T["defaultValue"]>;
82
+ queue<T extends S.Generic<unknown>>(name: string, schema: T, mode: RoomSubscriptionMode, autoJoin?: boolean): KokimokiQueue<T>;
83
+ awareness<T extends S.Generic<unknown>>(name: string, dataSchema: T, initialData?: T["defaultValue"], autoJoin?: boolean): KokimokiAwareness<T>;
84
+ reqRes<Req extends S.Generic<unknown>, Res extends S.Generic<unknown>>(serviceName: string, reqSchema: Req, resSchema: Res, handleRequest: (payload: Req["defaultValue"]) => Promise<Res["defaultValue"]>): KokimokiReqRes<Req, Res>;
74
85
  }
75
86
  export {};
@@ -2,10 +2,14 @@ import EventEmitter from "events";
2
2
  import { KOKIMOKI_APP_VERSION } from "./version";
3
3
  import * as Y from "yjs";
4
4
  import { KokimokiTransaction } from "./kokimoki-transaction";
5
+ import { KokimokiStore } from "./kokimoki-store";
5
6
  import { WsMessageType } from "./ws-message-type";
6
7
  import { WsMessageWriter } from "./ws-message-writer";
7
8
  import { WsMessageReader } from "./ws-message-reader";
8
9
  import { RoomSubscription } from "./room-subscription";
10
+ import { KokimokiQueue } from "./kokimoki-queue";
11
+ import { KokimokiAwareness } from "./kokimoki-awareness";
12
+ import { KokimokiReqRes } from "./kokimoki-req-res";
9
13
  export class KokimokiClient extends EventEmitter {
10
14
  host;
11
15
  appId;
@@ -30,6 +34,7 @@ export class KokimokiClient extends EventEmitter {
30
34
  _autoReconnect = true;
31
35
  _reconnectTimeout = 0;
32
36
  _pingInterval;
37
+ _clientTokenKey = "KM_TOKEN";
33
38
  constructor(host, appId, code = "") {
34
39
  super();
35
40
  this.host = host;
@@ -48,6 +53,15 @@ export class KokimokiClient extends EventEmitter {
48
53
  this.ws.send(pingBuffer);
49
54
  }
50
55
  }, 5000);
56
+ // Detect devtools
57
+ if (window.top && frameElement) {
58
+ this._clientTokenKey = `KM_TOKEN/${frameElement.id}`;
59
+ window.addEventListener("message", (e) => {
60
+ if (e.data === "km:clearStorage") {
61
+ localStorage.removeItem(this._clientTokenKey);
62
+ }
63
+ });
64
+ }
51
65
  }
52
66
  get id() {
53
67
  if (!this._id) {
@@ -98,6 +112,7 @@ export class KokimokiClient extends EventEmitter {
98
112
  if (this._connectPromise) {
99
113
  return await this._connectPromise;
100
114
  }
115
+ // Set up the WebSocket connection
101
116
  this._ws = new WebSocket(`${this._wsUrl}/apps/${this.appId}?clientVersion=${KOKIMOKI_APP_VERSION}`);
102
117
  this._ws.binaryType = "arraybuffer";
103
118
  // Close previous connection in hot-reload scenarios
@@ -114,7 +129,7 @@ export class KokimokiClient extends EventEmitter {
114
129
  // Wait for connection
115
130
  this._connectPromise = new Promise((onInit) => {
116
131
  // Fetch the auth token
117
- let clientToken = localStorage.getItem("KM_TOKEN");
132
+ let clientToken = localStorage.getItem(this._clientTokenKey);
118
133
  // Send the app token on first connect
119
134
  this.ws.onopen = () => {
120
135
  this.ws.send(JSON.stringify({ type: "auth", code: this.code, token: clientToken }));
@@ -180,7 +195,7 @@ export class KokimokiClient extends EventEmitter {
180
195
  this.emit("connected");
181
196
  }
182
197
  handleInitMessage(message) {
183
- localStorage.setItem("KM_TOKEN", message.clientToken);
198
+ localStorage.setItem(this._clientTokenKey, message.clientToken);
184
199
  this._id = message.clientId;
185
200
  this._connectionId = message.id;
186
201
  this._token = message.appToken;
@@ -419,6 +434,7 @@ export class KokimokiClient extends EventEmitter {
419
434
  await this.sendUnsubscribeReq(subscription.roomHash);
420
435
  this._subscriptionsByName.delete(store.roomName);
421
436
  this._subscriptionsByHash.delete(subscription.roomHash);
437
+ subscription.close();
422
438
  // Trigger onLeave event
423
439
  store.onLeave(this);
424
440
  }
@@ -428,7 +444,7 @@ export class KokimokiClient extends EventEmitter {
428
444
  throw new Error("Client not connected");
429
445
  }
430
446
  const transaction = new KokimokiTransaction(this);
431
- handler(transaction);
447
+ await handler(transaction);
432
448
  const { updates, consumedMessages } = await transaction.getUpdates();
433
449
  if (!updates.length) {
434
450
  return;
@@ -473,15 +489,6 @@ export class KokimokiClient extends EventEmitter {
473
489
  throw e;
474
490
  }
475
491
  });
476
- /* // Reset doc in Write-only mode
477
- for (const { roomName, update } of updates) {
478
- const mode = this._subscriptions.get(roomName);
479
-
480
- if (mode === RoomSubscriptionMode.Write) {
481
- // @ts-ignore
482
- // this._stores.get(this._roomHashes.get(roomName)!)!.doc = new Y.Doc();
483
- }
484
- } */
485
492
  }
486
493
  async close() {
487
494
  this._autoReconnect = false;
@@ -492,4 +499,47 @@ export class KokimokiClient extends EventEmitter {
492
499
  clearInterval(this._pingInterval);
493
500
  }
494
501
  }
502
+ // Get room hash value for store
503
+ getRoomHash(store) {
504
+ const subscription = this._subscriptionsByName.get(store.roomName);
505
+ if (!subscription) {
506
+ throw new Error(`Store "${store.roomName}" not joined`);
507
+ }
508
+ return subscription.roomHash;
509
+ }
510
+ /** Initializers */
511
+ // store
512
+ store(name, schema, autoJoin = true) {
513
+ const store = new KokimokiStore(name, schema);
514
+ if (autoJoin) {
515
+ this.join(store)
516
+ .then(() => { })
517
+ .catch(() => { });
518
+ }
519
+ return store;
520
+ }
521
+ // queue
522
+ queue(name, schema, mode, autoJoin = true) {
523
+ const queue = new KokimokiQueue(name, schema, mode);
524
+ if (autoJoin) {
525
+ this.join(queue)
526
+ .then(() => { })
527
+ .catch(() => { });
528
+ }
529
+ return queue;
530
+ }
531
+ // awareness
532
+ awareness(name, dataSchema, initialData = dataSchema.defaultValue, autoJoin = true) {
533
+ const awareness = new KokimokiAwareness(name, dataSchema, initialData);
534
+ if (autoJoin) {
535
+ this.join(awareness)
536
+ .then(() => { })
537
+ .catch(() => { });
538
+ }
539
+ return awareness;
540
+ }
541
+ // req-res
542
+ reqRes(serviceName, reqSchema, resSchema, handleRequest) {
543
+ return new KokimokiReqRes(this, serviceName, reqSchema, resSchema, handleRequest);
544
+ }
495
545
  }
@@ -2,10 +2,7 @@ import { KokimokiStore } from "./kokimoki-store";
2
2
  import { KokimokiSchema as S } from "./kokimoki-schema";
3
3
  import type TypedEventEmitter from "typed-emitter";
4
4
  import type { RoomSubscriptionMode } from "./room-subscription-mode";
5
- export declare class KokimokiQueue<Req extends S.Generic<unknown>> extends KokimokiStore<S.Dict<S.Struct<{
6
- timestamp: S.Number;
7
- payload: Req;
8
- }>>> {
5
+ export declare class KokimokiQueue<Req extends S.Generic<unknown>> extends KokimokiStore<S.Dict<Req>> {
9
6
  readonly payloadSchema: Req;
10
7
  private _emitter;
11
8
  readonly on: <E extends "messages">(event: E, listener: {
@@ -7,10 +7,7 @@ export class KokimokiQueue extends KokimokiStore {
7
7
  on = this._emitter.on.bind(this._emitter);
8
8
  off = this._emitter.off.bind(this._emitter);
9
9
  constructor(roomName, payloadSchema, mode) {
10
- super(`/q/${roomName}`, S.dict(S.struct({
11
- timestamp: S.number(),
12
- payload: payloadSchema,
13
- })), mode);
10
+ super(`/q/${roomName}`, S.dict(payloadSchema), mode);
14
11
  this.payloadSchema = payloadSchema;
15
12
  const emittedMessageIds = new Set();
16
13
  this.doc.on("update", () => {
@@ -20,7 +17,7 @@ export class KokimokiQueue extends KokimokiStore {
20
17
  continue;
21
18
  }
22
19
  emittedMessageIds.add(id);
23
- newMessages.push({ id, payload: this.proxy[id].payload });
20
+ newMessages.push({ id, payload: this.proxy[id] });
24
21
  }
25
22
  if (newMessages.length > 0) {
26
23
  this._emitter.emit("messages", newMessages);
@@ -0,0 +1,20 @@
1
+ import type { KokimokiClient } from "./kokimoki-client";
2
+ import { KokimokiSchema as S } from "./kokimoki-schema";
3
+ export declare class KokimokiReqRes<Req extends S.Generic<unknown>, Res extends S.Generic<unknown>> {
4
+ private kmClient;
5
+ readonly serviceName: string;
6
+ readonly reqSchema: Req;
7
+ readonly resSchema: Res;
8
+ private handleRequest;
9
+ private _reqQueueSchema;
10
+ private _resQueueSchema;
11
+ private _reqQueues;
12
+ private _resQueues;
13
+ private _reqPromises;
14
+ private _getReqQueue;
15
+ private _getResQueue;
16
+ constructor(kmClient: KokimokiClient, serviceName: string, reqSchema: Req, resSchema: Res, handleRequest: (payload: Req["defaultValue"]) => Promise<Res["defaultValue"]>);
17
+ private handleRequests;
18
+ private handleResponses;
19
+ send(toClientId: string, payload: Req["defaultValue"], timeout?: number): Promise<Res["defaultValue"]>;
20
+ }
@@ -0,0 +1,143 @@
1
+ import { KokimokiSchema as S } from "./kokimoki-schema";
2
+ import { RoomSubscriptionMode } from "./room-subscription-mode";
3
+ export class KokimokiReqRes {
4
+ kmClient;
5
+ serviceName;
6
+ reqSchema;
7
+ resSchema;
8
+ handleRequest;
9
+ _reqQueueSchema;
10
+ _resQueueSchema;
11
+ // Request queues are linked to client ids
12
+ _reqQueues = {};
13
+ // Response queues are linked to specific connection ids
14
+ _resQueues = {};
15
+ _reqPromises = {};
16
+ async _getReqQueue(clientId, mode = RoomSubscriptionMode.Write) {
17
+ if (!this._reqQueues.hasOwnProperty(clientId)) {
18
+ this._reqQueues[clientId] = this.kmClient.queue(`/r/${clientId}/req/${this.serviceName}`, this._reqQueueSchema, mode, false);
19
+ await this.kmClient.join(this._reqQueues[clientId]);
20
+ }
21
+ return this._reqQueues[clientId];
22
+ }
23
+ async _getResQueue(connectionId, mode = RoomSubscriptionMode.Write) {
24
+ if (!this._resQueues.hasOwnProperty(connectionId)) {
25
+ this._resQueues[connectionId] = this.kmClient.queue(`/r/${connectionId}/res/${this.serviceName}`, this._resQueueSchema, mode, false);
26
+ await this.kmClient.join(this._resQueues[connectionId]);
27
+ }
28
+ return this._resQueues[connectionId];
29
+ }
30
+ constructor(kmClient, serviceName, reqSchema, resSchema, handleRequest) {
31
+ this.kmClient = kmClient;
32
+ this.serviceName = serviceName;
33
+ this.reqSchema = reqSchema;
34
+ this.resSchema = resSchema;
35
+ this.handleRequest = handleRequest;
36
+ this._reqQueueSchema = S.struct({
37
+ connectionId: S.string(),
38
+ // roomHash: S.number(),
39
+ payload: reqSchema,
40
+ });
41
+ this._resQueueSchema = S.struct({
42
+ reqId: S.string(),
43
+ error: S.optional(S.string()),
44
+ payload: resSchema,
45
+ });
46
+ // Set up queues on connect
47
+ kmClient.on("connected", async () => {
48
+ // Handle responses to self
49
+ const resQueue = await this._getResQueue(kmClient.connectionId, RoomSubscriptionMode.ReadWrite);
50
+ let handlingResponses = false;
51
+ let rehandleResponses = false;
52
+ resQueue.subscribe(async (messages) => {
53
+ // console.log("RES", Object.keys(messages));
54
+ if (handlingResponses) {
55
+ rehandleResponses = true;
56
+ return;
57
+ }
58
+ handlingResponses = true;
59
+ await this.handleResponses(resQueue, messages);
60
+ if (rehandleResponses) {
61
+ rehandleResponses = false;
62
+ await this.handleResponses(resQueue, messages);
63
+ }
64
+ handlingResponses = false;
65
+ });
66
+ // Handle requests to self
67
+ const reqQueue = await this._getReqQueue(kmClient.id, RoomSubscriptionMode.ReadWrite);
68
+ let handlingRequests = false;
69
+ let rehandleRequests = false;
70
+ reqQueue.subscribe(async (messages) => {
71
+ // console.log("REQ", Object.keys(messages));
72
+ if (handlingRequests) {
73
+ rehandleRequests = true;
74
+ return;
75
+ }
76
+ handlingRequests = true;
77
+ await this.handleRequests(reqQueue, messages);
78
+ if (rehandleRequests) {
79
+ rehandleRequests = false;
80
+ await this.handleRequests(reqQueue, messages);
81
+ }
82
+ handlingRequests = false;
83
+ });
84
+ });
85
+ }
86
+ async handleRequests(reqQueue, messages) {
87
+ // Send responses
88
+ await this.kmClient.transact(async (t) => {
89
+ for (const reqId in messages) {
90
+ const { connectionId, payload } = messages[reqId];
91
+ const resQueue = await this._getResQueue(connectionId);
92
+ t.consumeMessage(reqQueue, reqId);
93
+ try {
94
+ t.queueMessage(resQueue, {
95
+ reqId,
96
+ error: undefined,
97
+ payload: await this.handleRequest(payload),
98
+ });
99
+ }
100
+ catch (e) {
101
+ t.queueMessage(resQueue, {
102
+ reqId,
103
+ error: e.toString(),
104
+ payload: null,
105
+ });
106
+ }
107
+ }
108
+ });
109
+ }
110
+ async handleResponses(resQueue, messages) {
111
+ // Handle responses
112
+ await this.kmClient.transact(async (t) => {
113
+ for (const resId in messages) {
114
+ const { reqId, error, payload } = messages[resId];
115
+ const promise = this._reqPromises[reqId];
116
+ t.consumeMessage(resQueue, resId);
117
+ if (promise) {
118
+ if (error) {
119
+ promise.reject(error);
120
+ }
121
+ else {
122
+ promise.resolve(payload);
123
+ }
124
+ delete this._reqPromises[reqId];
125
+ }
126
+ }
127
+ });
128
+ }
129
+ async send(toClientId, payload, timeout) {
130
+ const toReqQueue = await this._getReqQueue(toClientId);
131
+ // Create a promise for the response
132
+ return await new Promise(async (resolve, reject) => {
133
+ await this.kmClient.transact((t) => {
134
+ const reqId = t.queueMessage(toReqQueue, {
135
+ connectionId: this.kmClient.connectionId,
136
+ payload,
137
+ // roomHash,
138
+ });
139
+ this._reqPromises[reqId] = { resolve, reject };
140
+ });
141
+ });
142
+ }
143
+ }
@@ -6,6 +6,7 @@ export declare class KokimokiTransaction {
6
6
  private _clones;
7
7
  private _updates;
8
8
  private _consumeMessagesInRooms;
9
+ private _queueMessageCounter;
9
10
  constructor(kmClient: KokimokiClient<any>);
10
11
  private _parseTarget;
11
12
  private _parsePath;
@@ -14,7 +15,7 @@ export declare class KokimokiTransaction {
14
15
  set<T>(target: T, value: T): void;
15
16
  delete<T>(target: T): void;
16
17
  push<T>(target: T[], value: T): void;
17
- queueMessage<T>(queue: KokimokiQueue<S.Generic<T>>, payload: T): void;
18
+ queueMessage<T>(queue: KokimokiQueue<S.Generic<T>>, payload: T): string;
18
19
  consumeMessage<T>(queue: KokimokiQueue<S.Generic<T>>, messageId: string): void;
19
20
  getUpdates(): Promise<{
20
21
  updates: {
@@ -6,6 +6,7 @@ export class KokimokiTransaction {
6
6
  _clones = new Map();
7
7
  _updates = new Map();
8
8
  _consumeMessagesInRooms = new Set();
9
+ _queueMessageCounter = 0;
9
10
  constructor(kmClient) {
10
11
  this.kmClient = kmClient;
11
12
  }
@@ -73,11 +74,10 @@ export class KokimokiTransaction {
73
74
  obj[key].push(value);
74
75
  }
75
76
  queueMessage(queue, payload) {
76
- const messageId = Math.random().toString(36);
77
- this.set(queue.root[messageId], {
78
- timestamp: this.kmClient.serverTimestamp(),
79
- payload,
80
- });
77
+ const messageId = `${this.kmClient.serverTimestamp()}/${this
78
+ ._queueMessageCounter++}/${Math.random().toString(36).slice(2)}`;
79
+ this.set(queue.root[messageId], payload);
80
+ return messageId;
81
81
  }
82
82
  consumeMessage(queue, messageId) {
83
83
  if (!queue.proxy[messageId]) {
@@ -227,10 +227,7 @@ declare class KokimokiStore<T extends KokimokiSchema.Generic<unknown>, Subscribe
227
227
  onLeave(client: KokimokiClient): Promise<void>;
228
228
  }
229
229
 
230
- declare class KokimokiQueue<Req extends KokimokiSchema.Generic<unknown>> extends KokimokiStore<KokimokiSchema.Dict<KokimokiSchema.Struct<{
231
- timestamp: KokimokiSchema.Number;
232
- payload: Req;
233
- }>>> {
230
+ declare class KokimokiQueue<Req extends KokimokiSchema.Generic<unknown>> extends KokimokiStore<KokimokiSchema.Dict<Req>> {
234
231
  readonly payloadSchema: Req;
235
232
  private _emitter;
236
233
  readonly on: <E extends "messages">(event: E, listener: {
@@ -263,6 +260,7 @@ declare class KokimokiTransaction {
263
260
  private _clones;
264
261
  private _updates;
265
262
  private _consumeMessagesInRooms;
263
+ private _queueMessageCounter;
266
264
  constructor(kmClient: KokimokiClient<any>);
267
265
  private _parseTarget;
268
266
  private _parsePath;
@@ -271,7 +269,7 @@ declare class KokimokiTransaction {
271
269
  set<T>(target: T, value: T): void;
272
270
  delete<T>(target: T): void;
273
271
  push<T>(target: T[], value: T): void;
274
- queueMessage<T>(queue: KokimokiQueue<KokimokiSchema.Generic<T>>, payload: T): void;
272
+ queueMessage<T>(queue: KokimokiQueue<KokimokiSchema.Generic<T>>, payload: T): string;
275
273
  consumeMessage<T>(queue: KokimokiQueue<KokimokiSchema.Generic<T>>, messageId: string): void;
276
274
  getUpdates(): Promise<{
277
275
  updates: {
@@ -282,6 +280,44 @@ declare class KokimokiTransaction {
282
280
  }>;
283
281
  }
284
282
 
283
+ declare class KokimokiAwareness<Data extends KokimokiSchema.Generic<unknown>> extends KokimokiStore<KokimokiSchema.Dict<KokimokiSchema.Struct<{
284
+ clientId: KokimokiSchema.String;
285
+ lastPing: KokimokiSchema.Number;
286
+ data: Data;
287
+ }>>> {
288
+ readonly dataSchema: Data;
289
+ private _data;
290
+ private _pingInterval;
291
+ private _kmClients;
292
+ constructor(roomName: string, dataSchema: Data, _data: Data["defaultValue"], mode?: RoomSubscriptionMode, pingTimeout?: number);
293
+ onJoin(client: KokimokiClient<any>): Promise<void>;
294
+ onBeforeLeave(client: KokimokiClient<any>): Promise<void>;
295
+ onLeave(client: KokimokiClient<any>): Promise<void>;
296
+ getClients(): {
297
+ [clientId: string]: Data["defaultValue"];
298
+ };
299
+ setData(data: Data["defaultValue"]): Promise<void>;
300
+ }
301
+
302
+ declare class KokimokiReqRes<Req extends KokimokiSchema.Generic<unknown>, Res extends KokimokiSchema.Generic<unknown>> {
303
+ private kmClient;
304
+ readonly serviceName: string;
305
+ readonly reqSchema: Req;
306
+ readonly resSchema: Res;
307
+ private handleRequest;
308
+ private _reqQueueSchema;
309
+ private _resQueueSchema;
310
+ private _reqQueues;
311
+ private _resQueues;
312
+ private _reqPromises;
313
+ private _getReqQueue;
314
+ private _getResQueue;
315
+ constructor(kmClient: KokimokiClient, serviceName: string, reqSchema: Req, resSchema: Res, handleRequest: (payload: Req["defaultValue"]) => Promise<Res["defaultValue"]>);
316
+ private handleRequests;
317
+ private handleResponses;
318
+ send(toClientId: string, payload: Req["defaultValue"], timeout?: number): Promise<Res["defaultValue"]>;
319
+ }
320
+
285
321
  declare const KokimokiClient_base: new () => TypedEventEmitter<KokimokiClientEvents>;
286
322
  declare class KokimokiClient<ClientContextT = any> extends KokimokiClient_base {
287
323
  readonly host: string;
@@ -307,6 +343,7 @@ declare class KokimokiClient<ClientContextT = any> extends KokimokiClient_base {
307
343
  private _autoReconnect;
308
344
  private _reconnectTimeout;
309
345
  private _pingInterval;
346
+ private _clientTokenKey;
310
347
  constructor(host: string, appId: string, code?: string);
311
348
  get id(): string;
312
349
  get connectionId(): string;
@@ -346,8 +383,14 @@ declare class KokimokiClient<ClientContextT = any> extends KokimokiClient_base {
346
383
  private sendUnsubscribeReq;
347
384
  join<T extends KokimokiSchema.Generic<unknown>>(store: KokimokiStore<T>): Promise<void>;
348
385
  leave<T extends KokimokiSchema.Generic<unknown>>(store: KokimokiStore<T>): Promise<void>;
349
- transact(handler: (t: KokimokiTransaction) => void): Promise<void>;
386
+ transact(handler: (t: KokimokiTransaction) => void | Promise<void>): Promise<void>;
350
387
  close(): Promise<void>;
388
+ getRoomHash<T extends KokimokiSchema.Generic<unknown>>(store: KokimokiStore<T>): number;
389
+ /** Initializers */
390
+ store<T extends KokimokiSchema.Generic<unknown>>(name: string, schema: T, autoJoin?: boolean): KokimokiStore<T, T["defaultValue"]>;
391
+ queue<T extends KokimokiSchema.Generic<unknown>>(name: string, schema: T, mode: RoomSubscriptionMode, autoJoin?: boolean): KokimokiQueue<T>;
392
+ awareness<T extends KokimokiSchema.Generic<unknown>>(name: string, dataSchema: T, initialData?: T["defaultValue"], autoJoin?: boolean): KokimokiAwareness<T>;
393
+ reqRes<Req extends KokimokiSchema.Generic<unknown>, Res extends KokimokiSchema.Generic<unknown>>(serviceName: string, reqSchema: Req, resSchema: Res, handleRequest: (payload: Req["defaultValue"]) => Promise<Res["defaultValue"]>): KokimokiReqRes<Req, Res>;
351
394
  }
352
395
 
353
396
  declare class RoomSubscription {
@@ -364,23 +407,4 @@ declare class RoomSubscription {
364
407
  close(): void;
365
408
  }
366
409
 
367
- declare class KokimokiAwareness<Data extends KokimokiSchema.Generic<unknown>> extends KokimokiStore<KokimokiSchema.Dict<KokimokiSchema.Struct<{
368
- clientId: KokimokiSchema.String;
369
- lastPing: KokimokiSchema.Number;
370
- data: Data;
371
- }>>> {
372
- readonly dataSchema: Data;
373
- private _data;
374
- private _pingInterval;
375
- private _kmClients;
376
- constructor(roomName: string, dataSchema: Data, _data: Data["defaultValue"], mode?: RoomSubscriptionMode, pingTimeout?: number);
377
- onJoin(client: KokimokiClient<any>): Promise<void>;
378
- onBeforeLeave(client: KokimokiClient<any>): Promise<void>;
379
- onLeave(client: KokimokiClient<any>): Promise<void>;
380
- getClients(): {
381
- [clientId: string]: Data["defaultValue"];
382
- };
383
- setData(data: Data["defaultValue"]): Promise<void>;
384
- }
385
-
386
410
  export { BooleanField, ConstField, EnumField, Field, type FieldOptions, FloatField, Form, FormArray, FormGroup, ImageField, IntegerField, KokimokiAwareness, KokimokiClient, type KokimokiClientEvents, KokimokiQueue, KokimokiSchema, KokimokiStore, type Paginated, RoomSubscription, RoomSubscriptionMode, TextField, type Upload };