@kokimoki/app 1.1.1 → 1.2.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.
@@ -2,14 +2,23 @@ import { KokimokiStore } from "./kokimoki-store";
2
2
  import { KokimokiSchema as S } from "./kokimoki-schema";
3
3
  import { RoomSubscriptionMode } from "./room-subscription-mode";
4
4
  import type { KokimokiClient } from "./kokimoki-client";
5
- export declare class KokimokiAwareness extends KokimokiStore<S.Dict<S.Struct<{
5
+ export declare class KokimokiAwareness<Data extends S.Generic<unknown>> extends KokimokiStore<S.Dict<S.Struct<{
6
6
  clientId: S.String;
7
7
  lastPing: S.Number;
8
- }>>> {
8
+ data: Data;
9
+ }>>, {
10
+ [clientId: string]: Data["defaultValue"];
11
+ }> {
12
+ readonly dataSchema: Data;
13
+ readonly data: Data["defaultValue"];
9
14
  private _pingInterval;
10
15
  private _kmClients;
11
- constructor(roomName: string, mode?: RoomSubscriptionMode, pingTimeout?: number);
16
+ constructor(roomName: string, dataSchema: Data, data: Data["defaultValue"], mode?: RoomSubscriptionMode, pingTimeout?: number);
12
17
  onJoin(client: KokimokiClient<any>): Promise<void>;
13
18
  onBeforeLeave(client: KokimokiClient<any>): Promise<void>;
14
19
  onLeave(client: KokimokiClient<any>): Promise<void>;
20
+ private getConnectedClients;
21
+ subscribe(set: (value: {
22
+ [clientId: string]: Data["defaultValue"];
23
+ }) => void): () => void;
15
24
  }
@@ -1,35 +1,36 @@
1
1
  import { KokimokiStore } from "./kokimoki-store";
2
2
  import { KokimokiSchema as S } from "./kokimoki-schema";
3
3
  import { RoomSubscriptionMode } from "./room-subscription-mode";
4
- export class KokimokiAwareness
5
- // <Data extends S.Generic<unknown>>
6
- extends KokimokiStore {
4
+ export class KokimokiAwareness extends KokimokiStore {
5
+ dataSchema;
6
+ data;
7
7
  _pingInterval = null;
8
8
  _kmClients = new Set();
9
- // private _data: Data["defaultValue"];
10
- constructor(roomName,
11
- // public readonly dataSchema: Data,
12
- // initialData: Data["defaultValue"],
13
- mode = RoomSubscriptionMode.ReadWrite, pingTimeout = 2500) {
9
+ constructor(roomName, dataSchema, data, mode = RoomSubscriptionMode.ReadWrite, pingTimeout = 2500) {
14
10
  super(`/a/${roomName}`, S.dict(S.struct({
15
11
  clientId: S.string(),
16
12
  lastPing: S.number(),
17
- // data: dataSchema,
13
+ data: dataSchema,
18
14
  })), mode);
19
- // this._data = initialData;
15
+ this.dataSchema = dataSchema;
16
+ this.data = data;
20
17
  this._pingInterval = setInterval(async () => {
21
18
  const kmClients = Array.from(this._kmClients);
22
19
  await Promise.all(kmClients.map(async (client) => {
23
20
  try {
24
21
  await client.transact((t) => {
25
22
  const timestamp = client.serverTimestamp();
26
- // Update self last ping
27
- t.set(this.root[client.connectionId].lastPing, timestamp);
28
- /* if (!this.proxy[client.connectionId]) {
29
- t.set(this.root[client.connectionId].data, this._data);
30
- } else {
31
- this._data = this.proxy[client.connectionId].data;
32
- } */
23
+ // Update self
24
+ if (this.proxy[client.connectionId]) {
25
+ t.set(this.root[client.connectionId].lastPing, timestamp);
26
+ }
27
+ else {
28
+ t.set(this.root[client.connectionId], {
29
+ clientId: client.id,
30
+ lastPing: timestamp,
31
+ data: this.data,
32
+ });
33
+ }
33
34
  // Delete clients that haven't pinged in a while
34
35
  for (const connectionId in this.proxy) {
35
36
  const { lastPing } = this.proxy[connectionId];
@@ -49,7 +50,7 @@ export class KokimokiAwareness
49
50
  t.set(this.root[client.connectionId], {
50
51
  clientId: client.id,
51
52
  lastPing: client.serverTimestamp(),
52
- // data: this._data,
53
+ data: this.data,
53
54
  });
54
55
  });
55
56
  }
@@ -61,4 +62,37 @@ export class KokimokiAwareness
61
62
  async onLeave(client) {
62
63
  this._kmClients.delete(client);
63
64
  }
65
+ getConnectedClients() {
66
+ const clients = {};
67
+ for (const clientId in this.proxy) {
68
+ clients[clientId] = this.proxy[clientId].data;
69
+ }
70
+ return clients;
71
+ }
72
+ subscribe(set) {
73
+ let prevConnectionIds = {};
74
+ const handler = () => {
75
+ // Do nothing if only pings have changed
76
+ let changed = false;
77
+ const newConnectionIds = {};
78
+ for (const connectionId in this.proxy) {
79
+ newConnectionIds[connectionId] = true;
80
+ if (!prevConnectionIds[connectionId]) {
81
+ changed = true;
82
+ }
83
+ }
84
+ for (const connectionId in prevConnectionIds) {
85
+ if (!newConnectionIds[connectionId]) {
86
+ changed = true;
87
+ }
88
+ }
89
+ if (changed) {
90
+ set(this.getConnectedClients());
91
+ prevConnectionIds = newConnectionIds;
92
+ }
93
+ };
94
+ this.doc.on("update", handler);
95
+ set(this.getConnectedClients());
96
+ return () => this.doc.off("update", handler);
97
+ }
64
98
  }
@@ -364,16 +364,25 @@ declare class RoomSubscription {
364
364
  close(): void;
365
365
  }
366
366
 
367
- declare class KokimokiAwareness extends KokimokiStore<KokimokiSchema.Dict<KokimokiSchema.Struct<{
367
+ declare class KokimokiAwareness<Data extends KokimokiSchema.Generic<unknown>> extends KokimokiStore<KokimokiSchema.Dict<KokimokiSchema.Struct<{
368
368
  clientId: KokimokiSchema.String;
369
369
  lastPing: KokimokiSchema.Number;
370
- }>>> {
370
+ data: Data;
371
+ }>>, {
372
+ [clientId: string]: Data["defaultValue"];
373
+ }> {
374
+ readonly dataSchema: Data;
375
+ readonly data: Data["defaultValue"];
371
376
  private _pingInterval;
372
377
  private _kmClients;
373
- constructor(roomName: string, mode?: RoomSubscriptionMode, pingTimeout?: number);
378
+ constructor(roomName: string, dataSchema: Data, data: Data["defaultValue"], mode?: RoomSubscriptionMode, pingTimeout?: number);
374
379
  onJoin(client: KokimokiClient<any>): Promise<void>;
375
380
  onBeforeLeave(client: KokimokiClient<any>): Promise<void>;
376
381
  onLeave(client: KokimokiClient<any>): Promise<void>;
382
+ private getConnectedClients;
383
+ subscribe(set: (value: {
384
+ [clientId: string]: Data["defaultValue"];
385
+ }) => void): () => void;
377
386
  }
378
387
 
379
388
  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 };
@@ -634,7 +634,7 @@ function eventTargetAgnosticAddListener(emitter, name, listener, flags) {
634
634
  var eventsExports = events.exports;
635
635
  var EventEmitter$1 = /*@__PURE__*/getDefaultExportFromCjs(eventsExports);
636
636
 
637
- const KOKIMOKI_APP_VERSION = "1.1.1";
637
+ const KOKIMOKI_APP_VERSION = "1.2.0";
638
638
 
639
639
  /**
640
640
  * Utility module to work with key-value stores.
@@ -12492,35 +12492,36 @@ class KokimokiQueue extends KokimokiStore {
12492
12492
  }
12493
12493
  }
12494
12494
 
12495
- class KokimokiAwareness
12496
- // <Data extends S.Generic<unknown>>
12497
- extends KokimokiStore {
12495
+ class KokimokiAwareness extends KokimokiStore {
12496
+ dataSchema;
12497
+ data;
12498
12498
  _pingInterval = null;
12499
12499
  _kmClients = new Set();
12500
- // private _data: Data["defaultValue"];
12501
- constructor(roomName,
12502
- // public readonly dataSchema: Data,
12503
- // initialData: Data["defaultValue"],
12504
- mode = RoomSubscriptionMode.ReadWrite, pingTimeout = 2500) {
12500
+ constructor(roomName, dataSchema, data, mode = RoomSubscriptionMode.ReadWrite, pingTimeout = 2500) {
12505
12501
  super(`/a/${roomName}`, KokimokiSchema.dict(KokimokiSchema.struct({
12506
12502
  clientId: KokimokiSchema.string(),
12507
12503
  lastPing: KokimokiSchema.number(),
12508
- // data: dataSchema,
12504
+ data: dataSchema,
12509
12505
  })), mode);
12510
- // this._data = initialData;
12506
+ this.dataSchema = dataSchema;
12507
+ this.data = data;
12511
12508
  this._pingInterval = setInterval(async () => {
12512
12509
  const kmClients = Array.from(this._kmClients);
12513
12510
  await Promise.all(kmClients.map(async (client) => {
12514
12511
  try {
12515
12512
  await client.transact((t) => {
12516
12513
  const timestamp = client.serverTimestamp();
12517
- // Update self last ping
12518
- t.set(this.root[client.connectionId].lastPing, timestamp);
12519
- /* if (!this.proxy[client.connectionId]) {
12520
- t.set(this.root[client.connectionId].data, this._data);
12521
- } else {
12522
- this._data = this.proxy[client.connectionId].data;
12523
- } */
12514
+ // Update self
12515
+ if (this.proxy[client.connectionId]) {
12516
+ t.set(this.root[client.connectionId].lastPing, timestamp);
12517
+ }
12518
+ else {
12519
+ t.set(this.root[client.connectionId], {
12520
+ clientId: client.id,
12521
+ lastPing: timestamp,
12522
+ data: this.data,
12523
+ });
12524
+ }
12524
12525
  // Delete clients that haven't pinged in a while
12525
12526
  for (const connectionId in this.proxy) {
12526
12527
  const { lastPing } = this.proxy[connectionId];
@@ -12540,7 +12541,7 @@ class KokimokiAwareness
12540
12541
  t.set(this.root[client.connectionId], {
12541
12542
  clientId: client.id,
12542
12543
  lastPing: client.serverTimestamp(),
12543
- // data: this._data,
12544
+ data: this.data,
12544
12545
  });
12545
12546
  });
12546
12547
  }
@@ -12552,6 +12553,39 @@ class KokimokiAwareness
12552
12553
  async onLeave(client) {
12553
12554
  this._kmClients.delete(client);
12554
12555
  }
12556
+ getConnectedClients() {
12557
+ const clients = {};
12558
+ for (const clientId in this.proxy) {
12559
+ clients[clientId] = this.proxy[clientId].data;
12560
+ }
12561
+ return clients;
12562
+ }
12563
+ subscribe(set) {
12564
+ let prevConnectionIds = {};
12565
+ const handler = () => {
12566
+ // Do nothing if only pings have changed
12567
+ let changed = false;
12568
+ const newConnectionIds = {};
12569
+ for (const connectionId in this.proxy) {
12570
+ newConnectionIds[connectionId] = true;
12571
+ if (!prevConnectionIds[connectionId]) {
12572
+ changed = true;
12573
+ }
12574
+ }
12575
+ for (const connectionId in prevConnectionIds) {
12576
+ if (!newConnectionIds[connectionId]) {
12577
+ changed = true;
12578
+ }
12579
+ }
12580
+ if (changed) {
12581
+ set(this.getConnectedClients());
12582
+ prevConnectionIds = newConnectionIds;
12583
+ }
12584
+ };
12585
+ this.doc.on("update", handler);
12586
+ set(this.getConnectedClients());
12587
+ return () => this.doc.off("update", handler);
12588
+ }
12555
12589
  }
12556
12590
 
12557
12591
  export { BooleanField, ConstField, EnumField, Field, FloatField, Form, FormArray, FormGroup, ImageField, IntegerField, KokimokiAwareness, KokimokiClient, KokimokiQueue, KokimokiSchema, KokimokiStore, RoomSubscription, RoomSubscriptionMode, TextField };