@meshagent/meshagent 0.29.4 → 0.30.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 (83) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/dist/browser/agent-client.js +3 -28
  3. package/dist/browser/agent.js +6 -6
  4. package/dist/browser/containers-client.d.ts +125 -0
  5. package/dist/browser/containers-client.js +458 -0
  6. package/dist/browser/database-client.d.ts +42 -6
  7. package/dist/browser/database-client.js +610 -77
  8. package/dist/browser/developer-client.d.ts +2 -2
  9. package/dist/browser/developer-client.js +60 -15
  10. package/dist/browser/helpers.js +4 -3
  11. package/dist/browser/index.d.ts +1 -0
  12. package/dist/browser/index.js +1 -0
  13. package/dist/browser/lk-client.js +12 -3
  14. package/dist/browser/meshagent-client.d.ts +5 -0
  15. package/dist/browser/messaging-client.d.ts +1 -0
  16. package/dist/browser/messaging-client.js +52 -8
  17. package/dist/browser/queues-client.d.ts +2 -0
  18. package/dist/browser/queues-client.js +34 -7
  19. package/dist/browser/response.d.ts +28 -0
  20. package/dist/browser/response.js +76 -1
  21. package/dist/browser/room-client.d.ts +43 -1
  22. package/dist/browser/room-client.js +204 -0
  23. package/dist/browser/secrets-client.d.ts +1 -0
  24. package/dist/browser/secrets-client.js +32 -27
  25. package/dist/browser/storage-client.d.ts +22 -7
  26. package/dist/browser/storage-client.js +353 -15
  27. package/dist/browser/sync-client.d.ts +12 -13
  28. package/dist/browser/sync-client.js +263 -65
  29. package/dist/esm/agent-client.js +3 -28
  30. package/dist/esm/agent.js +6 -6
  31. package/dist/esm/containers-client.d.ts +125 -0
  32. package/dist/esm/containers-client.js +453 -0
  33. package/dist/esm/database-client.d.ts +42 -6
  34. package/dist/esm/database-client.js +611 -78
  35. package/dist/esm/developer-client.d.ts +2 -2
  36. package/dist/esm/developer-client.js +61 -16
  37. package/dist/esm/helpers.js +4 -3
  38. package/dist/esm/index.d.ts +1 -0
  39. package/dist/esm/index.js +1 -0
  40. package/dist/esm/lk-client.js +12 -3
  41. package/dist/esm/meshagent-client.d.ts +5 -0
  42. package/dist/esm/messaging-client.d.ts +1 -0
  43. package/dist/esm/messaging-client.js +52 -8
  44. package/dist/esm/queues-client.d.ts +2 -0
  45. package/dist/esm/queues-client.js +35 -8
  46. package/dist/esm/response.d.ts +28 -0
  47. package/dist/esm/response.js +73 -0
  48. package/dist/esm/room-client.d.ts +43 -1
  49. package/dist/esm/room-client.js +207 -3
  50. package/dist/esm/secrets-client.d.ts +1 -0
  51. package/dist/esm/secrets-client.js +33 -28
  52. package/dist/esm/storage-client.d.ts +22 -7
  53. package/dist/esm/storage-client.js +353 -15
  54. package/dist/esm/sync-client.d.ts +12 -13
  55. package/dist/esm/sync-client.js +263 -64
  56. package/dist/node/agent-client.js +3 -28
  57. package/dist/node/agent.js +6 -6
  58. package/dist/node/containers-client.d.ts +125 -0
  59. package/dist/node/containers-client.js +458 -0
  60. package/dist/node/database-client.d.ts +42 -6
  61. package/dist/node/database-client.js +610 -77
  62. package/dist/node/developer-client.d.ts +2 -2
  63. package/dist/node/developer-client.js +60 -15
  64. package/dist/node/helpers.js +4 -3
  65. package/dist/node/index.d.ts +1 -0
  66. package/dist/node/index.js +1 -0
  67. package/dist/node/lk-client.js +12 -3
  68. package/dist/node/meshagent-client.d.ts +5 -0
  69. package/dist/node/messaging-client.d.ts +1 -0
  70. package/dist/node/messaging-client.js +52 -8
  71. package/dist/node/queues-client.d.ts +2 -0
  72. package/dist/node/queues-client.js +34 -7
  73. package/dist/node/response.d.ts +28 -0
  74. package/dist/node/response.js +76 -1
  75. package/dist/node/room-client.d.ts +43 -1
  76. package/dist/node/room-client.js +204 -0
  77. package/dist/node/secrets-client.d.ts +1 -0
  78. package/dist/node/secrets-client.js +32 -27
  79. package/dist/node/storage-client.d.ts +22 -7
  80. package/dist/node/storage-client.js +353 -15
  81. package/dist/node/sync-client.d.ts +12 -13
  82. package/dist/node/sync-client.js +263 -65
  83. package/package.json +1 -1
