@kokimoki/app 0.6.9 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +5 -1
- package/dist/index.js +5 -1
- package/dist/kokimoki-client copy.d.ts +57 -0
- package/dist/kokimoki-client copy.js +259 -0
- package/dist/kokimoki-client-refactored.d.ts +67 -0
- package/dist/kokimoki-client-refactored.js +422 -0
- package/dist/kokimoki-client.d.ts +25 -14
- package/dist/kokimoki-client.js +286 -103
- package/dist/kokimoki-queue.d.ts +34 -0
- package/dist/kokimoki-queue.js +30 -0
- package/dist/kokimoki-schema.d.ts +58 -0
- package/dist/kokimoki-schema.js +106 -0
- package/dist/kokimoki-store.d.ts +13 -0
- package/dist/kokimoki-store.js +48 -0
- package/dist/kokimoki-transaction.d.ts +25 -0
- package/dist/kokimoki-transaction.js +99 -0
- package/dist/message-queue.d.ts +8 -0
- package/dist/message-queue.js +19 -0
- package/dist/room-subscription-mode.d.ts +5 -0
- package/dist/room-subscription-mode.js +6 -0
- package/dist/room-subscription.d.ts +15 -0
- package/dist/room-subscription.js +49 -0
- package/dist/synced-schema.d.ts +52 -0
- package/dist/synced-schema.js +92 -0
- package/dist/synced-store copy.d.ts +7 -0
- package/dist/synced-store copy.js +9 -0
- package/dist/synced-types.d.ts +45 -0
- package/dist/synced-types.js +72 -0
- package/dist/types/events.d.ts +1 -2
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/ws-message/index.d.ts +3 -0
- package/dist/ws-message/index.js +3 -0
- package/dist/ws-message/ws-message-reader.d.ts +9 -0
- package/dist/ws-message/ws-message-reader.js +17 -0
- package/dist/ws-message/ws-message-type.d.ts +4 -0
- package/dist/ws-message/ws-message-type.js +5 -0
- package/dist/ws-message/ws-message-writer.d.ts +12 -0
- package/dist/ws-message/ws-message-writer.js +30 -0
- package/dist/ws-message-reader.d.ts +11 -0
- package/dist/ws-message-reader.js +36 -0
- package/dist/ws-message-type.d.ts +9 -0
- package/dist/ws-message-type.js +10 -0
- package/dist/ws-message-writer.d.ts +9 -0
- package/dist/ws-message-writer.js +45 -0
- package/dist/ws-message.d.ts +14 -0
- package/dist/ws-message.js +34 -0
- package/package.json +4 -4
package/dist/index.d.ts
CHANGED
|
@@ -3,4 +3,8 @@ export * from "./types/events";
|
|
|
3
3
|
export * from "./types/upload";
|
|
4
4
|
export * from "./fields";
|
|
5
5
|
export * from "./kokimoki-client";
|
|
6
|
-
export * from "./
|
|
6
|
+
export * from "./kokimoki-schema";
|
|
7
|
+
export * from "./kokimoki-store";
|
|
8
|
+
export * from "./kokimoki-queue";
|
|
9
|
+
export * from "./room-subscription";
|
|
10
|
+
export * from "./room-subscription-mode";
|
package/dist/index.js
CHANGED
|
@@ -3,4 +3,8 @@ export * from "./types/events";
|
|
|
3
3
|
export * from "./types/upload";
|
|
4
4
|
export * from "./fields";
|
|
5
5
|
export * from "./kokimoki-client";
|
|
6
|
-
export * from "./
|
|
6
|
+
export * from "./kokimoki-schema";
|
|
7
|
+
export * from "./kokimoki-store";
|
|
8
|
+
export * from "./kokimoki-queue";
|
|
9
|
+
export * from "./room-subscription";
|
|
10
|
+
export * from "./room-subscription-mode";
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { HocuspocusProvider } from "@hocuspocus/provider";
|
|
2
|
+
import type TypedEmitter from "typed-emitter";
|
|
3
|
+
import type { SyncedStore } from "./synced-store";
|
|
4
|
+
import type { DocTypeDescription } from "@syncedstore/core/types/doc";
|
|
5
|
+
import type { KokimokiClientEvents } from "./types/events";
|
|
6
|
+
import type { Upload } from "./types/upload";
|
|
7
|
+
import type { Paginated } from "./types/common";
|
|
8
|
+
declare const KokimokiClient_base: new <T>() => TypedEmitter<KokimokiClientEvents<T>>;
|
|
9
|
+
export declare class KokimokiClient<StatelessDataT = any, ClientContextT = any> extends KokimokiClient_base<StatelessDataT> {
|
|
10
|
+
readonly host: string;
|
|
11
|
+
readonly appId: string;
|
|
12
|
+
readonly code: string;
|
|
13
|
+
private _wsUrl;
|
|
14
|
+
private _apiUrl;
|
|
15
|
+
private _id?;
|
|
16
|
+
private _token?;
|
|
17
|
+
private _apiHeaders?;
|
|
18
|
+
private _providers;
|
|
19
|
+
private _serverTimeOffset;
|
|
20
|
+
private _clientContext?;
|
|
21
|
+
private _connected;
|
|
22
|
+
private _lastPongAt;
|
|
23
|
+
constructor(host: string, appId: string, code?: string);
|
|
24
|
+
get id(): string;
|
|
25
|
+
get token(): string;
|
|
26
|
+
get apiUrl(): string;
|
|
27
|
+
get apiHeaders(): Headers;
|
|
28
|
+
get clientContext(): ClientContextT & ({} | null);
|
|
29
|
+
connect(): Promise<void>;
|
|
30
|
+
serverTimestamp(): number;
|
|
31
|
+
private receivePong;
|
|
32
|
+
private checkConnectionState;
|
|
33
|
+
setProvider<T extends DocTypeDescription>(name: string, store: SyncedStore<T>): Promise<void>;
|
|
34
|
+
removeProvider(name: string): void;
|
|
35
|
+
getProvider(name: string): HocuspocusProvider | undefined;
|
|
36
|
+
sendStatelessToClient(room: string, clientId: string, data: StatelessDataT): void;
|
|
37
|
+
sendStatelessToRoom(room: string, data: StatelessDataT, self?: boolean): void;
|
|
38
|
+
patchRoomState(room: string, update: Uint8Array): Promise<any>;
|
|
39
|
+
private createUpload;
|
|
40
|
+
private uploadChunks;
|
|
41
|
+
private completeUpload;
|
|
42
|
+
upload(name: string, blob: Blob, tags?: string[]): Promise<Upload>;
|
|
43
|
+
updateUpload(id: string, update: {
|
|
44
|
+
tags?: string[];
|
|
45
|
+
}): Promise<Upload>;
|
|
46
|
+
listUploads(filter?: {
|
|
47
|
+
clientId?: string;
|
|
48
|
+
mimeTypes?: string[];
|
|
49
|
+
tags?: string[];
|
|
50
|
+
}, skip?: number, limit?: number): Promise<Paginated<Upload>>;
|
|
51
|
+
deleteUpload(id: string): Promise<{
|
|
52
|
+
acknowledged: boolean;
|
|
53
|
+
deletedCount: number;
|
|
54
|
+
}>;
|
|
55
|
+
exposeScriptingContext(context: any): Promise<void>;
|
|
56
|
+
}
|
|
57
|
+
export {};
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import { HocuspocusProvider } from "@hocuspocus/provider";
|
|
2
|
+
import EventEmitter from "events";
|
|
3
|
+
import { KOKIMOKI_APP_VERSION } from "./version";
|
|
4
|
+
export class KokimokiClient extends EventEmitter {
|
|
5
|
+
host;
|
|
6
|
+
appId;
|
|
7
|
+
code;
|
|
8
|
+
_wsUrl;
|
|
9
|
+
_apiUrl;
|
|
10
|
+
_id;
|
|
11
|
+
_token;
|
|
12
|
+
_apiHeaders;
|
|
13
|
+
_providers = new Map();
|
|
14
|
+
_serverTimeOffset = 0;
|
|
15
|
+
_clientContext;
|
|
16
|
+
_connected = false;
|
|
17
|
+
_lastPongAt = 0;
|
|
18
|
+
constructor(host, appId, code = "") {
|
|
19
|
+
super();
|
|
20
|
+
this.host = host;
|
|
21
|
+
this.appId = appId;
|
|
22
|
+
this.code = code;
|
|
23
|
+
// Set up the URLs
|
|
24
|
+
const secure = this.host.indexOf(":") === -1;
|
|
25
|
+
this._wsUrl = `ws${secure ? "s" : ""}://${this.host}`;
|
|
26
|
+
this._apiUrl = `http${secure ? "s" : ""}://${this.host}`;
|
|
27
|
+
}
|
|
28
|
+
get id() {
|
|
29
|
+
if (!this._id) {
|
|
30
|
+
throw new Error("Client not connected");
|
|
31
|
+
}
|
|
32
|
+
return this._id;
|
|
33
|
+
}
|
|
34
|
+
get token() {
|
|
35
|
+
if (!this._token) {
|
|
36
|
+
throw new Error("Client not connected");
|
|
37
|
+
}
|
|
38
|
+
return this._token;
|
|
39
|
+
}
|
|
40
|
+
get apiUrl() {
|
|
41
|
+
if (!this._apiUrl) {
|
|
42
|
+
throw new Error("Client not connected");
|
|
43
|
+
}
|
|
44
|
+
return this._apiUrl;
|
|
45
|
+
}
|
|
46
|
+
get apiHeaders() {
|
|
47
|
+
if (!this._apiHeaders) {
|
|
48
|
+
throw new Error("Client not connected");
|
|
49
|
+
}
|
|
50
|
+
return this._apiHeaders;
|
|
51
|
+
}
|
|
52
|
+
get clientContext() {
|
|
53
|
+
if (this._clientContext === undefined) {
|
|
54
|
+
throw new Error("Client not connected");
|
|
55
|
+
}
|
|
56
|
+
return this._clientContext;
|
|
57
|
+
}
|
|
58
|
+
async connect() {
|
|
59
|
+
// Fetch the auth token
|
|
60
|
+
let clientToken = localStorage.getItem("KM_TOKEN");
|
|
61
|
+
const startTime = Date.now();
|
|
62
|
+
const res = await fetch(`${this.apiUrl}/auth/token?appId=${this.appId}&code=${this.code}&clientVersion=${KOKIMOKI_APP_VERSION}`, {
|
|
63
|
+
method: "GET",
|
|
64
|
+
headers: new Headers({
|
|
65
|
+
"Content-Type": "application/json",
|
|
66
|
+
Authorization: `Bearer ${clientToken}`,
|
|
67
|
+
}),
|
|
68
|
+
});
|
|
69
|
+
const { clientId, appToken, serverTime, token, clientContext } = await res.json();
|
|
70
|
+
const endTime = Date.now();
|
|
71
|
+
const ping = Math.round((endTime - startTime) / 2);
|
|
72
|
+
this._id = clientId;
|
|
73
|
+
this._token = appToken;
|
|
74
|
+
this._serverTimeOffset = Date.now() - serverTime - ping;
|
|
75
|
+
this._clientContext = clientContext;
|
|
76
|
+
localStorage.setItem("KM_TOKEN", token);
|
|
77
|
+
// Set up the auth headers
|
|
78
|
+
this._apiHeaders = new Headers({
|
|
79
|
+
Authorization: `Bearer ${this.token}`,
|
|
80
|
+
"Content-Type": "application/json",
|
|
81
|
+
});
|
|
82
|
+
// Ping interval
|
|
83
|
+
setInterval(() => {
|
|
84
|
+
this._providers.forEach((provider) => provider.sendStateless("ping"));
|
|
85
|
+
}, 5000);
|
|
86
|
+
// Connection state interval
|
|
87
|
+
setInterval(() => {
|
|
88
|
+
this.checkConnectionState();
|
|
89
|
+
}, 1000);
|
|
90
|
+
// Check initial connected state
|
|
91
|
+
this.receivePong();
|
|
92
|
+
}
|
|
93
|
+
serverTimestamp() {
|
|
94
|
+
return Date.now() - this._serverTimeOffset;
|
|
95
|
+
}
|
|
96
|
+
receivePong() {
|
|
97
|
+
this._lastPongAt = Date.now();
|
|
98
|
+
this.checkConnectionState();
|
|
99
|
+
}
|
|
100
|
+
checkConnectionState() {
|
|
101
|
+
const connected = this._providers.size === 0 || Date.now() - this._lastPongAt < 6000;
|
|
102
|
+
if (connected && !this._connected) {
|
|
103
|
+
this._connected = true;
|
|
104
|
+
this.emit("connected");
|
|
105
|
+
}
|
|
106
|
+
else if (!connected && this._connected) {
|
|
107
|
+
this._connected = false;
|
|
108
|
+
this.emit("disconnected");
|
|
109
|
+
// Reset connections to providers
|
|
110
|
+
this._providers.forEach(async (provider) => {
|
|
111
|
+
provider.disconnect();
|
|
112
|
+
await provider.connect();
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// Realtime database
|
|
117
|
+
async setProvider(name, store) {
|
|
118
|
+
const provider = new HocuspocusProvider({
|
|
119
|
+
url: `${this._wsUrl}/connection`,
|
|
120
|
+
name: `${this.appId}/${name}`,
|
|
121
|
+
document: store.doc,
|
|
122
|
+
token: this.token,
|
|
123
|
+
parameters: {
|
|
124
|
+
clientVersion: KOKIMOKI_APP_VERSION,
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
// Handle incoming stateless messages
|
|
128
|
+
provider.on("stateless", (e) => {
|
|
129
|
+
if (e.payload === "pong") {
|
|
130
|
+
this.receivePong();
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
const payload = JSON.parse(e.payload);
|
|
134
|
+
this.emit("stateless", name, payload.from, payload.data);
|
|
135
|
+
});
|
|
136
|
+
// Wait for initial sync
|
|
137
|
+
await new Promise((resolve) => {
|
|
138
|
+
const handler = () => {
|
|
139
|
+
provider.off("synced", handler);
|
|
140
|
+
resolve();
|
|
141
|
+
};
|
|
142
|
+
provider.on("synced", handler);
|
|
143
|
+
});
|
|
144
|
+
this._lastPongAt = Date.now();
|
|
145
|
+
this._providers.set(name, provider);
|
|
146
|
+
this.checkConnectionState();
|
|
147
|
+
}
|
|
148
|
+
removeProvider(name) {
|
|
149
|
+
const provider = this._providers.get(name);
|
|
150
|
+
if (!provider) {
|
|
151
|
+
throw new Error(`No provider for room ${name}`);
|
|
152
|
+
}
|
|
153
|
+
provider.destroy();
|
|
154
|
+
this._providers.delete(name);
|
|
155
|
+
// Connection state can change if the removed provider was not connected or synced
|
|
156
|
+
this.checkConnectionState();
|
|
157
|
+
}
|
|
158
|
+
getProvider(name) {
|
|
159
|
+
return this._providers.get(name);
|
|
160
|
+
}
|
|
161
|
+
sendStatelessToClient(room, clientId, data) {
|
|
162
|
+
const provider = this._providers.get(room);
|
|
163
|
+
if (!provider) {
|
|
164
|
+
throw new Error(`No provider for room ${room}`);
|
|
165
|
+
}
|
|
166
|
+
provider.sendStateless(JSON.stringify({ to: clientId, data }));
|
|
167
|
+
}
|
|
168
|
+
sendStatelessToRoom(room, data, self = false) {
|
|
169
|
+
const provider = this._providers.get(room);
|
|
170
|
+
if (!provider) {
|
|
171
|
+
throw new Error(`No provider for room ${room}`);
|
|
172
|
+
}
|
|
173
|
+
provider.sendStateless(JSON.stringify({ data, self }));
|
|
174
|
+
}
|
|
175
|
+
// Send Y update to room
|
|
176
|
+
async patchRoomState(room, update) {
|
|
177
|
+
const res = await fetch(`${this._apiUrl}/rooms/${room}`, {
|
|
178
|
+
method: "PATCH",
|
|
179
|
+
headers: this.apiHeaders,
|
|
180
|
+
body: update,
|
|
181
|
+
});
|
|
182
|
+
return await res.json();
|
|
183
|
+
}
|
|
184
|
+
// Storage
|
|
185
|
+
async createUpload(name, blob, tags) {
|
|
186
|
+
const res = await fetch(`${this._apiUrl}/uploads`, {
|
|
187
|
+
method: "POST",
|
|
188
|
+
headers: this.apiHeaders,
|
|
189
|
+
body: JSON.stringify({
|
|
190
|
+
name,
|
|
191
|
+
size: blob.size,
|
|
192
|
+
mimeType: blob.type,
|
|
193
|
+
tags,
|
|
194
|
+
}),
|
|
195
|
+
});
|
|
196
|
+
return await res.json();
|
|
197
|
+
}
|
|
198
|
+
async uploadChunks(blob, chunkSize, signedUrls) {
|
|
199
|
+
return await Promise.all(signedUrls.map(async (url, index) => {
|
|
200
|
+
const start = index * chunkSize;
|
|
201
|
+
const end = Math.min(start + chunkSize, blob.size);
|
|
202
|
+
const chunk = blob.slice(start, end);
|
|
203
|
+
const res = await fetch(url, { method: "PUT", body: chunk });
|
|
204
|
+
return JSON.parse(res.headers.get("ETag") || '""');
|
|
205
|
+
}));
|
|
206
|
+
}
|
|
207
|
+
async completeUpload(id, etags) {
|
|
208
|
+
const res = await fetch(`${this._apiUrl}/uploads/${id}`, {
|
|
209
|
+
method: "PUT",
|
|
210
|
+
headers: this.apiHeaders,
|
|
211
|
+
body: JSON.stringify({ etags }),
|
|
212
|
+
});
|
|
213
|
+
return await res.json();
|
|
214
|
+
}
|
|
215
|
+
async upload(name, blob, tags = []) {
|
|
216
|
+
const { id, chunkSize, urls } = await this.createUpload(name, blob, tags);
|
|
217
|
+
const etags = await this.uploadChunks(blob, chunkSize, urls);
|
|
218
|
+
return await this.completeUpload(id, etags);
|
|
219
|
+
}
|
|
220
|
+
async updateUpload(id, update) {
|
|
221
|
+
const res = await fetch(`${this._apiUrl}/uploads/${id}`, {
|
|
222
|
+
method: "PUT",
|
|
223
|
+
headers: this.apiHeaders,
|
|
224
|
+
body: JSON.stringify(update),
|
|
225
|
+
});
|
|
226
|
+
return await res.json();
|
|
227
|
+
}
|
|
228
|
+
async listUploads(filter = {}, skip = 0, limit = 100) {
|
|
229
|
+
const url = new URL("/uploads", this._apiUrl);
|
|
230
|
+
url.searchParams.set("skip", skip.toString());
|
|
231
|
+
url.searchParams.set("limit", limit.toString());
|
|
232
|
+
if (filter.clientId) {
|
|
233
|
+
url.searchParams.set("clientId", filter.clientId);
|
|
234
|
+
}
|
|
235
|
+
if (filter.mimeTypes) {
|
|
236
|
+
url.searchParams.set("mimeTypes", filter.mimeTypes.join());
|
|
237
|
+
}
|
|
238
|
+
if (filter.tags) {
|
|
239
|
+
url.searchParams.set("tags", filter.tags.join());
|
|
240
|
+
}
|
|
241
|
+
const res = await fetch(url.href, {
|
|
242
|
+
headers: this.apiHeaders,
|
|
243
|
+
});
|
|
244
|
+
return await res.json();
|
|
245
|
+
}
|
|
246
|
+
async deleteUpload(id) {
|
|
247
|
+
const res = await fetch(`${this._apiUrl}/uploads/${id}`, {
|
|
248
|
+
method: "DELETE",
|
|
249
|
+
headers: this.apiHeaders,
|
|
250
|
+
});
|
|
251
|
+
return await res.json();
|
|
252
|
+
}
|
|
253
|
+
async exposeScriptingContext(context) {
|
|
254
|
+
// @ts-ignore
|
|
255
|
+
window.KM_SCRIPTING_CONTEXT = context;
|
|
256
|
+
// @ts-ignore
|
|
257
|
+
window.dispatchEvent(new CustomEvent("km:scriptingContextExposed"));
|
|
258
|
+
}
|
|
259
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
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 { KokimokiSchema as S } from "./kokimoki-schema";
|
|
8
|
+
import { RoomSubscriptionMode } from "./room-subscription-mode";
|
|
9
|
+
declare const KokimokiClient_base: new () => TypedEmitter<KokimokiClientEventsRefactored>;
|
|
10
|
+
export declare class KokimokiClient<ClientContextT = any> extends KokimokiClient_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 _subscriptionsByName;
|
|
23
|
+
private _subscriptionsByHash;
|
|
24
|
+
private _subscribeReqPromises;
|
|
25
|
+
private _transactionPromises;
|
|
26
|
+
private _connected;
|
|
27
|
+
private _connectPromise?;
|
|
28
|
+
private _messageId;
|
|
29
|
+
private _reconnectTimeout;
|
|
30
|
+
constructor(host: string, appId: string, code?: string);
|
|
31
|
+
get id(): string;
|
|
32
|
+
get token(): string;
|
|
33
|
+
get apiUrl(): string;
|
|
34
|
+
get apiHeaders(): Headers;
|
|
35
|
+
get clientContext(): ClientContextT & ({} | null);
|
|
36
|
+
get connected(): boolean;
|
|
37
|
+
get ws(): WebSocket;
|
|
38
|
+
connect(): Promise<void>;
|
|
39
|
+
private handleInitMessage;
|
|
40
|
+
private handleBinaryMessage;
|
|
41
|
+
private handleErrorMessage;
|
|
42
|
+
private handleSubscribeResMessage;
|
|
43
|
+
private handleRoomUpdateMessage;
|
|
44
|
+
serverTimestamp(): number;
|
|
45
|
+
patchRoomState(room: string, update: Uint8Array): Promise<any>;
|
|
46
|
+
private createUpload;
|
|
47
|
+
private uploadChunks;
|
|
48
|
+
private completeUpload;
|
|
49
|
+
upload(name: string, blob: Blob, tags?: string[]): Promise<Upload>;
|
|
50
|
+
updateUpload(id: string, update: {
|
|
51
|
+
tags?: string[];
|
|
52
|
+
}): Promise<Upload>;
|
|
53
|
+
listUploads(filter?: {
|
|
54
|
+
clientId?: string;
|
|
55
|
+
mimeTypes?: string[];
|
|
56
|
+
tags?: string[];
|
|
57
|
+
}, skip?: number, limit?: number): Promise<Paginated<Upload>>;
|
|
58
|
+
deleteUpload(id: string): Promise<{
|
|
59
|
+
acknowledged: boolean;
|
|
60
|
+
deletedCount: number;
|
|
61
|
+
}>;
|
|
62
|
+
exposeScriptingContext(context: any): Promise<void>;
|
|
63
|
+
private sendSubscriptionReq;
|
|
64
|
+
join<T extends S.Generic<unknown>>(store: KokimokiStore<T>, mode?: RoomSubscriptionMode): Promise<void>;
|
|
65
|
+
transact(handler: (t: KokimokiTransaction) => void): Promise<void>;
|
|
66
|
+
}
|
|
67
|
+
export {};
|