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