@kokimoki/app 1.3.2 → 1.4.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/dist/kokimoki-client.d.ts +12 -2
- package/dist/kokimoki-client.js +49 -10
- package/dist/kokimoki-queue.d.ts +1 -4
- package/dist/kokimoki-queue.js +2 -5
- package/dist/kokimoki-req-res.d.ts +20 -0
- package/dist/kokimoki-req-res.js +143 -0
- package/dist/kokimoki-transaction.d.ts +2 -1
- package/dist/kokimoki-transaction.js +5 -5
- package/dist/kokimoki.min.d.ts +48 -25
- package/dist/kokimoki.min.js +1026 -852
- package/dist/kokimoki.min.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/kokimoki.min.js
CHANGED
|
@@ -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.
|
|
637
|
+
const KOKIMOKI_APP_VERSION = "1.4.0";
|
|
638
638
|
|
|
639
639
|
/**
|
|
640
640
|
* Utility module to work with key-value stores.
|
|
@@ -11401,6 +11401,7 @@ class KokimokiTransaction {
|
|
|
11401
11401
|
_clones = new Map();
|
|
11402
11402
|
_updates = new Map();
|
|
11403
11403
|
_consumeMessagesInRooms = new Set();
|
|
11404
|
+
_queueMessageCounter = 0;
|
|
11404
11405
|
constructor(kmClient) {
|
|
11405
11406
|
this.kmClient = kmClient;
|
|
11406
11407
|
}
|
|
@@ -11468,11 +11469,10 @@ class KokimokiTransaction {
|
|
|
11468
11469
|
obj[key].push(value);
|
|
11469
11470
|
}
|
|
11470
11471
|
queueMessage(queue, payload) {
|
|
11471
|
-
const messageId =
|
|
11472
|
-
|
|
11473
|
-
|
|
11474
|
-
|
|
11475
|
-
});
|
|
11472
|
+
const messageId = `${this.kmClient.serverTimestamp()}/${this
|
|
11473
|
+
._queueMessageCounter++}/${Math.random().toString(36).slice(2)}`;
|
|
11474
|
+
this.set(queue.root[messageId], payload);
|
|
11475
|
+
return messageId;
|
|
11476
11476
|
}
|
|
11477
11477
|
consumeMessage(queue, messageId) {
|
|
11478
11478
|
if (!queue.proxy[messageId]) {
|
|
@@ -11499,6 +11499,175 @@ class KokimokiTransaction {
|
|
|
11499
11499
|
}
|
|
11500
11500
|
}
|
|
11501
11501
|
|
|
11502
|
+
function parsePath(text) {
|
|
11503
|
+
return text.split('.')
|
|
11504
|
+
}
|
|
11505
|
+
|
|
11506
|
+
function push(arr, el) {
|
|
11507
|
+
const newArr = arr.slice();
|
|
11508
|
+
newArr.push(el);
|
|
11509
|
+
return newArr;
|
|
11510
|
+
}
|
|
11511
|
+
|
|
11512
|
+
// names of the traps that can be registered with ES6's Proxy object
|
|
11513
|
+
const trapNames = [
|
|
11514
|
+
'apply',
|
|
11515
|
+
'construct',
|
|
11516
|
+
'defineProperty',
|
|
11517
|
+
'deleteProperty',
|
|
11518
|
+
'enumerate', // deprecated
|
|
11519
|
+
'get',
|
|
11520
|
+
'getOwnPropertyDescriptor',
|
|
11521
|
+
'getPrototypeOf',
|
|
11522
|
+
'has',
|
|
11523
|
+
'isExtensible',
|
|
11524
|
+
'ownKeys',
|
|
11525
|
+
'preventExtensions',
|
|
11526
|
+
'set',
|
|
11527
|
+
'setPrototypeOf',
|
|
11528
|
+
];
|
|
11529
|
+
|
|
11530
|
+
// a list of paramer indexes that indicate that the a recieves a key at that parameter
|
|
11531
|
+
// this information will be used to update the path accordingly
|
|
11532
|
+
const keys = {
|
|
11533
|
+
get: 1,
|
|
11534
|
+
set: 1,
|
|
11535
|
+
deleteProperty: 1,
|
|
11536
|
+
has: 1,
|
|
11537
|
+
defineProperty: 1,
|
|
11538
|
+
getOwnPropertyDescriptor: 1,
|
|
11539
|
+
};
|
|
11540
|
+
|
|
11541
|
+
function DeepProxy(rootTarget, traps, options) {
|
|
11542
|
+
|
|
11543
|
+
let path = [];
|
|
11544
|
+
let userData = {};
|
|
11545
|
+
|
|
11546
|
+
if (options !== undefined && typeof options.path !== 'undefined') {
|
|
11547
|
+
path = parsePath(options.path);
|
|
11548
|
+
}
|
|
11549
|
+
if (options !== undefined && typeof options.userData !== 'undefined') {
|
|
11550
|
+
userData = options.userData;
|
|
11551
|
+
}
|
|
11552
|
+
|
|
11553
|
+
function createProxy(target, path) {
|
|
11554
|
+
|
|
11555
|
+
// avoid creating a new object between two traps
|
|
11556
|
+
const context = { rootTarget, path };
|
|
11557
|
+
Object.assign(context, userData);
|
|
11558
|
+
|
|
11559
|
+
const realTraps = {};
|
|
11560
|
+
|
|
11561
|
+
for (const trapName of trapNames) {
|
|
11562
|
+
const keyParamIdx = keys[trapName]
|
|
11563
|
+
, trap = traps[trapName];
|
|
11564
|
+
|
|
11565
|
+
if (typeof trap !== 'undefined') {
|
|
11566
|
+
|
|
11567
|
+
if (typeof keyParamIdx !== 'undefined') {
|
|
11568
|
+
|
|
11569
|
+
realTraps[trapName] = function () {
|
|
11570
|
+
|
|
11571
|
+
const key = arguments[keyParamIdx];
|
|
11572
|
+
|
|
11573
|
+
// update context for this trap
|
|
11574
|
+
context.nest = function (nestedTarget) {
|
|
11575
|
+
if (nestedTarget === undefined)
|
|
11576
|
+
nestedTarget = rootTarget;
|
|
11577
|
+
return createProxy(nestedTarget, push(path, key));
|
|
11578
|
+
};
|
|
11579
|
+
|
|
11580
|
+
return trap.apply(context, arguments);
|
|
11581
|
+
};
|
|
11582
|
+
} else {
|
|
11583
|
+
|
|
11584
|
+
realTraps[trapName] = function () {
|
|
11585
|
+
|
|
11586
|
+
// update context for this trap
|
|
11587
|
+
context.nest = function (nestedTarget) {
|
|
11588
|
+
if (nestedTarget === undefined)
|
|
11589
|
+
nestedTarget = {};
|
|
11590
|
+
return createProxy(nestedTarget, path);
|
|
11591
|
+
};
|
|
11592
|
+
|
|
11593
|
+
return trap.apply(context, arguments);
|
|
11594
|
+
};
|
|
11595
|
+
}
|
|
11596
|
+
}
|
|
11597
|
+
}
|
|
11598
|
+
|
|
11599
|
+
return new Proxy(target, realTraps);
|
|
11600
|
+
}
|
|
11601
|
+
|
|
11602
|
+
return createProxy(rootTarget, path);
|
|
11603
|
+
|
|
11604
|
+
}
|
|
11605
|
+
|
|
11606
|
+
var RoomSubscriptionMode;
|
|
11607
|
+
(function (RoomSubscriptionMode) {
|
|
11608
|
+
RoomSubscriptionMode["Read"] = "r";
|
|
11609
|
+
RoomSubscriptionMode["Write"] = "w";
|
|
11610
|
+
RoomSubscriptionMode["ReadWrite"] = "b";
|
|
11611
|
+
})(RoomSubscriptionMode || (RoomSubscriptionMode = {}));
|
|
11612
|
+
|
|
11613
|
+
class KokimokiStore {
|
|
11614
|
+
roomName;
|
|
11615
|
+
mode;
|
|
11616
|
+
doc;
|
|
11617
|
+
proxy;
|
|
11618
|
+
root;
|
|
11619
|
+
defaultValue;
|
|
11620
|
+
docRoot;
|
|
11621
|
+
constructor(roomName, schema, mode = RoomSubscriptionMode.ReadWrite) {
|
|
11622
|
+
this.roomName = roomName;
|
|
11623
|
+
this.mode = mode;
|
|
11624
|
+
// Construct Y doc
|
|
11625
|
+
this.doc = new Doc();
|
|
11626
|
+
this.docRoot = this.doc.getMap("root");
|
|
11627
|
+
// Construct proxy object
|
|
11628
|
+
this.proxy = proxy();
|
|
11629
|
+
// @ts-ignore
|
|
11630
|
+
f(this.proxy, this.docRoot);
|
|
11631
|
+
// Create root proxy
|
|
11632
|
+
const store = this;
|
|
11633
|
+
// @ts-ignore
|
|
11634
|
+
this.root = new DeepProxy(this.proxy, {
|
|
11635
|
+
get() {
|
|
11636
|
+
return this.nest(function () { });
|
|
11637
|
+
},
|
|
11638
|
+
apply() {
|
|
11639
|
+
let obj = store.proxy;
|
|
11640
|
+
for (let i = 0; i < this.path.length - 1; i++) {
|
|
11641
|
+
obj = obj[this.path[i]];
|
|
11642
|
+
}
|
|
11643
|
+
return {
|
|
11644
|
+
roomName: store.roomName,
|
|
11645
|
+
doc: store.doc,
|
|
11646
|
+
path: this.path,
|
|
11647
|
+
obj,
|
|
11648
|
+
key: this.path[this.path.length - 1],
|
|
11649
|
+
};
|
|
11650
|
+
},
|
|
11651
|
+
});
|
|
11652
|
+
// Set default value
|
|
11653
|
+
this.defaultValue = schema.defaultValue;
|
|
11654
|
+
}
|
|
11655
|
+
get() {
|
|
11656
|
+
return this.proxy;
|
|
11657
|
+
}
|
|
11658
|
+
subscribe(set) {
|
|
11659
|
+
// @ts-ignore
|
|
11660
|
+
const handler = () => set(this.proxy);
|
|
11661
|
+
this.doc.on("update", handler);
|
|
11662
|
+
// @ts-ignore
|
|
11663
|
+
set(this.proxy);
|
|
11664
|
+
return () => this.doc.off("update", handler);
|
|
11665
|
+
}
|
|
11666
|
+
async onJoin(client) { }
|
|
11667
|
+
async onBeforeLeave(client) { }
|
|
11668
|
+
async onLeave(client) { }
|
|
11669
|
+
}
|
|
11670
|
+
|
|
11502
11671
|
var WsMessageType;
|
|
11503
11672
|
(function (WsMessageType) {
|
|
11504
11673
|
WsMessageType[WsMessageType["SubscribeReq"] = 1] = "SubscribeReq";
|
|
@@ -11595,13 +11764,6 @@ class WsMessageReader {
|
|
|
11595
11764
|
}
|
|
11596
11765
|
}
|
|
11597
11766
|
|
|
11598
|
-
var RoomSubscriptionMode;
|
|
11599
|
-
(function (RoomSubscriptionMode) {
|
|
11600
|
-
RoomSubscriptionMode["Read"] = "r";
|
|
11601
|
-
RoomSubscriptionMode["Write"] = "w";
|
|
11602
|
-
RoomSubscriptionMode["ReadWrite"] = "b";
|
|
11603
|
-
})(RoomSubscriptionMode || (RoomSubscriptionMode = {}));
|
|
11604
|
-
|
|
11605
11767
|
class RoomSubscription {
|
|
11606
11768
|
kmClient;
|
|
11607
11769
|
store;
|
|
@@ -11650,925 +11812,937 @@ class RoomSubscription {
|
|
|
11650
11812
|
}
|
|
11651
11813
|
}
|
|
11652
11814
|
|
|
11653
|
-
|
|
11654
|
-
|
|
11655
|
-
|
|
11656
|
-
code;
|
|
11657
|
-
_wsUrl;
|
|
11658
|
-
_apiUrl;
|
|
11659
|
-
_id;
|
|
11660
|
-
_connectionId;
|
|
11661
|
-
_token;
|
|
11662
|
-
_apiHeaders;
|
|
11663
|
-
_serverTimeOffset = 0;
|
|
11664
|
-
_clientContext;
|
|
11665
|
-
_ws;
|
|
11666
|
-
_subscriptionsByName = new Map();
|
|
11667
|
-
_subscriptionsByHash = new Map();
|
|
11668
|
-
_subscribeReqPromises = new Map();
|
|
11669
|
-
_unsubscribeReqPromises = new Map();
|
|
11670
|
-
_transactionPromises = new Map();
|
|
11671
|
-
_connected = false;
|
|
11672
|
-
_connectPromise;
|
|
11673
|
-
_messageId = 0;
|
|
11674
|
-
_autoReconnect = true;
|
|
11675
|
-
_reconnectTimeout = 0;
|
|
11676
|
-
_pingInterval;
|
|
11677
|
-
constructor(host, appId, code = "") {
|
|
11678
|
-
super();
|
|
11679
|
-
this.host = host;
|
|
11680
|
-
this.appId = appId;
|
|
11681
|
-
this.code = code;
|
|
11682
|
-
// Set up the URLs
|
|
11683
|
-
const secure = this.host.indexOf(":") === -1;
|
|
11684
|
-
this._wsUrl = `ws${secure ? "s" : ""}://${this.host}`;
|
|
11685
|
-
this._apiUrl = `http${secure ? "s" : ""}://${this.host}`;
|
|
11686
|
-
// Set up ping interval
|
|
11687
|
-
const pingMsg = new WsMessageWriter();
|
|
11688
|
-
pingMsg.writeInt32(WsMessageType.Ping);
|
|
11689
|
-
const pingBuffer = pingMsg.getBuffer();
|
|
11690
|
-
this._pingInterval = setInterval(() => {
|
|
11691
|
-
if (this.connected) {
|
|
11692
|
-
this.ws.send(pingBuffer);
|
|
11693
|
-
}
|
|
11694
|
-
}, 5000);
|
|
11695
|
-
}
|
|
11696
|
-
get id() {
|
|
11697
|
-
if (!this._id) {
|
|
11698
|
-
throw new Error("Client not connected");
|
|
11699
|
-
}
|
|
11700
|
-
return this._id;
|
|
11815
|
+
var KokimokiSchema;
|
|
11816
|
+
(function (KokimokiSchema) {
|
|
11817
|
+
class Generic {
|
|
11701
11818
|
}
|
|
11702
|
-
|
|
11703
|
-
|
|
11704
|
-
|
|
11819
|
+
KokimokiSchema.Generic = Generic;
|
|
11820
|
+
class Number extends Generic {
|
|
11821
|
+
defaultValue;
|
|
11822
|
+
constructor(defaultValue = 0) {
|
|
11823
|
+
super();
|
|
11824
|
+
this.defaultValue = defaultValue;
|
|
11705
11825
|
}
|
|
11706
|
-
return this._connectionId;
|
|
11707
11826
|
}
|
|
11708
|
-
|
|
11709
|
-
|
|
11710
|
-
|
|
11711
|
-
}
|
|
11712
|
-
return this._token;
|
|
11827
|
+
KokimokiSchema.Number = Number;
|
|
11828
|
+
function number(defaultValue = 0) {
|
|
11829
|
+
return new Number(defaultValue);
|
|
11713
11830
|
}
|
|
11714
|
-
|
|
11715
|
-
|
|
11716
|
-
|
|
11831
|
+
KokimokiSchema.number = number;
|
|
11832
|
+
class String extends Generic {
|
|
11833
|
+
defaultValue;
|
|
11834
|
+
constructor(defaultValue = "") {
|
|
11835
|
+
super();
|
|
11836
|
+
this.defaultValue = defaultValue;
|
|
11717
11837
|
}
|
|
11718
|
-
return this._apiUrl;
|
|
11719
11838
|
}
|
|
11720
|
-
|
|
11721
|
-
|
|
11722
|
-
|
|
11723
|
-
}
|
|
11724
|
-
return this._apiHeaders;
|
|
11839
|
+
KokimokiSchema.String = String;
|
|
11840
|
+
function string(defaultValue = "") {
|
|
11841
|
+
return new String(defaultValue);
|
|
11725
11842
|
}
|
|
11726
|
-
|
|
11727
|
-
|
|
11728
|
-
|
|
11843
|
+
KokimokiSchema.string = string;
|
|
11844
|
+
class Boolean extends Generic {
|
|
11845
|
+
defaultValue;
|
|
11846
|
+
constructor(defaultValue = false) {
|
|
11847
|
+
super();
|
|
11848
|
+
this.defaultValue = defaultValue;
|
|
11729
11849
|
}
|
|
11730
|
-
return this._clientContext;
|
|
11731
|
-
}
|
|
11732
|
-
get connected() {
|
|
11733
|
-
return this._connected;
|
|
11734
11850
|
}
|
|
11735
|
-
|
|
11736
|
-
|
|
11737
|
-
|
|
11738
|
-
}
|
|
11739
|
-
return this._ws;
|
|
11851
|
+
KokimokiSchema.Boolean = Boolean;
|
|
11852
|
+
function boolean(defaultValue = false) {
|
|
11853
|
+
return new Boolean(defaultValue);
|
|
11740
11854
|
}
|
|
11741
|
-
|
|
11742
|
-
|
|
11743
|
-
|
|
11744
|
-
|
|
11745
|
-
|
|
11746
|
-
|
|
11747
|
-
// Close previous connection in hot-reload scenarios
|
|
11748
|
-
if (window) {
|
|
11749
|
-
if (!window.__KOKIMOKI_WS__) {
|
|
11750
|
-
window.__KOKIMOKI_WS__ = {};
|
|
11751
|
-
}
|
|
11752
|
-
if (this.appId in window.__KOKIMOKI_WS__) {
|
|
11753
|
-
console.log(`[Kokimoki] Closing previous connection for ${this.appId}`);
|
|
11754
|
-
window.__KOKIMOKI_WS__[this.appId].close();
|
|
11755
|
-
}
|
|
11756
|
-
window.__KOKIMOKI_WS__[this.appId] = this;
|
|
11855
|
+
KokimokiSchema.boolean = boolean;
|
|
11856
|
+
class Struct extends Generic {
|
|
11857
|
+
fields;
|
|
11858
|
+
constructor(fields) {
|
|
11859
|
+
super();
|
|
11860
|
+
this.fields = fields;
|
|
11757
11861
|
}
|
|
11758
|
-
|
|
11759
|
-
|
|
11760
|
-
|
|
11761
|
-
|
|
11762
|
-
|
|
11763
|
-
this.ws.onopen = () => {
|
|
11764
|
-
this.ws.send(JSON.stringify({ type: "auth", code: this.code, token: clientToken }));
|
|
11765
|
-
};
|
|
11766
|
-
this.ws.onclose = () => {
|
|
11767
|
-
this._connected = false;
|
|
11768
|
-
this._connectPromise = undefined;
|
|
11769
|
-
this._ws.onmessage = null;
|
|
11770
|
-
this._ws = undefined;
|
|
11771
|
-
if (window && window.__KOKIMOKI_WS__) {
|
|
11772
|
-
delete window.__KOKIMOKI_WS__[this.appId];
|
|
11773
|
-
}
|
|
11774
|
-
// Clean up
|
|
11775
|
-
this._subscribeReqPromises.clear();
|
|
11776
|
-
this._transactionPromises.clear();
|
|
11777
|
-
// Attempt to reconnect
|
|
11778
|
-
if (!this._autoReconnect) {
|
|
11779
|
-
return;
|
|
11780
|
-
}
|
|
11781
|
-
console.log(`[Kokimoki] Connection lost, attempting to reconnect in ${this._reconnectTimeout} seconds...`);
|
|
11782
|
-
setTimeout(async () => await this.connect(), this._reconnectTimeout * 1000);
|
|
11783
|
-
this._reconnectTimeout = Math.min(3, this._reconnectTimeout + 1);
|
|
11784
|
-
// Emit disconnected event
|
|
11785
|
-
this.emit("disconnected");
|
|
11786
|
-
};
|
|
11787
|
-
this.ws.onmessage = (e) => {
|
|
11788
|
-
// console.log(`Received WS message: ${e.data}`);
|
|
11789
|
-
// Handle JSON messages
|
|
11790
|
-
if (typeof e.data === "string") {
|
|
11791
|
-
const message = JSON.parse(e.data);
|
|
11792
|
-
switch (message.type) {
|
|
11793
|
-
case "init":
|
|
11794
|
-
this.handleInitMessage(message);
|
|
11795
|
-
onInit();
|
|
11796
|
-
break;
|
|
11797
|
-
}
|
|
11798
|
-
return;
|
|
11799
|
-
}
|
|
11800
|
-
// Handle binary messages
|
|
11801
|
-
this.handleBinaryMessage(e.data);
|
|
11802
|
-
};
|
|
11803
|
-
});
|
|
11804
|
-
await this._connectPromise;
|
|
11805
|
-
this._connected = true;
|
|
11806
|
-
// Connection established
|
|
11807
|
-
console.log(`[Kokimoki] Client id: ${this.id}`);
|
|
11808
|
-
console.log(`[Kokimoki] Client context:`, this.clientContext);
|
|
11809
|
-
// Restore subscriptions if reconnected
|
|
11810
|
-
const roomNames = Array.from(this._subscriptionsByName.keys()).map((name) => `"${name}"`);
|
|
11811
|
-
if (roomNames.length) {
|
|
11812
|
-
console.log(`[Kokimoki] Restoring subscriptions: ${roomNames}`);
|
|
11862
|
+
get defaultValue() {
|
|
11863
|
+
return Object.entries(this.fields).reduce((acc, [key, field]) => {
|
|
11864
|
+
acc[key] = field.defaultValue;
|
|
11865
|
+
return acc;
|
|
11866
|
+
}, {});
|
|
11813
11867
|
}
|
|
11814
|
-
|
|
11815
|
-
|
|
11816
|
-
|
|
11817
|
-
}
|
|
11818
|
-
catch (err) {
|
|
11819
|
-
console.error(`[Kokimoki] Failed to restore subscription for "${subscription.roomName}":`, err);
|
|
11868
|
+
set defaultValue(value) {
|
|
11869
|
+
for (const [key, field] of Object.entries(this.fields)) {
|
|
11870
|
+
field.defaultValue = value[key];
|
|
11820
11871
|
}
|
|
11821
11872
|
}
|
|
11822
|
-
// Emit connected event
|
|
11823
|
-
this._reconnectTimeout = 0;
|
|
11824
|
-
this.emit("connected");
|
|
11825
11873
|
}
|
|
11826
|
-
|
|
11827
|
-
|
|
11828
|
-
|
|
11829
|
-
this._connectionId = message.id;
|
|
11830
|
-
this._token = message.appToken;
|
|
11831
|
-
this._clientContext = message.clientContext;
|
|
11832
|
-
// Set up the auth headers
|
|
11833
|
-
this._apiHeaders = new Headers({
|
|
11834
|
-
Authorization: `Bearer ${this.token}`,
|
|
11835
|
-
"Content-Type": "application/json",
|
|
11836
|
-
});
|
|
11874
|
+
KokimokiSchema.Struct = Struct;
|
|
11875
|
+
function struct(schema) {
|
|
11876
|
+
return new Struct(schema);
|
|
11837
11877
|
}
|
|
11838
|
-
|
|
11839
|
-
|
|
11840
|
-
|
|
11841
|
-
|
|
11842
|
-
|
|
11843
|
-
|
|
11844
|
-
|
|
11845
|
-
case WsMessageType.UnsubscribeRes:
|
|
11846
|
-
this.handleUnsubscribeResMessage(reader);
|
|
11847
|
-
break;
|
|
11848
|
-
case WsMessageType.RoomUpdate:
|
|
11849
|
-
this.handleRoomUpdateMessage(reader);
|
|
11850
|
-
break;
|
|
11851
|
-
case WsMessageType.Error:
|
|
11852
|
-
this.handleErrorMessage(reader);
|
|
11853
|
-
break;
|
|
11854
|
-
case WsMessageType.Pong: {
|
|
11855
|
-
const s = reader.readInt32();
|
|
11856
|
-
const ms = reader.readInt32();
|
|
11857
|
-
this._serverTimeOffset = Date.now() - s * 1000 - ms;
|
|
11858
|
-
break;
|
|
11859
|
-
}
|
|
11878
|
+
KokimokiSchema.struct = struct;
|
|
11879
|
+
class Dict {
|
|
11880
|
+
schema;
|
|
11881
|
+
defaultValue;
|
|
11882
|
+
constructor(schema, defaultValue = {}) {
|
|
11883
|
+
this.schema = schema;
|
|
11884
|
+
this.defaultValue = defaultValue;
|
|
11860
11885
|
}
|
|
11861
11886
|
}
|
|
11862
|
-
|
|
11863
|
-
|
|
11864
|
-
|
|
11865
|
-
const subscribeReqPromise = this._subscribeReqPromises.get(reqId);
|
|
11866
|
-
const transactionPromise = this._transactionPromises.get(reqId);
|
|
11867
|
-
if (subscribeReqPromise) {
|
|
11868
|
-
this._subscribeReqPromises.delete(reqId);
|
|
11869
|
-
subscribeReqPromise.reject(error);
|
|
11870
|
-
}
|
|
11871
|
-
else if (transactionPromise) {
|
|
11872
|
-
this._transactionPromises.delete(reqId);
|
|
11873
|
-
transactionPromise.reject(error);
|
|
11874
|
-
}
|
|
11875
|
-
else {
|
|
11876
|
-
console.warn(`Received error for unknown request ${reqId}: ${error}`);
|
|
11877
|
-
}
|
|
11887
|
+
KokimokiSchema.Dict = Dict;
|
|
11888
|
+
function dict(schema, defaultValue = {}) {
|
|
11889
|
+
return new Dict(schema, defaultValue);
|
|
11878
11890
|
}
|
|
11879
|
-
|
|
11880
|
-
|
|
11881
|
-
|
|
11882
|
-
|
|
11883
|
-
|
|
11884
|
-
|
|
11885
|
-
|
|
11886
|
-
|
|
11887
|
-
promise.resolve(roomHash, msg.readUint8Array());
|
|
11888
|
-
}
|
|
11889
|
-
else {
|
|
11890
|
-
promise.resolve(roomHash);
|
|
11891
|
-
}
|
|
11891
|
+
KokimokiSchema.dict = dict;
|
|
11892
|
+
class List extends Generic {
|
|
11893
|
+
schema;
|
|
11894
|
+
defaultValue;
|
|
11895
|
+
constructor(schema, defaultValue = []) {
|
|
11896
|
+
super();
|
|
11897
|
+
this.schema = schema;
|
|
11898
|
+
this.defaultValue = defaultValue;
|
|
11892
11899
|
}
|
|
11893
11900
|
}
|
|
11894
|
-
|
|
11895
|
-
|
|
11896
|
-
|
|
11897
|
-
if (promise) {
|
|
11898
|
-
this._unsubscribeReqPromises.delete(reqId);
|
|
11899
|
-
promise.resolve();
|
|
11900
|
-
}
|
|
11901
|
+
KokimokiSchema.List = List;
|
|
11902
|
+
function list(schema, defaultValue = []) {
|
|
11903
|
+
return new List(schema, defaultValue);
|
|
11901
11904
|
}
|
|
11902
|
-
|
|
11903
|
-
|
|
11904
|
-
|
|
11905
|
-
|
|
11906
|
-
|
|
11907
|
-
|
|
11908
|
-
|
|
11909
|
-
|
|
11910
|
-
|
|
11911
|
-
|
|
11912
|
-
|
|
11913
|
-
}
|
|
11914
|
-
}
|
|
11915
|
-
// Check transaction resolves
|
|
11916
|
-
for (const [transactionId, { resolve },] of this._transactionPromises.entries()) {
|
|
11917
|
-
if (appliedId >= transactionId) {
|
|
11918
|
-
this._transactionPromises.delete(transactionId);
|
|
11919
|
-
resolve();
|
|
11920
|
-
}
|
|
11905
|
+
KokimokiSchema.list = list;
|
|
11906
|
+
/**
|
|
11907
|
+
* Nullable
|
|
11908
|
+
*/
|
|
11909
|
+
class Nullable extends Generic {
|
|
11910
|
+
schema;
|
|
11911
|
+
defaultValue;
|
|
11912
|
+
constructor(schema, defaultValue) {
|
|
11913
|
+
super();
|
|
11914
|
+
this.schema = schema;
|
|
11915
|
+
this.defaultValue = defaultValue;
|
|
11921
11916
|
}
|
|
11922
11917
|
}
|
|
11923
|
-
|
|
11924
|
-
|
|
11918
|
+
KokimokiSchema.Nullable = Nullable;
|
|
11919
|
+
function nullable(schema, defaultValue = null) {
|
|
11920
|
+
return new Nullable(schema, defaultValue);
|
|
11925
11921
|
}
|
|
11926
|
-
|
|
11927
|
-
|
|
11928
|
-
|
|
11929
|
-
|
|
11930
|
-
|
|
11931
|
-
|
|
11932
|
-
|
|
11933
|
-
|
|
11934
|
-
}
|
|
11935
|
-
// Storage
|
|
11936
|
-
async createUpload(name, blob, tags) {
|
|
11937
|
-
const res = await fetch(`${this._apiUrl}/uploads`, {
|
|
11938
|
-
method: "POST",
|
|
11939
|
-
headers: this.apiHeaders,
|
|
11940
|
-
body: JSON.stringify({
|
|
11941
|
-
name,
|
|
11942
|
-
size: blob.size,
|
|
11943
|
-
mimeType: blob.type,
|
|
11944
|
-
tags,
|
|
11945
|
-
}),
|
|
11946
|
-
});
|
|
11947
|
-
return await res.json();
|
|
11948
|
-
}
|
|
11949
|
-
async uploadChunks(blob, chunkSize, signedUrls) {
|
|
11950
|
-
return await Promise.all(signedUrls.map(async (url, index) => {
|
|
11951
|
-
const start = index * chunkSize;
|
|
11952
|
-
const end = Math.min(start + chunkSize, blob.size);
|
|
11953
|
-
const chunk = blob.slice(start, end);
|
|
11954
|
-
const res = await fetch(url, { method: "PUT", body: chunk });
|
|
11955
|
-
return JSON.parse(res.headers.get("ETag") || '""');
|
|
11956
|
-
}));
|
|
11957
|
-
}
|
|
11958
|
-
async completeUpload(id, etags) {
|
|
11959
|
-
const res = await fetch(`${this._apiUrl}/uploads/${id}`, {
|
|
11960
|
-
method: "PUT",
|
|
11961
|
-
headers: this.apiHeaders,
|
|
11962
|
-
body: JSON.stringify({ etags }),
|
|
11963
|
-
});
|
|
11964
|
-
return await res.json();
|
|
11965
|
-
}
|
|
11966
|
-
async upload(name, blob, tags = []) {
|
|
11967
|
-
const { id, chunkSize, urls } = await this.createUpload(name, blob, tags);
|
|
11968
|
-
const etags = await this.uploadChunks(blob, chunkSize, urls);
|
|
11969
|
-
return await this.completeUpload(id, etags);
|
|
11970
|
-
}
|
|
11971
|
-
async updateUpload(id, update) {
|
|
11972
|
-
const res = await fetch(`${this._apiUrl}/uploads/${id}`, {
|
|
11973
|
-
method: "PUT",
|
|
11974
|
-
headers: this.apiHeaders,
|
|
11975
|
-
body: JSON.stringify(update),
|
|
11976
|
-
});
|
|
11977
|
-
return await res.json();
|
|
11978
|
-
}
|
|
11979
|
-
async listUploads(filter = {}, skip = 0, limit = 100) {
|
|
11980
|
-
const url = new URL("/uploads", this._apiUrl);
|
|
11981
|
-
url.searchParams.set("skip", skip.toString());
|
|
11982
|
-
url.searchParams.set("limit", limit.toString());
|
|
11983
|
-
if (filter.clientId) {
|
|
11984
|
-
url.searchParams.set("clientId", filter.clientId);
|
|
11985
|
-
}
|
|
11986
|
-
if (filter.mimeTypes) {
|
|
11987
|
-
url.searchParams.set("mimeTypes", filter.mimeTypes.join());
|
|
11988
|
-
}
|
|
11989
|
-
if (filter.tags) {
|
|
11990
|
-
url.searchParams.set("tags", filter.tags.join());
|
|
11991
|
-
}
|
|
11992
|
-
const res = await fetch(url.href, {
|
|
11993
|
-
headers: this.apiHeaders,
|
|
11994
|
-
});
|
|
11995
|
-
return await res.json();
|
|
11996
|
-
}
|
|
11997
|
-
async deleteUpload(id) {
|
|
11998
|
-
const res = await fetch(`${this._apiUrl}/uploads/${id}`, {
|
|
11999
|
-
method: "DELETE",
|
|
12000
|
-
headers: this.apiHeaders,
|
|
12001
|
-
});
|
|
12002
|
-
return await res.json();
|
|
12003
|
-
}
|
|
12004
|
-
async exposeScriptingContext(context) {
|
|
12005
|
-
// @ts-ignore
|
|
12006
|
-
window.KM_SCRIPTING_CONTEXT = context;
|
|
12007
|
-
// @ts-ignore
|
|
12008
|
-
window.dispatchEvent(new CustomEvent("km:scriptingContextExposed"));
|
|
12009
|
-
}
|
|
12010
|
-
async sendSubscribeReq(roomName, mode) {
|
|
12011
|
-
// Set up sync resolver
|
|
12012
|
-
const reqId = ++this._messageId;
|
|
12013
|
-
return await new Promise((resolve, reject) => {
|
|
12014
|
-
this._subscribeReqPromises.set(reqId, {
|
|
12015
|
-
resolve: (roomHash, initialUpdate) => {
|
|
12016
|
-
resolve({ roomHash, initialUpdate });
|
|
12017
|
-
},
|
|
12018
|
-
reject,
|
|
12019
|
-
});
|
|
12020
|
-
// Send subscription request
|
|
12021
|
-
const msg = new WsMessageWriter();
|
|
12022
|
-
msg.writeInt32(WsMessageType.SubscribeReq);
|
|
12023
|
-
msg.writeInt32(reqId);
|
|
12024
|
-
msg.writeString(roomName);
|
|
12025
|
-
msg.writeChar(mode);
|
|
12026
|
-
this.ws.send(msg.getBuffer());
|
|
12027
|
-
});
|
|
12028
|
-
}
|
|
12029
|
-
async sendUnsubscribeReq(roomHash) {
|
|
12030
|
-
const reqId = ++this._messageId;
|
|
12031
|
-
return await new Promise((resolve, reject) => {
|
|
12032
|
-
this._unsubscribeReqPromises.set(reqId, {
|
|
12033
|
-
resolve,
|
|
12034
|
-
reject,
|
|
12035
|
-
});
|
|
12036
|
-
// Send unsubscribe request
|
|
12037
|
-
const msg = new WsMessageWriter();
|
|
12038
|
-
msg.writeInt32(WsMessageType.UnsubscribeReq);
|
|
12039
|
-
msg.writeInt32(reqId);
|
|
12040
|
-
msg.writeUint32(roomHash);
|
|
12041
|
-
this.ws.send(msg.getBuffer());
|
|
12042
|
-
});
|
|
12043
|
-
}
|
|
12044
|
-
async join(store) {
|
|
12045
|
-
let subscription = this._subscriptionsByName.get(store.roomName);
|
|
12046
|
-
if (!subscription) {
|
|
12047
|
-
subscription = new RoomSubscription(this, store);
|
|
12048
|
-
this._subscriptionsByName.set(store.roomName, subscription);
|
|
12049
|
-
}
|
|
12050
|
-
// Send subscription request if connected to server
|
|
12051
|
-
if (!subscription.joined) {
|
|
12052
|
-
const res = await this.sendSubscribeReq(store.roomName, store.mode);
|
|
12053
|
-
this._subscriptionsByHash.set(res.roomHash, subscription);
|
|
12054
|
-
await subscription.applyInitialResponse(res.roomHash, res.initialUpdate);
|
|
12055
|
-
// Trigger onJoin event
|
|
12056
|
-
store.onJoin(this);
|
|
12057
|
-
}
|
|
12058
|
-
}
|
|
12059
|
-
async leave(store) {
|
|
12060
|
-
const subscription = this._subscriptionsByName.get(store.roomName);
|
|
12061
|
-
if (subscription) {
|
|
12062
|
-
await store.onBeforeLeave(this);
|
|
12063
|
-
await this.sendUnsubscribeReq(subscription.roomHash);
|
|
12064
|
-
this._subscriptionsByName.delete(store.roomName);
|
|
12065
|
-
this._subscriptionsByHash.delete(subscription.roomHash);
|
|
12066
|
-
// Trigger onLeave event
|
|
12067
|
-
store.onLeave(this);
|
|
12068
|
-
}
|
|
12069
|
-
}
|
|
12070
|
-
async transact(handler) {
|
|
12071
|
-
if (!this._connected) {
|
|
12072
|
-
throw new Error("Client not connected");
|
|
12073
|
-
}
|
|
12074
|
-
const transaction = new KokimokiTransaction(this);
|
|
12075
|
-
handler(transaction);
|
|
12076
|
-
const { updates, consumedMessages } = await transaction.getUpdates();
|
|
12077
|
-
if (!updates.length) {
|
|
12078
|
-
return;
|
|
12079
|
-
}
|
|
12080
|
-
// Construct buffer
|
|
12081
|
-
const writer = new WsMessageWriter();
|
|
12082
|
-
// Write message type
|
|
12083
|
-
writer.writeInt32(WsMessageType.Transaction);
|
|
12084
|
-
// Update and write transaction ID
|
|
12085
|
-
const transactionId = ++this._messageId;
|
|
12086
|
-
writer.writeInt32(transactionId);
|
|
12087
|
-
// Write room hashes where messages were consumed
|
|
12088
|
-
writer.writeInt32(consumedMessages.size);
|
|
12089
|
-
for (const roomName of consumedMessages) {
|
|
12090
|
-
const subscription = this._subscriptionsByName.get(roomName);
|
|
12091
|
-
if (!subscription) {
|
|
12092
|
-
throw new Error(`Cannot consume message in "${roomName}" because it hasn't been joined`);
|
|
12093
|
-
}
|
|
12094
|
-
writer.writeUint32(subscription.roomHash);
|
|
12095
|
-
}
|
|
12096
|
-
// Write updates
|
|
12097
|
-
for (const { roomName, update } of updates) {
|
|
12098
|
-
const subscription = this._subscriptionsByName.get(roomName);
|
|
12099
|
-
if (!subscription) {
|
|
12100
|
-
throw new Error(`Cannot send update to "${roomName}" because it hasn't been joined`);
|
|
12101
|
-
}
|
|
12102
|
-
writer.writeUint32(subscription.roomHash);
|
|
12103
|
-
writer.writeUint8Array(update);
|
|
12104
|
-
}
|
|
12105
|
-
const buffer = writer.getBuffer();
|
|
12106
|
-
// Wait for server to apply transaction
|
|
12107
|
-
await new Promise((resolve, reject) => {
|
|
12108
|
-
this._transactionPromises.set(transactionId, { resolve, reject });
|
|
12109
|
-
// Send update to server
|
|
12110
|
-
try {
|
|
12111
|
-
this.ws.send(buffer);
|
|
12112
|
-
}
|
|
12113
|
-
catch (e) {
|
|
12114
|
-
// Not connected
|
|
12115
|
-
console.log("Failed to send update to server:", e);
|
|
12116
|
-
// TODO: merge updates or something
|
|
12117
|
-
throw e;
|
|
12118
|
-
}
|
|
12119
|
-
});
|
|
12120
|
-
/* // Reset doc in Write-only mode
|
|
12121
|
-
for (const { roomName, update } of updates) {
|
|
12122
|
-
const mode = this._subscriptions.get(roomName);
|
|
12123
|
-
|
|
12124
|
-
if (mode === RoomSubscriptionMode.Write) {
|
|
12125
|
-
// @ts-ignore
|
|
12126
|
-
// this._stores.get(this._roomHashes.get(roomName)!)!.doc = new Y.Doc();
|
|
12127
|
-
}
|
|
12128
|
-
} */
|
|
12129
|
-
}
|
|
12130
|
-
async close() {
|
|
12131
|
-
this._autoReconnect = false;
|
|
12132
|
-
if (this._ws) {
|
|
12133
|
-
this._ws.close();
|
|
12134
|
-
}
|
|
12135
|
-
if (this._pingInterval) {
|
|
12136
|
-
clearInterval(this._pingInterval);
|
|
11922
|
+
KokimokiSchema.nullable = nullable;
|
|
11923
|
+
class StringEnum extends Generic {
|
|
11924
|
+
options;
|
|
11925
|
+
defaultValue;
|
|
11926
|
+
constructor(options, defaultValue) {
|
|
11927
|
+
super();
|
|
11928
|
+
this.options = options;
|
|
11929
|
+
this.defaultValue = defaultValue;
|
|
12137
11930
|
}
|
|
12138
11931
|
}
|
|
12139
|
-
|
|
12140
|
-
|
|
12141
|
-
|
|
12142
|
-
(function (KokimokiSchema) {
|
|
12143
|
-
class Generic {
|
|
11932
|
+
KokimokiSchema.StringEnum = StringEnum;
|
|
11933
|
+
function stringEnum(options, defaultValue) {
|
|
11934
|
+
return new StringEnum(options, defaultValue);
|
|
12144
11935
|
}
|
|
12145
|
-
KokimokiSchema.
|
|
12146
|
-
class
|
|
11936
|
+
KokimokiSchema.stringEnum = stringEnum;
|
|
11937
|
+
class StringConst extends Generic {
|
|
12147
11938
|
defaultValue;
|
|
12148
|
-
constructor(defaultValue
|
|
11939
|
+
constructor(defaultValue) {
|
|
12149
11940
|
super();
|
|
12150
11941
|
this.defaultValue = defaultValue;
|
|
12151
11942
|
}
|
|
12152
11943
|
}
|
|
12153
|
-
KokimokiSchema.
|
|
12154
|
-
function
|
|
12155
|
-
return new
|
|
11944
|
+
KokimokiSchema.StringConst = StringConst;
|
|
11945
|
+
function stringConst(defaultValue) {
|
|
11946
|
+
return new StringConst(defaultValue);
|
|
12156
11947
|
}
|
|
12157
|
-
KokimokiSchema.
|
|
12158
|
-
class
|
|
11948
|
+
KokimokiSchema.stringConst = stringConst;
|
|
11949
|
+
class AnyOf extends Generic {
|
|
11950
|
+
schemas;
|
|
12159
11951
|
defaultValue;
|
|
12160
|
-
constructor(defaultValue
|
|
11952
|
+
constructor(schemas, defaultValue) {
|
|
12161
11953
|
super();
|
|
11954
|
+
this.schemas = schemas;
|
|
12162
11955
|
this.defaultValue = defaultValue;
|
|
12163
11956
|
}
|
|
12164
11957
|
}
|
|
12165
|
-
KokimokiSchema.
|
|
12166
|
-
function
|
|
12167
|
-
return new
|
|
11958
|
+
KokimokiSchema.AnyOf = AnyOf;
|
|
11959
|
+
function anyOf(schemas, defaultValue) {
|
|
11960
|
+
return new AnyOf(schemas, defaultValue);
|
|
12168
11961
|
}
|
|
12169
|
-
KokimokiSchema.
|
|
12170
|
-
class
|
|
11962
|
+
KokimokiSchema.anyOf = anyOf;
|
|
11963
|
+
class Optional extends Generic {
|
|
11964
|
+
schema;
|
|
12171
11965
|
defaultValue;
|
|
12172
|
-
constructor(defaultValue =
|
|
11966
|
+
constructor(schema, defaultValue = undefined) {
|
|
12173
11967
|
super();
|
|
11968
|
+
this.schema = schema;
|
|
12174
11969
|
this.defaultValue = defaultValue;
|
|
12175
11970
|
}
|
|
12176
11971
|
}
|
|
12177
|
-
KokimokiSchema.
|
|
12178
|
-
function
|
|
12179
|
-
return new
|
|
11972
|
+
KokimokiSchema.Optional = Optional;
|
|
11973
|
+
function optional(schema, defaultValue = undefined) {
|
|
11974
|
+
return new Optional(schema, defaultValue);
|
|
12180
11975
|
}
|
|
12181
|
-
KokimokiSchema.
|
|
12182
|
-
|
|
12183
|
-
|
|
12184
|
-
|
|
12185
|
-
|
|
12186
|
-
|
|
12187
|
-
|
|
12188
|
-
|
|
12189
|
-
|
|
12190
|
-
|
|
12191
|
-
|
|
12192
|
-
|
|
12193
|
-
|
|
12194
|
-
|
|
12195
|
-
for (const
|
|
12196
|
-
|
|
11976
|
+
KokimokiSchema.optional = optional;
|
|
11977
|
+
})(KokimokiSchema || (KokimokiSchema = {}));
|
|
11978
|
+
|
|
11979
|
+
class KokimokiQueue extends KokimokiStore {
|
|
11980
|
+
payloadSchema;
|
|
11981
|
+
_emitter = new EventEmitter$1();
|
|
11982
|
+
on = this._emitter.on.bind(this._emitter);
|
|
11983
|
+
off = this._emitter.off.bind(this._emitter);
|
|
11984
|
+
constructor(roomName, payloadSchema, mode) {
|
|
11985
|
+
super(`/q/${roomName}`, KokimokiSchema.dict(payloadSchema), mode);
|
|
11986
|
+
this.payloadSchema = payloadSchema;
|
|
11987
|
+
const emittedMessageIds = new Set();
|
|
11988
|
+
this.doc.on("update", () => {
|
|
11989
|
+
const newMessages = [];
|
|
11990
|
+
for (const id in this.proxy) {
|
|
11991
|
+
if (emittedMessageIds.has(id)) {
|
|
11992
|
+
continue;
|
|
11993
|
+
}
|
|
11994
|
+
emittedMessageIds.add(id);
|
|
11995
|
+
newMessages.push({ id, payload: this.proxy[id] });
|
|
12197
11996
|
}
|
|
12198
|
-
|
|
12199
|
-
|
|
12200
|
-
|
|
12201
|
-
|
|
12202
|
-
return new Struct(schema);
|
|
12203
|
-
}
|
|
12204
|
-
KokimokiSchema.struct = struct;
|
|
12205
|
-
class Dict {
|
|
12206
|
-
schema;
|
|
12207
|
-
defaultValue;
|
|
12208
|
-
constructor(schema, defaultValue = {}) {
|
|
12209
|
-
this.schema = schema;
|
|
12210
|
-
this.defaultValue = defaultValue;
|
|
12211
|
-
}
|
|
12212
|
-
}
|
|
12213
|
-
KokimokiSchema.Dict = Dict;
|
|
12214
|
-
function dict(schema, defaultValue = {}) {
|
|
12215
|
-
return new Dict(schema, defaultValue);
|
|
12216
|
-
}
|
|
12217
|
-
KokimokiSchema.dict = dict;
|
|
12218
|
-
class List extends Generic {
|
|
12219
|
-
schema;
|
|
12220
|
-
defaultValue;
|
|
12221
|
-
constructor(schema, defaultValue = []) {
|
|
12222
|
-
super();
|
|
12223
|
-
this.schema = schema;
|
|
12224
|
-
this.defaultValue = defaultValue;
|
|
12225
|
-
}
|
|
12226
|
-
}
|
|
12227
|
-
KokimokiSchema.List = List;
|
|
12228
|
-
function list(schema, defaultValue = []) {
|
|
12229
|
-
return new List(schema, defaultValue);
|
|
11997
|
+
if (newMessages.length > 0) {
|
|
11998
|
+
this._emitter.emit("messages", newMessages);
|
|
11999
|
+
}
|
|
12000
|
+
});
|
|
12230
12001
|
}
|
|
12231
|
-
|
|
12232
|
-
|
|
12233
|
-
|
|
12234
|
-
|
|
12235
|
-
|
|
12236
|
-
|
|
12237
|
-
|
|
12238
|
-
|
|
12239
|
-
|
|
12240
|
-
|
|
12241
|
-
|
|
12242
|
-
|
|
12002
|
+
}
|
|
12003
|
+
|
|
12004
|
+
class KokimokiAwareness extends KokimokiStore {
|
|
12005
|
+
dataSchema;
|
|
12006
|
+
_data;
|
|
12007
|
+
_pingInterval = null;
|
|
12008
|
+
_kmClients = new Set();
|
|
12009
|
+
constructor(roomName, dataSchema, _data, mode = RoomSubscriptionMode.ReadWrite, pingTimeout = 3000) {
|
|
12010
|
+
super(`/a/${roomName}`, KokimokiSchema.dict(KokimokiSchema.struct({
|
|
12011
|
+
clientId: KokimokiSchema.string(),
|
|
12012
|
+
lastPing: KokimokiSchema.number(),
|
|
12013
|
+
data: dataSchema,
|
|
12014
|
+
})), mode);
|
|
12015
|
+
this.dataSchema = dataSchema;
|
|
12016
|
+
this._data = _data;
|
|
12017
|
+
this._pingInterval = setInterval(async () => {
|
|
12018
|
+
const kmClients = Array.from(this._kmClients);
|
|
12019
|
+
await Promise.all(kmClients.map(async (client) => {
|
|
12020
|
+
try {
|
|
12021
|
+
await client.transact((t) => {
|
|
12022
|
+
const timestamp = client.serverTimestamp();
|
|
12023
|
+
// Update self
|
|
12024
|
+
if (this.proxy[client.connectionId]) {
|
|
12025
|
+
t.set(this.root[client.connectionId].lastPing, timestamp);
|
|
12026
|
+
}
|
|
12027
|
+
else {
|
|
12028
|
+
t.set(this.root[client.connectionId], {
|
|
12029
|
+
clientId: client.id,
|
|
12030
|
+
lastPing: timestamp,
|
|
12031
|
+
data: this._data,
|
|
12032
|
+
});
|
|
12033
|
+
}
|
|
12034
|
+
// Delete clients that haven't pinged in a while
|
|
12035
|
+
for (const connectionId in this.proxy) {
|
|
12036
|
+
const { lastPing } = this.proxy[connectionId];
|
|
12037
|
+
if (timestamp - lastPing > pingTimeout * 2) {
|
|
12038
|
+
t.delete(this.root[connectionId]);
|
|
12039
|
+
}
|
|
12040
|
+
}
|
|
12041
|
+
});
|
|
12042
|
+
}
|
|
12043
|
+
catch (e) { }
|
|
12044
|
+
}));
|
|
12045
|
+
}, pingTimeout);
|
|
12243
12046
|
}
|
|
12244
|
-
|
|
12245
|
-
|
|
12246
|
-
|
|
12047
|
+
async onJoin(client) {
|
|
12048
|
+
this._kmClients.add(client);
|
|
12049
|
+
await client.transact((t) => {
|
|
12050
|
+
t.set(this.root[client.connectionId], {
|
|
12051
|
+
clientId: client.id,
|
|
12052
|
+
lastPing: client.serverTimestamp(),
|
|
12053
|
+
data: this._data,
|
|
12054
|
+
});
|
|
12055
|
+
});
|
|
12247
12056
|
}
|
|
12248
|
-
|
|
12249
|
-
|
|
12250
|
-
|
|
12251
|
-
|
|
12252
|
-
constructor(options, defaultValue) {
|
|
12253
|
-
super();
|
|
12254
|
-
this.options = options;
|
|
12255
|
-
this.defaultValue = defaultValue;
|
|
12256
|
-
}
|
|
12057
|
+
async onBeforeLeave(client) {
|
|
12058
|
+
await client.transact((t) => {
|
|
12059
|
+
t.delete(this.root[client.connectionId]);
|
|
12060
|
+
});
|
|
12257
12061
|
}
|
|
12258
|
-
|
|
12259
|
-
|
|
12260
|
-
return new StringEnum(options, defaultValue);
|
|
12062
|
+
async onLeave(client) {
|
|
12063
|
+
this._kmClients.delete(client);
|
|
12261
12064
|
}
|
|
12262
|
-
|
|
12263
|
-
|
|
12264
|
-
|
|
12265
|
-
|
|
12266
|
-
|
|
12267
|
-
this.defaultValue = defaultValue;
|
|
12065
|
+
getClients() {
|
|
12066
|
+
const clients = {};
|
|
12067
|
+
for (const connectionId in this.proxy) {
|
|
12068
|
+
clients[this.proxy[connectionId].clientId] =
|
|
12069
|
+
this.proxy[connectionId].data;
|
|
12268
12070
|
}
|
|
12071
|
+
return clients;
|
|
12269
12072
|
}
|
|
12270
|
-
|
|
12271
|
-
|
|
12272
|
-
|
|
12073
|
+
async setData(data) {
|
|
12074
|
+
this._data = data;
|
|
12075
|
+
const kmClients = Array.from(this._kmClients);
|
|
12076
|
+
await Promise.all(kmClients.map(async (client) => {
|
|
12077
|
+
await client.transact((t) => {
|
|
12078
|
+
t.set(this.root[client.connectionId].data, this._data);
|
|
12079
|
+
});
|
|
12080
|
+
}));
|
|
12273
12081
|
}
|
|
12274
|
-
|
|
12275
|
-
|
|
12276
|
-
|
|
12277
|
-
|
|
12278
|
-
|
|
12279
|
-
|
|
12280
|
-
|
|
12281
|
-
|
|
12282
|
-
|
|
12082
|
+
}
|
|
12083
|
+
|
|
12084
|
+
class KokimokiReqRes {
|
|
12085
|
+
kmClient;
|
|
12086
|
+
serviceName;
|
|
12087
|
+
reqSchema;
|
|
12088
|
+
resSchema;
|
|
12089
|
+
handleRequest;
|
|
12090
|
+
_reqQueueSchema;
|
|
12091
|
+
_resQueueSchema;
|
|
12092
|
+
// Request queues are linked to client ids
|
|
12093
|
+
_reqQueues = {};
|
|
12094
|
+
// Response queues are linked to specific connection ids
|
|
12095
|
+
_resQueues = {};
|
|
12096
|
+
_reqPromises = {};
|
|
12097
|
+
async _getReqQueue(clientId, mode = RoomSubscriptionMode.Write) {
|
|
12098
|
+
if (!this._reqQueues.hasOwnProperty(clientId)) {
|
|
12099
|
+
this._reqQueues[clientId] = this.kmClient.queue(`/r/${clientId}/req/${this.serviceName}`, this._reqQueueSchema, mode, false);
|
|
12100
|
+
await this.kmClient.join(this._reqQueues[clientId]);
|
|
12101
|
+
}
|
|
12102
|
+
return this._reqQueues[clientId];
|
|
12103
|
+
}
|
|
12104
|
+
async _getResQueue(connectionId, mode = RoomSubscriptionMode.Write) {
|
|
12105
|
+
if (!this._resQueues.hasOwnProperty(connectionId)) {
|
|
12106
|
+
this._resQueues[connectionId] = this.kmClient.queue(`/r/${connectionId}/res/${this.serviceName}`, this._resQueueSchema, mode, false);
|
|
12107
|
+
await this.kmClient.join(this._resQueues[connectionId]);
|
|
12108
|
+
}
|
|
12109
|
+
return this._resQueues[connectionId];
|
|
12110
|
+
}
|
|
12111
|
+
constructor(kmClient, serviceName, reqSchema, resSchema, handleRequest) {
|
|
12112
|
+
this.kmClient = kmClient;
|
|
12113
|
+
this.serviceName = serviceName;
|
|
12114
|
+
this.reqSchema = reqSchema;
|
|
12115
|
+
this.resSchema = resSchema;
|
|
12116
|
+
this.handleRequest = handleRequest;
|
|
12117
|
+
this._reqQueueSchema = KokimokiSchema.struct({
|
|
12118
|
+
connectionId: KokimokiSchema.string(),
|
|
12119
|
+
// roomHash: S.number(),
|
|
12120
|
+
payload: reqSchema,
|
|
12121
|
+
});
|
|
12122
|
+
this._resQueueSchema = KokimokiSchema.struct({
|
|
12123
|
+
reqId: KokimokiSchema.string(),
|
|
12124
|
+
error: KokimokiSchema.optional(KokimokiSchema.string()),
|
|
12125
|
+
payload: resSchema,
|
|
12126
|
+
});
|
|
12127
|
+
// Set up queues on connect
|
|
12128
|
+
kmClient.on("connected", async () => {
|
|
12129
|
+
// Handle responses to self
|
|
12130
|
+
const resQueue = await this._getResQueue(kmClient.connectionId, RoomSubscriptionMode.ReadWrite);
|
|
12131
|
+
let handlingResponses = false;
|
|
12132
|
+
let rehandleResponses = false;
|
|
12133
|
+
resQueue.subscribe(async (messages) => {
|
|
12134
|
+
// console.log("RES", Object.keys(messages));
|
|
12135
|
+
if (handlingResponses) {
|
|
12136
|
+
rehandleResponses = true;
|
|
12137
|
+
return;
|
|
12138
|
+
}
|
|
12139
|
+
handlingResponses = true;
|
|
12140
|
+
await this.handleResponses(resQueue, messages);
|
|
12141
|
+
if (rehandleResponses) {
|
|
12142
|
+
rehandleResponses = false;
|
|
12143
|
+
await this.handleResponses(resQueue, messages);
|
|
12144
|
+
}
|
|
12145
|
+
handlingResponses = false;
|
|
12146
|
+
});
|
|
12147
|
+
// Handle requests to self
|
|
12148
|
+
const reqQueue = await this._getReqQueue(kmClient.id, RoomSubscriptionMode.ReadWrite);
|
|
12149
|
+
let handlingRequests = false;
|
|
12150
|
+
let rehandleRequests = false;
|
|
12151
|
+
reqQueue.subscribe(async (messages) => {
|
|
12152
|
+
// console.log("REQ", Object.keys(messages));
|
|
12153
|
+
if (handlingRequests) {
|
|
12154
|
+
rehandleRequests = true;
|
|
12155
|
+
return;
|
|
12156
|
+
}
|
|
12157
|
+
handlingRequests = true;
|
|
12158
|
+
await this.handleRequests(reqQueue, messages);
|
|
12159
|
+
if (rehandleRequests) {
|
|
12160
|
+
rehandleRequests = false;
|
|
12161
|
+
await this.handleRequests(reqQueue, messages);
|
|
12162
|
+
}
|
|
12163
|
+
handlingRequests = false;
|
|
12164
|
+
});
|
|
12165
|
+
});
|
|
12283
12166
|
}
|
|
12284
|
-
|
|
12285
|
-
|
|
12286
|
-
|
|
12167
|
+
async handleRequests(reqQueue, messages) {
|
|
12168
|
+
// Send responses
|
|
12169
|
+
await this.kmClient.transact(async (t) => {
|
|
12170
|
+
for (const reqId in messages) {
|
|
12171
|
+
const { connectionId, payload } = messages[reqId];
|
|
12172
|
+
const resQueue = await this._getResQueue(connectionId);
|
|
12173
|
+
t.consumeMessage(reqQueue, reqId);
|
|
12174
|
+
try {
|
|
12175
|
+
t.queueMessage(resQueue, {
|
|
12176
|
+
reqId,
|
|
12177
|
+
error: undefined,
|
|
12178
|
+
payload: await this.handleRequest(payload),
|
|
12179
|
+
});
|
|
12180
|
+
}
|
|
12181
|
+
catch (e) {
|
|
12182
|
+
t.queueMessage(resQueue, {
|
|
12183
|
+
reqId,
|
|
12184
|
+
error: e.toString(),
|
|
12185
|
+
payload: null,
|
|
12186
|
+
});
|
|
12187
|
+
}
|
|
12188
|
+
}
|
|
12189
|
+
});
|
|
12287
12190
|
}
|
|
12288
|
-
|
|
12289
|
-
|
|
12290
|
-
|
|
12291
|
-
|
|
12292
|
-
|
|
12293
|
-
|
|
12294
|
-
|
|
12295
|
-
|
|
12296
|
-
|
|
12191
|
+
async handleResponses(resQueue, messages) {
|
|
12192
|
+
// Handle responses
|
|
12193
|
+
await this.kmClient.transact(async (t) => {
|
|
12194
|
+
for (const resId in messages) {
|
|
12195
|
+
const { reqId, error, payload } = messages[resId];
|
|
12196
|
+
const promise = this._reqPromises[reqId];
|
|
12197
|
+
t.consumeMessage(resQueue, resId);
|
|
12198
|
+
if (promise) {
|
|
12199
|
+
if (error) {
|
|
12200
|
+
promise.reject(error);
|
|
12201
|
+
}
|
|
12202
|
+
else {
|
|
12203
|
+
promise.resolve(payload);
|
|
12204
|
+
}
|
|
12205
|
+
delete this._reqPromises[reqId];
|
|
12206
|
+
}
|
|
12207
|
+
}
|
|
12208
|
+
});
|
|
12297
12209
|
}
|
|
12298
|
-
|
|
12299
|
-
|
|
12300
|
-
|
|
12210
|
+
async send(toClientId, payload, timeout) {
|
|
12211
|
+
const toReqQueue = await this._getReqQueue(toClientId);
|
|
12212
|
+
// Create a promise for the response
|
|
12213
|
+
return await new Promise(async (resolve, reject) => {
|
|
12214
|
+
await this.kmClient.transact((t) => {
|
|
12215
|
+
const reqId = t.queueMessage(toReqQueue, {
|
|
12216
|
+
connectionId: this.kmClient.connectionId,
|
|
12217
|
+
payload,
|
|
12218
|
+
// roomHash,
|
|
12219
|
+
});
|
|
12220
|
+
this._reqPromises[reqId] = { resolve, reject };
|
|
12221
|
+
});
|
|
12222
|
+
});
|
|
12301
12223
|
}
|
|
12302
|
-
KokimokiSchema.optional = optional;
|
|
12303
|
-
})(KokimokiSchema || (KokimokiSchema = {}));
|
|
12304
|
-
|
|
12305
|
-
function parsePath(text) {
|
|
12306
|
-
return text.split('.')
|
|
12307
|
-
}
|
|
12308
|
-
|
|
12309
|
-
function push(arr, el) {
|
|
12310
|
-
const newArr = arr.slice();
|
|
12311
|
-
newArr.push(el);
|
|
12312
|
-
return newArr;
|
|
12313
12224
|
}
|
|
12314
12225
|
|
|
12315
|
-
|
|
12316
|
-
|
|
12317
|
-
|
|
12318
|
-
|
|
12319
|
-
|
|
12320
|
-
|
|
12321
|
-
|
|
12322
|
-
|
|
12323
|
-
|
|
12324
|
-
|
|
12325
|
-
|
|
12326
|
-
|
|
12327
|
-
|
|
12328
|
-
|
|
12329
|
-
|
|
12330
|
-
|
|
12331
|
-
|
|
12332
|
-
|
|
12333
|
-
|
|
12334
|
-
|
|
12335
|
-
|
|
12336
|
-
|
|
12337
|
-
|
|
12338
|
-
|
|
12339
|
-
|
|
12340
|
-
|
|
12341
|
-
|
|
12342
|
-
|
|
12343
|
-
|
|
12344
|
-
|
|
12345
|
-
|
|
12346
|
-
|
|
12347
|
-
|
|
12348
|
-
|
|
12349
|
-
|
|
12350
|
-
|
|
12351
|
-
|
|
12352
|
-
|
|
12353
|
-
|
|
12354
|
-
|
|
12355
|
-
|
|
12356
|
-
|
|
12357
|
-
|
|
12358
|
-
|
|
12359
|
-
|
|
12360
|
-
|
|
12361
|
-
|
|
12362
|
-
|
|
12363
|
-
|
|
12364
|
-
|
|
12365
|
-
|
|
12366
|
-
|
|
12367
|
-
|
|
12368
|
-
|
|
12369
|
-
|
|
12370
|
-
|
|
12371
|
-
|
|
12372
|
-
|
|
12373
|
-
|
|
12374
|
-
|
|
12375
|
-
|
|
12376
|
-
|
|
12377
|
-
|
|
12378
|
-
|
|
12379
|
-
|
|
12380
|
-
|
|
12226
|
+
class KokimokiClient extends EventEmitter$1 {
|
|
12227
|
+
host;
|
|
12228
|
+
appId;
|
|
12229
|
+
code;
|
|
12230
|
+
_wsUrl;
|
|
12231
|
+
_apiUrl;
|
|
12232
|
+
_id;
|
|
12233
|
+
_connectionId;
|
|
12234
|
+
_token;
|
|
12235
|
+
_apiHeaders;
|
|
12236
|
+
_serverTimeOffset = 0;
|
|
12237
|
+
_clientContext;
|
|
12238
|
+
_ws;
|
|
12239
|
+
_subscriptionsByName = new Map();
|
|
12240
|
+
_subscriptionsByHash = new Map();
|
|
12241
|
+
_subscribeReqPromises = new Map();
|
|
12242
|
+
_unsubscribeReqPromises = new Map();
|
|
12243
|
+
_transactionPromises = new Map();
|
|
12244
|
+
_connected = false;
|
|
12245
|
+
_connectPromise;
|
|
12246
|
+
_messageId = 0;
|
|
12247
|
+
_autoReconnect = true;
|
|
12248
|
+
_reconnectTimeout = 0;
|
|
12249
|
+
_pingInterval;
|
|
12250
|
+
constructor(host, appId, code = "") {
|
|
12251
|
+
super();
|
|
12252
|
+
this.host = host;
|
|
12253
|
+
this.appId = appId;
|
|
12254
|
+
this.code = code;
|
|
12255
|
+
// Set up the URLs
|
|
12256
|
+
const secure = this.host.indexOf(":") === -1;
|
|
12257
|
+
this._wsUrl = `ws${secure ? "s" : ""}://${this.host}`;
|
|
12258
|
+
this._apiUrl = `http${secure ? "s" : ""}://${this.host}`;
|
|
12259
|
+
// Set up ping interval
|
|
12260
|
+
const pingMsg = new WsMessageWriter();
|
|
12261
|
+
pingMsg.writeInt32(WsMessageType.Ping);
|
|
12262
|
+
const pingBuffer = pingMsg.getBuffer();
|
|
12263
|
+
this._pingInterval = setInterval(() => {
|
|
12264
|
+
if (this.connected) {
|
|
12265
|
+
this.ws.send(pingBuffer);
|
|
12266
|
+
}
|
|
12267
|
+
}, 5000);
|
|
12268
|
+
}
|
|
12269
|
+
get id() {
|
|
12270
|
+
if (!this._id) {
|
|
12271
|
+
throw new Error("Client not connected");
|
|
12272
|
+
}
|
|
12273
|
+
return this._id;
|
|
12274
|
+
}
|
|
12275
|
+
get connectionId() {
|
|
12276
|
+
if (!this._connectionId) {
|
|
12277
|
+
throw new Error("Client not connected");
|
|
12278
|
+
}
|
|
12279
|
+
return this._connectionId;
|
|
12280
|
+
}
|
|
12281
|
+
get token() {
|
|
12282
|
+
if (!this._token) {
|
|
12283
|
+
throw new Error("Client not connected");
|
|
12284
|
+
}
|
|
12285
|
+
return this._token;
|
|
12286
|
+
}
|
|
12287
|
+
get apiUrl() {
|
|
12288
|
+
if (!this._apiUrl) {
|
|
12289
|
+
throw new Error("Client not connected");
|
|
12290
|
+
}
|
|
12291
|
+
return this._apiUrl;
|
|
12292
|
+
}
|
|
12293
|
+
get apiHeaders() {
|
|
12294
|
+
if (!this._apiHeaders) {
|
|
12295
|
+
throw new Error("Client not connected");
|
|
12296
|
+
}
|
|
12297
|
+
return this._apiHeaders;
|
|
12298
|
+
}
|
|
12299
|
+
get clientContext() {
|
|
12300
|
+
if (this._clientContext === undefined) {
|
|
12301
|
+
throw new Error("Client not connected");
|
|
12302
|
+
}
|
|
12303
|
+
return this._clientContext;
|
|
12304
|
+
}
|
|
12305
|
+
get connected() {
|
|
12306
|
+
return this._connected;
|
|
12307
|
+
}
|
|
12308
|
+
get ws() {
|
|
12309
|
+
if (!this._ws) {
|
|
12310
|
+
throw new Error("Not connected");
|
|
12311
|
+
}
|
|
12312
|
+
return this._ws;
|
|
12313
|
+
}
|
|
12314
|
+
async connect() {
|
|
12315
|
+
if (this._connectPromise) {
|
|
12316
|
+
return await this._connectPromise;
|
|
12317
|
+
}
|
|
12318
|
+
this._ws = new WebSocket(`${this._wsUrl}/apps/${this.appId}?clientVersion=${KOKIMOKI_APP_VERSION}`);
|
|
12319
|
+
this._ws.binaryType = "arraybuffer";
|
|
12320
|
+
// Close previous connection in hot-reload scenarios
|
|
12321
|
+
if (window) {
|
|
12322
|
+
if (!window.__KOKIMOKI_WS__) {
|
|
12323
|
+
window.__KOKIMOKI_WS__ = {};
|
|
12324
|
+
}
|
|
12325
|
+
if (this.appId in window.__KOKIMOKI_WS__) {
|
|
12326
|
+
console.log(`[Kokimoki] Closing previous connection for ${this.appId}`);
|
|
12327
|
+
window.__KOKIMOKI_WS__[this.appId].close();
|
|
12328
|
+
}
|
|
12329
|
+
window.__KOKIMOKI_WS__[this.appId] = this;
|
|
12330
|
+
}
|
|
12331
|
+
// Wait for connection
|
|
12332
|
+
this._connectPromise = new Promise((onInit) => {
|
|
12333
|
+
// Fetch the auth token
|
|
12334
|
+
let clientToken = localStorage.getItem("KM_TOKEN");
|
|
12335
|
+
// Send the app token on first connect
|
|
12336
|
+
this.ws.onopen = () => {
|
|
12337
|
+
this.ws.send(JSON.stringify({ type: "auth", code: this.code, token: clientToken }));
|
|
12381
12338
|
};
|
|
12382
|
-
|
|
12383
|
-
|
|
12384
|
-
|
|
12385
|
-
|
|
12386
|
-
|
|
12387
|
-
|
|
12388
|
-
|
|
12389
|
-
|
|
12390
|
-
|
|
12391
|
-
|
|
12392
|
-
|
|
12393
|
-
|
|
12339
|
+
this.ws.onclose = () => {
|
|
12340
|
+
this._connected = false;
|
|
12341
|
+
this._connectPromise = undefined;
|
|
12342
|
+
this._ws.onmessage = null;
|
|
12343
|
+
this._ws = undefined;
|
|
12344
|
+
if (window && window.__KOKIMOKI_WS__) {
|
|
12345
|
+
delete window.__KOKIMOKI_WS__[this.appId];
|
|
12346
|
+
}
|
|
12347
|
+
// Clean up
|
|
12348
|
+
this._subscribeReqPromises.clear();
|
|
12349
|
+
this._transactionPromises.clear();
|
|
12350
|
+
// Attempt to reconnect
|
|
12351
|
+
if (!this._autoReconnect) {
|
|
12352
|
+
return;
|
|
12353
|
+
}
|
|
12354
|
+
console.log(`[Kokimoki] Connection lost, attempting to reconnect in ${this._reconnectTimeout} seconds...`);
|
|
12355
|
+
setTimeout(async () => await this.connect(), this._reconnectTimeout * 1000);
|
|
12356
|
+
this._reconnectTimeout = Math.min(3, this._reconnectTimeout + 1);
|
|
12357
|
+
// Emit disconnected event
|
|
12358
|
+
this.emit("disconnected");
|
|
12394
12359
|
};
|
|
12395
|
-
|
|
12396
|
-
|
|
12397
|
-
|
|
12360
|
+
this.ws.onmessage = (e) => {
|
|
12361
|
+
// console.log(`Received WS message: ${e.data}`);
|
|
12362
|
+
// Handle JSON messages
|
|
12363
|
+
if (typeof e.data === "string") {
|
|
12364
|
+
const message = JSON.parse(e.data);
|
|
12365
|
+
switch (message.type) {
|
|
12366
|
+
case "init":
|
|
12367
|
+
this.handleInitMessage(message);
|
|
12368
|
+
onInit();
|
|
12369
|
+
break;
|
|
12370
|
+
}
|
|
12371
|
+
return;
|
|
12372
|
+
}
|
|
12373
|
+
// Handle binary messages
|
|
12374
|
+
this.handleBinaryMessage(e.data);
|
|
12375
|
+
};
|
|
12376
|
+
});
|
|
12377
|
+
await this._connectPromise;
|
|
12378
|
+
this._connected = true;
|
|
12379
|
+
// Connection established
|
|
12380
|
+
console.log(`[Kokimoki] Client id: ${this.id}`);
|
|
12381
|
+
console.log(`[Kokimoki] Client context:`, this.clientContext);
|
|
12382
|
+
// Restore subscriptions if reconnected
|
|
12383
|
+
const roomNames = Array.from(this._subscriptionsByName.keys()).map((name) => `"${name}"`);
|
|
12384
|
+
if (roomNames.length) {
|
|
12385
|
+
console.log(`[Kokimoki] Restoring subscriptions: ${roomNames}`);
|
|
12398
12386
|
}
|
|
12399
|
-
|
|
12387
|
+
for (const subscription of this._subscriptionsByName.values()) {
|
|
12388
|
+
try {
|
|
12389
|
+
await this.join(subscription.store);
|
|
12390
|
+
}
|
|
12391
|
+
catch (err) {
|
|
12392
|
+
console.error(`[Kokimoki] Failed to restore subscription for "${subscription.roomName}":`, err);
|
|
12393
|
+
}
|
|
12394
|
+
}
|
|
12395
|
+
// Emit connected event
|
|
12396
|
+
this._reconnectTimeout = 0;
|
|
12397
|
+
this.emit("connected");
|
|
12400
12398
|
}
|
|
12401
|
-
|
|
12402
|
-
|
|
12403
|
-
|
|
12404
|
-
|
|
12405
|
-
|
|
12406
|
-
|
|
12407
|
-
|
|
12408
|
-
|
|
12409
|
-
|
|
12410
|
-
|
|
12411
|
-
|
|
12412
|
-
|
|
12413
|
-
|
|
12414
|
-
|
|
12415
|
-
|
|
12416
|
-
|
|
12417
|
-
|
|
12418
|
-
|
|
12419
|
-
|
|
12420
|
-
|
|
12421
|
-
|
|
12422
|
-
|
|
12423
|
-
|
|
12424
|
-
|
|
12425
|
-
|
|
12426
|
-
|
|
12427
|
-
|
|
12428
|
-
|
|
12429
|
-
|
|
12430
|
-
|
|
12431
|
-
|
|
12432
|
-
|
|
12433
|
-
|
|
12434
|
-
|
|
12435
|
-
|
|
12436
|
-
|
|
12437
|
-
|
|
12438
|
-
|
|
12439
|
-
|
|
12440
|
-
|
|
12441
|
-
|
|
12442
|
-
|
|
12443
|
-
|
|
12444
|
-
|
|
12445
|
-
|
|
12446
|
-
|
|
12399
|
+
handleInitMessage(message) {
|
|
12400
|
+
localStorage.setItem("KM_TOKEN", message.clientToken);
|
|
12401
|
+
this._id = message.clientId;
|
|
12402
|
+
this._connectionId = message.id;
|
|
12403
|
+
this._token = message.appToken;
|
|
12404
|
+
this._clientContext = message.clientContext;
|
|
12405
|
+
// Set up the auth headers
|
|
12406
|
+
this._apiHeaders = new Headers({
|
|
12407
|
+
Authorization: `Bearer ${this.token}`,
|
|
12408
|
+
"Content-Type": "application/json",
|
|
12409
|
+
});
|
|
12410
|
+
}
|
|
12411
|
+
handleBinaryMessage(data) {
|
|
12412
|
+
const reader = new WsMessageReader(data);
|
|
12413
|
+
const type = reader.readInt32();
|
|
12414
|
+
switch (type) {
|
|
12415
|
+
case WsMessageType.SubscribeRes:
|
|
12416
|
+
this.handleSubscribeResMessage(reader);
|
|
12417
|
+
break;
|
|
12418
|
+
case WsMessageType.UnsubscribeRes:
|
|
12419
|
+
this.handleUnsubscribeResMessage(reader);
|
|
12420
|
+
break;
|
|
12421
|
+
case WsMessageType.RoomUpdate:
|
|
12422
|
+
this.handleRoomUpdateMessage(reader);
|
|
12423
|
+
break;
|
|
12424
|
+
case WsMessageType.Error:
|
|
12425
|
+
this.handleErrorMessage(reader);
|
|
12426
|
+
break;
|
|
12427
|
+
case WsMessageType.Pong: {
|
|
12428
|
+
const s = reader.readInt32();
|
|
12429
|
+
const ms = reader.readInt32();
|
|
12430
|
+
this._serverTimeOffset = Date.now() - s * 1000 - ms;
|
|
12431
|
+
break;
|
|
12432
|
+
}
|
|
12433
|
+
}
|
|
12434
|
+
}
|
|
12435
|
+
handleErrorMessage(msg) {
|
|
12436
|
+
const reqId = msg.readInt32();
|
|
12437
|
+
const error = msg.readString();
|
|
12438
|
+
const subscribeReqPromise = this._subscribeReqPromises.get(reqId);
|
|
12439
|
+
const transactionPromise = this._transactionPromises.get(reqId);
|
|
12440
|
+
if (subscribeReqPromise) {
|
|
12441
|
+
this._subscribeReqPromises.delete(reqId);
|
|
12442
|
+
subscribeReqPromise.reject(error);
|
|
12443
|
+
}
|
|
12444
|
+
else if (transactionPromise) {
|
|
12445
|
+
this._transactionPromises.delete(reqId);
|
|
12446
|
+
transactionPromise.reject(error);
|
|
12447
|
+
}
|
|
12448
|
+
else {
|
|
12449
|
+
console.warn(`Received error for unknown request ${reqId}: ${error}`);
|
|
12450
|
+
}
|
|
12451
|
+
}
|
|
12452
|
+
handleSubscribeResMessage(msg) {
|
|
12453
|
+
const reqId = msg.readInt32();
|
|
12454
|
+
const roomHash = msg.readUint32();
|
|
12455
|
+
const promise = this._subscribeReqPromises.get(reqId);
|
|
12456
|
+
if (promise) {
|
|
12457
|
+
this._subscribeReqPromises.delete(reqId);
|
|
12458
|
+
// In Write mode, no initial state is sent
|
|
12459
|
+
if (!msg.end) {
|
|
12460
|
+
promise.resolve(roomHash, msg.readUint8Array());
|
|
12461
|
+
}
|
|
12462
|
+
else {
|
|
12463
|
+
promise.resolve(roomHash);
|
|
12464
|
+
}
|
|
12465
|
+
}
|
|
12466
|
+
}
|
|
12467
|
+
handleUnsubscribeResMessage(msg) {
|
|
12468
|
+
const reqId = msg.readInt32();
|
|
12469
|
+
const promise = this._unsubscribeReqPromises.get(reqId);
|
|
12470
|
+
if (promise) {
|
|
12471
|
+
this._unsubscribeReqPromises.delete(reqId);
|
|
12472
|
+
promise.resolve();
|
|
12473
|
+
}
|
|
12474
|
+
}
|
|
12475
|
+
handleRoomUpdateMessage(msg) {
|
|
12476
|
+
const appliedId = msg.readInt32();
|
|
12477
|
+
const roomHash = msg.readUint32();
|
|
12478
|
+
// Apply update if not in Write mode
|
|
12479
|
+
if (!msg.end) {
|
|
12480
|
+
const subscription = this._subscriptionsByHash.get(roomHash);
|
|
12481
|
+
if (subscription) {
|
|
12482
|
+
applyUpdate(subscription.store.doc, msg.readUint8Array(), this);
|
|
12483
|
+
}
|
|
12484
|
+
else {
|
|
12485
|
+
console.warn(`Received update for unknown room ${roomHash}`);
|
|
12486
|
+
}
|
|
12487
|
+
}
|
|
12488
|
+
// Check transaction resolves
|
|
12489
|
+
for (const [transactionId, { resolve },] of this._transactionPromises.entries()) {
|
|
12490
|
+
if (appliedId >= transactionId) {
|
|
12491
|
+
this._transactionPromises.delete(transactionId);
|
|
12492
|
+
resolve();
|
|
12493
|
+
}
|
|
12494
|
+
}
|
|
12495
|
+
}
|
|
12496
|
+
serverTimestamp() {
|
|
12497
|
+
return Date.now() - this._serverTimeOffset;
|
|
12498
|
+
}
|
|
12499
|
+
// Send Y update to room
|
|
12500
|
+
async patchRoomState(room, update) {
|
|
12501
|
+
const res = await fetch(`${this._apiUrl}/rooms/${room}`, {
|
|
12502
|
+
method: "PATCH",
|
|
12503
|
+
headers: this.apiHeaders,
|
|
12504
|
+
body: update,
|
|
12505
|
+
});
|
|
12506
|
+
return await res.json();
|
|
12507
|
+
}
|
|
12508
|
+
// Storage
|
|
12509
|
+
async createUpload(name, blob, tags) {
|
|
12510
|
+
const res = await fetch(`${this._apiUrl}/uploads`, {
|
|
12511
|
+
method: "POST",
|
|
12512
|
+
headers: this.apiHeaders,
|
|
12513
|
+
body: JSON.stringify({
|
|
12514
|
+
name,
|
|
12515
|
+
size: blob.size,
|
|
12516
|
+
mimeType: blob.type,
|
|
12517
|
+
tags,
|
|
12518
|
+
}),
|
|
12519
|
+
});
|
|
12520
|
+
return await res.json();
|
|
12521
|
+
}
|
|
12522
|
+
async uploadChunks(blob, chunkSize, signedUrls) {
|
|
12523
|
+
return await Promise.all(signedUrls.map(async (url, index) => {
|
|
12524
|
+
const start = index * chunkSize;
|
|
12525
|
+
const end = Math.min(start + chunkSize, blob.size);
|
|
12526
|
+
const chunk = blob.slice(start, end);
|
|
12527
|
+
const res = await fetch(url, { method: "PUT", body: chunk });
|
|
12528
|
+
return JSON.parse(res.headers.get("ETag") || '""');
|
|
12529
|
+
}));
|
|
12530
|
+
}
|
|
12531
|
+
async completeUpload(id, etags) {
|
|
12532
|
+
const res = await fetch(`${this._apiUrl}/uploads/${id}`, {
|
|
12533
|
+
method: "PUT",
|
|
12534
|
+
headers: this.apiHeaders,
|
|
12535
|
+
body: JSON.stringify({ etags }),
|
|
12536
|
+
});
|
|
12537
|
+
return await res.json();
|
|
12538
|
+
}
|
|
12539
|
+
async upload(name, blob, tags = []) {
|
|
12540
|
+
const { id, chunkSize, urls } = await this.createUpload(name, blob, tags);
|
|
12541
|
+
const etags = await this.uploadChunks(blob, chunkSize, urls);
|
|
12542
|
+
return await this.completeUpload(id, etags);
|
|
12543
|
+
}
|
|
12544
|
+
async updateUpload(id, update) {
|
|
12545
|
+
const res = await fetch(`${this._apiUrl}/uploads/${id}`, {
|
|
12546
|
+
method: "PUT",
|
|
12547
|
+
headers: this.apiHeaders,
|
|
12548
|
+
body: JSON.stringify(update),
|
|
12447
12549
|
});
|
|
12448
|
-
|
|
12449
|
-
this.defaultValue = schema.defaultValue;
|
|
12550
|
+
return await res.json();
|
|
12450
12551
|
}
|
|
12451
|
-
|
|
12452
|
-
|
|
12552
|
+
async listUploads(filter = {}, skip = 0, limit = 100) {
|
|
12553
|
+
const url = new URL("/uploads", this._apiUrl);
|
|
12554
|
+
url.searchParams.set("skip", skip.toString());
|
|
12555
|
+
url.searchParams.set("limit", limit.toString());
|
|
12556
|
+
if (filter.clientId) {
|
|
12557
|
+
url.searchParams.set("clientId", filter.clientId);
|
|
12558
|
+
}
|
|
12559
|
+
if (filter.mimeTypes) {
|
|
12560
|
+
url.searchParams.set("mimeTypes", filter.mimeTypes.join());
|
|
12561
|
+
}
|
|
12562
|
+
if (filter.tags) {
|
|
12563
|
+
url.searchParams.set("tags", filter.tags.join());
|
|
12564
|
+
}
|
|
12565
|
+
const res = await fetch(url.href, {
|
|
12566
|
+
headers: this.apiHeaders,
|
|
12567
|
+
});
|
|
12568
|
+
return await res.json();
|
|
12453
12569
|
}
|
|
12454
|
-
|
|
12570
|
+
async deleteUpload(id) {
|
|
12571
|
+
const res = await fetch(`${this._apiUrl}/uploads/${id}`, {
|
|
12572
|
+
method: "DELETE",
|
|
12573
|
+
headers: this.apiHeaders,
|
|
12574
|
+
});
|
|
12575
|
+
return await res.json();
|
|
12576
|
+
}
|
|
12577
|
+
async exposeScriptingContext(context) {
|
|
12455
12578
|
// @ts-ignore
|
|
12456
|
-
|
|
12457
|
-
this.doc.on("update", handler);
|
|
12579
|
+
window.KM_SCRIPTING_CONTEXT = context;
|
|
12458
12580
|
// @ts-ignore
|
|
12459
|
-
|
|
12460
|
-
return () => this.doc.off("update", handler);
|
|
12581
|
+
window.dispatchEvent(new CustomEvent("km:scriptingContextExposed"));
|
|
12461
12582
|
}
|
|
12462
|
-
async
|
|
12463
|
-
|
|
12464
|
-
|
|
12465
|
-
|
|
12466
|
-
|
|
12467
|
-
|
|
12468
|
-
|
|
12469
|
-
|
|
12470
|
-
|
|
12471
|
-
|
|
12472
|
-
|
|
12473
|
-
|
|
12474
|
-
|
|
12475
|
-
|
|
12476
|
-
|
|
12477
|
-
|
|
12478
|
-
|
|
12479
|
-
this.doc.on("update", () => {
|
|
12480
|
-
const newMessages = [];
|
|
12481
|
-
for (const id in this.proxy) {
|
|
12482
|
-
if (emittedMessageIds.has(id)) {
|
|
12483
|
-
continue;
|
|
12484
|
-
}
|
|
12485
|
-
emittedMessageIds.add(id);
|
|
12486
|
-
newMessages.push({ id, payload: this.proxy[id].payload });
|
|
12487
|
-
}
|
|
12488
|
-
if (newMessages.length > 0) {
|
|
12489
|
-
this._emitter.emit("messages", newMessages);
|
|
12490
|
-
}
|
|
12583
|
+
async sendSubscribeReq(roomName, mode) {
|
|
12584
|
+
// Set up sync resolver
|
|
12585
|
+
const reqId = ++this._messageId;
|
|
12586
|
+
return await new Promise((resolve, reject) => {
|
|
12587
|
+
this._subscribeReqPromises.set(reqId, {
|
|
12588
|
+
resolve: (roomHash, initialUpdate) => {
|
|
12589
|
+
resolve({ roomHash, initialUpdate });
|
|
12590
|
+
},
|
|
12591
|
+
reject,
|
|
12592
|
+
});
|
|
12593
|
+
// Send subscription request
|
|
12594
|
+
const msg = new WsMessageWriter();
|
|
12595
|
+
msg.writeInt32(WsMessageType.SubscribeReq);
|
|
12596
|
+
msg.writeInt32(reqId);
|
|
12597
|
+
msg.writeString(roomName);
|
|
12598
|
+
msg.writeChar(mode);
|
|
12599
|
+
this.ws.send(msg.getBuffer());
|
|
12491
12600
|
});
|
|
12492
12601
|
}
|
|
12493
|
-
|
|
12494
|
-
|
|
12495
|
-
|
|
12496
|
-
|
|
12497
|
-
|
|
12498
|
-
|
|
12499
|
-
_kmClients = new Set();
|
|
12500
|
-
constructor(roomName, dataSchema, _data, mode = RoomSubscriptionMode.ReadWrite, pingTimeout = 3000) {
|
|
12501
|
-
super(`/a/${roomName}`, KokimokiSchema.dict(KokimokiSchema.struct({
|
|
12502
|
-
clientId: KokimokiSchema.string(),
|
|
12503
|
-
lastPing: KokimokiSchema.number(),
|
|
12504
|
-
data: dataSchema,
|
|
12505
|
-
})), mode);
|
|
12506
|
-
this.dataSchema = dataSchema;
|
|
12507
|
-
this._data = _data;
|
|
12508
|
-
this._pingInterval = setInterval(async () => {
|
|
12509
|
-
const kmClients = Array.from(this._kmClients);
|
|
12510
|
-
await Promise.all(kmClients.map(async (client) => {
|
|
12511
|
-
try {
|
|
12512
|
-
await client.transact((t) => {
|
|
12513
|
-
const timestamp = client.serverTimestamp();
|
|
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
|
-
}
|
|
12525
|
-
// Delete clients that haven't pinged in a while
|
|
12526
|
-
for (const connectionId in this.proxy) {
|
|
12527
|
-
const { lastPing } = this.proxy[connectionId];
|
|
12528
|
-
if (timestamp - lastPing > pingTimeout * 2) {
|
|
12529
|
-
t.delete(this.root[connectionId]);
|
|
12530
|
-
}
|
|
12531
|
-
}
|
|
12532
|
-
});
|
|
12533
|
-
}
|
|
12534
|
-
catch (e) { }
|
|
12535
|
-
}));
|
|
12536
|
-
}, pingTimeout);
|
|
12537
|
-
}
|
|
12538
|
-
async onJoin(client) {
|
|
12539
|
-
this._kmClients.add(client);
|
|
12540
|
-
await client.transact((t) => {
|
|
12541
|
-
t.set(this.root[client.connectionId], {
|
|
12542
|
-
clientId: client.id,
|
|
12543
|
-
lastPing: client.serverTimestamp(),
|
|
12544
|
-
data: this._data,
|
|
12602
|
+
async sendUnsubscribeReq(roomHash) {
|
|
12603
|
+
const reqId = ++this._messageId;
|
|
12604
|
+
return await new Promise((resolve, reject) => {
|
|
12605
|
+
this._unsubscribeReqPromises.set(reqId, {
|
|
12606
|
+
resolve,
|
|
12607
|
+
reject,
|
|
12545
12608
|
});
|
|
12609
|
+
// Send unsubscribe request
|
|
12610
|
+
const msg = new WsMessageWriter();
|
|
12611
|
+
msg.writeInt32(WsMessageType.UnsubscribeReq);
|
|
12612
|
+
msg.writeInt32(reqId);
|
|
12613
|
+
msg.writeUint32(roomHash);
|
|
12614
|
+
this.ws.send(msg.getBuffer());
|
|
12546
12615
|
});
|
|
12547
12616
|
}
|
|
12548
|
-
async
|
|
12549
|
-
|
|
12550
|
-
|
|
12551
|
-
|
|
12617
|
+
async join(store) {
|
|
12618
|
+
let subscription = this._subscriptionsByName.get(store.roomName);
|
|
12619
|
+
if (!subscription) {
|
|
12620
|
+
subscription = new RoomSubscription(this, store);
|
|
12621
|
+
this._subscriptionsByName.set(store.roomName, subscription);
|
|
12622
|
+
}
|
|
12623
|
+
// Send subscription request if connected to server
|
|
12624
|
+
if (!subscription.joined) {
|
|
12625
|
+
const res = await this.sendSubscribeReq(store.roomName, store.mode);
|
|
12626
|
+
this._subscriptionsByHash.set(res.roomHash, subscription);
|
|
12627
|
+
await subscription.applyInitialResponse(res.roomHash, res.initialUpdate);
|
|
12628
|
+
// Trigger onJoin event
|
|
12629
|
+
store.onJoin(this);
|
|
12630
|
+
}
|
|
12552
12631
|
}
|
|
12553
|
-
async
|
|
12554
|
-
this.
|
|
12632
|
+
async leave(store) {
|
|
12633
|
+
const subscription = this._subscriptionsByName.get(store.roomName);
|
|
12634
|
+
if (subscription) {
|
|
12635
|
+
await store.onBeforeLeave(this);
|
|
12636
|
+
await this.sendUnsubscribeReq(subscription.roomHash);
|
|
12637
|
+
this._subscriptionsByName.delete(store.roomName);
|
|
12638
|
+
this._subscriptionsByHash.delete(subscription.roomHash);
|
|
12639
|
+
subscription.close();
|
|
12640
|
+
// Trigger onLeave event
|
|
12641
|
+
store.onLeave(this);
|
|
12642
|
+
}
|
|
12555
12643
|
}
|
|
12556
|
-
|
|
12557
|
-
|
|
12558
|
-
|
|
12559
|
-
clients[this.proxy[connectionId].clientId] =
|
|
12560
|
-
this.proxy[connectionId].data;
|
|
12644
|
+
async transact(handler) {
|
|
12645
|
+
if (!this._connected) {
|
|
12646
|
+
throw new Error("Client not connected");
|
|
12561
12647
|
}
|
|
12562
|
-
|
|
12648
|
+
const transaction = new KokimokiTransaction(this);
|
|
12649
|
+
await handler(transaction);
|
|
12650
|
+
const { updates, consumedMessages } = await transaction.getUpdates();
|
|
12651
|
+
if (!updates.length) {
|
|
12652
|
+
return;
|
|
12653
|
+
}
|
|
12654
|
+
// Construct buffer
|
|
12655
|
+
const writer = new WsMessageWriter();
|
|
12656
|
+
// Write message type
|
|
12657
|
+
writer.writeInt32(WsMessageType.Transaction);
|
|
12658
|
+
// Update and write transaction ID
|
|
12659
|
+
const transactionId = ++this._messageId;
|
|
12660
|
+
writer.writeInt32(transactionId);
|
|
12661
|
+
// Write room hashes where messages were consumed
|
|
12662
|
+
writer.writeInt32(consumedMessages.size);
|
|
12663
|
+
for (const roomName of consumedMessages) {
|
|
12664
|
+
const subscription = this._subscriptionsByName.get(roomName);
|
|
12665
|
+
if (!subscription) {
|
|
12666
|
+
throw new Error(`Cannot consume message in "${roomName}" because it hasn't been joined`);
|
|
12667
|
+
}
|
|
12668
|
+
writer.writeUint32(subscription.roomHash);
|
|
12669
|
+
}
|
|
12670
|
+
// Write updates
|
|
12671
|
+
for (const { roomName, update } of updates) {
|
|
12672
|
+
const subscription = this._subscriptionsByName.get(roomName);
|
|
12673
|
+
if (!subscription) {
|
|
12674
|
+
throw new Error(`Cannot send update to "${roomName}" because it hasn't been joined`);
|
|
12675
|
+
}
|
|
12676
|
+
writer.writeUint32(subscription.roomHash);
|
|
12677
|
+
writer.writeUint8Array(update);
|
|
12678
|
+
}
|
|
12679
|
+
const buffer = writer.getBuffer();
|
|
12680
|
+
// Wait for server to apply transaction
|
|
12681
|
+
await new Promise((resolve, reject) => {
|
|
12682
|
+
this._transactionPromises.set(transactionId, { resolve, reject });
|
|
12683
|
+
// Send update to server
|
|
12684
|
+
try {
|
|
12685
|
+
this.ws.send(buffer);
|
|
12686
|
+
}
|
|
12687
|
+
catch (e) {
|
|
12688
|
+
// Not connected
|
|
12689
|
+
console.log("Failed to send update to server:", e);
|
|
12690
|
+
// TODO: merge updates or something
|
|
12691
|
+
throw e;
|
|
12692
|
+
}
|
|
12693
|
+
});
|
|
12563
12694
|
}
|
|
12564
|
-
async
|
|
12565
|
-
this.
|
|
12566
|
-
|
|
12567
|
-
|
|
12568
|
-
|
|
12569
|
-
|
|
12570
|
-
|
|
12571
|
-
}
|
|
12695
|
+
async close() {
|
|
12696
|
+
this._autoReconnect = false;
|
|
12697
|
+
if (this._ws) {
|
|
12698
|
+
this._ws.close();
|
|
12699
|
+
}
|
|
12700
|
+
if (this._pingInterval) {
|
|
12701
|
+
clearInterval(this._pingInterval);
|
|
12702
|
+
}
|
|
12703
|
+
}
|
|
12704
|
+
// Get room hash value for store
|
|
12705
|
+
getRoomHash(store) {
|
|
12706
|
+
const subscription = this._subscriptionsByName.get(store.roomName);
|
|
12707
|
+
if (!subscription) {
|
|
12708
|
+
throw new Error(`Store "${store.roomName}" not joined`);
|
|
12709
|
+
}
|
|
12710
|
+
return subscription.roomHash;
|
|
12711
|
+
}
|
|
12712
|
+
/** Initializers */
|
|
12713
|
+
// store
|
|
12714
|
+
store(name, schema, autoJoin = true) {
|
|
12715
|
+
const store = new KokimokiStore(name, schema);
|
|
12716
|
+
if (autoJoin) {
|
|
12717
|
+
this.join(store)
|
|
12718
|
+
.then(() => { })
|
|
12719
|
+
.catch(() => { });
|
|
12720
|
+
}
|
|
12721
|
+
return store;
|
|
12722
|
+
}
|
|
12723
|
+
// queue
|
|
12724
|
+
queue(name, schema, mode, autoJoin = true) {
|
|
12725
|
+
const queue = new KokimokiQueue(name, schema, mode);
|
|
12726
|
+
if (autoJoin) {
|
|
12727
|
+
this.join(queue)
|
|
12728
|
+
.then(() => { })
|
|
12729
|
+
.catch(() => { });
|
|
12730
|
+
}
|
|
12731
|
+
return queue;
|
|
12732
|
+
}
|
|
12733
|
+
// awareness
|
|
12734
|
+
awareness(name, dataSchema, initialData = dataSchema.defaultValue, autoJoin = true) {
|
|
12735
|
+
const awareness = new KokimokiAwareness(name, dataSchema, initialData);
|
|
12736
|
+
if (autoJoin) {
|
|
12737
|
+
this.join(awareness)
|
|
12738
|
+
.then(() => { })
|
|
12739
|
+
.catch(() => { });
|
|
12740
|
+
}
|
|
12741
|
+
return awareness;
|
|
12742
|
+
}
|
|
12743
|
+
// req-res
|
|
12744
|
+
reqRes(serviceName, reqSchema, resSchema, handleRequest) {
|
|
12745
|
+
return new KokimokiReqRes(this, serviceName, reqSchema, resSchema, handleRequest);
|
|
12572
12746
|
}
|
|
12573
12747
|
}
|
|
12574
12748
|
|