@kokimoki/app 1.4.3 → 1.4.5
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-awareness.js +1 -1
- package/dist/kokimoki-client-refactored.d.ts +68 -0
- package/dist/kokimoki-client-refactored.js +394 -0
- package/dist/kokimoki-client.d.ts +0 -1
- package/dist/kokimoki-client.js +9 -10
- package/dist/kokimoki.min.d.ts +0 -1
- package/dist/kokimoki.min.js +11 -12
- package/dist/kokimoki.min.js.map +1 -1
- package/dist/message-queue.d.ts +8 -0
- package/dist/message-queue.js +19 -0
- package/dist/synced-schema.d.ts +59 -0
- package/dist/synced-schema.js +84 -0
- package/dist/synced-store.d.ts +7 -0
- package/dist/synced-store.js +9 -0
- package/dist/synced-types.d.ts +38 -0
- package/dist/synced-types.js +68 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/ws-message-type copy.d.ts +6 -0
- package/dist/ws-message-type copy.js +7 -0
- package/package.json +1 -1
|
@@ -34,7 +34,7 @@ export class KokimokiAwareness extends KokimokiStore {
|
|
|
34
34
|
// Delete clients that haven't pinged in a while
|
|
35
35
|
for (const connectionId in this.proxy) {
|
|
36
36
|
const { lastPing } = this.proxy[connectionId];
|
|
37
|
-
if (timestamp - lastPing > pingTimeout * 2) {
|
|
37
|
+
if (!lastPing || timestamp - lastPing > pingTimeout * 2) {
|
|
38
38
|
t.delete(this.root[connectionId]);
|
|
39
39
|
}
|
|
40
40
|
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type TypedEmitter from "typed-emitter";
|
|
2
|
+
import type { KokimokiClientEventsRefactored } from "./types/events";
|
|
3
|
+
import type { Upload } from "./types/upload";
|
|
4
|
+
import type { Paginated } from "./types/common";
|
|
5
|
+
import { KokimokiTransaction } from "./kokimoki-transaction";
|
|
6
|
+
import type { KokimokiStore } from "./kokimoki-store";
|
|
7
|
+
import type { SyncedGeneric } from "./synced-schema";
|
|
8
|
+
import { RoomSubscriptionMode } from "./room-subscription-mode";
|
|
9
|
+
declare const KokimokiClientRefactored_base: new () => TypedEmitter<KokimokiClientEventsRefactored>;
|
|
10
|
+
export declare class KokimokiClientRefactored<ClientContextT = any> extends KokimokiClientRefactored_base {
|
|
11
|
+
readonly host: string;
|
|
12
|
+
readonly appId: string;
|
|
13
|
+
readonly code: string;
|
|
14
|
+
private _wsUrl;
|
|
15
|
+
private _apiUrl;
|
|
16
|
+
private _id?;
|
|
17
|
+
private _token?;
|
|
18
|
+
private _apiHeaders?;
|
|
19
|
+
private _serverTimeOffset;
|
|
20
|
+
private _clientContext?;
|
|
21
|
+
private _ws?;
|
|
22
|
+
private _subscriptions;
|
|
23
|
+
private _stores;
|
|
24
|
+
private _reqPromises;
|
|
25
|
+
private _transactionResolves;
|
|
26
|
+
private _roomHashes;
|
|
27
|
+
private _roomHashToDoc;
|
|
28
|
+
private _connected;
|
|
29
|
+
private _connectPromise?;
|
|
30
|
+
private _messageId;
|
|
31
|
+
constructor(host: string, appId: string, code?: string);
|
|
32
|
+
get id(): string;
|
|
33
|
+
get token(): string;
|
|
34
|
+
get apiUrl(): string;
|
|
35
|
+
get apiHeaders(): Headers;
|
|
36
|
+
get clientContext(): ClientContextT & ({} | null);
|
|
37
|
+
get connected(): boolean;
|
|
38
|
+
get ws(): WebSocket;
|
|
39
|
+
connect(): Promise<void>;
|
|
40
|
+
private handleInitMessage;
|
|
41
|
+
private handleBinaryMessage;
|
|
42
|
+
private handleErrorMessage;
|
|
43
|
+
private handleSubscribeResMessage;
|
|
44
|
+
private handleRoomUpdateMessage;
|
|
45
|
+
serverTimestamp(): number;
|
|
46
|
+
patchRoomState(room: string, update: Uint8Array): Promise<any>;
|
|
47
|
+
private createUpload;
|
|
48
|
+
private uploadChunks;
|
|
49
|
+
private completeUpload;
|
|
50
|
+
upload(name: string, blob: Blob, tags?: string[]): Promise<Upload>;
|
|
51
|
+
updateUpload(id: string, update: {
|
|
52
|
+
tags?: string[];
|
|
53
|
+
}): Promise<Upload>;
|
|
54
|
+
listUploads(filter?: {
|
|
55
|
+
clientId?: string;
|
|
56
|
+
mimeTypes?: string[];
|
|
57
|
+
tags?: string[];
|
|
58
|
+
}, skip?: number, limit?: number): Promise<Paginated<Upload>>;
|
|
59
|
+
deleteUpload(id: string): Promise<{
|
|
60
|
+
acknowledged: boolean;
|
|
61
|
+
deletedCount: number;
|
|
62
|
+
}>;
|
|
63
|
+
exposeScriptingContext(context: any): Promise<void>;
|
|
64
|
+
private sendSubscriptionReq;
|
|
65
|
+
join<T extends SyncedGeneric<unknown>>(store: KokimokiStore<T>, mode?: RoomSubscriptionMode): Promise<void>;
|
|
66
|
+
transact(handler: (t: KokimokiTransaction) => void): Promise<void>;
|
|
67
|
+
}
|
|
68
|
+
export {};
|
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
import EventEmitter from "events";
|
|
2
|
+
import { KOKIMOKI_APP_VERSION } from "./version";
|
|
3
|
+
import * as Y from "yjs";
|
|
4
|
+
import { KokimokiTransaction } from "./kokimoki-transaction";
|
|
5
|
+
import { WsMessageType } from "./ws-message-type";
|
|
6
|
+
import { WsMessageWriter } from "./ws-message-writer";
|
|
7
|
+
import { WsMessageReader } from "./ws-message-reader";
|
|
8
|
+
import { RoomSubscriptionMode } from "./room-subscription-mode";
|
|
9
|
+
export class KokimokiClientRefactored extends EventEmitter {
|
|
10
|
+
host;
|
|
11
|
+
appId;
|
|
12
|
+
code;
|
|
13
|
+
_wsUrl;
|
|
14
|
+
_apiUrl;
|
|
15
|
+
_id;
|
|
16
|
+
_token;
|
|
17
|
+
_apiHeaders;
|
|
18
|
+
_serverTimeOffset = 0;
|
|
19
|
+
_clientContext;
|
|
20
|
+
_ws;
|
|
21
|
+
_subscriptions = new Map();
|
|
22
|
+
_stores = new Map();
|
|
23
|
+
_reqPromises = new Map();
|
|
24
|
+
_transactionResolves = new Map();
|
|
25
|
+
_roomHashes = new Map();
|
|
26
|
+
_roomHashToDoc = new Map();
|
|
27
|
+
_connected = false;
|
|
28
|
+
_connectPromise;
|
|
29
|
+
_messageId = 0;
|
|
30
|
+
constructor(host, appId, code = "") {
|
|
31
|
+
super();
|
|
32
|
+
this.host = host;
|
|
33
|
+
this.appId = appId;
|
|
34
|
+
this.code = code;
|
|
35
|
+
// Set up the URLs
|
|
36
|
+
const secure = this.host.indexOf(":") === -1;
|
|
37
|
+
this._wsUrl = `ws${secure ? "s" : ""}://${this.host}`;
|
|
38
|
+
this._apiUrl = `http${secure ? "s" : ""}://${this.host}`;
|
|
39
|
+
}
|
|
40
|
+
get id() {
|
|
41
|
+
if (!this._id) {
|
|
42
|
+
throw new Error("Client not connected");
|
|
43
|
+
}
|
|
44
|
+
return this._id;
|
|
45
|
+
}
|
|
46
|
+
get token() {
|
|
47
|
+
if (!this._token) {
|
|
48
|
+
throw new Error("Client not connected");
|
|
49
|
+
}
|
|
50
|
+
return this._token;
|
|
51
|
+
}
|
|
52
|
+
get apiUrl() {
|
|
53
|
+
if (!this._apiUrl) {
|
|
54
|
+
throw new Error("Client not connected");
|
|
55
|
+
}
|
|
56
|
+
return this._apiUrl;
|
|
57
|
+
}
|
|
58
|
+
get apiHeaders() {
|
|
59
|
+
if (!this._apiHeaders) {
|
|
60
|
+
throw new Error("Client not connected");
|
|
61
|
+
}
|
|
62
|
+
return this._apiHeaders;
|
|
63
|
+
}
|
|
64
|
+
get clientContext() {
|
|
65
|
+
if (this._clientContext === undefined) {
|
|
66
|
+
throw new Error("Client not connected");
|
|
67
|
+
}
|
|
68
|
+
return this._clientContext;
|
|
69
|
+
}
|
|
70
|
+
get connected() {
|
|
71
|
+
return this._connected;
|
|
72
|
+
}
|
|
73
|
+
get ws() {
|
|
74
|
+
if (!this._ws) {
|
|
75
|
+
throw new Error("Not connected");
|
|
76
|
+
}
|
|
77
|
+
return this._ws;
|
|
78
|
+
}
|
|
79
|
+
async connect() {
|
|
80
|
+
if (this._connectPromise) {
|
|
81
|
+
return await this._connectPromise;
|
|
82
|
+
}
|
|
83
|
+
this._ws = new WebSocket(`${this._wsUrl}/apps/${this.appId}?clientVersion=${KOKIMOKI_APP_VERSION}`);
|
|
84
|
+
this._ws.binaryType = "arraybuffer";
|
|
85
|
+
this._connectPromise = new Promise((onInit) => {
|
|
86
|
+
// Fetch the auth token
|
|
87
|
+
let clientToken = localStorage.getItem("KM_TOKEN");
|
|
88
|
+
// Send the app token on first connect
|
|
89
|
+
this.ws.onopen = () => {
|
|
90
|
+
this.ws.send(JSON.stringify({ type: "auth", code: this.code, token: clientToken }));
|
|
91
|
+
};
|
|
92
|
+
this.ws.onclose = () => {
|
|
93
|
+
this._connected = false;
|
|
94
|
+
this._connectPromise = undefined;
|
|
95
|
+
this._ws = undefined;
|
|
96
|
+
// Clean up
|
|
97
|
+
this._reqPromises.clear();
|
|
98
|
+
this._transactionResolves.clear();
|
|
99
|
+
this._roomHashes.clear();
|
|
100
|
+
this._roomHashToDoc.clear();
|
|
101
|
+
this._subscriptions.clear();
|
|
102
|
+
// Emit disconnected event
|
|
103
|
+
this.emit("disconnected");
|
|
104
|
+
// Attempt to reconnect
|
|
105
|
+
// setTimeout(async () => await this.connect(), 2000);
|
|
106
|
+
};
|
|
107
|
+
this.ws.onmessage = (e) => {
|
|
108
|
+
console.log(`Received WS message: ${e.data}`);
|
|
109
|
+
// Handle JSON messages
|
|
110
|
+
if (typeof e.data === "string") {
|
|
111
|
+
const message = JSON.parse(e.data);
|
|
112
|
+
switch (message.type) {
|
|
113
|
+
case "serverTime": {
|
|
114
|
+
this._serverTimeOffset = Date.now() - message.serverTime;
|
|
115
|
+
console.log(`Server time offset: ${this._serverTimeOffset}`);
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
case "init":
|
|
119
|
+
this.handleInitMessage(message);
|
|
120
|
+
onInit();
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
// Handle binary messages
|
|
126
|
+
this.handleBinaryMessage(e.data);
|
|
127
|
+
};
|
|
128
|
+
});
|
|
129
|
+
await this._connectPromise;
|
|
130
|
+
}
|
|
131
|
+
handleInitMessage(message) {
|
|
132
|
+
localStorage.setItem("KM_TOKEN", message.clientToken);
|
|
133
|
+
this._id = message.clientId;
|
|
134
|
+
this._token = message.appToken;
|
|
135
|
+
this._clientContext = message.clientContext;
|
|
136
|
+
// Set up the auth headers
|
|
137
|
+
this._apiHeaders = new Headers({
|
|
138
|
+
Authorization: `Bearer ${this.token}`,
|
|
139
|
+
"Content-Type": "application/json",
|
|
140
|
+
});
|
|
141
|
+
this._connected = true;
|
|
142
|
+
this.emit("connected");
|
|
143
|
+
}
|
|
144
|
+
handleBinaryMessage(data) {
|
|
145
|
+
const reader = new WsMessageReader(data);
|
|
146
|
+
const type = reader.readInt32();
|
|
147
|
+
switch (type) {
|
|
148
|
+
case WsMessageType.SubscribeRes:
|
|
149
|
+
this.handleSubscribeResMessage(reader);
|
|
150
|
+
break;
|
|
151
|
+
case WsMessageType.RoomUpdate:
|
|
152
|
+
this.handleRoomUpdateMessage(reader);
|
|
153
|
+
break;
|
|
154
|
+
case WsMessageType.Error:
|
|
155
|
+
this.handleErrorMessage(reader);
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
handleErrorMessage(msg) {
|
|
160
|
+
const reqId = msg.readInt32();
|
|
161
|
+
const error = msg.readString();
|
|
162
|
+
const promise = this._reqPromises.get(reqId);
|
|
163
|
+
if (promise) {
|
|
164
|
+
this._reqPromises.delete(reqId);
|
|
165
|
+
promise.reject(error);
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
console.warn(`Received error for unknown request ${reqId}: ${error}`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
handleSubscribeResMessage(msg) {
|
|
172
|
+
const reqId = msg.readInt32();
|
|
173
|
+
const roomHash = msg.readUint32();
|
|
174
|
+
const promise = this._reqPromises.get(reqId);
|
|
175
|
+
if (promise) {
|
|
176
|
+
this._reqPromises.delete(reqId);
|
|
177
|
+
// In Write mode, no initial state is sent
|
|
178
|
+
if (!msg.end) {
|
|
179
|
+
promise.resolve(roomHash, msg.readUint8Array());
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
promise.resolve(roomHash);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
handleRoomUpdateMessage(msg) {
|
|
187
|
+
const appliedId = msg.readInt32();
|
|
188
|
+
const roomHash = msg.readUint32();
|
|
189
|
+
// Apply update if not in Write mode
|
|
190
|
+
if (!msg.end) {
|
|
191
|
+
const doc = this._roomHashToDoc.get(roomHash);
|
|
192
|
+
if (doc) {
|
|
193
|
+
Y.applyUpdate(doc, msg.readUint8Array(), this);
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
console.warn(`Received update for unknown room ${roomHash}`);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
// Check transaction resolves
|
|
200
|
+
for (const [transactionId, resolve,] of this._transactionResolves.entries()) {
|
|
201
|
+
if (appliedId >= transactionId) {
|
|
202
|
+
this._transactionResolves.delete(transactionId);
|
|
203
|
+
resolve();
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
serverTimestamp() {
|
|
208
|
+
return Date.now() - this._serverTimeOffset;
|
|
209
|
+
}
|
|
210
|
+
// Send Y update to room
|
|
211
|
+
async patchRoomState(room, update) {
|
|
212
|
+
const res = await fetch(`${this._apiUrl}/rooms/${room}`, {
|
|
213
|
+
method: "PATCH",
|
|
214
|
+
headers: this.apiHeaders,
|
|
215
|
+
body: update,
|
|
216
|
+
});
|
|
217
|
+
return await res.json();
|
|
218
|
+
}
|
|
219
|
+
// Storage
|
|
220
|
+
async createUpload(name, blob, tags) {
|
|
221
|
+
const res = await fetch(`${this._apiUrl}/uploads`, {
|
|
222
|
+
method: "POST",
|
|
223
|
+
headers: this.apiHeaders,
|
|
224
|
+
body: JSON.stringify({
|
|
225
|
+
name,
|
|
226
|
+
size: blob.size,
|
|
227
|
+
mimeType: blob.type,
|
|
228
|
+
tags,
|
|
229
|
+
}),
|
|
230
|
+
});
|
|
231
|
+
return await res.json();
|
|
232
|
+
}
|
|
233
|
+
async uploadChunks(blob, chunkSize, signedUrls) {
|
|
234
|
+
return await Promise.all(signedUrls.map(async (url, index) => {
|
|
235
|
+
const start = index * chunkSize;
|
|
236
|
+
const end = Math.min(start + chunkSize, blob.size);
|
|
237
|
+
const chunk = blob.slice(start, end);
|
|
238
|
+
const res = await fetch(url, { method: "PUT", body: chunk });
|
|
239
|
+
return JSON.parse(res.headers.get("ETag") || '""');
|
|
240
|
+
}));
|
|
241
|
+
}
|
|
242
|
+
async completeUpload(id, etags) {
|
|
243
|
+
const res = await fetch(`${this._apiUrl}/uploads/${id}`, {
|
|
244
|
+
method: "PUT",
|
|
245
|
+
headers: this.apiHeaders,
|
|
246
|
+
body: JSON.stringify({ etags }),
|
|
247
|
+
});
|
|
248
|
+
return await res.json();
|
|
249
|
+
}
|
|
250
|
+
async upload(name, blob, tags = []) {
|
|
251
|
+
const { id, chunkSize, urls } = await this.createUpload(name, blob, tags);
|
|
252
|
+
const etags = await this.uploadChunks(blob, chunkSize, urls);
|
|
253
|
+
return await this.completeUpload(id, etags);
|
|
254
|
+
}
|
|
255
|
+
async updateUpload(id, update) {
|
|
256
|
+
const res = await fetch(`${this._apiUrl}/uploads/${id}`, {
|
|
257
|
+
method: "PUT",
|
|
258
|
+
headers: this.apiHeaders,
|
|
259
|
+
body: JSON.stringify(update),
|
|
260
|
+
});
|
|
261
|
+
return await res.json();
|
|
262
|
+
}
|
|
263
|
+
async listUploads(filter = {}, skip = 0, limit = 100) {
|
|
264
|
+
const url = new URL("/uploads", this._apiUrl);
|
|
265
|
+
url.searchParams.set("skip", skip.toString());
|
|
266
|
+
url.searchParams.set("limit", limit.toString());
|
|
267
|
+
if (filter.clientId) {
|
|
268
|
+
url.searchParams.set("clientId", filter.clientId);
|
|
269
|
+
}
|
|
270
|
+
if (filter.mimeTypes) {
|
|
271
|
+
url.searchParams.set("mimeTypes", filter.mimeTypes.join());
|
|
272
|
+
}
|
|
273
|
+
if (filter.tags) {
|
|
274
|
+
url.searchParams.set("tags", filter.tags.join());
|
|
275
|
+
}
|
|
276
|
+
const res = await fetch(url.href, {
|
|
277
|
+
headers: this.apiHeaders,
|
|
278
|
+
});
|
|
279
|
+
return await res.json();
|
|
280
|
+
}
|
|
281
|
+
async deleteUpload(id) {
|
|
282
|
+
const res = await fetch(`${this._apiUrl}/uploads/${id}`, {
|
|
283
|
+
method: "DELETE",
|
|
284
|
+
headers: this.apiHeaders,
|
|
285
|
+
});
|
|
286
|
+
return await res.json();
|
|
287
|
+
}
|
|
288
|
+
async exposeScriptingContext(context) {
|
|
289
|
+
// @ts-ignore
|
|
290
|
+
window.KM_SCRIPTING_CONTEXT = context;
|
|
291
|
+
// @ts-ignore
|
|
292
|
+
window.dispatchEvent(new CustomEvent("km:scriptingContextExposed"));
|
|
293
|
+
}
|
|
294
|
+
async sendSubscriptionReq(roomName, mode) {
|
|
295
|
+
// Set up sync resolver
|
|
296
|
+
const reqId = ++this._messageId;
|
|
297
|
+
return await new Promise((resolve, reject) => {
|
|
298
|
+
this._reqPromises.set(reqId, {
|
|
299
|
+
resolve: (roomHash, initialUpdate) => {
|
|
300
|
+
resolve({ roomHash, initialUpdate });
|
|
301
|
+
},
|
|
302
|
+
reject,
|
|
303
|
+
});
|
|
304
|
+
// Send subscription request
|
|
305
|
+
const msg = new WsMessageWriter();
|
|
306
|
+
msg.writeInt32(WsMessageType.SubscribeReq);
|
|
307
|
+
msg.writeInt32(reqId);
|
|
308
|
+
msg.writeString(roomName);
|
|
309
|
+
msg.writeChar(mode);
|
|
310
|
+
this.ws.send(msg.getBuffer());
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
async join(store, mode = RoomSubscriptionMode.ReadWrite) {
|
|
314
|
+
if (this._subscriptions.has(store.roomName)) {
|
|
315
|
+
throw new Error(`Already joined room "${store.roomName}"`);
|
|
316
|
+
}
|
|
317
|
+
this._subscriptions.set(store.roomName, mode);
|
|
318
|
+
// Send req
|
|
319
|
+
try {
|
|
320
|
+
const res = await this.sendSubscriptionReq(store.roomName, mode);
|
|
321
|
+
// Set room hash
|
|
322
|
+
this._roomHashes.set(store.roomName, res.roomHash);
|
|
323
|
+
this._roomHashToDoc.set(res.roomHash, store.doc);
|
|
324
|
+
this._stores.set(res.roomHash, store);
|
|
325
|
+
// Apply initial state
|
|
326
|
+
if (res.initialUpdate) {
|
|
327
|
+
Y.applyUpdate(store.doc, res.initialUpdate, this);
|
|
328
|
+
}
|
|
329
|
+
// Set defaults if doc is empty after sync (only possible in ReadWrite mode)
|
|
330
|
+
if (mode === RoomSubscriptionMode.ReadWrite) {
|
|
331
|
+
await this.transact((t) => {
|
|
332
|
+
for (const key in store.defaultValue) {
|
|
333
|
+
// @ts-ignore
|
|
334
|
+
if (!store.proxy.hasOwnProperty(key)) {
|
|
335
|
+
t.set(store.root[key], store.defaultValue[key]);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
catch (err) {
|
|
342
|
+
this._subscriptions.delete(store.roomName);
|
|
343
|
+
throw err;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
async transact(handler) {
|
|
347
|
+
const transaction = new KokimokiTransaction(this);
|
|
348
|
+
handler(transaction);
|
|
349
|
+
const updates = await transaction.getUpdates();
|
|
350
|
+
if (!updates.length) {
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
// Construct buffer
|
|
354
|
+
const writer = new WsMessageWriter();
|
|
355
|
+
// Write message type
|
|
356
|
+
writer.writeInt32(WsMessageType.Transaction);
|
|
357
|
+
// Update and write transaction ID
|
|
358
|
+
const transactionId = ++this._messageId;
|
|
359
|
+
writer.writeInt32(transactionId);
|
|
360
|
+
// Write updates
|
|
361
|
+
for (const { roomName, update } of updates) {
|
|
362
|
+
const roomHash = this._roomHashes.get(roomName);
|
|
363
|
+
if (!roomHash) {
|
|
364
|
+
throw new Error(`Cannot send update to "${roomName}" because it hasn't been joined`);
|
|
365
|
+
}
|
|
366
|
+
writer.writeUint32(roomHash);
|
|
367
|
+
writer.writeUint8Array(update);
|
|
368
|
+
}
|
|
369
|
+
const buffer = writer.getBuffer();
|
|
370
|
+
// Wait for server to apply transaction
|
|
371
|
+
await new Promise((resolve) => {
|
|
372
|
+
this._transactionResolves.set(transactionId, resolve);
|
|
373
|
+
// Send update to server
|
|
374
|
+
try {
|
|
375
|
+
this.ws.send(buffer);
|
|
376
|
+
}
|
|
377
|
+
catch (e) {
|
|
378
|
+
// Not connected
|
|
379
|
+
console.log("Failed to send update to server:", e);
|
|
380
|
+
// TODO: merge updates or something
|
|
381
|
+
throw e;
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
/* // Reset doc in Write-only mode
|
|
385
|
+
for (const { roomName, update } of updates) {
|
|
386
|
+
const mode = this._subscriptions.get(roomName);
|
|
387
|
+
|
|
388
|
+
if (mode === RoomSubscriptionMode.Write) {
|
|
389
|
+
// @ts-ignore
|
|
390
|
+
// this._stores.get(this._roomHashes.get(roomName)!)!.doc = new Y.Doc();
|
|
391
|
+
}
|
|
392
|
+
} */
|
|
393
|
+
}
|
|
394
|
+
}
|
|
@@ -34,7 +34,6 @@ export declare class KokimokiClient<ClientContextT = any> extends KokimokiClient
|
|
|
34
34
|
private _autoReconnect;
|
|
35
35
|
private _reconnectTimeout;
|
|
36
36
|
private _pingInterval;
|
|
37
|
-
private _devMode;
|
|
38
37
|
private _clientTokenKey;
|
|
39
38
|
constructor(host: string, appId: string, code?: string);
|
|
40
39
|
get id(): string;
|
package/dist/kokimoki-client.js
CHANGED
|
@@ -34,7 +34,6 @@ export class KokimokiClient extends EventEmitter {
|
|
|
34
34
|
_autoReconnect = true;
|
|
35
35
|
_reconnectTimeout = 0;
|
|
36
36
|
_pingInterval;
|
|
37
|
-
_devMode = false;
|
|
38
37
|
_clientTokenKey = "KM_TOKEN";
|
|
39
38
|
constructor(host, appId, code = "") {
|
|
40
39
|
super();
|
|
@@ -55,7 +54,7 @@ export class KokimokiClient extends EventEmitter {
|
|
|
55
54
|
}
|
|
56
55
|
}, 5000);
|
|
57
56
|
// Listen for devtools messages
|
|
58
|
-
if (window.top) {
|
|
57
|
+
if (window.top && window.self !== window.top) {
|
|
59
58
|
window.addEventListener("message", (e) => {
|
|
60
59
|
console.log(`[KM TOOLS] ${e.data}`);
|
|
61
60
|
if (e.data === "km:clearStorage") {
|
|
@@ -118,17 +117,16 @@ export class KokimokiClient extends EventEmitter {
|
|
|
118
117
|
return await this._connectPromise;
|
|
119
118
|
}
|
|
120
119
|
// Detect devtools
|
|
121
|
-
if (window.top) {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
// Wait up to 50ms for parent to respond
|
|
120
|
+
if (window.top && window.self !== window.top) {
|
|
121
|
+
await new Promise((resolve) => {
|
|
122
|
+
/* // Wait up to 500ms for parent to respond
|
|
125
123
|
const timeout = setTimeout(() => {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
},
|
|
124
|
+
window.removeEventListener("message", onMessage);
|
|
125
|
+
resolve();
|
|
126
|
+
}, 500); */
|
|
129
127
|
// Listen for parent response
|
|
130
128
|
const onMessage = (e) => {
|
|
131
|
-
clearTimeout(timeout);
|
|
129
|
+
// clearTimeout(timeout);
|
|
132
130
|
window.removeEventListener("message", onMessage);
|
|
133
131
|
if (e.data.clientKey) {
|
|
134
132
|
this._clientTokenKey = `KM_TOKEN/${e.data.clientKey}`;
|
|
@@ -136,6 +134,7 @@ export class KokimokiClient extends EventEmitter {
|
|
|
136
134
|
resolve();
|
|
137
135
|
};
|
|
138
136
|
window.addEventListener("message", onMessage);
|
|
137
|
+
window.top.postMessage({ appId: this.appId }, "*");
|
|
139
138
|
});
|
|
140
139
|
}
|
|
141
140
|
// Set up the WebSocket connection
|
package/dist/kokimoki.min.d.ts
CHANGED
|
@@ -343,7 +343,6 @@ declare class KokimokiClient<ClientContextT = any> extends KokimokiClient_base {
|
|
|
343
343
|
private _autoReconnect;
|
|
344
344
|
private _reconnectTimeout;
|
|
345
345
|
private _pingInterval;
|
|
346
|
-
private _devMode;
|
|
347
346
|
private _clientTokenKey;
|
|
348
347
|
constructor(host: string, appId: string, code?: string);
|
|
349
348
|
get id(): string;
|
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.4.
|
|
637
|
+
const KOKIMOKI_APP_VERSION = "1.4.5";
|
|
638
638
|
|
|
639
639
|
/**
|
|
640
640
|
* Utility module to work with key-value stores.
|
|
@@ -12034,7 +12034,7 @@ class KokimokiAwareness extends KokimokiStore {
|
|
|
12034
12034
|
// Delete clients that haven't pinged in a while
|
|
12035
12035
|
for (const connectionId in this.proxy) {
|
|
12036
12036
|
const { lastPing } = this.proxy[connectionId];
|
|
12037
|
-
if (timestamp - lastPing > pingTimeout * 2) {
|
|
12037
|
+
if (!lastPing || timestamp - lastPing > pingTimeout * 2) {
|
|
12038
12038
|
t.delete(this.root[connectionId]);
|
|
12039
12039
|
}
|
|
12040
12040
|
}
|
|
@@ -12247,7 +12247,6 @@ class KokimokiClient extends EventEmitter$1 {
|
|
|
12247
12247
|
_autoReconnect = true;
|
|
12248
12248
|
_reconnectTimeout = 0;
|
|
12249
12249
|
_pingInterval;
|
|
12250
|
-
_devMode = false;
|
|
12251
12250
|
_clientTokenKey = "KM_TOKEN";
|
|
12252
12251
|
constructor(host, appId, code = "") {
|
|
12253
12252
|
super();
|
|
@@ -12268,7 +12267,7 @@ class KokimokiClient extends EventEmitter$1 {
|
|
|
12268
12267
|
}
|
|
12269
12268
|
}, 5000);
|
|
12270
12269
|
// Listen for devtools messages
|
|
12271
|
-
if (window.top) {
|
|
12270
|
+
if (window.top && window.self !== window.top) {
|
|
12272
12271
|
window.addEventListener("message", (e) => {
|
|
12273
12272
|
console.log(`[KM TOOLS] ${e.data}`);
|
|
12274
12273
|
if (e.data === "km:clearStorage") {
|
|
@@ -12331,17 +12330,16 @@ class KokimokiClient extends EventEmitter$1 {
|
|
|
12331
12330
|
return await this._connectPromise;
|
|
12332
12331
|
}
|
|
12333
12332
|
// Detect devtools
|
|
12334
|
-
if (window.top) {
|
|
12335
|
-
|
|
12336
|
-
|
|
12337
|
-
// Wait up to 50ms for parent to respond
|
|
12333
|
+
if (window.top && window.self !== window.top) {
|
|
12334
|
+
await new Promise((resolve) => {
|
|
12335
|
+
/* // Wait up to 500ms for parent to respond
|
|
12338
12336
|
const timeout = setTimeout(() => {
|
|
12339
|
-
|
|
12340
|
-
|
|
12341
|
-
},
|
|
12337
|
+
window.removeEventListener("message", onMessage);
|
|
12338
|
+
resolve();
|
|
12339
|
+
}, 500); */
|
|
12342
12340
|
// Listen for parent response
|
|
12343
12341
|
const onMessage = (e) => {
|
|
12344
|
-
clearTimeout(timeout);
|
|
12342
|
+
// clearTimeout(timeout);
|
|
12345
12343
|
window.removeEventListener("message", onMessage);
|
|
12346
12344
|
if (e.data.clientKey) {
|
|
12347
12345
|
this._clientTokenKey = `KM_TOKEN/${e.data.clientKey}`;
|
|
@@ -12349,6 +12347,7 @@ class KokimokiClient extends EventEmitter$1 {
|
|
|
12349
12347
|
resolve();
|
|
12350
12348
|
};
|
|
12351
12349
|
window.addEventListener("message", onMessage);
|
|
12350
|
+
window.top.postMessage({ appId: this.appId }, "*");
|
|
12352
12351
|
});
|
|
12353
12352
|
}
|
|
12354
12353
|
// Set up the WebSocket connection
|