@@ -1,10 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SyncClient = exports.QueuedSync = void 0;
3
+ exports.SyncClient = void 0;
4
4
  const event_emitter_1 = require("./event-emitter");
5
5
  const schema_1 = require("./schema");
6
- const stream_controller_1 = require("./stream-controller");
7
6
  const room_server_client_1 = require("./room-server-client");
7
+ const response_1 = require("./response");
8
8
  const utils_1 = require("./utils");
9
9
  const runtime_1 = require("./runtime");
10
10
  const completer_1 = require("./completer");
@@ -21,99 +21,251 @@ function normalizeSyncPath(path) {
21
21
  }
22
22
  return normalized;
23
23
  }
24
- ;
25
- class QueuedSync {
26
- constructor({ path, base64 }) {
27
- this.path = path;
28
- this.base64 = base64;
24
+ function parseSyncOpenStateChunkHeaders(headers) {
25
+ if (headers["kind"] !== "state" ||
26
+ typeof headers["path"] !== "string" ||
27
+ typeof headers["schema"] !== "object" ||
28
+ headers["schema"] == null) {
29
+ throw new room_server_client_1.RoomServerException("unexpected return type from sync.open");
29
30
  }
31
+ return {
32
+ kind: "state",
33
+ path: headers["path"],
34
+ schema: headers["schema"],
35
+ };
30
36
  }
31
- exports.QueuedSync = QueuedSync;
37
+ function parseSyncOpenOutputChunkHeaders(headers) {
38
+ const kind = headers["kind"];
39
+ if ((kind !== "state" && kind !== "sync") ||
40
+ typeof headers["path"] !== "string") {
41
+ throw new room_server_client_1.RoomServerException("unexpected return type from sync.open");
42
+ }
43
+ return {
44
+ kind,
45
+ path: headers["path"],
46
+ };
47
+ }
48
+ class SyncOpenStreamState {
49
+ constructor(params) {
50
+ this.params = params;
51
+ this._inputQueue = [];
52
+ this._inputWaiters = [];
53
+ this._inputClosed = false;
54
+ }
55
+ _enqueueChunk(chunk) {
56
+ const waiter = this._inputWaiters.shift();
57
+ if (waiter) {
58
+ waiter(chunk);
59
+ return;
60
+ }
61
+ this._inputQueue.push(chunk);
62
+ }
63
+ _nextChunk() {
64
+ const queued = this._inputQueue.shift();
65
+ if (queued !== undefined) {
66
+ return Promise.resolve(queued);
67
+ }
68
+ return new Promise((resolve) => {
69
+ this._inputWaiters.push(resolve);
70
+ });
71
+ }
72
+ async *inputStream() {
73
+ yield new response_1.BinaryContent({
74
+ data: new Uint8Array(),
75
+ headers: {
76
+ kind: "start",
77
+ path: this.params.path,
78
+ create: this.params.create,
79
+ vector: this.params.vector,
80
+ schema: this.params.schemaJson,
81
+ schema_path: this.params.schemaPath,
82
+ initial_json: this.params.initialJson,
83
+ },
84
+ });
85
+ while (true) {
86
+ const chunk = await this._nextChunk();
87
+ if (chunk === SyncOpenStreamState.INPUT_STREAM_CLOSE) {
88
+ return;
89
+ }
90
+ yield chunk;
91
+ }
92
+ }
93
+ attachTask(task) {
94
+ this._task = task
95
+ .catch((error) => {
96
+ this._error = error;
97
+ throw error;
98
+ })
99
+ .finally(() => {
100
+ this.closeInputStream();
101
+ });
102
+ void this._task.catch(() => undefined);
103
+ }
104
+ closeInputStream() {
105
+ if (this._inputClosed) {
106
+ return;
107
+ }
108
+ this._inputClosed = true;
109
+ this._enqueueChunk(SyncOpenStreamState.INPUT_STREAM_CLOSE);
110
+ }
111
+ queueSync(data) {
112
+ if (this._error instanceof Error) {
113
+ throw this._error;
114
+ }
115
+ if (this._error !== undefined) {
116
+ throw new room_server_client_1.RoomServerException(`sync stream failed: ${String(this._error)}`);
117
+ }
118
+ if (this._inputClosed) {
119
+ throw new room_server_client_1.RoomServerException("attempted to sync to a document that is not connected");
120
+ }
121
+ this._enqueueChunk(new response_1.BinaryContent({
122
+ data,
123
+ headers: { kind: "sync" },
124
+ }));
125
+ }
126
+ async wait() {
127
+ await this._task;
128
+ }
129
+ }
130
+ SyncOpenStreamState.INPUT_STREAM_CLOSE = Symbol("sync-open-input-close");
32
131
  class SyncClient extends event_emitter_1.EventEmitter {
33
132
  constructor({ room }) {
34
133
  super();
35
134
  this._connectingDocuments = {};
36
- this._changesToSync = new stream_controller_1.StreamController();
37
135
  this._connectedDocuments = {};
136
+ this._documentStreams = {};
38
137
  this.client = room;
39
- this.client.protocol.addHandler("room.sync", this._handleSync.bind(this));
40
138
  this.client.protocol.addHandler("room.status", this._handleStatus.bind(this));
41
139
  }
140
+ _unexpectedResponseError(operation) {
141
+ return new room_server_client_1.RoomServerException(`unexpected return type from sync.${operation}`);
142
+ }
143
+ async _invoke(operation, input) {
144
+ return await this.client.invoke({
145
+ toolkit: "sync",
146
+ tool: operation,
147
+ input,
148
+ });
149
+ }
42
150
  start({ onDone, onError } = {}) {
43
151
  this.client.protocol.start({ onDone, onError });
44
- (async () => {
45
- for await (const message of this._changesToSync.stream) {
46
- await this.client.sendRequest("room.sync", { path: message.path }, utils_1.encoder.encode(message.base64));
47
- }
48
- })();
49
152
  }
50
153
  dispose() {
51
154
  super.dispose();
52
- this._changesToSync.close();
53
- }
54
- async _handleSync(protocol, messageId, data, bytes) {
55
- const headerStr = (0, utils_1.splitMessageHeader)(bytes || new Uint8Array());
56
- const payload = (0, utils_1.splitMessagePayload)(bytes || new Uint8Array());
57
- const header = JSON.parse(headerStr);
58
- const path = normalizeSyncPath(header["path"]);
59
- const isConnecting = this._connectingDocuments[path];
60
- if (isConnecting) {
61
- await isConnecting;
62
- }
63
- if (this._connectedDocuments[path]) {
64
- const rc = this._connectedDocuments[path];
65
- const doc = rc.ref;
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
+ _applySyncPayload(rc, payload) {
166
+ const doc = rc.ref;
167
+ if (payload.length > 0) {
66
168
  const base64 = utils_1.decoder.decode(payload);
67
169
  (0, runtime_1.applyBackendChanges)(doc.id, base64);
68
- this.emit("synced", { type: "sync", doc });
69
- if (!doc.isSynchronized) {
70
- doc.setSynchronizedComplete();
71
- }
72
170
  }
73
- else {
74
- throw new room_server_client_1.RoomServerException(`received change for a document that is not connected: ${path}`);
171
+ this.emit("synced", { type: "sync", doc });
172
+ if (!doc.isSynchronized) {
173
+ doc.setSynchronizedComplete();
75
174
  }
76
175
  }
77
- async _handleStatus(protocol, messageId, data, bytes) {
78
- const headerStr = (0, utils_1.splitMessageHeader)(bytes || new Uint8Array());
79
- const header = JSON.parse(headerStr);
80
- this.emit("status", header.status);
176
+ async _handleStatus(_protocol, _messageId, _type, bytes) {
177
+ if (!bytes) {
178
+ return;
179
+ }
180
+ const [header] = (0, utils_1.unpackMessage)(bytes);
181
+ this.emit("status", { type: "status", status: header.status });
81
182
  }
82
183
  async create(path, json) {
83
- path = normalizeSyncPath(path);
84
- await this.client.sendRequest("room.create", { path, json });
184
+ const normalizedPath = normalizeSyncPath(path);
185
+ await this._invoke("create", {
186
+ path: normalizedPath,
187
+ json: json ?? null,
188
+ schema: null,
189
+ schema_path: null,
190
+ });
85
191
  }
86
- async open(path, { create = true } = {}) {
192
+ async open(path, { create = true, initialJson, schema, } = {}) {
87
193
  path = normalizeSyncPath(path);
88
194
  const pending = this._connectingDocuments[path];
89
195
  if (pending) {
90
196
  await pending;
91
197
  }
92
- if (this._connectedDocuments[path]) {
93
- const rc = this._connectedDocuments[path];
94
- rc.count++;
95
- return rc.ref;
198
+ const connected = this._connectedDocuments[path];
199
+ if (connected) {
200
+ connected.count++;
201
+ return connected.ref;
96
202
  }
97
- const c = new completer_1.Completer();
98
- this._connectingDocuments[path] = c.fut;
203
+ const connecting = new completer_1.Completer();
204
+ this._connectingDocuments[path] = connecting.fut;
205
+ let streamState;
206
+ let iterator;
99
207
  try {
100
- const result = (await this.client.sendRequest("room.connect", { path, create }));
101
- const schema = schema_1.MeshSchema.fromJson(result.json["schema"]);
208
+ streamState = new SyncOpenStreamState({
209
+ path,
210
+ create,
211
+ vector: null,
212
+ schemaJson: schema == null ? null : schema.toJson(),
213
+ schemaPath: null,
214
+ initialJson: initialJson ?? null,
215
+ });
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
+ }
102
237
  const doc = new room_server_client_1.MeshDocument({
103
- schema,
104
- sendChangesToBackend: (base64Str) => {
105
- this._changesToSync.add({ path, base64: base64Str });
238
+ schema: schema_1.MeshSchema.fromJson(stateHeaders.schema),
239
+ sendChangesToBackend: (base64) => {
240
+ try {
241
+ streamState?.queueSync(utils_1.encoder.encode(base64));
242
+ }
243
+ catch {
244
+ }
106
245
  },
107
246
  });
108
247
  const rc = new utils_1.RefCount(doc);
109
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
+ }));
110
257
  this.emit("connected", { type: "connect", doc });
111
- c.complete();
258
+ connecting.complete(rc);
259
+ await doc.synchronized;
112
260
  return doc;
113
261
  }
114
- catch (err) {
115
- c.completeError(err);
116
- throw err;
262
+ catch (error) {
263
+ streamState?.closeInputStream();
264
+ if (iterator) {
265
+ await iterator.return?.();
266
+ }
267
+ connecting.completeError(error);
268
+ throw error;
117
269
  }
118
270
  finally {
119
271
  delete this._connectingDocuments[path];
@@ -121,11 +273,6 @@ class SyncClient extends event_emitter_1.EventEmitter {
121
273
  }
122
274
  async close(path) {
123
275
  path = normalizeSyncPath(path);
124
- const pending = this._connectingDocuments[path];
125
- if (pending) {
126
- await pending;
127
- await new Promise(resolve => setTimeout(resolve, 50));
128
- }
129
276
  const rc = this._connectedDocuments[path];
130
277
  if (!rc) {
131
278
  throw new room_server_client_1.RoomServerException(`Not connected to ${path}`);
@@ -134,14 +281,65 @@ class SyncClient extends event_emitter_1.EventEmitter {
134
281
  rc.count--;
135
282
  if (rc.count === 0) {
136
283
  delete this._connectedDocuments[path];
137
- await this.client.sendRequest("room.disconnect", { path });
138
- (0, runtime_1.unregisterDocument)(doc.id);
284
+ const streamState = this._documentStreams[path];
285
+ delete this._documentStreams[path];
286
+ if (streamState) {
287
+ streamState.closeInputStream();
288
+ try {
289
+ await streamState.wait();
290
+ }
291
+ finally {
292
+ (0, runtime_1.unregisterDocument)(doc.id);
293
+ }
294
+ }
295
+ else {
296
+ (0, runtime_1.unregisterDocument)(doc.id);
297
+ }
139
298
  }
140
299
  this.emit("closed", { type: "close", doc });
141
300
  }
142
301
  async sync(path, data) {
143
302
  path = normalizeSyncPath(path);
144
- await this.client.sendRequest("room.sync", { path }, data);
303
+ if (!this._connectedDocuments[path]) {
304
+ throw new room_server_client_1.RoomServerException("attempted to sync to a document that is not connected");
305
+ }
306
+ const streamState = this._documentStreams[path];
307
+ if (!streamState) {
308
+ throw new room_server_client_1.RoomServerException("attempted to sync to a document that is not connected");
309
+ }
310
+ streamState.queueSync(data);
311
+ }
312
+ async _consumeOpenStream(params) {
313
+ try {
314
+ while (true) {
315
+ const next = await params.iterator.next();
316
+ if (next.done || next.value === undefined) {
317
+ return;
318
+ }
319
+ const chunk = next.value;
320
+ if (chunk instanceof response_1.ErrorContent) {
321
+ throw new room_server_client_1.RoomServerException(chunk.text, chunk.code);
322
+ }
323
+ if (chunk instanceof response_1.ControlContent) {
324
+ if (chunk.method === "close") {
325
+ return;
326
+ }
327
+ throw this._unexpectedResponseError("open");
328
+ }
329
+ if (!(chunk instanceof response_1.BinaryContent)) {
330
+ throw this._unexpectedResponseError("open");
331
+ }
332
+ const headers = parseSyncOpenOutputChunkHeaders(chunk.headers);
333
+ if (normalizeSyncPath(headers.path) !== params.path) {
334
+ throw new room_server_client_1.RoomServerException("sync.open stream returned a mismatched path");
335
+ }
336
+ this._applySyncPayload(params.rc, chunk.data);
337
+ }
338
+ }
339
+ finally {
340
+ params.streamState.closeInputStream();
341
+ await params.iterator.return?.();
342
+ }
145
343
  }
146
344
  }
147
345
  exports.SyncClient = SyncClient;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meshagent/meshagent",
3
- "version": "0.29.4",
3
+ "version": "0.30.1",
4
4
  "description": "Meshagent Client",
5
5
  "homepage": "https://github.com/meshagent/meshagent-ts",
6
6
  "scripts": {