@meshagent/meshagent 0.37.1 → 0.38.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/CHANGELOG.md +6 -0
- package/dist/browser/agent.js +74 -10
- package/dist/browser/developer-client.js +3 -0
- package/dist/browser/helpers.d.ts +2 -2
- package/dist/browser/helpers.js +1 -1
- package/dist/browser/meshagent-client.d.ts +25 -0
- package/dist/browser/meshagent-client.js +65 -0
- package/dist/browser/messaging-client.d.ts +29 -16
- package/dist/browser/messaging-client.js +256 -154
- package/dist/browser/participant.d.ts +7 -2
- package/dist/browser/participant.js +9 -9
- package/dist/browser/protocol.d.ts +85 -28
- package/dist/browser/protocol.js +356 -119
- package/dist/browser/room-client.d.ts +165 -29
- package/dist/browser/room-client.js +1114 -74
- package/dist/browser/room-event.d.ts +11 -0
- package/dist/browser/room-event.js +21 -1
- package/dist/browser/room-server-client.d.ts +2 -0
- package/dist/browser/room-server-client.js +6 -0
- package/dist/browser/runtime.d.ts +1 -1
- package/dist/browser/runtime.js +3 -1
- package/dist/browser/secrets-client.js +6 -2
- package/dist/browser/storage-client.d.ts +1 -0
- package/dist/browser/storage-client.js +9 -0
- package/dist/browser/sync-client.d.ts +16 -14
- package/dist/browser/sync-client.js +195 -116
- package/dist/esm/agent.js +74 -10
- package/dist/esm/developer-client.js +3 -0
- package/dist/esm/helpers.d.ts +2 -2
- package/dist/esm/helpers.js +1 -1
- package/dist/esm/meshagent-client.d.ts +25 -0
- package/dist/esm/meshagent-client.js +65 -0
- package/dist/esm/messaging-client.d.ts +29 -16
- package/dist/esm/messaging-client.js +256 -154
- package/dist/esm/participant.d.ts +7 -2
- package/dist/esm/participant.js +9 -9
- package/dist/esm/protocol.d.ts +85 -28
- package/dist/esm/protocol.js +352 -118
- package/dist/esm/room-client.d.ts +165 -29
- package/dist/esm/room-client.js +1112 -73
- package/dist/esm/room-event.d.ts +11 -0
- package/dist/esm/room-event.js +19 -0
- package/dist/esm/room-server-client.d.ts +2 -0
- package/dist/esm/room-server-client.js +7 -1
- package/dist/esm/runtime.d.ts +1 -1
- package/dist/esm/runtime.js +1 -1
- package/dist/esm/secrets-client.js +6 -2
- package/dist/esm/storage-client.d.ts +1 -0
- package/dist/esm/storage-client.js +9 -0
- package/dist/esm/sync-client.d.ts +16 -14
- package/dist/esm/sync-client.js +196 -117
- package/dist/node/agent.js +74 -10
- package/dist/node/developer-client.js +3 -0
- package/dist/node/helpers.d.ts +2 -2
- package/dist/node/helpers.js +1 -1
- package/dist/node/meshagent-client.d.ts +25 -0
- package/dist/node/meshagent-client.js +65 -0
- package/dist/node/messaging-client.d.ts +29 -16
- package/dist/node/messaging-client.js +256 -154
- package/dist/node/participant.d.ts +7 -2
- package/dist/node/participant.js +9 -9
- package/dist/node/protocol.d.ts +85 -28
- package/dist/node/protocol.js +356 -119
- package/dist/node/room-client.d.ts +165 -29
- package/dist/node/room-client.js +1114 -74
- package/dist/node/room-event.d.ts +11 -0
- package/dist/node/room-event.js +21 -1
- package/dist/node/room-server-client.d.ts +2 -0
- package/dist/node/room-server-client.js +6 -0
- package/dist/node/runtime.d.ts +1 -1
- package/dist/node/runtime.js +3 -1
- package/dist/node/secrets-client.js +6 -2
- package/dist/node/storage-client.d.ts +1 -0
- package/dist/node/storage-client.js +9 -0
- package/dist/node/sync-client.d.ts +16 -14
- package/dist/node/sync-client.js +195 -116
- package/package.json +6 -3
package/dist/esm/room-event.d.ts
CHANGED
|
@@ -2,6 +2,17 @@ export declare abstract class RoomEvent {
|
|
|
2
2
|
abstract get name(): string;
|
|
3
3
|
abstract get description(): string;
|
|
4
4
|
}
|
|
5
|
+
export declare class RoomStatusEvent extends RoomEvent {
|
|
6
|
+
readonly status: string;
|
|
7
|
+
readonly message: string;
|
|
8
|
+
constructor({ status, message }: {
|
|
9
|
+
status: string;
|
|
10
|
+
message: string;
|
|
11
|
+
});
|
|
12
|
+
get name(): string;
|
|
13
|
+
get description(): string;
|
|
14
|
+
static fromJson(json: Record<string, unknown>): RoomStatusEvent;
|
|
15
|
+
}
|
|
5
16
|
export declare class RoomMessage {
|
|
6
17
|
fromParticipantId: string;
|
|
7
18
|
type: string;
|
package/dist/esm/room-event.js
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
export class RoomEvent {
|
|
2
2
|
}
|
|
3
|
+
export class RoomStatusEvent extends RoomEvent {
|
|
4
|
+
constructor({ status, message }) {
|
|
5
|
+
super();
|
|
6
|
+
this.status = status;
|
|
7
|
+
this.message = message;
|
|
8
|
+
}
|
|
9
|
+
get name() {
|
|
10
|
+
return this.status;
|
|
11
|
+
}
|
|
12
|
+
get description() {
|
|
13
|
+
return this.message;
|
|
14
|
+
}
|
|
15
|
+
static fromJson(json) {
|
|
16
|
+
return new RoomStatusEvent({
|
|
17
|
+
status: String(json["status"]),
|
|
18
|
+
message: String(json["message"]),
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
}
|
|
3
22
|
export class RoomMessage {
|
|
4
23
|
constructor({ fromParticipantId, type, message, local = false, attachment }) {
|
|
5
24
|
this.fromParticipantId = fromParticipantId;
|
|
@@ -13,6 +13,8 @@ export declare class MeshDocument extends RuntimeDocument {
|
|
|
13
13
|
});
|
|
14
14
|
onSendUpdateToBackend: (base64: string) => void;
|
|
15
15
|
onSendUpdateToClient: (base64: string) => void;
|
|
16
|
+
getState(vector?: string): string;
|
|
17
|
+
getStateVector(): string;
|
|
16
18
|
get synchronized(): Promise<boolean>;
|
|
17
19
|
get isSynchronized(): boolean;
|
|
18
20
|
setSynchronizedComplete(): void;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { v4 as uuidv4 } from "uuid";
|
|
2
2
|
import { RuntimeDocument } from "./document";
|
|
3
3
|
import { Completer } from "./completer";
|
|
4
|
-
import { registerDocument, unregisterDocument, applyChanges, } from "./runtime";
|
|
4
|
+
import { registerDocument, unregisterDocument, applyChanges, getState, getStateVector, } from "./runtime";
|
|
5
5
|
export class RoomServerException extends Error {
|
|
6
6
|
constructor(message, code) {
|
|
7
7
|
super(message);
|
|
@@ -30,6 +30,12 @@ export class MeshDocument extends RuntimeDocument {
|
|
|
30
30
|
};
|
|
31
31
|
registerDocument(this.id, null, false, this.onSendUpdateToBackend, this.onSendUpdateToClient);
|
|
32
32
|
}
|
|
33
|
+
getState(vector) {
|
|
34
|
+
return getState(this.id, vector ?? null);
|
|
35
|
+
}
|
|
36
|
+
getStateVector() {
|
|
37
|
+
return getStateVector(this.id);
|
|
38
|
+
}
|
|
33
39
|
get synchronized() {
|
|
34
40
|
return this._synchronized.fut;
|
|
35
41
|
}
|
package/dist/esm/runtime.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { applyBackendChanges, applyChanges, registerDocument, unregisterDocument, } from './entrypoint.js';
|
|
1
|
+
export { applyBackendChanges, applyChanges, getState, getStateVector, registerDocument, unregisterDocument, } from './entrypoint.js';
|
|
2
2
|
export type SendUpdateFn = (msg: string) => void;
|
|
3
3
|
export interface UpdatePayload {
|
|
4
4
|
documentID: string;
|
package/dist/esm/runtime.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { applyBackendChanges, applyChanges, registerDocument, unregisterDocument, } from './entrypoint.js';
|
|
1
|
+
export { applyBackendChanges, applyChanges, getState, getStateVector, registerDocument, unregisterDocument, } from './entrypoint.js';
|
|
@@ -73,7 +73,9 @@ export class SecretsClient {
|
|
|
73
73
|
};
|
|
74
74
|
}
|
|
75
75
|
async _handleClientOAuthTokenRequest(protocol, messageId, type, bytes) {
|
|
76
|
-
|
|
76
|
+
if (!this.client.isActiveProtocol(protocol)) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
77
79
|
void messageId;
|
|
78
80
|
void type;
|
|
79
81
|
if (bytes == null) {
|
|
@@ -109,7 +111,9 @@ export class SecretsClient {
|
|
|
109
111
|
});
|
|
110
112
|
}
|
|
111
113
|
async _handleClientSecretRequest(protocol, messageId, type, bytes) {
|
|
112
|
-
|
|
114
|
+
if (!this.client.isActiveProtocol(protocol)) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
113
117
|
void messageId;
|
|
114
118
|
void type;
|
|
115
119
|
if (bytes == null) {
|
|
@@ -4,6 +4,7 @@ import { RoomEvent } from "./room-event";
|
|
|
4
4
|
import { BinaryContent, FileContent } from "./response";
|
|
5
5
|
import { EventEmitter } from "./event-emitter";
|
|
6
6
|
type StorageClientRoom = Pick<RoomClient, "invoke" | "invokeStream" | "emit"> & {
|
|
7
|
+
isActiveProtocol(protocol: Protocol): boolean;
|
|
7
8
|
protocol: Pick<Protocol, "addHandler">;
|
|
8
9
|
};
|
|
9
10
|
export declare class FileHandle {
|
|
@@ -84,18 +84,27 @@ export class StorageClient extends EventEmitter {
|
|
|
84
84
|
this.client.protocol.addHandler("storage.file.updated", this._handleFileUpdated.bind(this));
|
|
85
85
|
}
|
|
86
86
|
async _handleFileUpdated(protocol, messageId, type, bytes) {
|
|
87
|
+
if (!this.client.isActiveProtocol(protocol)) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
87
90
|
const [data, _] = unpackMessage(bytes || new Uint8Array());
|
|
88
91
|
const event = new FileUpdatedEvent({ path: data["path"], participantId: data["participant_id"] });
|
|
89
92
|
this.client.emit(event);
|
|
90
93
|
this.emit('file.updated', event);
|
|
91
94
|
}
|
|
92
95
|
async _handleFileDeleted(protocol, messageId, type, bytes) {
|
|
96
|
+
if (!this.client.isActiveProtocol(protocol)) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
93
99
|
const [data, _] = unpackMessage(bytes || new Uint8Array());
|
|
94
100
|
const event = new FileDeletedEvent({ path: data["path"], participantId: data["participant_id"] });
|
|
95
101
|
this.client.emit(event);
|
|
96
102
|
this.emit('file.deleted', event);
|
|
97
103
|
}
|
|
98
104
|
async _handleFileMoved(protocol, messageId, type, bytes) {
|
|
105
|
+
if (!this.client.isActiveProtocol(protocol)) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
99
108
|
const [data, _] = unpackMessage(bytes || new Uint8Array());
|
|
100
109
|
const event = new FileMovedEvent({
|
|
101
110
|
sourcePath: data["source_path"],
|
|
@@ -1,36 +1,38 @@
|
|
|
1
1
|
import { EventEmitter } from "./event-emitter";
|
|
2
|
-
import { RoomClient } from "./room-client";
|
|
3
2
|
import { MeshSchema } from "./schema";
|
|
3
|
+
import { RoomClient } from "./room-client";
|
|
4
4
|
import { MeshDocument } from "./room-server-client";
|
|
5
5
|
export interface SyncClientEvent {
|
|
6
6
|
type: string;
|
|
7
7
|
doc?: MeshDocument;
|
|
8
|
-
status?: unknown;
|
|
9
8
|
}
|
|
10
9
|
export declare class SyncClient extends EventEmitter<SyncClientEvent> {
|
|
11
|
-
private readonly
|
|
12
|
-
private _connectingDocuments;
|
|
13
|
-
private
|
|
14
|
-
private
|
|
10
|
+
private readonly room;
|
|
11
|
+
private readonly _connectingDocuments;
|
|
12
|
+
private readonly _closingDocuments;
|
|
13
|
+
private readonly _connectedDocuments;
|
|
14
|
+
private readonly _documentStreams;
|
|
15
|
+
private readonly _documentConfigs;
|
|
16
|
+
private _started;
|
|
15
17
|
constructor({ room }: {
|
|
16
18
|
room: RoomClient;
|
|
17
19
|
});
|
|
20
|
+
start(): void;
|
|
21
|
+
dispose(): void;
|
|
18
22
|
private _unexpectedResponseError;
|
|
19
23
|
private _invoke;
|
|
20
|
-
start({ onDone, onError }?: {
|
|
21
|
-
onDone?: () => void;
|
|
22
|
-
onError?: (error: Error) => void;
|
|
23
|
-
}): void;
|
|
24
|
-
dispose(): void;
|
|
25
24
|
private _applySyncPayload;
|
|
26
|
-
|
|
27
|
-
create(path: string, json?: Record<string, any>): Promise<void>;
|
|
25
|
+
create(path: string, json?: Record<string, unknown>): Promise<void>;
|
|
28
26
|
open(path: string, { create, initialJson, schema, }?: {
|
|
29
27
|
create?: boolean;
|
|
30
|
-
initialJson?: Record<string,
|
|
28
|
+
initialJson?: Record<string, unknown>;
|
|
31
29
|
schema?: MeshSchema;
|
|
32
30
|
}): Promise<MeshDocument>;
|
|
33
31
|
close(path: string): Promise<void>;
|
|
34
32
|
sync(path: string, data: Uint8Array): Promise<void>;
|
|
35
33
|
private _consumeOpenStream;
|
|
34
|
+
private _openStream;
|
|
35
|
+
private _attachStreamConsumer;
|
|
36
|
+
_onRoomDisconnect(): Promise<void>;
|
|
37
|
+
_onRoomReconnect(): Promise<void>;
|
|
36
38
|
}
|
package/dist/esm/sync-client.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
import { Completer } from "./completer";
|
|
1
2
|
import { EventEmitter } from "./event-emitter";
|
|
2
3
|
import { MeshSchema } from "./schema";
|
|
3
|
-
import { MeshDocument, RoomServerException } from "./room-server-client";
|
|
4
4
|
import { BinaryContent, ControlContent, ErrorContent } from "./response";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
5
|
+
import { MeshDocument, RoomServerException } from "./room-server-client";
|
|
6
|
+
import { applyBackendChanges, unregisterDocument } from "./runtime";
|
|
7
|
+
import { decoder, encoder, RefCount } from "./utils";
|
|
8
8
|
function normalizeSyncPath(path) {
|
|
9
9
|
let normalized = path;
|
|
10
10
|
while (normalized.startsWith("./")) {
|
|
@@ -22,7 +22,8 @@ function parseSyncOpenStateChunkHeaders(headers) {
|
|
|
22
22
|
if (headers["kind"] !== "state" ||
|
|
23
23
|
typeof headers["path"] !== "string" ||
|
|
24
24
|
typeof headers["schema"] !== "object" ||
|
|
25
|
-
headers["schema"] == null
|
|
25
|
+
headers["schema"] == null ||
|
|
26
|
+
Array.isArray(headers["schema"])) {
|
|
26
27
|
throw new RoomServerException("unexpected return type from sync.open");
|
|
27
28
|
}
|
|
28
29
|
return {
|
|
@@ -33,8 +34,7 @@ function parseSyncOpenStateChunkHeaders(headers) {
|
|
|
33
34
|
}
|
|
34
35
|
function parseSyncOpenOutputChunkHeaders(headers) {
|
|
35
36
|
const kind = headers["kind"];
|
|
36
|
-
if ((kind !== "state" && kind !== "sync") ||
|
|
37
|
-
typeof headers["path"] !== "string") {
|
|
37
|
+
if ((kind !== "state" && kind !== "sync") || typeof headers["path"] !== "string") {
|
|
38
38
|
throw new RoomServerException("unexpected return type from sync.open");
|
|
39
39
|
}
|
|
40
40
|
return {
|
|
@@ -51,7 +51,7 @@ class SyncOpenStreamState {
|
|
|
51
51
|
}
|
|
52
52
|
_enqueueChunk(chunk) {
|
|
53
53
|
const waiter = this._inputWaiters.shift();
|
|
54
|
-
if (waiter) {
|
|
54
|
+
if (waiter != null) {
|
|
55
55
|
waiter(chunk);
|
|
56
56
|
return;
|
|
57
57
|
}
|
|
@@ -129,54 +129,52 @@ export class SyncClient extends EventEmitter {
|
|
|
129
129
|
constructor({ room }) {
|
|
130
130
|
super();
|
|
131
131
|
this._connectingDocuments = {};
|
|
132
|
+
this._closingDocuments = {};
|
|
132
133
|
this._connectedDocuments = {};
|
|
133
134
|
this._documentStreams = {};
|
|
134
|
-
this.
|
|
135
|
-
this.
|
|
135
|
+
this._documentConfigs = {};
|
|
136
|
+
this._started = false;
|
|
137
|
+
this.room = room;
|
|
138
|
+
}
|
|
139
|
+
start() {
|
|
140
|
+
if (this._started) {
|
|
141
|
+
throw new RoomServerException("client already started");
|
|
142
|
+
}
|
|
143
|
+
this._started = true;
|
|
144
|
+
}
|
|
145
|
+
dispose() {
|
|
146
|
+
super.dispose();
|
|
147
|
+
for (const streamState of Object.values(this._documentStreams)) {
|
|
148
|
+
streamState.closeInputStream();
|
|
149
|
+
}
|
|
150
|
+
for (const doc of Object.values(this._connectedDocuments)) {
|
|
151
|
+
unregisterDocument(doc.ref.id);
|
|
152
|
+
}
|
|
153
|
+
Object.keys(this._documentStreams).forEach((key) => delete this._documentStreams[key]);
|
|
154
|
+
Object.keys(this._documentConfigs).forEach((key) => delete this._documentConfigs[key]);
|
|
155
|
+
Object.keys(this._connectedDocuments).forEach((key) => delete this._connectedDocuments[key]);
|
|
156
|
+
Object.keys(this._connectingDocuments).forEach((key) => delete this._connectingDocuments[key]);
|
|
157
|
+
Object.keys(this._closingDocuments).forEach((key) => delete this._closingDocuments[key]);
|
|
158
|
+
this._started = false;
|
|
136
159
|
}
|
|
137
160
|
_unexpectedResponseError(operation) {
|
|
138
161
|
return new RoomServerException(`unexpected return type from sync.${operation}`);
|
|
139
162
|
}
|
|
140
163
|
async _invoke(operation, input) {
|
|
141
|
-
return await this.
|
|
164
|
+
return await this.room.invoke({
|
|
142
165
|
toolkit: "sync",
|
|
143
166
|
tool: operation,
|
|
144
167
|
input,
|
|
145
168
|
});
|
|
146
169
|
}
|
|
147
|
-
start({ onDone, onError } = {}) {
|
|
148
|
-
this.client.protocol.start({ onDone, onError });
|
|
149
|
-
}
|
|
150
|
-
dispose() {
|
|
151
|
-
super.dispose();
|
|
152
|
-
for (const streamState of Object.values(this._documentStreams)) {
|
|
153
|
-
streamState.closeInputStream();
|
|
154
|
-
}
|
|
155
|
-
this._documentStreams = {};
|
|
156
|
-
for (const rc of Object.values(this._connectedDocuments)) {
|
|
157
|
-
unregisterDocument(rc.ref.id);
|
|
158
|
-
}
|
|
159
|
-
this._connectedDocuments = {};
|
|
160
|
-
this._connectingDocuments = {};
|
|
161
|
-
}
|
|
162
170
|
_applySyncPayload(rc, payload) {
|
|
163
|
-
const doc = rc.ref;
|
|
164
171
|
if (payload.length > 0) {
|
|
165
|
-
|
|
166
|
-
applyBackendChanges(doc.id, base64);
|
|
172
|
+
applyBackendChanges(rc.ref.id, decoder.decode(payload));
|
|
167
173
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
doc.setSynchronizedComplete();
|
|
174
|
+
if (!rc.ref.isSynchronized) {
|
|
175
|
+
rc.ref.setSynchronizedComplete();
|
|
171
176
|
}
|
|
172
177
|
}
|
|
173
|
-
async _handleStatus(_protocol, _messageId, _type, bytes) {
|
|
174
|
-
if (!bytes) {
|
|
175
|
-
return;
|
|
176
|
-
}
|
|
177
|
-
const [header] = unpackMessage(bytes);
|
|
178
|
-
this.emit("status", { type: "status", status: header.status });
|
|
179
|
-
}
|
|
180
178
|
async create(path, json) {
|
|
181
179
|
const normalizedPath = normalizeSyncPath(path);
|
|
182
180
|
await this._invoke("create", {
|
|
@@ -187,129 +185,126 @@ export class SyncClient extends EventEmitter {
|
|
|
187
185
|
});
|
|
188
186
|
}
|
|
189
187
|
async open(path, { create = true, initialJson, schema, } = {}) {
|
|
190
|
-
|
|
191
|
-
const
|
|
192
|
-
if (
|
|
188
|
+
const normalizedPath = normalizeSyncPath(path);
|
|
189
|
+
const closing = this._closingDocuments[normalizedPath];
|
|
190
|
+
if (closing != null) {
|
|
191
|
+
await closing;
|
|
192
|
+
}
|
|
193
|
+
const pending = this._connectingDocuments[normalizedPath];
|
|
194
|
+
if (pending != null) {
|
|
193
195
|
await pending;
|
|
194
196
|
}
|
|
195
|
-
const connected = this._connectedDocuments[
|
|
196
|
-
if (connected) {
|
|
197
|
-
connected.count
|
|
197
|
+
const connected = this._connectedDocuments[normalizedPath];
|
|
198
|
+
if (connected != null) {
|
|
199
|
+
connected.count += 1;
|
|
198
200
|
return connected.ref;
|
|
199
201
|
}
|
|
200
202
|
const connecting = new Completer();
|
|
201
|
-
this._connectingDocuments[
|
|
202
|
-
let streamState;
|
|
203
|
-
let iterator;
|
|
203
|
+
this._connectingDocuments[normalizedPath] = connecting.fut;
|
|
204
204
|
try {
|
|
205
|
-
|
|
206
|
-
path,
|
|
205
|
+
const config = {
|
|
207
206
|
create,
|
|
208
|
-
|
|
209
|
-
schemaJson: schema == null ? null : schema.toJson(),
|
|
207
|
+
schemaJson: schema?.toJson() ?? null,
|
|
210
208
|
schemaPath: null,
|
|
209
|
+
};
|
|
210
|
+
const openResult = await this._openStream({
|
|
211
|
+
path: normalizedPath,
|
|
212
|
+
config,
|
|
213
|
+
vector: null,
|
|
211
214
|
initialJson: initialJson ?? null,
|
|
212
215
|
});
|
|
213
|
-
const
|
|
214
|
-
toolkit: "sync",
|
|
215
|
-
tool: "open",
|
|
216
|
-
input: streamState.inputStream(),
|
|
217
|
-
});
|
|
218
|
-
iterator = responseStream[Symbol.asyncIterator]();
|
|
219
|
-
const first = await iterator.next();
|
|
220
|
-
if (first.done || first.value === undefined) {
|
|
221
|
-
throw new RoomServerException("sync.open stream closed before the initial document state was returned");
|
|
222
|
-
}
|
|
223
|
-
const firstChunk = first.value;
|
|
224
|
-
if (firstChunk instanceof ErrorContent) {
|
|
225
|
-
throw new RoomServerException(firstChunk.text, firstChunk.code);
|
|
226
|
-
}
|
|
227
|
-
if (!(firstChunk instanceof BinaryContent)) {
|
|
228
|
-
throw this._unexpectedResponseError("open");
|
|
229
|
-
}
|
|
230
|
-
const stateHeaders = parseSyncOpenStateChunkHeaders(firstChunk.headers);
|
|
231
|
-
if (normalizeSyncPath(stateHeaders.path) !== path) {
|
|
232
|
-
throw new RoomServerException("sync.open stream returned a mismatched path");
|
|
233
|
-
}
|
|
216
|
+
const resolvedSchema = MeshSchema.fromJson(openResult.stateHeaders.schema);
|
|
234
217
|
const doc = new MeshDocument({
|
|
235
|
-
schema:
|
|
218
|
+
schema: resolvedSchema,
|
|
236
219
|
sendChangesToBackend: (base64) => {
|
|
220
|
+
const currentStream = this._documentStreams[normalizedPath];
|
|
221
|
+
if (currentStream == null) {
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
237
224
|
try {
|
|
238
|
-
|
|
225
|
+
currentStream.queueSync(encoder.encode(base64));
|
|
239
226
|
}
|
|
240
227
|
catch {
|
|
241
228
|
}
|
|
242
229
|
},
|
|
243
230
|
});
|
|
244
231
|
const rc = new RefCount(doc);
|
|
245
|
-
this._connectedDocuments[
|
|
246
|
-
this.
|
|
247
|
-
this.
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
streamState,
|
|
253
|
-
|
|
232
|
+
this._connectedDocuments[normalizedPath] = rc;
|
|
233
|
+
this._documentConfigs[normalizedPath] = config;
|
|
234
|
+
this._documentStreams[normalizedPath] = openResult.streamState;
|
|
235
|
+
this._applySyncPayload(rc, openResult.firstChunk.data);
|
|
236
|
+
this._attachStreamConsumer({
|
|
237
|
+
path: normalizedPath,
|
|
238
|
+
doc: rc,
|
|
239
|
+
streamState: openResult.streamState,
|
|
240
|
+
iterator: openResult.iterator,
|
|
241
|
+
});
|
|
254
242
|
this.emit("connected", { type: "connect", doc });
|
|
255
243
|
connecting.complete(rc);
|
|
256
244
|
await doc.synchronized;
|
|
257
245
|
return doc;
|
|
258
246
|
}
|
|
259
247
|
catch (error) {
|
|
260
|
-
streamState?.closeInputStream();
|
|
261
|
-
if (iterator) {
|
|
262
|
-
await iterator.return?.();
|
|
263
|
-
}
|
|
264
248
|
connecting.completeError(error);
|
|
265
249
|
throw error;
|
|
266
250
|
}
|
|
267
251
|
finally {
|
|
268
|
-
delete this._connectingDocuments[
|
|
252
|
+
delete this._connectingDocuments[normalizedPath];
|
|
269
253
|
}
|
|
270
254
|
}
|
|
271
255
|
async close(path) {
|
|
272
|
-
|
|
273
|
-
const rc = this._connectedDocuments[
|
|
274
|
-
if (
|
|
275
|
-
throw new RoomServerException(`Not connected to ${
|
|
256
|
+
const normalizedPath = normalizeSyncPath(path);
|
|
257
|
+
const rc = this._connectedDocuments[normalizedPath];
|
|
258
|
+
if (rc == null) {
|
|
259
|
+
throw new RoomServerException(`Not connected to ${normalizedPath}`);
|
|
276
260
|
}
|
|
277
|
-
|
|
278
|
-
rc.count--;
|
|
261
|
+
rc.count -= 1;
|
|
279
262
|
if (rc.count === 0) {
|
|
280
|
-
delete this._connectedDocuments[
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
263
|
+
delete this._connectedDocuments[normalizedPath];
|
|
264
|
+
delete this._documentConfigs[normalizedPath];
|
|
265
|
+
const streamState = this._documentStreams[normalizedPath];
|
|
266
|
+
delete this._documentStreams[normalizedPath];
|
|
267
|
+
const closeFuture = (async () => {
|
|
268
|
+
if (streamState != null) {
|
|
269
|
+
streamState.closeInputStream();
|
|
270
|
+
try {
|
|
271
|
+
await streamState.wait();
|
|
272
|
+
}
|
|
273
|
+
finally {
|
|
274
|
+
unregisterDocument(rc.ref.id);
|
|
275
|
+
}
|
|
287
276
|
}
|
|
288
|
-
|
|
289
|
-
unregisterDocument(
|
|
277
|
+
else {
|
|
278
|
+
unregisterDocument(rc.ref.id);
|
|
290
279
|
}
|
|
280
|
+
})();
|
|
281
|
+
this._closingDocuments[normalizedPath] = closeFuture;
|
|
282
|
+
try {
|
|
283
|
+
await closeFuture;
|
|
291
284
|
}
|
|
292
|
-
|
|
293
|
-
|
|
285
|
+
finally {
|
|
286
|
+
if (this._closingDocuments[normalizedPath] === closeFuture) {
|
|
287
|
+
delete this._closingDocuments[normalizedPath];
|
|
288
|
+
}
|
|
294
289
|
}
|
|
295
290
|
}
|
|
296
|
-
this.emit("closed", { type: "close", doc });
|
|
291
|
+
this.emit("closed", { type: "close", doc: rc.ref });
|
|
297
292
|
}
|
|
298
293
|
async sync(path, data) {
|
|
299
|
-
|
|
300
|
-
if (
|
|
294
|
+
const normalizedPath = normalizeSyncPath(path);
|
|
295
|
+
if (this._connectedDocuments[normalizedPath] == null) {
|
|
301
296
|
throw new RoomServerException("attempted to sync to a document that is not connected");
|
|
302
297
|
}
|
|
303
|
-
const streamState = this._documentStreams[
|
|
304
|
-
if (
|
|
298
|
+
const streamState = this._documentStreams[normalizedPath];
|
|
299
|
+
if (streamState == null) {
|
|
305
300
|
throw new RoomServerException("attempted to sync to a document that is not connected");
|
|
306
301
|
}
|
|
307
302
|
streamState.queueSync(data);
|
|
308
303
|
}
|
|
309
|
-
async _consumeOpenStream(
|
|
304
|
+
async _consumeOpenStream({ path, rc, iterator, streamState, }) {
|
|
310
305
|
try {
|
|
311
306
|
while (true) {
|
|
312
|
-
const next = await
|
|
307
|
+
const next = await iterator.next();
|
|
313
308
|
if (next.done || next.value === undefined) {
|
|
314
309
|
return;
|
|
315
310
|
}
|
|
@@ -327,15 +322,99 @@ export class SyncClient extends EventEmitter {
|
|
|
327
322
|
throw this._unexpectedResponseError("open");
|
|
328
323
|
}
|
|
329
324
|
const headers = parseSyncOpenOutputChunkHeaders(chunk.headers);
|
|
330
|
-
if (normalizeSyncPath(headers.path) !==
|
|
325
|
+
if (normalizeSyncPath(headers.path) !== path) {
|
|
331
326
|
throw new RoomServerException("sync.open stream returned a mismatched path");
|
|
332
327
|
}
|
|
333
|
-
this._applySyncPayload(
|
|
328
|
+
this._applySyncPayload(rc, chunk.data);
|
|
334
329
|
}
|
|
335
330
|
}
|
|
336
331
|
finally {
|
|
337
|
-
|
|
338
|
-
await
|
|
332
|
+
streamState.closeInputStream();
|
|
333
|
+
await iterator.return?.();
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
async _openStream({ path, config, vector, initialJson, }) {
|
|
337
|
+
const streamState = new SyncOpenStreamState({
|
|
338
|
+
path,
|
|
339
|
+
create: config.create,
|
|
340
|
+
vector,
|
|
341
|
+
schemaJson: config.schemaJson,
|
|
342
|
+
schemaPath: config.schemaPath,
|
|
343
|
+
initialJson,
|
|
344
|
+
});
|
|
345
|
+
let iterator;
|
|
346
|
+
try {
|
|
347
|
+
const responseStream = await this.room.invokeStream({
|
|
348
|
+
toolkit: "sync",
|
|
349
|
+
tool: "open",
|
|
350
|
+
input: streamState.inputStream(),
|
|
351
|
+
});
|
|
352
|
+
iterator = responseStream[Symbol.asyncIterator]();
|
|
353
|
+
const first = await iterator.next();
|
|
354
|
+
if (first.done || first.value === undefined) {
|
|
355
|
+
throw new RoomServerException("sync.open stream closed before the initial document state was returned");
|
|
356
|
+
}
|
|
357
|
+
const firstChunk = first.value;
|
|
358
|
+
if (firstChunk instanceof ErrorContent) {
|
|
359
|
+
throw new RoomServerException(firstChunk.text, firstChunk.code);
|
|
360
|
+
}
|
|
361
|
+
if (!(firstChunk instanceof BinaryContent)) {
|
|
362
|
+
throw this._unexpectedResponseError("open");
|
|
363
|
+
}
|
|
364
|
+
const stateHeaders = parseSyncOpenStateChunkHeaders(firstChunk.headers);
|
|
365
|
+
if (normalizeSyncPath(stateHeaders.path) !== path) {
|
|
366
|
+
throw new RoomServerException("sync.open stream returned a mismatched path");
|
|
367
|
+
}
|
|
368
|
+
return {
|
|
369
|
+
streamState,
|
|
370
|
+
iterator,
|
|
371
|
+
stateHeaders,
|
|
372
|
+
firstChunk,
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
catch (error) {
|
|
376
|
+
streamState.closeInputStream();
|
|
377
|
+
if (iterator != null) {
|
|
378
|
+
await iterator.return?.();
|
|
379
|
+
}
|
|
380
|
+
throw error;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
_attachStreamConsumer({ path, doc, streamState, iterator, }) {
|
|
384
|
+
streamState.attachTask(this._consumeOpenStream({
|
|
385
|
+
path,
|
|
386
|
+
rc: doc,
|
|
387
|
+
iterator,
|
|
388
|
+
streamState,
|
|
389
|
+
}));
|
|
390
|
+
}
|
|
391
|
+
async _onRoomDisconnect() {
|
|
392
|
+
const openStreams = Object.values(this._documentStreams);
|
|
393
|
+
Object.keys(this._documentStreams).forEach((key) => delete this._documentStreams[key]);
|
|
394
|
+
for (const streamState of openStreams) {
|
|
395
|
+
streamState.closeInputStream();
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
async _onRoomReconnect() {
|
|
399
|
+
for (const [path, ref] of Object.entries(this._connectedDocuments)) {
|
|
400
|
+
const config = this._documentConfigs[path];
|
|
401
|
+
if (config == null) {
|
|
402
|
+
continue;
|
|
403
|
+
}
|
|
404
|
+
const openResult = await this._openStream({
|
|
405
|
+
path,
|
|
406
|
+
config,
|
|
407
|
+
vector: ref.ref.getStateVector(),
|
|
408
|
+
initialJson: null,
|
|
409
|
+
});
|
|
410
|
+
this._documentStreams[path] = openResult.streamState;
|
|
411
|
+
this._applySyncPayload(ref, openResult.firstChunk.data);
|
|
412
|
+
this._attachStreamConsumer({
|
|
413
|
+
path,
|
|
414
|
+
doc: ref,
|
|
415
|
+
streamState: openResult.streamState,
|
|
416
|
+
iterator: openResult.iterator,
|
|
417
|
+
});
|
|
339
418
|
}
|
|
340
419
|
}
|
|
341
420
|
}
|