@dxos/echo-pipeline 0.6.2 → 0.6.3-main.40d1cec

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 (62) hide show
  1. package/dist/lib/browser/{chunk-UJQ5VS5V.mjs → chunk-PEE7MYCZ.mjs} +2549 -1071
  2. package/dist/lib/browser/chunk-PEE7MYCZ.mjs.map +7 -0
  3. package/dist/lib/browser/index.mjs +12 -1049
  4. package/dist/lib/browser/index.mjs.map +4 -4
  5. package/dist/lib/browser/meta.json +1 -1
  6. package/dist/lib/browser/testing/index.mjs +224 -2
  7. package/dist/lib/browser/testing/index.mjs.map +4 -4
  8. package/dist/lib/node/{chunk-RH6TDRML.cjs → chunk-RLTLWNAY.cjs} +3082 -1632
  9. package/dist/lib/node/chunk-RLTLWNAY.cjs.map +7 -0
  10. package/dist/lib/node/index.cjs +37 -1056
  11. package/dist/lib/node/index.cjs.map +4 -4
  12. package/dist/lib/node/meta.json +1 -1
  13. package/dist/lib/node/testing/index.cjs +238 -13
  14. package/dist/lib/node/testing/index.cjs.map +4 -4
  15. package/dist/types/src/automerge/automerge-host.d.ts +29 -2
  16. package/dist/types/src/automerge/automerge-host.d.ts.map +1 -1
  17. package/dist/types/src/automerge/collection-synchronizer.d.ts +61 -0
  18. package/dist/types/src/automerge/collection-synchronizer.d.ts.map +1 -0
  19. package/dist/types/src/automerge/collection-synchronizer.test.d.ts +2 -0
  20. package/dist/types/src/automerge/collection-synchronizer.test.d.ts.map +1 -0
  21. package/dist/types/src/automerge/echo-network-adapter.d.ts +9 -2
  22. package/dist/types/src/automerge/echo-network-adapter.d.ts.map +1 -1
  23. package/dist/types/src/automerge/echo-replicator.d.ts +7 -0
  24. package/dist/types/src/automerge/echo-replicator.d.ts.map +1 -1
  25. package/dist/types/src/automerge/heads-store.d.ts +1 -1
  26. package/dist/types/src/automerge/heads-store.d.ts.map +1 -1
  27. package/dist/types/src/automerge/index.d.ts +2 -0
  28. package/dist/types/src/automerge/index.d.ts.map +1 -1
  29. package/dist/types/src/automerge/mesh-echo-replicator-connection.d.ts +3 -1
  30. package/dist/types/src/automerge/mesh-echo-replicator-connection.d.ts.map +1 -1
  31. package/dist/types/src/automerge/mesh-echo-replicator.d.ts +2 -2
  32. package/dist/types/src/automerge/mesh-echo-replicator.d.ts.map +1 -1
  33. package/dist/types/src/automerge/network-protocol.d.ts +31 -0
  34. package/dist/types/src/automerge/network-protocol.d.ts.map +1 -0
  35. package/dist/types/src/automerge/space-collection.d.ts +4 -0
  36. package/dist/types/src/automerge/space-collection.d.ts.map +1 -0
  37. package/dist/types/src/db-host/data-service.d.ts +2 -1
  38. package/dist/types/src/db-host/data-service.d.ts.map +1 -1
  39. package/dist/types/src/testing/index.d.ts +1 -0
  40. package/dist/types/src/testing/index.d.ts.map +1 -1
  41. package/dist/types/src/testing/test-replicator.d.ts +46 -0
  42. package/dist/types/src/testing/test-replicator.d.ts.map +1 -0
  43. package/package.json +33 -33
  44. package/src/automerge/automerge-host.test.ts +76 -14
  45. package/src/automerge/automerge-host.ts +217 -30
  46. package/src/automerge/automerge-repo.test.ts +2 -1
  47. package/src/automerge/collection-synchronizer.test.ts +91 -0
  48. package/src/automerge/collection-synchronizer.ts +204 -0
  49. package/src/automerge/echo-network-adapter.test.ts +5 -1
  50. package/src/automerge/echo-network-adapter.ts +69 -4
  51. package/src/automerge/echo-replicator.ts +9 -0
  52. package/src/automerge/heads-store.ts +6 -9
  53. package/src/automerge/index.ts +2 -0
  54. package/src/automerge/mesh-echo-replicator-connection.ts +6 -1
  55. package/src/automerge/mesh-echo-replicator.ts +28 -7
  56. package/src/automerge/network-protocol.ts +45 -0
  57. package/src/automerge/space-collection.ts +14 -0
  58. package/src/db-host/data-service.ts +26 -12
  59. package/src/testing/index.ts +1 -0
  60. package/src/testing/test-replicator.ts +194 -0
  61. package/dist/lib/browser/chunk-UJQ5VS5V.mjs.map +0 -7
  62. package/dist/lib/node/chunk-RH6TDRML.cjs.map +0 -7
@@ -262,111 +262,276 @@ var DocumentsSynchronizer = class extends Resource {
262
262
  }
263
263
  };
264
264
 
265
- // packages/core/echo/echo-pipeline/src/db-host/data-service.ts
266
- import { Stream } from "@dxos/codec-protobuf";
267
- import { invariant as invariant3 } from "@dxos/invariant";
268
- import { log as log2 } from "@dxos/log";
269
- var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/db-host/data-service.ts";
270
- var DataServiceImpl = class {
265
+ // packages/core/echo/echo-pipeline/src/automerge/collection-synchronizer.ts
266
+ import { asyncReturn, Event, scheduleTask, scheduleTaskInterval } from "@dxos/async";
267
+ import { next as am } from "@dxos/automerge/automerge";
268
+ import { Resource as Resource2 } from "@dxos/context";
269
+ import { defaultMap } from "@dxos/util";
270
+ var MIN_QUERY_INTERVAL = 5e3;
271
+ var POLL_INTERVAL = 3e4;
272
+ var CollectionSynchronizer = class extends Resource2 {
271
273
  constructor(params) {
274
+ super();
272
275
  /**
273
- * Map of subscriptions.
274
- * subscriptionId -> DocumentsSynchronizer
276
+ * CollectionId -> State.
275
277
  */
276
- this._subscriptions = /* @__PURE__ */ new Map();
277
- this._automergeHost = params.automergeHost;
278
- this._updateIndexes = params.updateIndexes;
278
+ this._perCollectionStates = /* @__PURE__ */ new Map();
279
+ this._connectedPeers = /* @__PURE__ */ new Set();
280
+ this.remoteStateUpdated = new Event();
281
+ this._sendCollectionState = params.sendCollectionState;
282
+ this._queryCollectionState = params.queryCollectionState;
283
+ this._shouldSyncCollection = params.shouldSyncCollection;
279
284
  }
280
- subscribe(request) {
281
- return new Stream(({ next, ready }) => {
282
- const synchronizer = new DocumentsSynchronizer({
283
- repo: this._automergeHost.repo,
284
- sendUpdates: (updates) => next(updates)
285
- });
286
- synchronizer.open().then(() => {
287
- this._subscriptions.set(request.subscriptionId, synchronizer);
288
- ready();
289
- }).catch((err) => log2.catch(err, void 0, {
290
- F: __dxlog_file3,
291
- L: 62,
292
- S: this,
293
- C: (f, a) => f(...a)
294
- }));
295
- return () => synchronizer.close();
285
+ async _open(ctx) {
286
+ scheduleTaskInterval(this._ctx, async () => {
287
+ for (const collectionId of this._perCollectionStates.keys()) {
288
+ this.refreshCollection(collectionId);
289
+ await asyncReturn();
290
+ }
291
+ }, POLL_INTERVAL);
292
+ }
293
+ getRegisteredCollectionIds() {
294
+ return [
295
+ ...this._perCollectionStates.keys()
296
+ ];
297
+ }
298
+ getLocalCollectionState(collectionId) {
299
+ return this._getPerCollectionState(collectionId).localState;
300
+ }
301
+ setLocalCollectionState(collectionId, state) {
302
+ this._getPerCollectionState(collectionId).localState = state;
303
+ queueMicrotask(async () => {
304
+ if (!this._ctx.disposed) {
305
+ this._refreshInterestedPeers(collectionId);
306
+ this.refreshCollection(collectionId);
307
+ }
296
308
  });
297
309
  }
298
- async updateSubscription(request) {
299
- const synchronizer = this._subscriptions.get(request.subscriptionId);
300
- invariant3(synchronizer, "Subscription not found", {
301
- F: __dxlog_file3,
302
- L: 69,
303
- S: this,
304
- A: [
305
- "synchronizer",
306
- "'Subscription not found'"
307
- ]
310
+ getRemoteCollectionStates(collectionId) {
311
+ return this._getPerCollectionState(collectionId).remoteStates;
312
+ }
313
+ refreshCollection(collectionId) {
314
+ let scheduleAnotherRefresh = false;
315
+ const state = this._getPerCollectionState(collectionId);
316
+ for (const peerId of this._connectedPeers) {
317
+ if (state.interestedPeers.has(peerId)) {
318
+ const lastQueried = state.lastQueried.get(peerId) ?? 0;
319
+ if (Date.now() - lastQueried > MIN_QUERY_INTERVAL) {
320
+ state.lastQueried.set(peerId, Date.now());
321
+ this._queryCollectionState(collectionId, peerId);
322
+ } else {
323
+ scheduleAnotherRefresh = true;
324
+ }
325
+ }
326
+ }
327
+ if (scheduleAnotherRefresh) {
328
+ scheduleTask(this._ctx, () => this.refreshCollection(collectionId), MIN_QUERY_INTERVAL);
329
+ }
330
+ }
331
+ /**
332
+ * Callback when a connection to a peer is established.
333
+ */
334
+ onConnectionOpen(peerId) {
335
+ this._connectedPeers.add(peerId);
336
+ queueMicrotask(async () => {
337
+ if (this._ctx.disposed) {
338
+ return;
339
+ }
340
+ for (const [collectionId, state] of this._perCollectionStates.entries()) {
341
+ if (this._shouldSyncCollection(collectionId, peerId)) {
342
+ state.interestedPeers.add(peerId);
343
+ state.lastQueried.set(peerId, Date.now());
344
+ this._queryCollectionState(collectionId, peerId);
345
+ }
346
+ }
308
347
  });
309
- if (request.addIds?.length) {
310
- await synchronizer.addDocuments(request.addIds);
348
+ }
349
+ /**
350
+ * Callback when a connection to a peer is closed.
351
+ */
352
+ onConnectionClosed(peerId) {
353
+ this._connectedPeers.delete(peerId);
354
+ for (const perCollectionState of this._perCollectionStates.values()) {
355
+ perCollectionState.remoteStates.delete(peerId);
311
356
  }
312
- if (request.removeIds?.length) {
313
- await synchronizer.removeDocuments(request.removeIds);
357
+ }
358
+ /**
359
+ * Callback when a peer queries the state of a collection.
360
+ */
361
+ onCollectionStateQueried(collectionId, peerId) {
362
+ const perCollectionState = this._getPerCollectionState(collectionId);
363
+ if (perCollectionState.localState) {
364
+ this._sendCollectionState(collectionId, peerId, perCollectionState.localState);
314
365
  }
315
366
  }
316
- async update(request) {
317
- if (!request.updates) {
318
- return;
367
+ /**
368
+ * Callback when a peer sends the state of a collection.
369
+ */
370
+ onRemoteStateReceived(collectionId, peerId, state) {
371
+ const perCollectionState = this._getPerCollectionState(collectionId);
372
+ perCollectionState.remoteStates.set(peerId, state);
373
+ this.remoteStateUpdated.emit({
374
+ peerId,
375
+ collectionId
376
+ });
377
+ }
378
+ _getPerCollectionState(collectionId) {
379
+ return defaultMap(this._perCollectionStates, collectionId, () => ({
380
+ localState: void 0,
381
+ remoteStates: /* @__PURE__ */ new Map(),
382
+ interestedPeers: /* @__PURE__ */ new Set(),
383
+ lastQueried: /* @__PURE__ */ new Map()
384
+ }));
385
+ }
386
+ _refreshInterestedPeers(collectionId) {
387
+ for (const peerId of this._connectedPeers) {
388
+ if (this._shouldSyncCollection(collectionId, peerId)) {
389
+ this._getPerCollectionState(collectionId).interestedPeers.add(peerId);
390
+ } else {
391
+ this._getPerCollectionState(collectionId).interestedPeers.delete(peerId);
392
+ }
319
393
  }
320
- const synchronizer = this._subscriptions.get(request.subscriptionId);
321
- invariant3(synchronizer, "Subscription not found", {
322
- F: __dxlog_file3,
323
- L: 84,
324
- S: this,
325
- A: [
326
- "synchronizer",
327
- "'Subscription not found'"
328
- ]
329
- });
330
- synchronizer.update(request.updates);
331
394
  }
332
- async flush(request) {
333
- await this._automergeHost.flush(request);
395
+ };
396
+ var diffCollectionState = (local, remote) => {
397
+ const allDocuments = /* @__PURE__ */ new Set([
398
+ ...Object.keys(local.documents),
399
+ ...Object.keys(remote.documents)
400
+ ]);
401
+ const different = [];
402
+ for (const documentId of allDocuments) {
403
+ if (!local.documents[documentId] || !remote.documents[documentId] || !am.equals(local.documents[documentId], remote.documents[documentId])) {
404
+ different.push(documentId);
405
+ }
334
406
  }
335
- async getDocumentHeads(request) {
336
- const entries = await Promise.all(request.documentIds?.map(async (documentId) => {
337
- const heads = await this._automergeHost.getHeads(documentId);
338
- return {
339
- documentId,
340
- heads
341
- };
342
- }) ?? []);
343
- return {
344
- heads: {
345
- entries
407
+ return {
408
+ different
409
+ };
410
+ };
411
+
412
+ // packages/core/echo/echo-pipeline/src/automerge/leveldb-storage-adapter.ts
413
+ import { LifecycleState, Resource as Resource3 } from "@dxos/context";
414
+ var LevelDBStorageAdapter = class extends Resource3 {
415
+ constructor(_params) {
416
+ super();
417
+ this._params = _params;
418
+ }
419
+ async load(keyArray) {
420
+ try {
421
+ if (this._lifecycleState !== LifecycleState.OPEN) {
422
+ return void 0;
346
423
  }
347
- };
424
+ return await this._params.db.get(keyArray, {
425
+ ...encodingOptions
426
+ });
427
+ } catch (err) {
428
+ if (isLevelDbNotFoundError(err)) {
429
+ return void 0;
430
+ }
431
+ throw err;
432
+ }
348
433
  }
349
- async waitUntilHeadsReplicated(request, options) {
350
- await this._automergeHost.waitUntilHeadsReplicated(request.heads);
434
+ async save(keyArray, binary) {
435
+ if (this._lifecycleState !== LifecycleState.OPEN) {
436
+ return void 0;
437
+ }
438
+ const batch = this._params.db.batch();
439
+ await this._params.callbacks?.beforeSave?.({
440
+ path: keyArray,
441
+ batch
442
+ });
443
+ batch.put(keyArray, Buffer.from(binary), {
444
+ ...encodingOptions
445
+ });
446
+ await batch.write();
447
+ await this._params.callbacks?.afterSave?.(keyArray);
351
448
  }
352
- async reIndexHeads(request, options) {
353
- await this._automergeHost.reIndexHeads(request.documentIds ?? []);
449
+ async remove(keyArray) {
450
+ if (this._lifecycleState !== LifecycleState.OPEN) {
451
+ return void 0;
452
+ }
453
+ await this._params.db.del(keyArray, {
454
+ ...encodingOptions
455
+ });
354
456
  }
355
- async updateIndexes() {
356
- await this._updateIndexes();
457
+ async loadRange(keyPrefix) {
458
+ if (this._lifecycleState !== LifecycleState.OPEN) {
459
+ return [];
460
+ }
461
+ const result = [];
462
+ for await (const [key, value] of this._params.db.iterator({
463
+ gte: keyPrefix,
464
+ lte: [
465
+ ...keyPrefix,
466
+ "\uFFFF"
467
+ ],
468
+ ...encodingOptions
469
+ })) {
470
+ result.push({
471
+ key,
472
+ data: value
473
+ });
474
+ }
475
+ return result;
476
+ }
477
+ async removeRange(keyPrefix) {
478
+ if (this._lifecycleState !== LifecycleState.OPEN) {
479
+ return void 0;
480
+ }
481
+ const batch = this._params.db.batch();
482
+ for await (const [key] of this._params.db.iterator({
483
+ gte: keyPrefix,
484
+ lte: [
485
+ ...keyPrefix,
486
+ "\uFFFF"
487
+ ],
488
+ ...encodingOptions
489
+ })) {
490
+ batch.del(key, {
491
+ ...encodingOptions
492
+ });
493
+ }
494
+ await batch.write();
357
495
  }
358
496
  };
497
+ var keyEncoder = {
498
+ encode: (key) => Buffer.from(key.map((k) => k.replaceAll("%", "%25").replaceAll("-", "%2D")).join("-")),
499
+ decode: (key) => Buffer.from(key).toString().split("-").map((k) => k.replaceAll("%2D", "-").replaceAll("%25", "%")),
500
+ format: "buffer"
501
+ };
502
+ var encodingOptions = {
503
+ keyEncoding: keyEncoder,
504
+ valueEncoding: "buffer"
505
+ };
506
+ var isLevelDbNotFoundError = (err) => err.code === "LEVEL_NOT_FOUND";
359
507
 
360
- // packages/core/echo/echo-pipeline/src/metadata/metadata-store.ts
361
- import CRC32 from "crc-32";
362
- import { Event, scheduleTaskInterval, synchronized } from "@dxos/async";
363
- import { Context } from "@dxos/context";
508
+ // packages/core/echo/echo-pipeline/src/automerge/automerge-host.ts
509
+ import { Event as Event2, asyncTimeout } from "@dxos/async";
510
+ import { next as automerge, getBackend, getHeads, isAutomerge, equals as headsEquals, save } from "@dxos/automerge/automerge";
511
+ import { Repo } from "@dxos/automerge/automerge-repo";
512
+ import { Context, Resource as Resource4, cancelWithContext as cancelWithContext2 } from "@dxos/context";
364
513
  import { invariant as invariant4 } from "@dxos/invariant";
365
514
  import { PublicKey as PublicKey2 } from "@dxos/keys";
366
515
  import { log as log3 } from "@dxos/log";
367
- import { DataCorruptionError, STORAGE_VERSION, schema as schema4 } from "@dxos/protocols";
368
- import { Invitation, SpaceState } from "@dxos/protocols/proto/dxos/client/services";
369
- import { ComplexMap, arrayToBuffer, forEachAsync, isNotNullOrUndefined } from "@dxos/util";
516
+ import { objectPointerCodec } from "@dxos/protocols";
517
+ import { trace } from "@dxos/tracing";
518
+ import { mapValues } from "@dxos/util";
519
+
520
+ // packages/core/echo/echo-pipeline/src/automerge/echo-network-adapter.ts
521
+ import { synchronized, Trigger } from "@dxos/async";
522
+ import { NetworkAdapter } from "@dxos/automerge/automerge-repo";
523
+ import { LifecycleState as LifecycleState2 } from "@dxos/context";
524
+ import { invariant as invariant3 } from "@dxos/invariant";
525
+ import { log as log2 } from "@dxos/log";
526
+ import { nonNullable } from "@dxos/util";
527
+
528
+ // packages/core/echo/echo-pipeline/src/automerge/network-protocol.ts
529
+ var MESSAGE_TYPE_COLLECTION_QUERY = "collection-query";
530
+ var isCollectionQueryMessage = (message) => message.type === MESSAGE_TYPE_COLLECTION_QUERY;
531
+ var MESSAGE_TYPE_COLLECTION_STATE = "collection-state";
532
+ var isCollectionStateMessage = (message) => message.type === MESSAGE_TYPE_COLLECTION_STATE;
533
+
534
+ // packages/core/echo/echo-pipeline/src/automerge/echo-network-adapter.ts
370
535
  function _ts_decorate(decorators, target, key, desc) {
371
536
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
372
537
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
@@ -377,334 +542,348 @@ function _ts_decorate(decorators, target, key, desc) {
377
542
  r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
378
543
  return c > 3 && r && Object.defineProperty(target, key, r), r;
379
544
  }
380
- var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/metadata/metadata-store.ts";
381
- var EXPIRED_INVITATION_CLEANUP_INTERVAL = 60 * 60 * 1e3;
382
- var emptyEchoMetadata = () => ({
383
- version: STORAGE_VERSION,
384
- spaces: [],
385
- created: /* @__PURE__ */ new Date(),
386
- updated: /* @__PURE__ */ new Date()
387
- });
388
- var emptyLargeSpaceMetadata = () => ({});
389
- var EchoMetadata = schema4.getCodecForType("dxos.echo.metadata.EchoMetadata");
390
- var LargeSpaceMetadata = schema4.getCodecForType("dxos.echo.metadata.LargeSpaceMetadata");
391
- var MetadataStore = class {
392
- constructor(directory) {
393
- this._metadata = emptyEchoMetadata();
394
- this._spaceLargeMetadata = new ComplexMap(PublicKey2.hash);
395
- this._metadataFile = void 0;
396
- this.update = new Event();
397
- this._invitationCleanupCtx = new Context(void 0, {
398
- F: __dxlog_file4,
399
- L: 53
545
+ var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/automerge/echo-network-adapter.ts";
546
+ var EchoNetworkAdapter = class extends NetworkAdapter {
547
+ constructor(_params) {
548
+ super();
549
+ this._params = _params;
550
+ this._replicators = /* @__PURE__ */ new Set();
551
+ this._connections = /* @__PURE__ */ new Map();
552
+ this._lifecycleState = LifecycleState2.CLOSED;
553
+ this._connected = new Trigger();
554
+ }
555
+ connect(peerId, peerMetadata) {
556
+ this.peerId = peerId;
557
+ this.peerMetadata = peerMetadata;
558
+ this._connected.wake();
559
+ }
560
+ send(message) {
561
+ const connectionEntry = this._connections.get(message.targetId);
562
+ if (!connectionEntry) {
563
+ throw new Error("Connection not found.");
564
+ }
565
+ connectionEntry.writer.write(message).catch((err) => {
566
+ if (connectionEntry.isOpen) {
567
+ log2.catch(err, void 0, {
568
+ F: __dxlog_file3,
569
+ L: 63,
570
+ S: this,
571
+ C: (f, a) => f(...a)
572
+ });
573
+ }
400
574
  });
401
- this._directory = directory;
402
575
  }
403
- get metadata() {
404
- return this._metadata;
405
- }
406
- get version() {
407
- return this._metadata.version ?? 0;
408
- }
409
- /**
410
- * Returns a list of currently saved spaces. The list and objects in it can be modified addSpace and
411
- * addSpaceFeed functions.
412
- */
413
- get spaces() {
414
- return this._metadata.spaces ?? [];
576
+ disconnect() {
415
577
  }
416
- async _readFile(file, codec2) {
417
- try {
418
- const { size: fileLength } = await file.stat();
419
- if (fileLength < 8) {
420
- return;
421
- }
422
- const dataSize = fromBytesInt32(await file.read(0, 4));
423
- const checksum = fromBytesInt32(await file.read(4, 4));
424
- log3("loaded", {
425
- size: dataSize,
426
- checksum,
427
- name: file.filename
428
- }, {
429
- F: __dxlog_file4,
430
- L: 89,
431
- S: this,
432
- C: (f, a) => f(...a)
433
- });
434
- if (fileLength < dataSize + 8) {
435
- throw new DataCorruptionError("Metadata size is smaller than expected.", {
436
- fileLength,
437
- dataSize
438
- });
439
- }
440
- const data = await file.read(8, dataSize);
441
- const calculatedChecksum = CRC32.buf(data);
442
- if (calculatedChecksum !== checksum) {
443
- throw new DataCorruptionError("Metadata checksum is invalid.");
444
- }
445
- return codec2.decode(data);
446
- } finally {
447
- await file.close();
578
+ async open() {
579
+ if (this._lifecycleState === LifecycleState2.OPEN) {
580
+ return;
448
581
  }
449
- }
450
- /**
451
- * @internal
452
- */
453
- async _writeFile(file, codec2, data) {
454
- const encoded = arrayToBuffer(codec2.encode(data));
455
- const checksum = CRC32.buf(encoded);
456
- const result = Buffer.alloc(8 + encoded.length);
457
- result.writeInt32LE(encoded.length, 0);
458
- result.writeInt32LE(checksum, 4);
459
- encoded.copy(result, 8);
460
- await file.write(0, result);
461
- log3("saved", {
462
- size: encoded.length,
463
- checksum
464
- }, {
465
- F: __dxlog_file4,
466
- L: 124,
582
+ this._lifecycleState = LifecycleState2.OPEN;
583
+ log2("emit ready", void 0, {
584
+ F: __dxlog_file3,
585
+ L: 79,
467
586
  S: this,
468
587
  C: (f, a) => f(...a)
469
588
  });
589
+ this.emit("ready", {
590
+ network: this
591
+ });
470
592
  }
471
593
  async close() {
472
- await this._invitationCleanupCtx.dispose();
473
- await this.flush();
474
- await this._metadataFile?.close();
475
- this._metadataFile = void 0;
476
- this._metadata = emptyEchoMetadata();
477
- this._spaceLargeMetadata.clear();
478
- }
479
- /**
480
- * Loads metadata from persistent storage.
481
- */
482
- async load() {
483
- if (!this._metadataFile || this._metadataFile.closed) {
484
- this._metadataFile = this._directory.getOrCreateFile("EchoMetadata");
594
+ if (this._lifecycleState === LifecycleState2.CLOSED) {
595
+ return this;
485
596
  }
486
- try {
487
- const metadata = await this._readFile(this._metadataFile, EchoMetadata);
488
- if (metadata) {
489
- this._metadata = metadata;
490
- }
491
- this._metadata.spaces?.forEach((space) => {
492
- space.state ??= SpaceState.ACTIVE;
493
- });
494
- } catch (err) {
495
- log3.error("failed to load metadata", {
496
- err
497
- }, {
498
- F: __dxlog_file4,
499
- L: 156,
500
- S: this,
501
- C: (f, a) => f(...a)
502
- });
503
- this._metadata = emptyEchoMetadata();
597
+ for (const replicator of this._replicators) {
598
+ await replicator.disconnect();
504
599
  }
505
- await forEachAsync([
506
- this._metadata.identity?.haloSpace.key,
507
- ...this._metadata.spaces?.map((space) => space.key) ?? []
508
- ].filter(isNotNullOrUndefined), async (key) => {
509
- try {
510
- await this._loadSpaceLargeMetadata(key);
511
- } catch (err) {
512
- log3.error("failed to load space large metadata", {
513
- err
514
- }, {
515
- F: __dxlog_file4,
516
- L: 168,
517
- S: this,
518
- C: (f, a) => f(...a)
519
- });
520
- }
521
- });
522
- scheduleTaskInterval(this._invitationCleanupCtx, async () => {
523
- for (const invitation of this._metadata.invitations ?? []) {
524
- if (hasInvitationExpired(invitation) || isLegacyInvitationFormat(invitation)) {
525
- await this.removeInvitation(invitation.invitationId);
526
- }
527
- }
528
- }, EXPIRED_INVITATION_CLEANUP_INTERVAL);
600
+ this._replicators.clear();
601
+ this._lifecycleState = LifecycleState2.CLOSED;
529
602
  }
530
- async _save() {
531
- const data = {
532
- ...this._metadata,
533
- version: STORAGE_VERSION,
534
- created: this._metadata.created ?? /* @__PURE__ */ new Date(),
535
- updated: /* @__PURE__ */ new Date()
536
- };
537
- this.update.emit(data);
538
- const file = this._directory.getOrCreateFile("EchoMetadata");
539
- await this._writeFile(file, EchoMetadata, data);
603
+ async whenConnected() {
604
+ await this._connected.wait({
605
+ timeout: 1e4
606
+ });
540
607
  }
541
- async _loadSpaceLargeMetadata(key) {
542
- const file = this._directory.getOrCreateFile(`space_${key.toHex()}_large`);
543
- try {
544
- const metadata = await this._readFile(file, LargeSpaceMetadata);
545
- if (metadata) {
546
- this._spaceLargeMetadata.set(key, metadata);
547
- }
548
- } catch (err) {
549
- log3.error("failed to load space large metadata", {
550
- err
551
- }, {
552
- F: __dxlog_file4,
553
- L: 210,
554
- S: this,
555
- C: (f, a) => f(...a)
556
- });
557
- }
608
+ async addReplicator(replicator) {
609
+ invariant3(this._lifecycleState === LifecycleState2.OPEN, void 0, {
610
+ F: __dxlog_file3,
611
+ L: 105,
612
+ S: this,
613
+ A: [
614
+ "this._lifecycleState === LifecycleState.OPEN",
615
+ ""
616
+ ]
617
+ });
618
+ invariant3(this.peerId, void 0, {
619
+ F: __dxlog_file3,
620
+ L: 106,
621
+ S: this,
622
+ A: [
623
+ "this.peerId",
624
+ ""
625
+ ]
626
+ });
627
+ invariant3(!this._replicators.has(replicator), void 0, {
628
+ F: __dxlog_file3,
629
+ L: 107,
630
+ S: this,
631
+ A: [
632
+ "!this._replicators.has(replicator)",
633
+ ""
634
+ ]
635
+ });
636
+ this._replicators.add(replicator);
637
+ await replicator.connect({
638
+ peerId: this.peerId,
639
+ onConnectionOpen: this._onConnectionOpen.bind(this),
640
+ onConnectionClosed: this._onConnectionClosed.bind(this),
641
+ onConnectionAuthScopeChanged: this._onConnectionAuthScopeChanged.bind(this),
642
+ getContainingSpaceForDocument: this._params.getContainingSpaceForDocument
643
+ });
558
644
  }
559
- async _saveSpaceLargeMetadata(key) {
560
- const data = this._getLargeSpaceMetadata(key);
561
- const file = this._directory.getOrCreateFile(`space_${key.toHex()}_large`);
562
- await this._writeFile(file, LargeSpaceMetadata, data);
645
+ async removeReplicator(replicator) {
646
+ invariant3(this._lifecycleState === LifecycleState2.OPEN, void 0, {
647
+ F: __dxlog_file3,
648
+ L: 121,
649
+ S: this,
650
+ A: [
651
+ "this._lifecycleState === LifecycleState.OPEN",
652
+ ""
653
+ ]
654
+ });
655
+ invariant3(this._replicators.has(replicator), void 0, {
656
+ F: __dxlog_file3,
657
+ L: 122,
658
+ S: this,
659
+ A: [
660
+ "this._replicators.has(replicator)",
661
+ ""
662
+ ]
663
+ });
664
+ await replicator.disconnect();
665
+ this._replicators.delete(replicator);
563
666
  }
564
- async flush() {
565
- await this._directory.flush();
667
+ async shouldAdvertise(peerId, params) {
668
+ const connection = this._connections.get(peerId);
669
+ if (!connection) {
670
+ return false;
671
+ }
672
+ return connection.connection.shouldAdvertise(params);
566
673
  }
567
- _getSpace(spaceKey) {
568
- if (this._metadata.identity?.haloSpace.key.equals(spaceKey)) {
569
- return this._metadata.identity.haloSpace;
674
+ shouldSyncCollection(peerId, params) {
675
+ const connection = this._connections.get(peerId);
676
+ if (!connection) {
677
+ return false;
570
678
  }
571
- const space = this.spaces.find((space2) => space2.key === spaceKey);
572
- invariant4(space, "Space not found", {
573
- F: __dxlog_file4,
574
- L: 232,
679
+ return connection.connection.shouldSyncCollection(params);
680
+ }
681
+ queryCollectionState(collectionId, targetId) {
682
+ const message = {
683
+ type: "collection-query",
684
+ senderId: this.peerId,
685
+ targetId,
686
+ collectionId
687
+ };
688
+ this.send(message);
689
+ }
690
+ sendCollectionState(collectionId, targetId, state) {
691
+ const message = {
692
+ type: "collection-state",
693
+ senderId: this.peerId,
694
+ targetId,
695
+ collectionId,
696
+ state
697
+ };
698
+ this.send(message);
699
+ }
700
+ // TODO(dmaretskyi): Remove.
701
+ getPeersInterestedInCollection(collectionId) {
702
+ return Array.from(this._connections.values()).map((connection) => {
703
+ return connection.connection.shouldSyncCollection({
704
+ collectionId
705
+ }) ? connection.connection.peerId : null;
706
+ }).filter(nonNullable);
707
+ }
708
+ _onConnectionOpen(connection) {
709
+ log2("Connection opened", {
710
+ peerId: connection.peerId
711
+ }, {
712
+ F: __dxlog_file3,
713
+ L: 178,
714
+ S: this,
715
+ C: (f, a) => f(...a)
716
+ });
717
+ invariant3(!this._connections.has(connection.peerId), void 0, {
718
+ F: __dxlog_file3,
719
+ L: 179,
575
720
  S: this,
576
721
  A: [
577
- "space",
578
- "'Space not found'"
722
+ "!this._connections.has(connection.peerId as PeerId)",
723
+ ""
579
724
  ]
580
725
  });
581
- return space;
726
+ const reader = connection.readable.getReader();
727
+ const writer = connection.writable.getWriter();
728
+ const connectionEntry = {
729
+ connection,
730
+ reader,
731
+ writer,
732
+ isOpen: true
733
+ };
734
+ this._connections.set(connection.peerId, connectionEntry);
735
+ queueMicrotask(async () => {
736
+ try {
737
+ while (true) {
738
+ const { done, value } = await reader.read();
739
+ if (done) {
740
+ break;
741
+ }
742
+ this._onMessage(value);
743
+ }
744
+ } catch (err) {
745
+ if (connectionEntry.isOpen) {
746
+ log2.catch(err, void 0, {
747
+ F: __dxlog_file3,
748
+ L: 198,
749
+ S: this,
750
+ C: (f, a) => f(...a)
751
+ });
752
+ }
753
+ }
754
+ });
755
+ log2("emit peer-candidate", {
756
+ peerId: connection.peerId
757
+ }, {
758
+ F: __dxlog_file3,
759
+ L: 203,
760
+ S: this,
761
+ C: (f, a) => f(...a)
762
+ });
763
+ this._emitPeerCandidate(connection);
582
764
  }
583
- _getLargeSpaceMetadata(key) {
584
- let entry = this._spaceLargeMetadata.get(key);
585
- if (entry) {
586
- return entry;
765
+ _onMessage(message) {
766
+ if (isCollectionQueryMessage(message)) {
767
+ this._params.onCollectionStateQueried(message.collectionId, message.senderId);
768
+ } else if (isCollectionStateMessage(message)) {
769
+ this._params.onCollectionStateReceived(message.collectionId, message.senderId, message.state);
770
+ } else {
771
+ this.emit("message", message);
587
772
  }
588
- entry = emptyLargeSpaceMetadata();
589
- this._spaceLargeMetadata.set(key, entry);
590
- return entry;
591
773
  }
592
774
  /**
593
- * Clears storage - doesn't work for now.
775
+ * Trigger doc-synchronizer shared documents set recalculation. Happens on peer-candidate.
776
+ * TODO(y): replace with a proper API call when sharePolicy update becomes supported by automerge-repo
594
777
  */
595
- async clear() {
596
- log3("clearing all metadata", void 0, {
597
- F: __dxlog_file4,
598
- L: 251,
778
+ _onConnectionAuthScopeChanged(connection) {
779
+ log2("Connection auth scope changed", {
780
+ peerId: connection.peerId
781
+ }, {
782
+ F: __dxlog_file3,
783
+ L: 222,
599
784
  S: this,
600
785
  C: (f, a) => f(...a)
601
786
  });
602
- await this._directory.delete();
603
- this._metadata = emptyEchoMetadata();
604
- }
605
- getIdentityRecord() {
606
- return this._metadata.identity;
607
- }
608
- async setIdentityRecord(record) {
609
- invariant4(!this._metadata.identity, "Cannot overwrite existing identity in metadata", {
610
- F: __dxlog_file4,
611
- L: 261,
787
+ const entry = this._connections.get(connection.peerId);
788
+ invariant3(entry, void 0, {
789
+ F: __dxlog_file3,
790
+ L: 224,
612
791
  S: this,
613
792
  A: [
614
- "!this._metadata.identity",
615
- "'Cannot overwrite existing identity in metadata'"
793
+ "entry",
794
+ ""
616
795
  ]
617
796
  });
618
- this._metadata.identity = record;
619
- await this._save();
620
- await this.flush();
621
- }
622
- getInvitations() {
623
- return this._metadata.invitations ?? [];
624
- }
625
- async addInvitation(invitation) {
626
- if (this._metadata.invitations?.find((i) => i.invitationId === invitation.invitationId)) {
627
- return;
628
- }
629
- (this._metadata.invitations ??= []).push(invitation);
630
- await this._save();
631
- await this.flush();
632
- }
633
- async removeInvitation(invitationId) {
634
- this._metadata.invitations = (this._metadata.invitations ?? []).filter((i) => i.invitationId !== invitationId);
635
- await this._save();
636
- await this.flush();
797
+ this.emit("peer-disconnected", {
798
+ peerId: connection.peerId
799
+ });
800
+ this._emitPeerCandidate(connection);
637
801
  }
638
- async addSpace(record) {
639
- invariant4(!(this._metadata.spaces ?? []).find((space) => space.key === record.key), "Cannot overwrite existing space in metadata", {
640
- F: __dxlog_file4,
641
- L: 289,
802
+ _onConnectionClosed(connection) {
803
+ log2("Connection closed", {
804
+ peerId: connection.peerId
805
+ }, {
806
+ F: __dxlog_file3,
807
+ L: 230,
808
+ S: this,
809
+ C: (f, a) => f(...a)
810
+ });
811
+ const entry = this._connections.get(connection.peerId);
812
+ invariant3(entry, void 0, {
813
+ F: __dxlog_file3,
814
+ L: 232,
642
815
  S: this,
643
816
  A: [
644
- "!(this._metadata.spaces ?? []).find((space) => space.key === record.key)",
645
- "'Cannot overwrite existing space in metadata'"
817
+ "entry",
818
+ ""
646
819
  ]
647
820
  });
648
- (this._metadata.spaces ??= []).push(record);
649
- await this._save();
650
- await this.flush();
651
- }
652
- async setSpaceDataLatestTimeframe(spaceKey, timeframe) {
653
- this._getSpace(spaceKey).dataTimeframe = timeframe;
654
- await this._save();
655
- }
656
- async setSpaceControlLatestTimeframe(spaceKey, timeframe) {
657
- this._getSpace(spaceKey).controlTimeframe = timeframe;
658
- await this._save();
659
- await this.flush();
660
- }
661
- async setCache(spaceKey, cache) {
662
- this._getSpace(spaceKey).cache = cache;
663
- await this._save();
664
- }
665
- async setWritableFeedKeys(spaceKey, controlFeedKey, dataFeedKey) {
666
- const space = this._getSpace(spaceKey);
667
- space.controlFeedKey = controlFeedKey;
668
- space.dataFeedKey = dataFeedKey;
669
- await this._save();
670
- await this.flush();
671
- }
672
- async setSpaceState(spaceKey, state) {
673
- this._getSpace(spaceKey).state = state;
674
- await this._save();
675
- await this.flush();
676
- }
677
- getSpaceControlPipelineSnapshot(spaceKey) {
678
- return this._getLargeSpaceMetadata(spaceKey).controlPipelineSnapshot;
821
+ entry.isOpen = false;
822
+ this.emit("peer-disconnected", {
823
+ peerId: connection.peerId
824
+ });
825
+ void entry.reader.cancel().catch((err) => log2.catch(err, void 0, {
826
+ F: __dxlog_file3,
827
+ L: 237,
828
+ S: this,
829
+ C: (f, a) => f(...a)
830
+ }));
831
+ void entry.writer.abort().catch((err) => log2.catch(err, void 0, {
832
+ F: __dxlog_file3,
833
+ L: 238,
834
+ S: this,
835
+ C: (f, a) => f(...a)
836
+ }));
837
+ this._connections.delete(connection.peerId);
679
838
  }
680
- async setSpaceControlPipelineSnapshot(spaceKey, snapshot) {
681
- this._getLargeSpaceMetadata(spaceKey).controlPipelineSnapshot = snapshot;
682
- await this._saveSpaceLargeMetadata(spaceKey);
683
- await this.flush();
839
+ _emitPeerCandidate(connection) {
840
+ this.emit("peer-candidate", {
841
+ peerId: connection.peerId,
842
+ peerMetadata: createEchoPeerMetadata()
843
+ });
684
844
  }
685
845
  };
686
846
  _ts_decorate([
687
847
  synchronized
688
- ], MetadataStore.prototype, "load", null);
848
+ ], EchoNetworkAdapter.prototype, "open", null);
689
849
  _ts_decorate([
690
850
  synchronized
691
- ], MetadataStore.prototype, "_save", null);
851
+ ], EchoNetworkAdapter.prototype, "close", null);
692
852
  _ts_decorate([
693
853
  synchronized
694
- ], MetadataStore.prototype, "_saveSpaceLargeMetadata", null);
695
- var fromBytesInt32 = (buf) => buf.readInt32LE(0);
696
- var hasInvitationExpired = (invitation) => {
697
- return Boolean(invitation.created && invitation.lifetime && invitation.lifetime !== 0 && invitation.created.getTime() + invitation.lifetime * 1e3 < Date.now());
698
- };
699
- var isLegacyInvitationFormat = (invitation) => {
700
- return invitation.type === Invitation.Type.MULTIUSE;
854
+ ], EchoNetworkAdapter.prototype, "addReplicator", null);
855
+ _ts_decorate([
856
+ synchronized
857
+ ], EchoNetworkAdapter.prototype, "removeReplicator", null);
858
+ var createEchoPeerMetadata = () => ({
859
+ // TODO(dmaretskyi): Refactor this.
860
+ dxos_peerSource: "EchoNetworkAdapter"
861
+ });
862
+ var isEchoPeerMetadata = (metadata) => metadata?.dxos_peerSource === "EchoNetworkAdapter";
863
+
864
+ // packages/core/echo/echo-pipeline/src/automerge/heads-store.ts
865
+ import { headsEncoding } from "@dxos/indexing";
866
+ var HeadsStore = class {
867
+ constructor({ db }) {
868
+ this._db = db;
869
+ }
870
+ setHeads(documentId, heads, batch) {
871
+ batch.put(documentId, heads, {
872
+ sublevel: this._db,
873
+ keyEncoding: "utf8",
874
+ valueEncoding: headsEncoding
875
+ });
876
+ }
877
+ // TODO(dmaretskyi): Make batched.
878
+ async getHeads(documentIds) {
879
+ return this._db.getMany(documentIds, {
880
+ keyEncoding: "utf8",
881
+ valueEncoding: headsEncoding
882
+ });
883
+ }
701
884
  };
702
885
 
703
- // packages/core/echo/echo-pipeline/src/pipeline/timeframe-clock.ts
704
- import { Event as Event2 } from "@dxos/async";
705
- import { timed } from "@dxos/debug";
706
- import { log as log4 } from "@dxos/log";
707
- import { Timeframe } from "@dxos/timeframe";
886
+ // packages/core/echo/echo-pipeline/src/automerge/automerge-host.ts
708
887
  function _ts_decorate2(decorators, target, key, desc) {
709
888
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
710
889
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
@@ -715,241 +894,848 @@ function _ts_decorate2(decorators, target, key, desc) {
715
894
  r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
716
895
  return c > 3 && r && Object.defineProperty(target, key, r), r;
717
896
  }
718
- var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/pipeline/timeframe-clock.ts";
719
- var mapTimeframeToFeedIndexes = (timeframe) => timeframe.frames().map(([feedKey, index]) => ({
720
- feedKey,
721
- index
722
- }));
723
- var mapFeedIndexesToTimeframe = (indexes) => new Timeframe(indexes.map(({ feedKey, index }) => [
724
- feedKey,
725
- index
726
- ]));
727
- var startAfter = (timeframe) => timeframe.frames().map(([feedKey, index]) => ({
728
- feedKey,
729
- index: index + 1
730
- }));
731
- var TimeframeClock = class {
732
- constructor(_timeframe = new Timeframe()) {
733
- this._timeframe = _timeframe;
734
- this.update = new Event2();
735
- this._pendingTimeframe = _timeframe;
897
+ var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/automerge/automerge-host.ts";
898
+ var AutomergeHost = class extends Resource4 {
899
+ constructor({ db, indexMetadataStore }) {
900
+ super();
901
+ this._echoNetworkAdapter = new EchoNetworkAdapter({
902
+ getContainingSpaceForDocument: this._getContainingSpaceForDocument.bind(this),
903
+ onCollectionStateQueried: this._onCollectionStateQueried.bind(this),
904
+ onCollectionStateReceived: this._onCollectionStateReceived.bind(this)
905
+ });
906
+ this._collectionSynchronizer = new CollectionSynchronizer({
907
+ queryCollectionState: this._queryCollectionState.bind(this),
908
+ sendCollectionState: this._sendCollectionState.bind(this),
909
+ shouldSyncCollection: this._shouldSyncCollection.bind(this)
910
+ });
911
+ this._db = db;
912
+ this._storage = new LevelDBStorageAdapter({
913
+ db: db.sublevel("automerge"),
914
+ callbacks: {
915
+ beforeSave: async (params) => this._beforeSave(params),
916
+ afterSave: async (key) => this._afterSave(key)
917
+ }
918
+ });
919
+ this._headsStore = new HeadsStore({
920
+ db: db.sublevel("heads")
921
+ });
922
+ this._indexMetadataStore = indexMetadataStore;
736
923
  }
737
- /**
738
- * Timeframe that was processed by ECHO.
739
- */
740
- get timeframe() {
741
- return this._timeframe;
924
+ async _open() {
925
+ this._peerId = `host-${PublicKey2.random().toHex()}`;
926
+ await this._storage.open?.();
927
+ this._repo = new Repo({
928
+ peerId: this._peerId,
929
+ sharePolicy: this._sharePolicy.bind(this),
930
+ storage: this._storage,
931
+ network: [
932
+ // Upstream swarm.
933
+ this._echoNetworkAdapter
934
+ ]
935
+ });
936
+ Event2.wrap(this._echoNetworkAdapter, "peer-candidate").on(this._ctx, (e) => this._onPeerConnected(e.peerId));
937
+ Event2.wrap(this._echoNetworkAdapter, "peer-disconnected").on(this._ctx, (e) => this._onPeerDisconnected(e.peerId));
938
+ this._collectionSynchronizer.remoteStateUpdated.on(this._ctx, ({ collectionId, peerId }) => {
939
+ this._onRemoteCollectionStateUpdated(collectionId, peerId);
940
+ });
941
+ await this._echoNetworkAdapter.open();
942
+ await this._collectionSynchronizer.open();
943
+ await this._echoNetworkAdapter.open();
944
+ await this._echoNetworkAdapter.whenConnected();
945
+ }
946
+ async _close() {
947
+ await this._collectionSynchronizer.close();
948
+ await this._storage.close?.();
949
+ await this._echoNetworkAdapter.close();
950
+ await this._ctx.dispose();
742
951
  }
743
952
  /**
744
- * Timeframe that is currently being processed by ECHO.
745
- * Will be equal to `timeframe` after the processing is complete.
953
+ * @deprecated To be abstracted away.
746
954
  */
747
- get pendingTimeframe() {
748
- return this._pendingTimeframe;
955
+ get repo() {
956
+ return this._repo;
749
957
  }
750
- setTimeframe(timeframe) {
751
- this._timeframe = timeframe;
752
- this._pendingTimeframe = timeframe;
753
- this.update.emit(this._timeframe);
958
+ get peerId() {
959
+ return this._peerId;
754
960
  }
755
- updatePendingTimeframe(key, seq) {
756
- this._pendingTimeframe = Timeframe.merge(this._pendingTimeframe, new Timeframe([
757
- [
758
- key,
759
- seq
760
- ]
761
- ]));
961
+ get loadedDocsCount() {
962
+ return Object.keys(this._repo.handles).length;
762
963
  }
763
- updateTimeframe() {
764
- this._timeframe = this._pendingTimeframe;
765
- this.update.emit(this._timeframe);
964
+ async addReplicator(replicator) {
965
+ await this._echoNetworkAdapter.addReplicator(replicator);
766
966
  }
767
- hasGaps(timeframe) {
768
- const gaps = Timeframe.dependencies(timeframe, this._timeframe);
769
- return !gaps.isEmpty();
967
+ async removeReplicator(replicator) {
968
+ await this._echoNetworkAdapter.removeReplicator(replicator);
770
969
  }
771
- async waitUntilReached(target) {
772
- log4("waitUntilReached", {
773
- target,
774
- current: this._timeframe
775
- }, {
776
- F: __dxlog_file5,
777
- L: 70,
778
- S: this,
779
- C: (f, a) => f(...a)
970
+ /**
971
+ * Loads the document handle from the repo and waits for it to be ready.
972
+ */
973
+ async loadDoc(ctx, documentId, opts) {
974
+ let handle;
975
+ if (typeof documentId === "string") {
976
+ handle = this._repo.handles[documentId];
977
+ }
978
+ if (!handle) {
979
+ handle = this._repo.find(documentId);
980
+ }
981
+ if (!handle.isReady()) {
982
+ if (!opts?.timeout) {
983
+ await cancelWithContext2(ctx, handle.whenReady());
984
+ } else {
985
+ await cancelWithContext2(ctx, asyncTimeout(handle.whenReady(), opts.timeout));
986
+ }
987
+ }
988
+ return handle;
989
+ }
990
+ /**
991
+ * Create new persisted document.
992
+ */
993
+ createDoc(initialValue, opts) {
994
+ if (opts?.preserveHistory) {
995
+ if (!isAutomerge(initialValue)) {
996
+ throw new TypeError("Initial value must be an Automerge document");
997
+ }
998
+ return this._repo.import(save(initialValue));
999
+ } else {
1000
+ return this._repo.create(initialValue);
1001
+ }
1002
+ }
1003
+ async waitUntilHeadsReplicated(heads) {
1004
+ const entries = heads.entries;
1005
+ if (!entries?.length) {
1006
+ return;
1007
+ }
1008
+ const documentIds = entries.map((entry) => entry.documentId);
1009
+ const documentHeads = await this.getHeads(documentIds);
1010
+ const headsToWait = entries.filter((entry, index) => {
1011
+ const targetHeads = entry.heads;
1012
+ if (!targetHeads || targetHeads.length === 0) {
1013
+ return false;
1014
+ }
1015
+ const currentHeads = documentHeads[index];
1016
+ return !(currentHeads !== null && headsEquals(currentHeads, targetHeads));
780
1017
  });
781
- await this.update.waitForCondition(() => {
782
- log4("check if reached", {
783
- target,
784
- current: this._timeframe,
785
- deps: Timeframe.dependencies(target, this._timeframe)
1018
+ if (headsToWait.length > 0) {
1019
+ await Promise.all(headsToWait.map(async (entry, index) => {
1020
+ const handle = await this.loadDoc(Context.default(void 0, {
1021
+ F: __dxlog_file4,
1022
+ L: 223
1023
+ }), entry.documentId);
1024
+ await waitForHeads(handle, entry.heads);
1025
+ }));
1026
+ }
1027
+ await this._repo.flush(heads.entries?.map((entry) => entry.documentId) ?? []);
1028
+ }
1029
+ async reIndexHeads(documentIds) {
1030
+ for (const documentId of documentIds) {
1031
+ log3.info("re-indexing heads for document", {
1032
+ documentId
786
1033
  }, {
787
- F: __dxlog_file5,
788
- L: 72,
1034
+ F: __dxlog_file4,
1035
+ L: 235,
789
1036
  S: this,
790
1037
  C: (f, a) => f(...a)
791
1038
  });
792
- return Timeframe.dependencies(target, this._timeframe).isEmpty();
793
- });
794
- }
795
- };
796
- _ts_decorate2([
797
- timed(5e3)
798
- ], TimeframeClock.prototype, "waitUntilReached", null);
799
-
800
- // packages/core/echo/echo-pipeline/src/pipeline/pipeline.ts
801
- import { Event as Event3, sleepWithContext, synchronized as synchronized2, Trigger } from "@dxos/async";
802
- import { Context as Context2, rejectOnDispose } from "@dxos/context";
803
- import { failUndefined } from "@dxos/debug";
804
- import { FeedSetIterator } from "@dxos/feed-store";
805
- import { invariant as invariant6 } from "@dxos/invariant";
806
- import { PublicKey as PublicKey3 } from "@dxos/keys";
807
- import { log as log6 } from "@dxos/log";
808
- import { Timeframe as Timeframe2 } from "@dxos/timeframe";
809
- import { ComplexMap as ComplexMap2 } from "@dxos/util";
810
-
811
- // packages/core/echo/echo-pipeline/src/pipeline/message-selector.ts
812
- import { invariant as invariant5 } from "@dxos/invariant";
813
- import { log as log5 } from "@dxos/log";
814
- var __dxlog_file6 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/pipeline/message-selector.ts";
815
- var createMessageSelector = (timeframeClock) => {
816
- return (messages) => {
817
- for (let i = 0; i < messages.length; i++) {
818
- const { data: { timeframe } } = messages[i];
819
- invariant5(timeframe, void 0, {
820
- F: __dxlog_file6,
821
- L: 25,
822
- S: void 0,
1039
+ const handle = this._repo.find(documentId);
1040
+ await handle.whenReady([
1041
+ "ready",
1042
+ "requesting"
1043
+ ]);
1044
+ if (handle.inState([
1045
+ "requesting"
1046
+ ])) {
1047
+ log3.warn("document is not available locally, skipping", {
1048
+ documentId
1049
+ }, {
1050
+ F: __dxlog_file4,
1051
+ L: 239,
1052
+ S: this,
1053
+ C: (f, a) => f(...a)
1054
+ });
1055
+ continue;
1056
+ }
1057
+ const doc = handle.docSync();
1058
+ invariant4(doc, void 0, {
1059
+ F: __dxlog_file4,
1060
+ L: 244,
1061
+ S: this,
823
1062
  A: [
824
- "timeframe",
1063
+ "doc",
825
1064
  ""
826
1065
  ]
827
1066
  });
828
- if (!timeframeClock.hasGaps(timeframe)) {
829
- return i;
830
- }
1067
+ const heads = getHeads(doc);
1068
+ const batch = this._db.batch();
1069
+ this._headsStore.setHeads(documentId, heads, batch);
1070
+ await batch.write();
831
1071
  }
832
- log5("Skipping...", void 0, {
833
- F: __dxlog_file6,
834
- L: 33,
835
- S: void 0,
1072
+ log3.info("done re-indexing heads", void 0, {
1073
+ F: __dxlog_file4,
1074
+ L: 251,
1075
+ S: this,
836
1076
  C: (f, a) => f(...a)
837
1077
  });
838
- };
839
- };
840
-
841
- // packages/core/echo/echo-pipeline/src/pipeline/pipeline.ts
842
- function _ts_decorate3(decorators, target, key, desc) {
843
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
844
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
845
- r = Reflect.decorate(decorators, target, key, desc);
846
- else
847
- for (var i = decorators.length - 1; i >= 0; i--)
848
- if (d = decorators[i])
849
- r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
850
- return c > 3 && r && Object.defineProperty(target, key, r), r;
851
- }
852
- var __dxlog_file7 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/pipeline/pipeline.ts";
853
- var PipelineState = class {
854
- constructor(_feeds, _timeframeClock) {
855
- this._feeds = _feeds;
856
- this._timeframeClock = _timeframeClock;
857
- this._ctx = new Context2(void 0, {
858
- F: __dxlog_file7,
859
- L: 41
860
- });
861
- this.timeframeUpdate = this._timeframeClock.update;
862
- this.stalled = new Event3();
863
- this._startTimeframe = new Timeframe2();
864
- this._reachedTarget = false;
1078
+ }
1079
+ // TODO(dmaretskyi): Share based on HALO permissions and space affinity.
1080
+ // Hosts, running in the worker, don't share documents unless requested by other peers.
1081
+ // NOTE: If both peers return sharePolicy=false the replication will not happen
1082
+ // https://github.com/automerge/automerge-repo/pull/292
1083
+ async _sharePolicy(peerId, documentId) {
1084
+ if (peerId.startsWith("client-")) {
1085
+ return false;
1086
+ }
1087
+ if (!documentId) {
1088
+ return false;
1089
+ }
1090
+ const peerMetadata = this.repo.peerMetadataByPeerId[peerId];
1091
+ if (isEchoPeerMetadata(peerMetadata)) {
1092
+ return this._echoNetworkAdapter.shouldAdvertise(peerId, {
1093
+ documentId
1094
+ });
1095
+ }
1096
+ return false;
1097
+ }
1098
+ async _beforeSave({ path, batch }) {
1099
+ const handle = this._repo.handles[path[0]];
1100
+ if (!handle) {
1101
+ return;
1102
+ }
1103
+ const doc = handle.docSync();
1104
+ if (!doc) {
1105
+ return;
1106
+ }
1107
+ const heads = getHeads(doc);
1108
+ this._headsStore.setHeads(handle.documentId, heads, batch);
1109
+ const spaceKey = getSpaceKeyFromDoc(doc) ?? void 0;
1110
+ const objectIds = Object.keys(doc.objects ?? {});
1111
+ const encodedIds = objectIds.map((objectId) => objectPointerCodec.encode({
1112
+ documentId: handle.documentId,
1113
+ objectId,
1114
+ spaceKey
1115
+ }));
1116
+ const idToLastHash = new Map(encodedIds.map((id) => [
1117
+ id,
1118
+ heads
1119
+ ]));
1120
+ this._indexMetadataStore.markDirty(idToLastHash, batch);
1121
+ }
1122
+ _shouldSyncCollection(collectionId, peerId) {
1123
+ const peerMetadata = this._repo.peerMetadataByPeerId[peerId];
1124
+ if (isEchoPeerMetadata(peerMetadata)) {
1125
+ return this._echoNetworkAdapter.shouldSyncCollection(peerId, {
1126
+ collectionId
1127
+ });
1128
+ }
1129
+ return false;
865
1130
  }
866
1131
  /**
867
- * Latest theoretical timeframe based on the last mutation in each feed.
868
- * NOTE: This might never be reached if the mutation dependencies
1132
+ * Called by AutomergeStorageAdapter after levelDB batch commit.
869
1133
  */
870
- // TODO(dmaretskyi): Rename `totalTimeframe`? or `lastTimeframe`.
871
- get endTimeframe() {
872
- return mapFeedIndexesToTimeframe(Array.from(this._feeds.values()).filter((feed) => feed.length > 0).map((feed) => ({
873
- feedKey: feed.key,
874
- index: feed.length - 1
875
- })));
1134
+ async _afterSave(path) {
1135
+ this._indexMetadataStore.notifyMarkedDirty();
1136
+ const documentId = path[0];
1137
+ const document = this._repo.handles[documentId]?.docSync();
1138
+ if (document) {
1139
+ const heads = getHeads(document);
1140
+ this._onHeadsChanged(documentId, heads);
1141
+ }
876
1142
  }
877
- get startTimeframe() {
878
- return this._startTimeframe;
1143
+ _automergeDocs() {
1144
+ return mapValues(this._repo.handles, (handle) => ({
1145
+ state: handle.state,
1146
+ hasDoc: !!handle.docSync(),
1147
+ heads: handle.docSync() ? automerge.getHeads(handle.docSync()) : null,
1148
+ data: handle.docSync() && mapValues(handle.docSync(), (value, key) => {
1149
+ try {
1150
+ switch (key) {
1151
+ case "access":
1152
+ case "links":
1153
+ return value;
1154
+ case "objects":
1155
+ return Object.keys(value);
1156
+ default:
1157
+ return `${value}`;
1158
+ }
1159
+ } catch (err) {
1160
+ return `${err}`;
1161
+ }
1162
+ })
1163
+ }));
879
1164
  }
880
- get timeframe() {
881
- return this._timeframeClock.timeframe;
1165
+ _automergePeers() {
1166
+ return this._repo.peers;
882
1167
  }
883
- get pendingTimeframe() {
884
- return this._timeframeClock.pendingTimeframe;
1168
+ async _getContainingSpaceForDocument(documentId) {
1169
+ const doc = this._repo.handles[documentId]?.docSync();
1170
+ if (!doc) {
1171
+ return null;
1172
+ }
1173
+ const spaceKeyHex = getSpaceKeyFromDoc(doc);
1174
+ if (!spaceKeyHex) {
1175
+ return null;
1176
+ }
1177
+ return PublicKey2.from(spaceKeyHex);
885
1178
  }
886
- get targetTimeframe() {
887
- return this._targetTimeframe ? this._targetTimeframe : new Timeframe2();
1179
+ /**
1180
+ * Flush documents to disk.
1181
+ */
1182
+ async flush({ documentIds } = {}) {
1183
+ await this._repo.flush(documentIds);
888
1184
  }
889
- get reachedTarget() {
890
- return this._reachedTarget;
1185
+ async getHeads(documentIds) {
1186
+ const result = [];
1187
+ const storeRequestIds = [];
1188
+ const storeResultIndices = [];
1189
+ for (const documentId of documentIds) {
1190
+ const doc = this._repo.handles[documentId]?.docSync();
1191
+ if (doc) {
1192
+ result.push(getHeads(doc));
1193
+ } else {
1194
+ storeRequestIds.push(documentId);
1195
+ storeResultIndices.push(result.length);
1196
+ result.push(void 0);
1197
+ }
1198
+ }
1199
+ if (storeRequestIds.length > 0) {
1200
+ const storedHeads = await this._headsStore.getHeads(storeRequestIds);
1201
+ for (let i = 0; i < storedHeads.length; i++) {
1202
+ result[storeResultIndices[i]] = storedHeads[i];
1203
+ }
1204
+ }
1205
+ return result;
891
1206
  }
892
- get feeds() {
893
- return Array.from(this._feeds.values());
1207
+ //
1208
+ // Collection sync.
1209
+ //
1210
+ getLocalCollectionState(collectionId) {
1211
+ return this._collectionSynchronizer.getLocalCollectionState(collectionId);
894
1212
  }
895
- async waitUntilTimeframe(target) {
896
- await this._timeframeClock.waitUntilReached(target);
1213
+ getRemoteCollectionStates(collectionId) {
1214
+ return this._collectionSynchronizer.getRemoteCollectionStates(collectionId);
897
1215
  }
898
- setTargetTimeframe(target) {
899
- this._targetTimeframe = target;
1216
+ refreshCollection(collectionId) {
1217
+ this._collectionSynchronizer.refreshCollection(collectionId);
1218
+ }
1219
+ async getCollectionSyncState(collectionId) {
1220
+ const result = {
1221
+ peers: []
1222
+ };
1223
+ const localState = this.getLocalCollectionState(collectionId);
1224
+ const remoteState = this.getRemoteCollectionStates(collectionId);
1225
+ if (!localState) {
1226
+ return result;
1227
+ }
1228
+ for (const [peerId, state] of remoteState) {
1229
+ const diff = diffCollectionState(localState, state);
1230
+ result.peers.push({
1231
+ peerId,
1232
+ differentDocuments: diff.different.length
1233
+ });
1234
+ }
1235
+ return result;
900
1236
  }
901
1237
  /**
902
- * Wait until the pipeline processes all messages in the feed and reaches the target timeframe if that is set.
903
- *
904
- * This function will resolve immediately if the pipeline is stalled.
905
- *
906
- * @param timeout Timeout in milliseconds to specify the maximum wait time.
1238
+ * Update the local collection state based on the locally stored document heads.
907
1239
  */
908
- async waitUntilReachedTargetTimeframe({ ctx = new Context2(void 0, {
909
- F: __dxlog_file7,
910
- L: 129
911
- }), timeout, breakOnStall = true } = {}) {
912
- log6("waitUntilReachedTargetTimeframe", {
913
- timeout,
914
- current: this.timeframe,
915
- target: this.targetTimeframe
1240
+ async updateLocalCollectionState(collectionId, documentIds) {
1241
+ const heads = await this.getHeads(documentIds);
1242
+ const documents = Object.fromEntries(heads.map((heads2, index) => [
1243
+ documentIds[index],
1244
+ heads2 ?? []
1245
+ ]));
1246
+ this._collectionSynchronizer.setLocalCollectionState(collectionId, {
1247
+ documents
1248
+ });
1249
+ }
1250
+ _onCollectionStateQueried(collectionId, peerId) {
1251
+ this._collectionSynchronizer.onCollectionStateQueried(collectionId, peerId);
1252
+ }
1253
+ _onCollectionStateReceived(collectionId, peerId, state) {
1254
+ this._collectionSynchronizer.onRemoteStateReceived(collectionId, peerId, decodeCollectionState(state));
1255
+ }
1256
+ _queryCollectionState(collectionId, peerId) {
1257
+ this._echoNetworkAdapter.queryCollectionState(collectionId, peerId);
1258
+ }
1259
+ _sendCollectionState(collectionId, peerId, state) {
1260
+ this._echoNetworkAdapter.sendCollectionState(collectionId, peerId, encodeCollectionState(state));
1261
+ }
1262
+ _onPeerConnected(peerId) {
1263
+ this._collectionSynchronizer.onConnectionOpen(peerId);
1264
+ }
1265
+ _onPeerDisconnected(peerId) {
1266
+ this._collectionSynchronizer.onConnectionClosed(peerId);
1267
+ }
1268
+ _onRemoteCollectionStateUpdated(collectionId, peerId) {
1269
+ const localState = this._collectionSynchronizer.getLocalCollectionState(collectionId);
1270
+ const remoteState = this._collectionSynchronizer.getRemoteCollectionStates(collectionId).get(peerId);
1271
+ if (!localState || !remoteState) {
1272
+ return;
1273
+ }
1274
+ const { different } = diffCollectionState(localState, remoteState);
1275
+ if (different.length === 0) {
1276
+ return;
1277
+ }
1278
+ log3.info("replication documents after collection sync", {
1279
+ count: different.length
916
1280
  }, {
917
- F: __dxlog_file7,
918
- L: 133,
1281
+ F: __dxlog_file4,
1282
+ L: 486,
919
1283
  S: this,
920
1284
  C: (f, a) => f(...a)
921
1285
  });
922
- this._reachedTargetPromise ??= Promise.race([
923
- this._timeframeClock.update.waitForCondition(() => {
924
- return Timeframe2.dependencies(this.targetTimeframe, this.timeframe).isEmpty();
925
- }),
926
- ...breakOnStall ? [
927
- this.stalled.discardParameter().waitForCount(1)
928
- ] : []
929
- ]);
930
- let done = false;
931
- if (timeout) {
932
- return Promise.race([
933
- rejectOnDispose(ctx),
934
- rejectOnDispose(this._ctx),
935
- this._reachedTargetPromise.then(() => {
936
- done = true;
937
- this._reachedTarget = true;
938
- }),
939
- sleepWithContext(this._ctx, timeout).then(() => {
940
- if (done) {
941
- return;
942
- }
943
- log6.warn("waitUntilReachedTargetTimeframe timed out", {
944
- timeout,
945
- current: this.timeframe,
946
- target: this.targetTimeframe,
947
- dependencies: Timeframe2.dependencies(this.targetTimeframe, this.timeframe)
948
- }, {
949
- F: __dxlog_file7,
950
- L: 161,
951
- S: this,
952
- C: (f, a) => f(...a)
1286
+ for (const documentId of different) {
1287
+ this._repo.find(documentId);
1288
+ }
1289
+ }
1290
+ _onHeadsChanged(documentId, heads) {
1291
+ for (const collectionId of this._collectionSynchronizer.getRegisteredCollectionIds()) {
1292
+ const state = this._collectionSynchronizer.getLocalCollectionState(collectionId);
1293
+ if (state?.documents[documentId]) {
1294
+ const newState = structuredClone(state);
1295
+ newState.documents[documentId] = heads;
1296
+ this._collectionSynchronizer.setLocalCollectionState(collectionId, newState);
1297
+ }
1298
+ }
1299
+ }
1300
+ };
1301
+ _ts_decorate2([
1302
+ trace.info()
1303
+ ], AutomergeHost.prototype, "_peerId", void 0);
1304
+ _ts_decorate2([
1305
+ trace.info({
1306
+ depth: null
1307
+ })
1308
+ ], AutomergeHost.prototype, "_automergeDocs", null);
1309
+ _ts_decorate2([
1310
+ trace.info({
1311
+ depth: null
1312
+ })
1313
+ ], AutomergeHost.prototype, "_automergePeers", null);
1314
+ _ts_decorate2([
1315
+ trace.span({
1316
+ showInBrowserTimeline: true
1317
+ })
1318
+ ], AutomergeHost.prototype, "flush", null);
1319
+ AutomergeHost = _ts_decorate2([
1320
+ trace.resource()
1321
+ ], AutomergeHost);
1322
+ var getSpaceKeyFromDoc = (doc) => {
1323
+ const rawSpaceKey = doc.access?.spaceKey ?? doc.experimental_spaceKey;
1324
+ if (rawSpaceKey == null) {
1325
+ return null;
1326
+ }
1327
+ return String(rawSpaceKey);
1328
+ };
1329
+ var waitForHeads = async (handle, heads) => {
1330
+ const unavailableHeads = new Set(heads);
1331
+ await handle.whenReady();
1332
+ await Event2.wrap(handle, "change").waitForCondition(() => {
1333
+ for (const changeHash of unavailableHeads.values()) {
1334
+ if (changeIsPresentInDoc(handle.docSync(), changeHash)) {
1335
+ unavailableHeads.delete(changeHash);
1336
+ }
1337
+ }
1338
+ return unavailableHeads.size === 0;
1339
+ });
1340
+ };
1341
+ var changeIsPresentInDoc = (doc, changeHash) => {
1342
+ return !!getBackend(doc).getChangeByHash(changeHash);
1343
+ };
1344
+ var decodeCollectionState = (state) => {
1345
+ invariant4(typeof state === "object" && state !== null, "Invalid state", {
1346
+ F: __dxlog_file4,
1347
+ L: 539,
1348
+ S: void 0,
1349
+ A: [
1350
+ "typeof state === 'object' && state !== null",
1351
+ "'Invalid state'"
1352
+ ]
1353
+ });
1354
+ return state;
1355
+ };
1356
+ var encodeCollectionState = (state) => {
1357
+ return state;
1358
+ };
1359
+
1360
+ // packages/core/echo/echo-pipeline/src/automerge/space-collection.ts
1361
+ import { invariant as invariant5 } from "@dxos/invariant";
1362
+ import { SpaceId } from "@dxos/keys";
1363
+ var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/automerge/space-collection.ts";
1364
+ var deriveCollectionIdFromSpaceId = (spaceId) => `space:${spaceId}`;
1365
+ var getSpaceIdFromCollectionId = (collectionId) => {
1366
+ const spaceId = collectionId.replace(/^space:/, "");
1367
+ invariant5(SpaceId.isValid(spaceId), void 0, {
1368
+ F: __dxlog_file5,
1369
+ L: 12,
1370
+ S: void 0,
1371
+ A: [
1372
+ "SpaceId.isValid(spaceId)",
1373
+ ""
1374
+ ]
1375
+ });
1376
+ return spaceId;
1377
+ };
1378
+
1379
+ // packages/core/echo/echo-pipeline/src/space/auth.ts
1380
+ import { runInContext, scheduleTask as scheduleTask2 } from "@dxos/async";
1381
+ import { Context as Context2 } from "@dxos/context";
1382
+ import { randomBytes } from "@dxos/crypto";
1383
+ import { invariant as invariant6 } from "@dxos/invariant";
1384
+ import { log as log4 } from "@dxos/log";
1385
+ import { schema as schema4 } from "@dxos/protocols";
1386
+ import { RpcExtension } from "@dxos/teleport";
1387
+ var __dxlog_file6 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/space/auth.ts";
1388
+ var AuthExtension = class extends RpcExtension {
1389
+ constructor(_authParams) {
1390
+ super({
1391
+ requested: {
1392
+ AuthService: schema4.getService("dxos.mesh.teleport.auth.AuthService")
1393
+ },
1394
+ exposed: {
1395
+ AuthService: schema4.getService("dxos.mesh.teleport.auth.AuthService")
1396
+ },
1397
+ timeout: 60 * 1e3
1398
+ });
1399
+ this._authParams = _authParams;
1400
+ this._ctx = new Context2({
1401
+ onError: (err) => {
1402
+ log4.catch(err, void 0, {
1403
+ F: __dxlog_file6,
1404
+ L: 28,
1405
+ S: this,
1406
+ C: (f, a) => f(...a)
1407
+ });
1408
+ }
1409
+ }, {
1410
+ F: __dxlog_file6,
1411
+ L: 26
1412
+ });
1413
+ }
1414
+ async getHandlers() {
1415
+ return {
1416
+ AuthService: {
1417
+ authenticate: async ({ challenge }) => {
1418
+ try {
1419
+ const credential = await this._authParams.provider(challenge);
1420
+ if (!credential) {
1421
+ throw new Error("auth rejected");
1422
+ }
1423
+ return {
1424
+ credential
1425
+ };
1426
+ } catch (err) {
1427
+ log4.error("failed to generate auth credentials", err, {
1428
+ F: __dxlog_file6,
1429
+ L: 55,
1430
+ S: this,
1431
+ C: (f, a) => f(...a)
1432
+ });
1433
+ throw new Error("auth rejected");
1434
+ }
1435
+ }
1436
+ }
1437
+ };
1438
+ }
1439
+ async onOpen(context) {
1440
+ await super.onOpen(context);
1441
+ scheduleTask2(this._ctx, async () => {
1442
+ try {
1443
+ const challenge = randomBytes(32);
1444
+ const { credential } = await this.rpc.AuthService.authenticate({
1445
+ challenge
1446
+ });
1447
+ invariant6(credential?.length > 0, "invalid credential", {
1448
+ F: __dxlog_file6,
1449
+ L: 69,
1450
+ S: this,
1451
+ A: [
1452
+ "credential?.length > 0",
1453
+ "'invalid credential'"
1454
+ ]
1455
+ });
1456
+ const success = await this._authParams.verifier(challenge, credential);
1457
+ invariant6(success, "credential not verified", {
1458
+ F: __dxlog_file6,
1459
+ L: 71,
1460
+ S: this,
1461
+ A: [
1462
+ "success",
1463
+ "'credential not verified'"
1464
+ ]
1465
+ });
1466
+ runInContext(this._ctx, () => this._authParams.onAuthSuccess());
1467
+ } catch (err) {
1468
+ log4("auth failed", err, {
1469
+ F: __dxlog_file6,
1470
+ L: 74,
1471
+ S: this,
1472
+ C: (f, a) => f(...a)
1473
+ });
1474
+ this.close();
1475
+ this._authParams.onAuthFailure();
1476
+ }
1477
+ });
1478
+ }
1479
+ async onClose() {
1480
+ await this._ctx.dispose();
1481
+ await super.onClose();
1482
+ }
1483
+ async onAbort() {
1484
+ await this._ctx.dispose();
1485
+ await super.onAbort();
1486
+ }
1487
+ };
1488
+
1489
+ // packages/core/echo/echo-pipeline/src/pipeline/timeframe-clock.ts
1490
+ import { Event as Event3 } from "@dxos/async";
1491
+ import { timed } from "@dxos/debug";
1492
+ import { log as log5 } from "@dxos/log";
1493
+ import { Timeframe } from "@dxos/timeframe";
1494
+ function _ts_decorate3(decorators, target, key, desc) {
1495
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1496
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
1497
+ r = Reflect.decorate(decorators, target, key, desc);
1498
+ else
1499
+ for (var i = decorators.length - 1; i >= 0; i--)
1500
+ if (d = decorators[i])
1501
+ r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1502
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
1503
+ }
1504
+ var __dxlog_file7 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/pipeline/timeframe-clock.ts";
1505
+ var mapTimeframeToFeedIndexes = (timeframe) => timeframe.frames().map(([feedKey, index]) => ({
1506
+ feedKey,
1507
+ index
1508
+ }));
1509
+ var mapFeedIndexesToTimeframe = (indexes) => new Timeframe(indexes.map(({ feedKey, index }) => [
1510
+ feedKey,
1511
+ index
1512
+ ]));
1513
+ var startAfter = (timeframe) => timeframe.frames().map(([feedKey, index]) => ({
1514
+ feedKey,
1515
+ index: index + 1
1516
+ }));
1517
+ var TimeframeClock = class {
1518
+ constructor(_timeframe = new Timeframe()) {
1519
+ this._timeframe = _timeframe;
1520
+ this.update = new Event3();
1521
+ this._pendingTimeframe = _timeframe;
1522
+ }
1523
+ /**
1524
+ * Timeframe that was processed by ECHO.
1525
+ */
1526
+ get timeframe() {
1527
+ return this._timeframe;
1528
+ }
1529
+ /**
1530
+ * Timeframe that is currently being processed by ECHO.
1531
+ * Will be equal to `timeframe` after the processing is complete.
1532
+ */
1533
+ get pendingTimeframe() {
1534
+ return this._pendingTimeframe;
1535
+ }
1536
+ setTimeframe(timeframe) {
1537
+ this._timeframe = timeframe;
1538
+ this._pendingTimeframe = timeframe;
1539
+ this.update.emit(this._timeframe);
1540
+ }
1541
+ updatePendingTimeframe(key, seq) {
1542
+ this._pendingTimeframe = Timeframe.merge(this._pendingTimeframe, new Timeframe([
1543
+ [
1544
+ key,
1545
+ seq
1546
+ ]
1547
+ ]));
1548
+ }
1549
+ updateTimeframe() {
1550
+ this._timeframe = this._pendingTimeframe;
1551
+ this.update.emit(this._timeframe);
1552
+ }
1553
+ hasGaps(timeframe) {
1554
+ const gaps = Timeframe.dependencies(timeframe, this._timeframe);
1555
+ return !gaps.isEmpty();
1556
+ }
1557
+ async waitUntilReached(target) {
1558
+ log5("waitUntilReached", {
1559
+ target,
1560
+ current: this._timeframe
1561
+ }, {
1562
+ F: __dxlog_file7,
1563
+ L: 70,
1564
+ S: this,
1565
+ C: (f, a) => f(...a)
1566
+ });
1567
+ await this.update.waitForCondition(() => {
1568
+ log5("check if reached", {
1569
+ target,
1570
+ current: this._timeframe,
1571
+ deps: Timeframe.dependencies(target, this._timeframe)
1572
+ }, {
1573
+ F: __dxlog_file7,
1574
+ L: 72,
1575
+ S: this,
1576
+ C: (f, a) => f(...a)
1577
+ });
1578
+ return Timeframe.dependencies(target, this._timeframe).isEmpty();
1579
+ });
1580
+ }
1581
+ };
1582
+ _ts_decorate3([
1583
+ timed(5e3)
1584
+ ], TimeframeClock.prototype, "waitUntilReached", null);
1585
+
1586
+ // packages/core/echo/echo-pipeline/src/pipeline/pipeline.ts
1587
+ import { Event as Event4, sleepWithContext, synchronized as synchronized2, Trigger as Trigger2 } from "@dxos/async";
1588
+ import { Context as Context3, rejectOnDispose } from "@dxos/context";
1589
+ import { failUndefined } from "@dxos/debug";
1590
+ import { FeedSetIterator } from "@dxos/feed-store";
1591
+ import { invariant as invariant8 } from "@dxos/invariant";
1592
+ import { PublicKey as PublicKey3 } from "@dxos/keys";
1593
+ import { log as log7 } from "@dxos/log";
1594
+ import { Timeframe as Timeframe2 } from "@dxos/timeframe";
1595
+ import { ComplexMap } from "@dxos/util";
1596
+
1597
+ // packages/core/echo/echo-pipeline/src/pipeline/message-selector.ts
1598
+ import { invariant as invariant7 } from "@dxos/invariant";
1599
+ import { log as log6 } from "@dxos/log";
1600
+ var __dxlog_file8 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/pipeline/message-selector.ts";
1601
+ var createMessageSelector = (timeframeClock) => {
1602
+ return (messages) => {
1603
+ for (let i = 0; i < messages.length; i++) {
1604
+ const { data: { timeframe } } = messages[i];
1605
+ invariant7(timeframe, void 0, {
1606
+ F: __dxlog_file8,
1607
+ L: 25,
1608
+ S: void 0,
1609
+ A: [
1610
+ "timeframe",
1611
+ ""
1612
+ ]
1613
+ });
1614
+ if (!timeframeClock.hasGaps(timeframe)) {
1615
+ return i;
1616
+ }
1617
+ }
1618
+ log6("Skipping...", void 0, {
1619
+ F: __dxlog_file8,
1620
+ L: 33,
1621
+ S: void 0,
1622
+ C: (f, a) => f(...a)
1623
+ });
1624
+ };
1625
+ };
1626
+
1627
+ // packages/core/echo/echo-pipeline/src/pipeline/pipeline.ts
1628
+ function _ts_decorate4(decorators, target, key, desc) {
1629
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1630
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
1631
+ r = Reflect.decorate(decorators, target, key, desc);
1632
+ else
1633
+ for (var i = decorators.length - 1; i >= 0; i--)
1634
+ if (d = decorators[i])
1635
+ r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1636
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
1637
+ }
1638
+ var __dxlog_file9 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/pipeline/pipeline.ts";
1639
+ var PipelineState = class {
1640
+ constructor(_feeds, _timeframeClock) {
1641
+ this._feeds = _feeds;
1642
+ this._timeframeClock = _timeframeClock;
1643
+ this._ctx = new Context3(void 0, {
1644
+ F: __dxlog_file9,
1645
+ L: 41
1646
+ });
1647
+ this.timeframeUpdate = this._timeframeClock.update;
1648
+ this.stalled = new Event4();
1649
+ this._startTimeframe = new Timeframe2();
1650
+ this._reachedTarget = false;
1651
+ }
1652
+ /**
1653
+ * Latest theoretical timeframe based on the last mutation in each feed.
1654
+ * NOTE: This might never be reached if the mutation dependencies
1655
+ */
1656
+ // TODO(dmaretskyi): Rename `totalTimeframe`? or `lastTimeframe`.
1657
+ get endTimeframe() {
1658
+ return mapFeedIndexesToTimeframe(Array.from(this._feeds.values()).filter((feed) => feed.length > 0).map((feed) => ({
1659
+ feedKey: feed.key,
1660
+ index: feed.length - 1
1661
+ })));
1662
+ }
1663
+ get startTimeframe() {
1664
+ return this._startTimeframe;
1665
+ }
1666
+ get timeframe() {
1667
+ return this._timeframeClock.timeframe;
1668
+ }
1669
+ get pendingTimeframe() {
1670
+ return this._timeframeClock.pendingTimeframe;
1671
+ }
1672
+ get targetTimeframe() {
1673
+ return this._targetTimeframe ? this._targetTimeframe : new Timeframe2();
1674
+ }
1675
+ get reachedTarget() {
1676
+ return this._reachedTarget;
1677
+ }
1678
+ get feeds() {
1679
+ return Array.from(this._feeds.values());
1680
+ }
1681
+ async waitUntilTimeframe(target) {
1682
+ await this._timeframeClock.waitUntilReached(target);
1683
+ }
1684
+ setTargetTimeframe(target) {
1685
+ this._targetTimeframe = target;
1686
+ }
1687
+ /**
1688
+ * Wait until the pipeline processes all messages in the feed and reaches the target timeframe if that is set.
1689
+ *
1690
+ * This function will resolve immediately if the pipeline is stalled.
1691
+ *
1692
+ * @param timeout Timeout in milliseconds to specify the maximum wait time.
1693
+ */
1694
+ async waitUntilReachedTargetTimeframe({ ctx = new Context3(void 0, {
1695
+ F: __dxlog_file9,
1696
+ L: 129
1697
+ }), timeout, breakOnStall = true } = {}) {
1698
+ log7("waitUntilReachedTargetTimeframe", {
1699
+ timeout,
1700
+ current: this.timeframe,
1701
+ target: this.targetTimeframe
1702
+ }, {
1703
+ F: __dxlog_file9,
1704
+ L: 133,
1705
+ S: this,
1706
+ C: (f, a) => f(...a)
1707
+ });
1708
+ this._reachedTargetPromise ??= Promise.race([
1709
+ this._timeframeClock.update.waitForCondition(() => {
1710
+ return Timeframe2.dependencies(this.targetTimeframe, this.timeframe).isEmpty();
1711
+ }),
1712
+ ...breakOnStall ? [
1713
+ this.stalled.discardParameter().waitForCount(1)
1714
+ ] : []
1715
+ ]);
1716
+ let done = false;
1717
+ if (timeout) {
1718
+ return Promise.race([
1719
+ rejectOnDispose(ctx),
1720
+ rejectOnDispose(this._ctx),
1721
+ this._reachedTargetPromise.then(() => {
1722
+ done = true;
1723
+ this._reachedTarget = true;
1724
+ }),
1725
+ sleepWithContext(this._ctx, timeout).then(() => {
1726
+ if (done) {
1727
+ return;
1728
+ }
1729
+ log7.warn("waitUntilReachedTargetTimeframe timed out", {
1730
+ timeout,
1731
+ current: this.timeframe,
1732
+ target: this.targetTimeframe,
1733
+ dependencies: Timeframe2.dependencies(this.targetTimeframe, this.timeframe)
1734
+ }, {
1735
+ F: __dxlog_file9,
1736
+ L: 161,
1737
+ S: this,
1738
+ C: (f, a) => f(...a)
953
1739
  });
954
1740
  })
955
1741
  ]);
@@ -961,14 +1747,14 @@ var PipelineState = class {
961
1747
  var Pipeline = class {
962
1748
  constructor() {
963
1749
  this._timeframeClock = new TimeframeClock(new Timeframe2());
964
- this._feeds = new ComplexMap2(PublicKey3.hash);
1750
+ this._feeds = new ComplexMap(PublicKey3.hash);
965
1751
  // External state accessor.
966
1752
  this._state = new PipelineState(this._feeds, this._timeframeClock);
967
1753
  // Waits for the message consumer to process the message and yield control back to the pipeline.
968
- this._processingTrigger = new Trigger().wake();
969
- this._pauseTrigger = new Trigger().wake();
1754
+ this._processingTrigger = new Trigger2().wake();
1755
+ this._pauseTrigger = new Trigger2().wake();
970
1756
  // Pending downloads.
971
- this._downloads = new ComplexMap2((value) => PublicKey3.hash(value.key));
1757
+ this._downloads = new ComplexMap((value) => PublicKey3.hash(value.key));
972
1758
  this._isStopping = false;
973
1759
  this._isStarted = false;
974
1760
  this._isBeingConsumed = false;
@@ -978,8 +1764,8 @@ var Pipeline = class {
978
1764
  return this._state;
979
1765
  }
980
1766
  get writer() {
981
- invariant6(this._writer, "Writer not set.", {
982
- F: __dxlog_file7,
1767
+ invariant8(this._writer, "Writer not set.", {
1768
+ F: __dxlog_file9,
983
1769
  L: 243,
984
1770
  S: this,
985
1771
  A: [
@@ -1007,8 +1793,8 @@ var Pipeline = class {
1007
1793
  }
1008
1794
  }
1009
1795
  setWriteFeed(feed) {
1010
- invariant6(!this._writer, "Writer already set.", {
1011
- F: __dxlog_file7,
1796
+ invariant8(!this._writer, "Writer already set.", {
1797
+ F: __dxlog_file9,
1012
1798
  L: 270,
1013
1799
  S: this,
1014
1800
  A: [
@@ -1016,8 +1802,8 @@ var Pipeline = class {
1016
1802
  "'Writer already set.'"
1017
1803
  ]
1018
1804
  });
1019
- invariant6(feed.properties.writable, "Feed must be writable.", {
1020
- F: __dxlog_file7,
1805
+ invariant8(feed.properties.writable, "Feed must be writable.", {
1806
+ F: __dxlog_file9,
1021
1807
  L: 271,
1022
1808
  S: this,
1023
1809
  A: [
@@ -1031,8 +1817,8 @@ var Pipeline = class {
1031
1817
  }), feed.createFeedWriter());
1032
1818
  }
1033
1819
  async start() {
1034
- invariant6(!this._isStarted, "Pipeline is already started.", {
1035
- F: __dxlog_file7,
1820
+ invariant8(!this._isStarted, "Pipeline is already started.", {
1821
+ F: __dxlog_file9,
1036
1822
  L: 284,
1037
1823
  S: this,
1038
1824
  A: [
@@ -1040,8 +1826,8 @@ var Pipeline = class {
1040
1826
  "'Pipeline is already started.'"
1041
1827
  ]
1042
1828
  });
1043
- log6("starting...", void 0, {
1044
- F: __dxlog_file7,
1829
+ log7("starting...", void 0, {
1830
+ F: __dxlog_file9,
1045
1831
  L: 285,
1046
1832
  S: this,
1047
1833
  C: (f, a) => f(...a)
@@ -1049,8 +1835,8 @@ var Pipeline = class {
1049
1835
  await this._initIterator();
1050
1836
  await this._feedSetIterator.open();
1051
1837
  this._isStarted = true;
1052
- log6("started", void 0, {
1053
- F: __dxlog_file7,
1838
+ log7("started", void 0, {
1839
+ F: __dxlog_file9,
1054
1840
  L: 289,
1055
1841
  S: this,
1056
1842
  C: (f, a) => f(...a)
@@ -1062,8 +1848,8 @@ var Pipeline = class {
1062
1848
  }
1063
1849
  }
1064
1850
  async stop() {
1065
- log6("stopping...", void 0, {
1066
- F: __dxlog_file7,
1851
+ log7("stopping...", void 0, {
1852
+ F: __dxlog_file9,
1067
1853
  L: 300,
1068
1854
  S: this,
1069
1855
  C: (f, a) => f(...a)
@@ -1076,15 +1862,15 @@ var Pipeline = class {
1076
1862
  await this._feedSetIterator?.close();
1077
1863
  await this._processingTrigger.wait();
1078
1864
  await this._state._ctx.dispose();
1079
- this._state._ctx = new Context2(void 0, {
1080
- F: __dxlog_file7,
1865
+ this._state._ctx = new Context3(void 0, {
1866
+ F: __dxlog_file9,
1081
1867
  L: 309
1082
1868
  });
1083
1869
  this._state._reachedTargetPromise = void 0;
1084
1870
  this._state._reachedTarget = false;
1085
1871
  this._isStarted = false;
1086
- log6("stopped", void 0, {
1087
- F: __dxlog_file7,
1872
+ log7("stopped", void 0, {
1873
+ F: __dxlog_file9,
1088
1874
  L: 313,
1089
1875
  S: this,
1090
1876
  C: (f, a) => f(...a)
@@ -1095,8 +1881,8 @@ var Pipeline = class {
1095
1881
  * The pipeline will start processing messages AFTER this timeframe.
1096
1882
  */
1097
1883
  async setCursor(timeframe) {
1098
- invariant6(!this._isStarted || this._isPaused, "Invalid state.", {
1099
- F: __dxlog_file7,
1884
+ invariant8(!this._isStarted || this._isPaused, "Invalid state.", {
1885
+ F: __dxlog_file9,
1100
1886
  L: 322,
1101
1887
  S: this,
1102
1888
  A: [
@@ -1124,8 +1910,8 @@ var Pipeline = class {
1124
1910
  this._isPaused = true;
1125
1911
  }
1126
1912
  async unpause() {
1127
- invariant6(this._isPaused, "Pipeline is not paused.", {
1128
- F: __dxlog_file7,
1913
+ invariant8(this._isPaused, "Pipeline is not paused.", {
1914
+ F: __dxlog_file9,
1129
1915
  L: 351,
1130
1916
  S: this,
1131
1917
  A: [
@@ -1144,8 +1930,8 @@ var Pipeline = class {
1144
1930
  * Updates the timeframe clock after the message has bee processed.
1145
1931
  */
1146
1932
  async *consume() {
1147
- invariant6(!this._isBeingConsumed, "Pipeline is already being consumed.", {
1148
- F: __dxlog_file7,
1933
+ invariant8(!this._isBeingConsumed, "Pipeline is already being consumed.", {
1934
+ F: __dxlog_file9,
1149
1935
  L: 366,
1150
1936
  S: this,
1151
1937
  A: [
@@ -1154,8 +1940,8 @@ var Pipeline = class {
1154
1940
  ]
1155
1941
  });
1156
1942
  this._isBeingConsumed = true;
1157
- invariant6(this._feedSetIterator, "Iterator not initialized.", {
1158
- F: __dxlog_file7,
1943
+ invariant8(this._feedSetIterator, "Iterator not initialized.", {
1944
+ F: __dxlog_file9,
1159
1945
  L: 369,
1160
1946
  S: this,
1161
1947
  A: [
@@ -1168,8 +1954,8 @@ var Pipeline = class {
1168
1954
  while (!this._isStopping) {
1169
1955
  await this._pauseTrigger.wait();
1170
1956
  if (lastFeedSetIterator !== this._feedSetIterator) {
1171
- invariant6(this._feedSetIterator, "Iterator not initialized.", {
1172
- F: __dxlog_file7,
1957
+ invariant8(this._feedSetIterator, "Iterator not initialized.", {
1958
+ F: __dxlog_file9,
1173
1959
  L: 378,
1174
1960
  S: this,
1175
1961
  A: [
@@ -1199,12 +1985,12 @@ var Pipeline = class {
1199
1985
  }
1200
1986
  const timeframe = this._state._startTimeframe;
1201
1987
  const seq = timeframe.get(feed.key) ?? -1;
1202
- log6("download", {
1988
+ log7("download", {
1203
1989
  feed: feed.key.truncate(),
1204
1990
  seq,
1205
1991
  length: feed.length
1206
1992
  }, {
1207
- F: __dxlog_file7,
1993
+ F: __dxlog_file9,
1208
1994
  L: 407,
1209
1995
  S: this,
1210
1996
  C: (f, a) => f(...a)
@@ -1215,10 +2001,10 @@ var Pipeline = class {
1215
2001
  }, (err, data) => {
1216
2002
  if (err) {
1217
2003
  } else {
1218
- log6.info("downloaded", {
2004
+ log7.info("downloaded", {
1219
2005
  data
1220
2006
  }, {
1221
- F: __dxlog_file7,
2007
+ F: __dxlog_file9,
1222
2008
  L: 412,
1223
2009
  S: this,
1224
2010
  C: (f, a) => f(...a)
@@ -1233,8 +2019,8 @@ var Pipeline = class {
1233
2019
  stallTimeout: 1e3
1234
2020
  });
1235
2021
  this._feedSetIterator.stalled.on((iterator) => {
1236
- log6.warn(`Stalled after ${iterator.options.stallTimeout}ms with ${iterator.size} feeds.`, void 0, {
1237
- F: __dxlog_file7,
2022
+ log7.warn(`Stalled after ${iterator.options.stallTimeout}ms with ${iterator.size} feeds.`, void 0, {
2023
+ F: __dxlog_file9,
1238
2024
  L: 426,
1239
2025
  S: this,
1240
2026
  C: (f, a) => f(...a)
@@ -1246,142 +2032,32 @@ var Pipeline = class {
1246
2032
  }
1247
2033
  }
1248
2034
  };
1249
- _ts_decorate3([
2035
+ _ts_decorate4([
1250
2036
  synchronized2
1251
2037
  ], Pipeline.prototype, "start", null);
1252
- _ts_decorate3([
2038
+ _ts_decorate4([
1253
2039
  synchronized2
1254
2040
  ], Pipeline.prototype, "stop", null);
1255
- _ts_decorate3([
2041
+ _ts_decorate4([
1256
2042
  synchronized2
1257
2043
  ], Pipeline.prototype, "setCursor", null);
1258
- _ts_decorate3([
2044
+ _ts_decorate4([
1259
2045
  synchronized2
1260
2046
  ], Pipeline.prototype, "pause", null);
1261
- _ts_decorate3([
2047
+ _ts_decorate4([
1262
2048
  synchronized2
1263
2049
  ], Pipeline.prototype, "unpause", null);
1264
2050
 
1265
- // packages/core/echo/echo-pipeline/src/space/auth.ts
1266
- import { runInContext, scheduleTask } from "@dxos/async";
1267
- import { Context as Context3 } from "@dxos/context";
1268
- import { randomBytes } from "@dxos/crypto";
1269
- import { invariant as invariant7 } from "@dxos/invariant";
1270
- import { log as log7 } from "@dxos/log";
1271
- import { schema as schema5 } from "@dxos/protocols";
1272
- import { RpcExtension } from "@dxos/teleport";
1273
- var __dxlog_file8 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/space/auth.ts";
1274
- var AuthExtension = class extends RpcExtension {
1275
- constructor(_authParams) {
1276
- super({
1277
- requested: {
1278
- AuthService: schema5.getService("dxos.mesh.teleport.auth.AuthService")
1279
- },
1280
- exposed: {
1281
- AuthService: schema5.getService("dxos.mesh.teleport.auth.AuthService")
1282
- },
1283
- timeout: 60 * 1e3
1284
- });
1285
- this._authParams = _authParams;
1286
- this._ctx = new Context3({
1287
- onError: (err) => {
1288
- log7.catch(err, void 0, {
1289
- F: __dxlog_file8,
1290
- L: 28,
1291
- S: this,
1292
- C: (f, a) => f(...a)
1293
- });
1294
- }
1295
- }, {
1296
- F: __dxlog_file8,
1297
- L: 26
1298
- });
1299
- }
1300
- async getHandlers() {
1301
- return {
1302
- AuthService: {
1303
- authenticate: async ({ challenge }) => {
1304
- try {
1305
- const credential = await this._authParams.provider(challenge);
1306
- if (!credential) {
1307
- throw new Error("auth rejected");
1308
- }
1309
- return {
1310
- credential
1311
- };
1312
- } catch (err) {
1313
- log7.error("failed to generate auth credentials", err, {
1314
- F: __dxlog_file8,
1315
- L: 55,
1316
- S: this,
1317
- C: (f, a) => f(...a)
1318
- });
1319
- throw new Error("auth rejected");
1320
- }
1321
- }
1322
- }
1323
- };
1324
- }
1325
- async onOpen(context) {
1326
- await super.onOpen(context);
1327
- scheduleTask(this._ctx, async () => {
1328
- try {
1329
- const challenge = randomBytes(32);
1330
- const { credential } = await this.rpc.AuthService.authenticate({
1331
- challenge
1332
- });
1333
- invariant7(credential?.length > 0, "invalid credential", {
1334
- F: __dxlog_file8,
1335
- L: 69,
1336
- S: this,
1337
- A: [
1338
- "credential?.length > 0",
1339
- "'invalid credential'"
1340
- ]
1341
- });
1342
- const success = await this._authParams.verifier(challenge, credential);
1343
- invariant7(success, "credential not verified", {
1344
- F: __dxlog_file8,
1345
- L: 71,
1346
- S: this,
1347
- A: [
1348
- "success",
1349
- "'credential not verified'"
1350
- ]
1351
- });
1352
- runInContext(this._ctx, () => this._authParams.onAuthSuccess());
1353
- } catch (err) {
1354
- log7("auth failed", err, {
1355
- F: __dxlog_file8,
1356
- L: 74,
1357
- S: this,
1358
- C: (f, a) => f(...a)
1359
- });
1360
- this.close();
1361
- this._authParams.onAuthFailure();
1362
- }
1363
- });
1364
- }
1365
- async onClose() {
1366
- await this._ctx.dispose();
1367
- await super.onClose();
1368
- }
1369
- async onAbort() {
1370
- await this._ctx.dispose();
1371
- await super.onAbort();
1372
- }
1373
- };
1374
-
1375
2051
  // packages/core/echo/echo-pipeline/src/space/space.ts
1376
- import { Event as Event4, Mutex, synchronized as synchronized3, trackLeaks as trackLeaks2 } from "@dxos/async";
1377
- import { LifecycleState, Resource as Resource2 } from "@dxos/context";
2052
+ import { Event as Event5, Mutex, synchronized as synchronized3, trackLeaks as trackLeaks2 } from "@dxos/async";
2053
+ import { LifecycleState as LifecycleState3, Resource as Resource5 } from "@dxos/context";
1378
2054
  import { subtleCrypto as subtleCrypto2 } from "@dxos/crypto";
1379
- import { invariant as invariant8 } from "@dxos/invariant";
1380
- import { PublicKey as PublicKey5, SpaceId } from "@dxos/keys";
2055
+ import { invariant as invariant9 } from "@dxos/invariant";
2056
+ import { PublicKey as PublicKey5, SpaceId as SpaceId2 } from "@dxos/keys";
1381
2057
  import { log as log9, logInfo } from "@dxos/log";
1382
2058
  import { AdmittedFeed as AdmittedFeed2 } from "@dxos/protocols/proto/dxos/halo/credentials";
1383
- import { trace as trace2 } from "@dxos/tracing";
1384
- import { Callback as Callback2, ComplexMap as ComplexMap3 } from "@dxos/util";
2059
+ import { trace as trace3 } from "@dxos/tracing";
2060
+ import { Callback as Callback2, ComplexMap as ComplexMap2 } from "@dxos/util";
1385
2061
 
1386
2062
  // packages/core/echo/echo-pipeline/src/space/control-pipeline.ts
1387
2063
  import { DeferredTask, sleepWithContext as sleepWithContext2, trackLeaks } from "@dxos/async";
@@ -1391,9 +2067,9 @@ import { PublicKey as PublicKey4 } from "@dxos/keys";
1391
2067
  import { log as log8 } from "@dxos/log";
1392
2068
  import { AdmittedFeed } from "@dxos/protocols/proto/dxos/halo/credentials";
1393
2069
  import { Timeframe as Timeframe3 } from "@dxos/timeframe";
1394
- import { TimeSeriesCounter, TimeUsageCounter, trace } from "@dxos/tracing";
2070
+ import { TimeSeriesCounter, TimeUsageCounter, trace as trace2 } from "@dxos/tracing";
1395
2071
  import { Callback, tracer } from "@dxos/util";
1396
- function _ts_decorate4(decorators, target, key, desc) {
2072
+ function _ts_decorate5(decorators, target, key, desc) {
1397
2073
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1398
2074
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
1399
2075
  r = Reflect.decorate(decorators, target, key, desc);
@@ -1403,14 +2079,14 @@ function _ts_decorate4(decorators, target, key, desc) {
1403
2079
  r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1404
2080
  return c > 3 && r && Object.defineProperty(target, key, r), r;
1405
2081
  }
1406
- var __dxlog_file9 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/space/control-pipeline.ts";
2082
+ var __dxlog_file10 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/space/control-pipeline.ts";
1407
2083
  var TIMEFRAME_SAVE_DEBOUNCE_INTERVAL = 500;
1408
2084
  var CONTROL_PIPELINE_SNAPSHOT_DELAY = 1e4;
1409
2085
  var USE_SNAPSHOTS = true;
1410
2086
  var ControlPipeline = class {
1411
2087
  constructor({ spaceKey, genesisFeed, feedProvider, metadataStore }) {
1412
2088
  this._ctx = new Context4(void 0, {
1413
- F: __dxlog_file9,
2089
+ F: __dxlog_file10,
1414
2090
  L: 47
1415
2091
  });
1416
2092
  this._lastTimeframeSaveTime = Date.now();
@@ -1430,7 +2106,7 @@ var ControlPipeline = class {
1430
2106
  log8("feed admitted", {
1431
2107
  key: info.key
1432
2108
  }, {
1433
- F: __dxlog_file9,
2109
+ F: __dxlog_file10,
1434
2110
  L: 82,
1435
2111
  S: this,
1436
2112
  C: (f, a) => f(...a)
@@ -1444,7 +2120,7 @@ var ControlPipeline = class {
1444
2120
  }
1445
2121
  } catch (err) {
1446
2122
  log8.catch(err, void 0, {
1447
- F: __dxlog_file9,
2123
+ F: __dxlog_file10,
1448
2124
  L: 93,
1449
2125
  S: this,
1450
2126
  C: (f, a) => f(...a)
@@ -1476,7 +2152,7 @@ var ControlPipeline = class {
1476
2152
  present: !!snapshot,
1477
2153
  tf: snapshot?.timeframe
1478
2154
  }, {
1479
- F: __dxlog_file9,
2155
+ F: __dxlog_file10,
1480
2156
  L: 123,
1481
2157
  S: this,
1482
2158
  C: (f, a) => f(...a)
@@ -1485,20 +2161,20 @@ var ControlPipeline = class {
1485
2161
  await this._processSnapshot(snapshot);
1486
2162
  }
1487
2163
  log8("starting...", void 0, {
1488
- F: __dxlog_file9,
2164
+ F: __dxlog_file10,
1489
2165
  L: 128,
1490
2166
  S: this,
1491
2167
  C: (f, a) => f(...a)
1492
2168
  });
1493
2169
  setTimeout(async () => {
1494
2170
  void this._consumePipeline(new Context4(void 0, {
1495
- F: __dxlog_file9,
2171
+ F: __dxlog_file10,
1496
2172
  L: 130
1497
2173
  }));
1498
2174
  });
1499
2175
  await this._pipeline.start();
1500
2176
  log8("started", void 0, {
1501
- F: __dxlog_file9,
2177
+ F: __dxlog_file10,
1502
2178
  L: 134,
1503
2179
  S: this,
1504
2180
  C: (f, a) => f(...a)
@@ -1515,7 +2191,7 @@ var ControlPipeline = class {
1515
2191
  log8.warn("credential processing failed from snapshot", {
1516
2192
  message
1517
2193
  }, {
1518
- F: __dxlog_file9,
2194
+ F: __dxlog_file10,
1519
2195
  L: 147,
1520
2196
  S: this,
1521
2197
  C: (f, a) => f(...a)
@@ -1537,7 +2213,7 @@ var ControlPipeline = class {
1537
2213
  key: this._spaceKey,
1538
2214
  snapshot
1539
2215
  }, {
1540
- F: __dxlog_file9,
2216
+ F: __dxlog_file10,
1541
2217
  L: 163,
1542
2218
  S: this,
1543
2219
  C: (f, a) => f(...a)
@@ -1552,7 +2228,7 @@ var ControlPipeline = class {
1552
2228
  await this._processMessage(ctx, msg);
1553
2229
  } catch (err) {
1554
2230
  log8.catch(err, void 0, {
1555
- F: __dxlog_file9,
2231
+ F: __dxlog_file10,
1556
2232
  L: 176,
1557
2233
  S: this,
1558
2234
  C: (f, a) => f(...a)
@@ -1566,7 +2242,7 @@ var ControlPipeline = class {
1566
2242
  key: msg.feedKey,
1567
2243
  seq: msg.seq
1568
2244
  }, {
1569
- F: __dxlog_file9,
2245
+ F: __dxlog_file10,
1570
2246
  L: 186,
1571
2247
  S: this,
1572
2248
  C: (f, a) => f(...a)
@@ -1581,7 +2257,7 @@ var ControlPipeline = class {
1581
2257
  log8.warn("processing failed", {
1582
2258
  msg
1583
2259
  }, {
1584
- F: __dxlog_file9,
2260
+ F: __dxlog_file10,
1585
2261
  L: 195,
1586
2262
  S: this,
1587
2263
  C: (f, a) => f(...a)
@@ -1600,7 +2276,7 @@ var ControlPipeline = class {
1600
2276
  }
1601
2277
  async stop() {
1602
2278
  log8("stopping...", void 0, {
1603
- F: __dxlog_file9,
2279
+ F: __dxlog_file10,
1604
2280
  L: 215,
1605
2281
  S: this,
1606
2282
  C: (f, a) => f(...a)
@@ -1609,7 +2285,7 @@ var ControlPipeline = class {
1609
2285
  await this._pipeline.stop();
1610
2286
  await this._saveTargetTimeframe(this._pipeline.state.timeframe);
1611
2287
  log8("stopped", void 0, {
1612
- F: __dxlog_file9,
2288
+ F: __dxlog_file10,
1613
2289
  L: 219,
1614
2290
  S: this,
1615
2291
  C: (f, a) => f(...a)
@@ -1622,7 +2298,7 @@ var ControlPipeline = class {
1622
2298
  this._targetTimeframe = newTimeframe;
1623
2299
  } catch (err) {
1624
2300
  log8(err, void 0, {
1625
- F: __dxlog_file9,
2301
+ F: __dxlog_file10,
1626
2302
  L: 228,
1627
2303
  S: this,
1628
2304
  C: (f, a) => f(...a)
@@ -1630,30 +2306,30 @@ var ControlPipeline = class {
1630
2306
  }
1631
2307
  }
1632
2308
  };
1633
- _ts_decorate4([
1634
- trace.metricsCounter()
2309
+ _ts_decorate5([
2310
+ trace2.metricsCounter()
1635
2311
  ], ControlPipeline.prototype, "_usage", void 0);
1636
- _ts_decorate4([
1637
- trace.metricsCounter()
2312
+ _ts_decorate5([
2313
+ trace2.metricsCounter()
1638
2314
  ], ControlPipeline.prototype, "_mutations", void 0);
1639
- _ts_decorate4([
1640
- trace.span({
2315
+ _ts_decorate5([
2316
+ trace2.span({
1641
2317
  showInBrowserTimeline: true
1642
2318
  })
1643
2319
  ], ControlPipeline.prototype, "start", null);
1644
- _ts_decorate4([
1645
- trace.span()
2320
+ _ts_decorate5([
2321
+ trace2.span()
1646
2322
  ], ControlPipeline.prototype, "_consumePipeline", null);
1647
- _ts_decorate4([
1648
- trace.span()
2323
+ _ts_decorate5([
2324
+ trace2.span()
1649
2325
  ], ControlPipeline.prototype, "_processMessage", null);
1650
- ControlPipeline = _ts_decorate4([
1651
- trace.resource(),
2326
+ ControlPipeline = _ts_decorate5([
2327
+ trace2.resource(),
1652
2328
  trackLeaks("start", "stop")
1653
2329
  ], ControlPipeline);
1654
2330
 
1655
2331
  // packages/core/echo/echo-pipeline/src/space/space.ts
1656
- function _ts_decorate5(decorators, target, key, desc) {
2332
+ function _ts_decorate6(decorators, target, key, desc) {
1657
2333
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1658
2334
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
1659
2335
  r = Reflect.decorate(decorators, target, key, desc);
@@ -1663,15 +2339,15 @@ function _ts_decorate5(decorators, target, key, desc) {
1663
2339
  r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1664
2340
  return c > 3 && r && Object.defineProperty(target, key, r), r;
1665
2341
  }
1666
- var __dxlog_file10 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/space/space.ts";
1667
- var Space = class extends Resource2 {
2342
+ var __dxlog_file11 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/space/space.ts";
2343
+ var Space = class extends Resource5 {
1668
2344
  constructor(params) {
1669
2345
  super();
1670
2346
  this._addFeedMutex = new Mutex();
1671
2347
  this.onCredentialProcessed = new Callback2();
1672
- this.stateUpdate = new Event4();
1673
- invariant8(params.spaceKey && params.feedProvider, void 0, {
1674
- F: __dxlog_file10,
2348
+ this.stateUpdate = new Event5();
2349
+ invariant9(params.spaceKey && params.feedProvider, void 0, {
2350
+ F: __dxlog_file11,
1675
2351
  L: 78,
1676
2352
  S: this,
1677
2353
  A: [
@@ -1705,7 +2381,7 @@ var Space = class extends Resource2 {
1705
2381
  log9("onCredentialProcessed", {
1706
2382
  credential
1707
2383
  }, {
1708
- F: __dxlog_file10,
2384
+ F: __dxlog_file11,
1709
2385
  L: 106,
1710
2386
  S: this,
1711
2387
  C: (f, a) => f(...a)
@@ -1716,7 +2392,7 @@ var Space = class extends Resource2 {
1716
2392
  log9("onDelegatedInvitation", {
1717
2393
  invitation
1718
2394
  }, {
1719
- F: __dxlog_file10,
2395
+ F: __dxlog_file11,
1720
2396
  L: 110,
1721
2397
  S: this,
1722
2398
  C: (f, a) => f(...a)
@@ -1727,7 +2403,7 @@ var Space = class extends Resource2 {
1727
2403
  log9("onDelegatedInvitationRemoved", {
1728
2404
  invitation
1729
2405
  }, {
1730
- F: __dxlog_file10,
2406
+ F: __dxlog_file11,
1731
2407
  L: 114,
1732
2408
  S: this,
1733
2409
  C: (f, a) => f(...a)
@@ -1741,7 +2417,7 @@ var Space = class extends Resource2 {
1741
2417
  m.role
1742
2418
  ])
1743
2419
  }), {
1744
- F: __dxlog_file10,
2420
+ F: __dxlog_file11,
1745
2421
  L: 118,
1746
2422
  S: this,
1747
2423
  C: (f, a) => f(...a)
@@ -1758,7 +2434,7 @@ var Space = class extends Resource2 {
1758
2434
  return this._key;
1759
2435
  }
1760
2436
  get isOpen() {
1761
- return this._lifecycleState === LifecycleState.OPEN;
2437
+ return this._lifecycleState === LifecycleState3.OPEN;
1762
2438
  }
1763
2439
  get genesisFeedKey() {
1764
2440
  return this._genesisFeedKey;
@@ -1782,8 +2458,8 @@ var Space = class extends Resource2 {
1782
2458
  return this._snapshotManager;
1783
2459
  }
1784
2460
  async setControlFeed(feed) {
1785
- invariant8(!this._controlFeed, "Control feed already set.", {
1786
- F: __dxlog_file10,
2461
+ invariant9(!this._controlFeed, "Control feed already set.", {
2462
+ F: __dxlog_file11,
1787
2463
  L: 171,
1788
2464
  S: this,
1789
2465
  A: [
@@ -1796,8 +2472,8 @@ var Space = class extends Resource2 {
1796
2472
  return this;
1797
2473
  }
1798
2474
  async setDataFeed(feed) {
1799
- invariant8(!this._dataFeed, "Data feed already set.", {
1800
- F: __dxlog_file10,
2475
+ invariant9(!this._dataFeed, "Data feed already set.", {
2476
+ F: __dxlog_file11,
1801
2477
  L: 178,
1802
2478
  S: this,
1803
2479
  A: [
@@ -1816,7 +2492,7 @@ var Space = class extends Resource2 {
1816
2492
  }
1817
2493
  async _open(ctx) {
1818
2494
  log9("opening...", void 0, {
1819
- F: __dxlog_file10,
2495
+ F: __dxlog_file11,
1820
2496
  L: 192,
1821
2497
  S: this,
1822
2498
  C: (f, a) => f(...a)
@@ -1824,7 +2500,7 @@ var Space = class extends Resource2 {
1824
2500
  await this._controlPipeline.start();
1825
2501
  await this.protocol.start();
1826
2502
  log9("opened", void 0, {
1827
- F: __dxlog_file10,
2503
+ F: __dxlog_file11,
1828
2504
  L: 198,
1829
2505
  S: this,
1830
2506
  C: (f, a) => f(...a)
@@ -1834,7 +2510,7 @@ var Space = class extends Resource2 {
1834
2510
  log9("closing...", {
1835
2511
  key: this._key
1836
2512
  }, {
1837
- F: __dxlog_file10,
2513
+ F: __dxlog_file11,
1838
2514
  L: 203,
1839
2515
  S: this,
1840
2516
  C: (f, a) => f(...a)
@@ -1842,67 +2518,67 @@ var Space = class extends Resource2 {
1842
2518
  await this.protocol.stop();
1843
2519
  await this._controlPipeline.stop();
1844
2520
  log9("closed", void 0, {
1845
- F: __dxlog_file10,
2521
+ F: __dxlog_file11,
1846
2522
  L: 209,
1847
2523
  S: this,
1848
2524
  C: (f, a) => f(...a)
1849
2525
  });
1850
2526
  }
1851
2527
  };
1852
- _ts_decorate5([
1853
- trace2.info()
2528
+ _ts_decorate6([
2529
+ trace3.info()
1854
2530
  ], Space.prototype, "protocol", void 0);
1855
- _ts_decorate5([
1856
- trace2.info()
2531
+ _ts_decorate6([
2532
+ trace3.info()
1857
2533
  ], Space.prototype, "_controlPipeline", void 0);
1858
- _ts_decorate5([
2534
+ _ts_decorate6([
1859
2535
  logInfo,
1860
- trace2.info()
2536
+ trace3.info()
1861
2537
  ], Space.prototype, "id", null);
1862
- _ts_decorate5([
2538
+ _ts_decorate6([
1863
2539
  logInfo,
1864
- trace2.info()
2540
+ trace3.info()
1865
2541
  ], Space.prototype, "key", null);
1866
- _ts_decorate5([
1867
- trace2.span()
2542
+ _ts_decorate6([
2543
+ trace3.span()
1868
2544
  ], Space.prototype, "_open", null);
1869
- _ts_decorate5([
2545
+ _ts_decorate6([
1870
2546
  synchronized3
1871
2547
  ], Space.prototype, "_close", null);
1872
- Space = _ts_decorate5([
2548
+ Space = _ts_decorate6([
1873
2549
  trackLeaks2("open", "close"),
1874
- trace2.resource()
2550
+ trace3.resource()
1875
2551
  ], Space);
1876
- var SPACE_IDS_CACHE = new ComplexMap3(PublicKey5.hash);
2552
+ var SPACE_IDS_CACHE = new ComplexMap2(PublicKey5.hash);
1877
2553
  var createIdFromSpaceKey = async (spaceKey) => {
1878
2554
  const cachedValue = SPACE_IDS_CACHE.get(spaceKey);
1879
2555
  if (cachedValue !== void 0) {
1880
2556
  return cachedValue;
1881
2557
  }
1882
2558
  const digest = await subtleCrypto2.digest("SHA-256", spaceKey.asUint8Array());
1883
- const bytes = new Uint8Array(digest).slice(0, SpaceId.byteLength);
1884
- const spaceId = SpaceId.encode(bytes);
2559
+ const bytes = new Uint8Array(digest).slice(0, SpaceId2.byteLength);
2560
+ const spaceId = SpaceId2.encode(bytes);
1885
2561
  SPACE_IDS_CACHE.set(spaceKey, spaceId);
1886
2562
  return spaceId;
1887
2563
  };
1888
2564
 
1889
2565
  // packages/core/echo/echo-pipeline/src/space/admission-discovery-extension.ts
1890
- import { scheduleTask as scheduleTask2 } from "@dxos/async";
2566
+ import { scheduleTask as scheduleTask3 } from "@dxos/async";
1891
2567
  import { Context as Context5 } from "@dxos/context";
1892
- import { ProtocolError, schema as schema6 } from "@dxos/protocols";
2568
+ import { ProtocolError, schema as schema5 } from "@dxos/protocols";
1893
2569
  import { RpcExtension as RpcExtension2 } from "@dxos/teleport";
1894
- var __dxlog_file11 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/space/admission-discovery-extension.ts";
2570
+ var __dxlog_file12 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/space/admission-discovery-extension.ts";
1895
2571
  var CredentialRetrieverExtension = class extends RpcExtension2 {
1896
2572
  constructor(_request, _onResult) {
1897
2573
  super({
1898
2574
  requested: {
1899
- AdmissionDiscoveryService: schema6.getService("dxos.mesh.teleport.AdmissionDiscoveryService")
2575
+ AdmissionDiscoveryService: schema5.getService("dxos.mesh.teleport.AdmissionDiscoveryService")
1900
2576
  }
1901
2577
  });
1902
2578
  this._request = _request;
1903
2579
  this._onResult = _onResult;
1904
2580
  this._ctx = new Context5(void 0, {
1905
- F: __dxlog_file11,
2581
+ F: __dxlog_file12,
1906
2582
  L: 25
1907
2583
  });
1908
2584
  }
@@ -1911,7 +2587,7 @@ var CredentialRetrieverExtension = class extends RpcExtension2 {
1911
2587
  }
1912
2588
  async onOpen(context) {
1913
2589
  await super.onOpen(context);
1914
- scheduleTask2(this._ctx, async () => {
2590
+ scheduleTask3(this._ctx, async () => {
1915
2591
  try {
1916
2592
  const result = await this.rpc.AdmissionDiscoveryService.getAdmissionCredential(this._request);
1917
2593
  this._onResult.wake(result.admissionCredential);
@@ -1919,267 +2595,719 @@ var CredentialRetrieverExtension = class extends RpcExtension2 {
1919
2595
  context.close(err);
1920
2596
  }
1921
2597
  });
1922
- }
1923
- async onClose() {
1924
- await this._ctx.dispose();
1925
- }
1926
- async onAbort() {
1927
- await this._ctx.dispose();
1928
- }
1929
- };
1930
- var CredentialServerExtension = class extends RpcExtension2 {
1931
- constructor(_space) {
1932
- super({
1933
- exposed: {
1934
- AdmissionDiscoveryService: schema6.getService("dxos.mesh.teleport.AdmissionDiscoveryService")
2598
+ }
2599
+ async onClose() {
2600
+ await this._ctx.dispose();
2601
+ }
2602
+ async onAbort() {
2603
+ await this._ctx.dispose();
2604
+ }
2605
+ };
2606
+ var CredentialServerExtension = class extends RpcExtension2 {
2607
+ constructor(_space) {
2608
+ super({
2609
+ exposed: {
2610
+ AdmissionDiscoveryService: schema5.getService("dxos.mesh.teleport.AdmissionDiscoveryService")
2611
+ }
2612
+ });
2613
+ this._space = _space;
2614
+ }
2615
+ async getHandlers() {
2616
+ return {
2617
+ AdmissionDiscoveryService: {
2618
+ getAdmissionCredential: async (request) => {
2619
+ const memberInfo = this._space.spaceState.members.get(request.memberKey);
2620
+ if (!memberInfo?.credential) {
2621
+ throw new ProtocolError("Space member not found.", request);
2622
+ }
2623
+ return {
2624
+ admissionCredential: memberInfo.credential
2625
+ };
2626
+ }
2627
+ }
2628
+ };
2629
+ }
2630
+ };
2631
+
2632
+ // packages/core/echo/echo-pipeline/src/space/space-protocol.ts
2633
+ import { discoveryKey, subtleCrypto as subtleCrypto3 } from "@dxos/crypto";
2634
+ import { PublicKey as PublicKey6 } from "@dxos/keys";
2635
+ import { log as log10, logInfo as logInfo2 } from "@dxos/log";
2636
+ import { MMSTTopology } from "@dxos/network-manager";
2637
+ import { Teleport } from "@dxos/teleport";
2638
+ import { BlobSync } from "@dxos/teleport-extension-object-sync";
2639
+ import { ReplicatorExtension } from "@dxos/teleport-extension-replicator";
2640
+ import { trace as trace4 } from "@dxos/tracing";
2641
+ import { ComplexMap as ComplexMap3 } from "@dxos/util";
2642
+ function _ts_decorate7(decorators, target, key, desc) {
2643
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2644
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
2645
+ r = Reflect.decorate(decorators, target, key, desc);
2646
+ else
2647
+ for (var i = decorators.length - 1; i >= 0; i--)
2648
+ if (d = decorators[i])
2649
+ r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
2650
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
2651
+ }
2652
+ var __dxlog_file13 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/space/space-protocol.ts";
2653
+ var MOCK_AUTH_PROVIDER = async (nonce) => Buffer.from("mock");
2654
+ var MOCK_AUTH_VERIFIER = async (nonce, credential) => true;
2655
+ var SpaceProtocol = class {
2656
+ constructor({ topic, swarmIdentity, networkManager, onSessionAuth, onAuthFailure, blobStore }) {
2657
+ this._feeds = /* @__PURE__ */ new Set();
2658
+ this._sessions = new ComplexMap3(PublicKey6.hash);
2659
+ // TODO(burdon): Move to config (with sensible defaults).
2660
+ this._topology = new MMSTTopology({
2661
+ originateConnections: 4,
2662
+ maxPeers: 10,
2663
+ sampleSize: 20
2664
+ });
2665
+ this._spaceKey = topic;
2666
+ this._networkManager = networkManager;
2667
+ this._swarmIdentity = swarmIdentity;
2668
+ this._onSessionAuth = onSessionAuth;
2669
+ this._onAuthFailure = onAuthFailure;
2670
+ this.blobSync = new BlobSync({
2671
+ blobStore
2672
+ });
2673
+ this._topic = subtleCrypto3.digest("SHA-256", topic.asBuffer()).then(discoveryKey).then(PublicKey6.from);
2674
+ }
2675
+ get sessions() {
2676
+ return this._sessions;
2677
+ }
2678
+ get feeds() {
2679
+ return this._feeds;
2680
+ }
2681
+ get _ownPeerKey() {
2682
+ return this._swarmIdentity.peerKey;
2683
+ }
2684
+ // TODO(burdon): Create abstraction for Space (e.g., add keys and have provider).
2685
+ addFeed(feed) {
2686
+ log10("addFeed", {
2687
+ key: feed.key
2688
+ }, {
2689
+ F: __dxlog_file13,
2690
+ L: 109,
2691
+ S: this,
2692
+ C: (f, a) => f(...a)
2693
+ });
2694
+ this._feeds.add(feed);
2695
+ for (const session of this._sessions.values()) {
2696
+ session.replicator.addFeed(feed);
2697
+ }
2698
+ }
2699
+ // TODO(burdon): Rename open? Common open/close interfaces for all services?
2700
+ async start() {
2701
+ if (this._connection) {
2702
+ return;
2703
+ }
2704
+ const credentials = await this._swarmIdentity.credentialProvider(Buffer.from(""));
2705
+ await this.blobSync.open();
2706
+ log10("starting...", void 0, {
2707
+ F: __dxlog_file13,
2708
+ L: 128,
2709
+ S: this,
2710
+ C: (f, a) => f(...a)
2711
+ });
2712
+ const topic = await this._topic;
2713
+ this._connection = await this._networkManager.joinSwarm({
2714
+ protocolProvider: this._createProtocolProvider(credentials),
2715
+ peerId: this._swarmIdentity.peerKey,
2716
+ topic,
2717
+ topology: this._topology,
2718
+ label: `swarm ${topic.truncate()} for space ${this._spaceKey.truncate()}`
2719
+ });
2720
+ log10("started", void 0, {
2721
+ F: __dxlog_file13,
2722
+ L: 138,
2723
+ S: this,
2724
+ C: (f, a) => f(...a)
2725
+ });
2726
+ }
2727
+ updateTopology() {
2728
+ this._topology.forceUpdate();
2729
+ }
2730
+ async stop() {
2731
+ await this.blobSync.close();
2732
+ if (this._connection) {
2733
+ log10("stopping...", void 0, {
2734
+ F: __dxlog_file13,
2735
+ L: 149,
2736
+ S: this,
2737
+ C: (f, a) => f(...a)
2738
+ });
2739
+ await this._connection.close();
2740
+ log10("stopped", void 0, {
2741
+ F: __dxlog_file13,
2742
+ L: 151,
2743
+ S: this,
2744
+ C: (f, a) => f(...a)
2745
+ });
2746
+ }
2747
+ }
2748
+ _createProtocolProvider(credentials) {
2749
+ return (wireParams) => {
2750
+ const session = new SpaceProtocolSession({
2751
+ wireParams,
2752
+ swarmIdentity: this._swarmIdentity,
2753
+ onSessionAuth: this._onSessionAuth,
2754
+ onAuthFailure: this._onAuthFailure,
2755
+ blobSync: this.blobSync
2756
+ });
2757
+ this._sessions.set(wireParams.remotePeerId, session);
2758
+ for (const feed of this._feeds) {
2759
+ session.replicator.addFeed(feed);
2760
+ }
2761
+ return session;
2762
+ };
2763
+ }
2764
+ };
2765
+ _ts_decorate7([
2766
+ logInfo2,
2767
+ trace4.info()
2768
+ ], SpaceProtocol.prototype, "_topic", void 0);
2769
+ _ts_decorate7([
2770
+ trace4.info()
2771
+ ], SpaceProtocol.prototype, "_spaceKey", void 0);
2772
+ _ts_decorate7([
2773
+ logInfo2
2774
+ ], SpaceProtocol.prototype, "_ownPeerKey", null);
2775
+ SpaceProtocol = _ts_decorate7([
2776
+ trace4.resource()
2777
+ ], SpaceProtocol);
2778
+ var AuthStatus;
2779
+ (function(AuthStatus2) {
2780
+ AuthStatus2["INITIAL"] = "INITIAL";
2781
+ AuthStatus2["SUCCESS"] = "SUCCESS";
2782
+ AuthStatus2["FAILURE"] = "FAILURE";
2783
+ })(AuthStatus || (AuthStatus = {}));
2784
+ var SpaceProtocolSession = class {
2785
+ // TODO(dmaretskyi): Allow to pass in extra extensions.
2786
+ constructor({ wireParams, swarmIdentity, onSessionAuth, onAuthFailure, blobSync }) {
2787
+ // TODO(dmaretskyi): Start with upload=false when switching it on the fly works.
2788
+ this.replicator = new ReplicatorExtension().setOptions({
2789
+ upload: true
2790
+ });
2791
+ this._authStatus = "INITIAL";
2792
+ this._wireParams = wireParams;
2793
+ this._swarmIdentity = swarmIdentity;
2794
+ this._onSessionAuth = onSessionAuth;
2795
+ this._onAuthFailure = onAuthFailure;
2796
+ this._blobSync = blobSync;
2797
+ this._teleport = new Teleport(wireParams);
2798
+ }
2799
+ get authStatus() {
2800
+ return this._authStatus;
2801
+ }
2802
+ get stats() {
2803
+ return this._teleport.stats;
2804
+ }
2805
+ get stream() {
2806
+ return this._teleport.stream;
2807
+ }
2808
+ async open(sessionId) {
2809
+ await this._teleport.open(sessionId);
2810
+ this._teleport.addExtension("dxos.mesh.teleport.auth", new AuthExtension({
2811
+ provider: this._swarmIdentity.credentialProvider,
2812
+ verifier: this._swarmIdentity.credentialAuthenticator,
2813
+ onAuthSuccess: () => {
2814
+ log10("Peer authenticated", void 0, {
2815
+ F: __dxlog_file13,
2816
+ L: 248,
2817
+ S: this,
2818
+ C: (f, a) => f(...a)
2819
+ });
2820
+ this._authStatus = "SUCCESS";
2821
+ this._onSessionAuth?.(this._teleport);
2822
+ },
2823
+ onAuthFailure: () => {
2824
+ this._authStatus = "FAILURE";
2825
+ this._onAuthFailure?.(this._teleport);
2826
+ }
2827
+ }));
2828
+ this._teleport.addExtension("dxos.mesh.teleport.replicator", this.replicator);
2829
+ this._teleport.addExtension("dxos.mesh.teleport.blobsync", this._blobSync.createExtension());
2830
+ }
2831
+ async close() {
2832
+ log10("close", void 0, {
2833
+ F: __dxlog_file13,
2834
+ L: 264,
2835
+ S: this,
2836
+ C: (f, a) => f(...a)
2837
+ });
2838
+ await this._teleport.close();
2839
+ }
2840
+ async abort() {
2841
+ await this._teleport.abort();
2842
+ }
2843
+ };
2844
+ _ts_decorate7([
2845
+ logInfo2
2846
+ ], SpaceProtocolSession.prototype, "_wireParams", void 0);
2847
+ _ts_decorate7([
2848
+ logInfo2
2849
+ ], SpaceProtocolSession.prototype, "authStatus", null);
2850
+
2851
+ // packages/core/echo/echo-pipeline/src/space/space-manager.ts
2852
+ import { synchronized as synchronized4, trackLeaks as trackLeaks3, Trigger as Trigger3 } from "@dxos/async";
2853
+ import { failUndefined as failUndefined2 } from "@dxos/debug";
2854
+ import { PublicKey as PublicKey8 } from "@dxos/keys";
2855
+ import { log as log14 } from "@dxos/log";
2856
+ import { trace as trace5 } from "@dxos/protocols";
2857
+ import { ComplexMap as ComplexMap4 } from "@dxos/util";
2858
+
2859
+ // packages/core/echo/echo-pipeline/src/db-host/data-service.ts
2860
+ import { Stream } from "@dxos/codec-protobuf";
2861
+ import { invariant as invariant12 } from "@dxos/invariant";
2862
+ import { SpaceId as SpaceId3 } from "@dxos/keys";
2863
+ import { log as log13 } from "@dxos/log";
2864
+
2865
+ // packages/core/echo/echo-pipeline/src/automerge/mesh-echo-replicator.ts
2866
+ import { invariant as invariant11 } from "@dxos/invariant";
2867
+ import { PublicKey as PublicKey7 } from "@dxos/keys";
2868
+ import { log as log12 } from "@dxos/log";
2869
+ import { ComplexSet, defaultMap as defaultMap2 } from "@dxos/util";
2870
+
2871
+ // packages/core/echo/echo-pipeline/src/automerge/mesh-echo-replicator-connection.ts
2872
+ import { cbor } from "@dxos/automerge/automerge-repo";
2873
+ import { Resource as Resource6 } from "@dxos/context";
2874
+ import { invariant as invariant10 } from "@dxos/invariant";
2875
+ import { log as log11 } from "@dxos/log";
2876
+ import { AutomergeReplicator } from "@dxos/teleport-extension-automerge-replicator";
2877
+ var __dxlog_file14 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/automerge/mesh-echo-replicator-connection.ts";
2878
+ var DEFAULT_FACTORY = (params) => new AutomergeReplicator(...params);
2879
+ var MeshReplicatorConnection = class extends Resource6 {
2880
+ constructor(_params) {
2881
+ super();
2882
+ this._params = _params;
2883
+ this.remoteDeviceKey = null;
2884
+ this._remotePeerId = null;
2885
+ this._isEnabled = false;
2886
+ let readableStreamController;
2887
+ this.readable = new ReadableStream({
2888
+ start: (controller) => {
2889
+ readableStreamController = controller;
2890
+ this._ctx.onDispose(() => controller.close());
2891
+ }
2892
+ });
2893
+ this.writable = new WritableStream({
2894
+ write: async (message, controller) => {
2895
+ invariant10(this._isEnabled, "Writing to a disabled connection", {
2896
+ F: __dxlog_file14,
2897
+ L: 48,
2898
+ S: this,
2899
+ A: [
2900
+ "this._isEnabled",
2901
+ "'Writing to a disabled connection'"
2902
+ ]
2903
+ });
2904
+ try {
2905
+ await this.replicatorExtension.sendSyncMessage({
2906
+ payload: cbor.encode(message)
2907
+ });
2908
+ } catch (err) {
2909
+ controller.error(err);
2910
+ this._disconnectIfEnabled();
2911
+ }
1935
2912
  }
1936
2913
  });
1937
- this._space = _space;
1938
- }
1939
- async getHandlers() {
1940
- return {
1941
- AdmissionDiscoveryService: {
1942
- getAdmissionCredential: async (request) => {
1943
- const memberInfo = this._space.spaceState.members.get(request.memberKey);
1944
- if (!memberInfo?.credential) {
1945
- throw new ProtocolError("Space member not found.", request);
2914
+ const createAutomergeReplicator = this._params.replicatorFactory ?? DEFAULT_FACTORY;
2915
+ this.replicatorExtension = createAutomergeReplicator([
2916
+ {
2917
+ peerId: this._params.ownPeerId
2918
+ },
2919
+ {
2920
+ onStartReplication: async (info, remotePeerId) => {
2921
+ this.remoteDeviceKey = remotePeerId;
2922
+ this._remotePeerId = info.id;
2923
+ log11("onStartReplication", {
2924
+ id: info.id,
2925
+ thisPeerId: this.peerId,
2926
+ remotePeerId: remotePeerId.toHex()
2927
+ }, {
2928
+ F: __dxlog_file14,
2929
+ L: 82,
2930
+ S: this,
2931
+ C: (f, a) => f(...a)
2932
+ });
2933
+ this._params.onRemoteConnected();
2934
+ },
2935
+ onSyncMessage: async ({ payload }) => {
2936
+ if (!this._isEnabled) {
2937
+ return;
1946
2938
  }
1947
- return {
1948
- admissionCredential: memberInfo.credential
1949
- };
2939
+ const message = cbor.decode(payload);
2940
+ readableStreamController.enqueue(message);
2941
+ },
2942
+ onClose: async () => {
2943
+ this._disconnectIfEnabled();
1950
2944
  }
1951
2945
  }
1952
- };
2946
+ ]);
1953
2947
  }
1954
- };
1955
-
1956
- // packages/core/echo/echo-pipeline/src/space/space-protocol.ts
1957
- import { discoveryKey, subtleCrypto as subtleCrypto3 } from "@dxos/crypto";
1958
- import { PublicKey as PublicKey6 } from "@dxos/keys";
1959
- import { log as log10, logInfo as logInfo2 } from "@dxos/log";
1960
- import { MMSTTopology } from "@dxos/network-manager";
1961
- import { Teleport } from "@dxos/teleport";
1962
- import { BlobSync } from "@dxos/teleport-extension-object-sync";
1963
- import { ReplicatorExtension } from "@dxos/teleport-extension-replicator";
1964
- import { trace as trace3 } from "@dxos/tracing";
1965
- import { ComplexMap as ComplexMap4 } from "@dxos/util";
1966
- function _ts_decorate6(decorators, target, key, desc) {
1967
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1968
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
1969
- r = Reflect.decorate(decorators, target, key, desc);
1970
- else
1971
- for (var i = decorators.length - 1; i >= 0; i--)
1972
- if (d = decorators[i])
1973
- r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1974
- return c > 3 && r && Object.defineProperty(target, key, r), r;
1975
- }
1976
- var __dxlog_file12 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/space/space-protocol.ts";
1977
- var MOCK_AUTH_PROVIDER = async (nonce) => Buffer.from("mock");
1978
- var MOCK_AUTH_VERIFIER = async (nonce, credential) => true;
1979
- var SpaceProtocol = class {
1980
- constructor({ topic, swarmIdentity, networkManager, onSessionAuth, onAuthFailure, blobStore }) {
1981
- this._feeds = /* @__PURE__ */ new Set();
1982
- this._sessions = new ComplexMap4(PublicKey6.hash);
1983
- // TODO(burdon): Move to config (with sensible defaults).
1984
- this._topology = new MMSTTopology({
1985
- originateConnections: 4,
1986
- maxPeers: 10,
1987
- sampleSize: 20
1988
- });
1989
- this._spaceKey = topic;
1990
- this._networkManager = networkManager;
1991
- this._swarmIdentity = swarmIdentity;
1992
- this._onSessionAuth = onSessionAuth;
1993
- this._onAuthFailure = onAuthFailure;
1994
- this.blobSync = new BlobSync({
1995
- blobStore
1996
- });
1997
- this._topic = subtleCrypto3.digest("SHA-256", topic.asBuffer()).then(discoveryKey).then(PublicKey6.from);
2948
+ _disconnectIfEnabled() {
2949
+ if (this._isEnabled) {
2950
+ this._params.onRemoteDisconnected();
2951
+ }
1998
2952
  }
1999
- get sessions() {
2000
- return this._sessions;
2953
+ get peerId() {
2954
+ invariant10(this._remotePeerId != null, "Remote peer has not connected yet.", {
2955
+ F: __dxlog_file14,
2956
+ L: 108,
2957
+ S: this,
2958
+ A: [
2959
+ "this._remotePeerId != null",
2960
+ "'Remote peer has not connected yet.'"
2961
+ ]
2962
+ });
2963
+ return this._remotePeerId;
2001
2964
  }
2002
- get feeds() {
2003
- return this._feeds;
2965
+ async shouldAdvertise(params) {
2966
+ return this._params.shouldAdvertise(params);
2004
2967
  }
2005
- get _ownPeerKey() {
2006
- return this._swarmIdentity.peerKey;
2968
+ shouldSyncCollection(params) {
2969
+ return this._params.shouldSyncCollection(params);
2007
2970
  }
2008
- // TODO(burdon): Create abstraction for Space (e.g., add keys and have provider).
2009
- addFeed(feed) {
2010
- log10("addFeed", {
2011
- key: feed.key
2012
- }, {
2013
- F: __dxlog_file12,
2014
- L: 109,
2971
+ /**
2972
+ * Start exchanging messages with the remote peer.
2973
+ * Call after the remote peer has connected.
2974
+ */
2975
+ enable() {
2976
+ invariant10(this._remotePeerId != null, "Remote peer has not connected yet.", {
2977
+ F: __dxlog_file14,
2978
+ L: 125,
2015
2979
  S: this,
2016
- C: (f, a) => f(...a)
2980
+ A: [
2981
+ "this._remotePeerId != null",
2982
+ "'Remote peer has not connected yet.'"
2983
+ ]
2017
2984
  });
2018
- this._feeds.add(feed);
2019
- for (const session of this._sessions.values()) {
2020
- session.replicator.addFeed(feed);
2021
- }
2985
+ this._isEnabled = true;
2022
2986
  }
2023
- // TODO(burdon): Rename open? Common open/close interfaces for all services?
2024
- async start() {
2025
- if (this._connection) {
2026
- return;
2987
+ /**
2988
+ * Stop exchanging messages with the remote peer.
2989
+ */
2990
+ disable() {
2991
+ this._isEnabled = false;
2992
+ }
2993
+ };
2994
+
2995
+ // packages/core/echo/echo-pipeline/src/automerge/mesh-echo-replicator.ts
2996
+ var __dxlog_file15 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/automerge/mesh-echo-replicator.ts";
2997
+ var MeshEchoReplicator = class {
2998
+ constructor() {
2999
+ this._connections = /* @__PURE__ */ new Set();
3000
+ /**
3001
+ * Using automerge peerId as a key.
3002
+ */
3003
+ this._connectionsPerPeer = /* @__PURE__ */ new Map();
3004
+ /**
3005
+ * spaceId -> deviceKey[]
3006
+ */
3007
+ this._authorizedDevices = /* @__PURE__ */ new Map();
3008
+ this._context = null;
3009
+ }
3010
+ async connect(context) {
3011
+ this._context = context;
3012
+ }
3013
+ async disconnect() {
3014
+ for (const connection of this._connectionsPerPeer.values()) {
3015
+ this._context?.onConnectionClosed(connection);
2027
3016
  }
2028
- const credentials = await this._swarmIdentity.credentialProvider(Buffer.from(""));
2029
- await this.blobSync.open();
2030
- log10("starting...", void 0, {
2031
- F: __dxlog_file12,
2032
- L: 128,
3017
+ for (const connection of this._connections) {
3018
+ await connection.close();
3019
+ }
3020
+ this._connections.clear();
3021
+ this._connectionsPerPeer.clear();
3022
+ this._context = null;
3023
+ }
3024
+ createExtension(extensionFactory) {
3025
+ invariant11(this._context, void 0, {
3026
+ F: __dxlog_file15,
3027
+ L: 56,
2033
3028
  S: this,
2034
- C: (f, a) => f(...a)
3029
+ A: [
3030
+ "this._context",
3031
+ ""
3032
+ ]
2035
3033
  });
2036
- const topic = await this._topic;
2037
- this._connection = await this._networkManager.joinSwarm({
2038
- protocolProvider: this._createProtocolProvider(credentials),
2039
- peerId: this._swarmIdentity.peerKey,
2040
- topic,
2041
- topology: this._topology,
2042
- label: `swarm ${topic.truncate()} for space ${this._spaceKey.truncate()}`
3034
+ const connection = new MeshReplicatorConnection({
3035
+ ownPeerId: this._context.peerId,
3036
+ replicatorFactory: extensionFactory,
3037
+ onRemoteConnected: async () => {
3038
+ log12("onRemoteConnected", {
3039
+ peerId: connection.peerId
3040
+ }, {
3041
+ F: __dxlog_file15,
3042
+ L: 62,
3043
+ S: this,
3044
+ C: (f, a) => f(...a)
3045
+ });
3046
+ invariant11(this._context, void 0, {
3047
+ F: __dxlog_file15,
3048
+ L: 63,
3049
+ S: this,
3050
+ A: [
3051
+ "this._context",
3052
+ ""
3053
+ ]
3054
+ });
3055
+ if (this._connectionsPerPeer.has(connection.peerId)) {
3056
+ this._context.onConnectionAuthScopeChanged(connection);
3057
+ } else {
3058
+ this._connectionsPerPeer.set(connection.peerId, connection);
3059
+ this._context.onConnectionOpen(connection);
3060
+ connection.enable();
3061
+ }
3062
+ },
3063
+ onRemoteDisconnected: async () => {
3064
+ log12("onRemoteDisconnected", {
3065
+ peerId: connection.peerId
3066
+ }, {
3067
+ F: __dxlog_file15,
3068
+ L: 74,
3069
+ S: this,
3070
+ C: (f, a) => f(...a)
3071
+ });
3072
+ this._context?.onConnectionClosed(connection);
3073
+ this._connectionsPerPeer.delete(connection.peerId);
3074
+ connection.disable();
3075
+ this._connections.delete(connection);
3076
+ },
3077
+ shouldAdvertise: async (params) => {
3078
+ log12("shouldAdvertise", {
3079
+ peerId: connection.peerId,
3080
+ documentId: params.documentId
3081
+ }, {
3082
+ F: __dxlog_file15,
3083
+ L: 81,
3084
+ S: this,
3085
+ C: (f, a) => f(...a)
3086
+ });
3087
+ invariant11(this._context, void 0, {
3088
+ F: __dxlog_file15,
3089
+ L: 82,
3090
+ S: this,
3091
+ A: [
3092
+ "this._context",
3093
+ ""
3094
+ ]
3095
+ });
3096
+ try {
3097
+ const spaceKey = await this._context.getContainingSpaceForDocument(params.documentId);
3098
+ if (!spaceKey) {
3099
+ log12("space key not found for share policy check", {
3100
+ peerId: connection.peerId,
3101
+ documentId: params.documentId
3102
+ }, {
3103
+ F: __dxlog_file15,
3104
+ L: 86,
3105
+ S: this,
3106
+ C: (f, a) => f(...a)
3107
+ });
3108
+ return false;
3109
+ }
3110
+ const spaceId = await createIdFromSpaceKey(spaceKey);
3111
+ const authorizedDevices = this._authorizedDevices.get(spaceId);
3112
+ if (!connection.remoteDeviceKey) {
3113
+ log12("device key not found for share policy check", {
3114
+ peerId: connection.peerId,
3115
+ documentId: params.documentId
3116
+ }, {
3117
+ F: __dxlog_file15,
3118
+ L: 98,
3119
+ S: this,
3120
+ C: (f, a) => f(...a)
3121
+ });
3122
+ return false;
3123
+ }
3124
+ const isAuthorized = authorizedDevices?.has(connection.remoteDeviceKey) ?? false;
3125
+ log12("share policy check", {
3126
+ localPeer: this._context.peerId,
3127
+ remotePeer: connection.peerId,
3128
+ documentId: params.documentId,
3129
+ deviceKey: connection.remoteDeviceKey,
3130
+ spaceKey,
3131
+ isAuthorized
3132
+ }, {
3133
+ F: __dxlog_file15,
3134
+ L: 106,
3135
+ S: this,
3136
+ C: (f, a) => f(...a)
3137
+ });
3138
+ return isAuthorized;
3139
+ } catch (err) {
3140
+ log12.catch(err, void 0, {
3141
+ F: __dxlog_file15,
3142
+ L: 116,
3143
+ S: this,
3144
+ C: (f, a) => f(...a)
3145
+ });
3146
+ return false;
3147
+ }
3148
+ },
3149
+ shouldSyncCollection: ({ collectionId }) => {
3150
+ const spaceId = getSpaceIdFromCollectionId(collectionId);
3151
+ const authorizedDevices = this._authorizedDevices.get(spaceId);
3152
+ if (!connection.remoteDeviceKey) {
3153
+ log12("device key not found for collection sync check", {
3154
+ peerId: connection.peerId,
3155
+ collectionId
3156
+ }, {
3157
+ F: __dxlog_file15,
3158
+ L: 126,
3159
+ S: this,
3160
+ C: (f, a) => f(...a)
3161
+ });
3162
+ return false;
3163
+ }
3164
+ const isAuthorized = authorizedDevices?.has(connection.remoteDeviceKey) ?? false;
3165
+ return isAuthorized;
3166
+ }
2043
3167
  });
2044
- log10("started", void 0, {
2045
- F: __dxlog_file12,
2046
- L: 138,
3168
+ this._connections.add(connection);
3169
+ return connection.replicatorExtension;
3170
+ }
3171
+ async authorizeDevice(spaceKey, deviceKey) {
3172
+ log12("authorizeDevice", {
3173
+ spaceKey,
3174
+ deviceKey
3175
+ }, {
3176
+ F: __dxlog_file15,
3177
+ L: 143,
2047
3178
  S: this,
2048
3179
  C: (f, a) => f(...a)
2049
3180
  });
3181
+ const spaceId = await createIdFromSpaceKey(spaceKey);
3182
+ defaultMap2(this._authorizedDevices, spaceId, () => new ComplexSet(PublicKey7.hash)).add(deviceKey);
3183
+ for (const connection of this._connections) {
3184
+ if (connection.remoteDeviceKey && connection.remoteDeviceKey.equals(deviceKey)) {
3185
+ if (this._connectionsPerPeer.has(connection.peerId)) {
3186
+ this._context?.onConnectionAuthScopeChanged(connection);
3187
+ }
3188
+ }
3189
+ }
2050
3190
  }
2051
- updateTopology() {
2052
- this._topology.forceUpdate();
3191
+ };
3192
+
3193
+ // packages/core/echo/echo-pipeline/src/db-host/data-service.ts
3194
+ var __dxlog_file16 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/db-host/data-service.ts";
3195
+ var DataServiceImpl = class {
3196
+ constructor(params) {
3197
+ /**
3198
+ * Map of subscriptions.
3199
+ * subscriptionId -> DocumentsSynchronizer
3200
+ */
3201
+ this._subscriptions = /* @__PURE__ */ new Map();
3202
+ this._automergeHost = params.automergeHost;
3203
+ this._updateIndexes = params.updateIndexes;
2053
3204
  }
2054
- async stop() {
2055
- await this.blobSync.close();
2056
- if (this._connection) {
2057
- log10("stopping...", void 0, {
2058
- F: __dxlog_file12,
2059
- L: 149,
2060
- S: this,
2061
- C: (f, a) => f(...a)
3205
+ subscribe(request) {
3206
+ return new Stream(({ next, ready }) => {
3207
+ const synchronizer = new DocumentsSynchronizer({
3208
+ repo: this._automergeHost.repo,
3209
+ sendUpdates: (updates) => next(updates)
2062
3210
  });
2063
- await this._connection.close();
2064
- log10("stopped", void 0, {
2065
- F: __dxlog_file12,
2066
- L: 151,
3211
+ synchronizer.open().then(() => {
3212
+ this._subscriptions.set(request.subscriptionId, synchronizer);
3213
+ ready();
3214
+ }).catch((err) => log13.catch(err, void 0, {
3215
+ F: __dxlog_file16,
3216
+ L: 64,
2067
3217
  S: this,
2068
3218
  C: (f, a) => f(...a)
2069
- });
3219
+ }));
3220
+ return () => synchronizer.close();
3221
+ });
3222
+ }
3223
+ async updateSubscription(request) {
3224
+ const synchronizer = this._subscriptions.get(request.subscriptionId);
3225
+ invariant12(synchronizer, "Subscription not found", {
3226
+ F: __dxlog_file16,
3227
+ L: 71,
3228
+ S: this,
3229
+ A: [
3230
+ "synchronizer",
3231
+ "'Subscription not found'"
3232
+ ]
3233
+ });
3234
+ if (request.addIds?.length) {
3235
+ await synchronizer.addDocuments(request.addIds);
3236
+ }
3237
+ if (request.removeIds?.length) {
3238
+ await synchronizer.removeDocuments(request.removeIds);
3239
+ }
3240
+ }
3241
+ async update(request) {
3242
+ if (!request.updates) {
3243
+ return;
2070
3244
  }
3245
+ const synchronizer = this._subscriptions.get(request.subscriptionId);
3246
+ invariant12(synchronizer, "Subscription not found", {
3247
+ F: __dxlog_file16,
3248
+ L: 86,
3249
+ S: this,
3250
+ A: [
3251
+ "synchronizer",
3252
+ "'Subscription not found'"
3253
+ ]
3254
+ });
3255
+ synchronizer.update(request.updates);
2071
3256
  }
2072
- _createProtocolProvider(credentials) {
2073
- return (wireParams) => {
2074
- const session = new SpaceProtocolSession({
2075
- wireParams,
2076
- swarmIdentity: this._swarmIdentity,
2077
- onSessionAuth: this._onSessionAuth,
2078
- onAuthFailure: this._onAuthFailure,
2079
- blobSync: this.blobSync
2080
- });
2081
- this._sessions.set(wireParams.remotePeerId, session);
2082
- for (const feed of this._feeds) {
2083
- session.replicator.addFeed(feed);
3257
+ async flush(request) {
3258
+ await this._automergeHost.flush(request);
3259
+ }
3260
+ async getDocumentHeads(request) {
3261
+ const documentIds = request.documentIds;
3262
+ if (!documentIds) {
3263
+ return {
3264
+ heads: {
3265
+ entries: []
3266
+ }
3267
+ };
3268
+ }
3269
+ const heads = await this._automergeHost.getHeads(documentIds);
3270
+ return {
3271
+ heads: {
3272
+ entries: heads.map((heads2, idx) => ({
3273
+ documentId: documentIds[idx],
3274
+ heads: heads2
3275
+ }))
2084
3276
  }
2085
- return session;
2086
3277
  };
2087
3278
  }
2088
- };
2089
- _ts_decorate6([
2090
- logInfo2,
2091
- trace3.info()
2092
- ], SpaceProtocol.prototype, "_topic", void 0);
2093
- _ts_decorate6([
2094
- trace3.info()
2095
- ], SpaceProtocol.prototype, "_spaceKey", void 0);
2096
- _ts_decorate6([
2097
- logInfo2
2098
- ], SpaceProtocol.prototype, "_ownPeerKey", null);
2099
- SpaceProtocol = _ts_decorate6([
2100
- trace3.resource()
2101
- ], SpaceProtocol);
2102
- var AuthStatus;
2103
- (function(AuthStatus2) {
2104
- AuthStatus2["INITIAL"] = "INITIAL";
2105
- AuthStatus2["SUCCESS"] = "SUCCESS";
2106
- AuthStatus2["FAILURE"] = "FAILURE";
2107
- })(AuthStatus || (AuthStatus = {}));
2108
- var SpaceProtocolSession = class {
2109
- // TODO(dmaretskyi): Allow to pass in extra extensions.
2110
- constructor({ wireParams, swarmIdentity, onSessionAuth, onAuthFailure, blobSync }) {
2111
- // TODO(dmaretskyi): Start with upload=false when switching it on the fly works.
2112
- this.replicator = new ReplicatorExtension().setOptions({
2113
- upload: true
2114
- });
2115
- this._authStatus = "INITIAL";
2116
- this._wireParams = wireParams;
2117
- this._swarmIdentity = swarmIdentity;
2118
- this._onSessionAuth = onSessionAuth;
2119
- this._onAuthFailure = onAuthFailure;
2120
- this._blobSync = blobSync;
2121
- this._teleport = new Teleport(wireParams);
2122
- }
2123
- get authStatus() {
2124
- return this._authStatus;
2125
- }
2126
- get stats() {
2127
- return this._teleport.stats;
3279
+ async waitUntilHeadsReplicated(request, options) {
3280
+ await this._automergeHost.waitUntilHeadsReplicated(request.heads);
2128
3281
  }
2129
- get stream() {
2130
- return this._teleport.stream;
3282
+ async reIndexHeads(request, options) {
3283
+ await this._automergeHost.reIndexHeads(request.documentIds ?? []);
2131
3284
  }
2132
- async open(sessionId) {
2133
- await this._teleport.open(sessionId);
2134
- this._teleport.addExtension("dxos.mesh.teleport.auth", new AuthExtension({
2135
- provider: this._swarmIdentity.credentialProvider,
2136
- verifier: this._swarmIdentity.credentialAuthenticator,
2137
- onAuthSuccess: () => {
2138
- log10("Peer authenticated", void 0, {
2139
- F: __dxlog_file12,
2140
- L: 248,
2141
- S: this,
2142
- C: (f, a) => f(...a)
2143
- });
2144
- this._authStatus = "SUCCESS";
2145
- this._onSessionAuth?.(this._teleport);
2146
- },
2147
- onAuthFailure: () => {
2148
- this._authStatus = "FAILURE";
2149
- this._onAuthFailure?.(this._teleport);
2150
- }
2151
- }));
2152
- this._teleport.addExtension("dxos.mesh.teleport.replicator", this.replicator);
2153
- this._teleport.addExtension("dxos.mesh.teleport.blobsync", this._blobSync.createExtension());
3285
+ async updateIndexes() {
3286
+ await this._updateIndexes();
2154
3287
  }
2155
- async close() {
2156
- log10("close", void 0, {
2157
- F: __dxlog_file12,
2158
- L: 264,
3288
+ async getSpaceSyncState(request, options) {
3289
+ invariant12(SpaceId3.isValid(request.spaceId), void 0, {
3290
+ F: __dxlog_file16,
3291
+ L: 127,
2159
3292
  S: this,
2160
- C: (f, a) => f(...a)
3293
+ A: [
3294
+ "SpaceId.isValid(request.spaceId)",
3295
+ ""
3296
+ ]
2161
3297
  });
2162
- await this._teleport.close();
2163
- }
2164
- async abort() {
2165
- await this._teleport.abort();
3298
+ const collectionId = deriveCollectionIdFromSpaceId(request.spaceId);
3299
+ const state = await this._automergeHost.getCollectionSyncState(collectionId);
3300
+ return {
3301
+ peers: state.peers.map((peer) => ({
3302
+ peerId: peer.peerId,
3303
+ documentsToReconcile: peer.differentDocuments
3304
+ }))
3305
+ };
2166
3306
  }
2167
3307
  };
2168
- _ts_decorate6([
2169
- logInfo2
2170
- ], SpaceProtocolSession.prototype, "_wireParams", void 0);
2171
- _ts_decorate6([
2172
- logInfo2
2173
- ], SpaceProtocolSession.prototype, "authStatus", null);
2174
3308
 
2175
3309
  // packages/core/echo/echo-pipeline/src/space/space-manager.ts
2176
- import { synchronized as synchronized4, trackLeaks as trackLeaks3, Trigger as Trigger2 } from "@dxos/async";
2177
- import { failUndefined as failUndefined2 } from "@dxos/debug";
2178
- import { PublicKey as PublicKey7 } from "@dxos/keys";
2179
- import { log as log11 } from "@dxos/log";
2180
- import { trace as trace4 } from "@dxos/protocols";
2181
- import { ComplexMap as ComplexMap5 } from "@dxos/util";
2182
- function _ts_decorate7(decorators, target, key, desc) {
3310
+ function _ts_decorate8(decorators, target, key, desc) {
2183
3311
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2184
3312
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
2185
3313
  r = Reflect.decorate(decorators, target, key, desc);
@@ -2189,11 +3317,11 @@ function _ts_decorate7(decorators, target, key, desc) {
2189
3317
  r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
2190
3318
  return c > 3 && r && Object.defineProperty(target, key, r), r;
2191
3319
  }
2192
- var __dxlog_file13 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/space/space-manager.ts";
3320
+ var __dxlog_file17 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/space/space-manager.ts";
2193
3321
  var SpaceManager = class {
2194
3322
  constructor({ feedStore, networkManager, metadataStore, snapshotStore, blobStore }) {
2195
- this._spaces = new ComplexMap5(PublicKey7.hash);
2196
- this._instanceId = PublicKey7.random().toHex();
3323
+ this._spaces = new ComplexMap4(PublicKey8.hash);
3324
+ this._instanceId = PublicKey8.random().toHex();
2197
3325
  this._feedStore = feedStore;
2198
3326
  this._networkManager = networkManager;
2199
3327
  this._metadataStore = metadataStore;
@@ -2212,18 +3340,18 @@ var SpaceManager = class {
2212
3340
  ].map((space) => space.close()));
2213
3341
  }
2214
3342
  async constructSpace({ metadata, swarmIdentity, onAuthorizedConnection, onAuthFailure, onDelegatedInvitationStatusChange, onMemberRolesChanged, memberKey }) {
2215
- log11.trace("dxos.echo.space-manager.construct-space", trace4.begin({
3343
+ log14.trace("dxos.echo.space-manager.construct-space", trace5.begin({
2216
3344
  id: this._instanceId
2217
3345
  }), {
2218
- F: __dxlog_file13,
3346
+ F: __dxlog_file17,
2219
3347
  L: 103,
2220
3348
  S: this,
2221
3349
  C: (f, a) => f(...a)
2222
3350
  });
2223
- log11("constructing space...", {
3351
+ log14("constructing space...", {
2224
3352
  spaceKey: metadata.genesisFeedKey
2225
3353
  }, {
2226
- F: __dxlog_file13,
3354
+ F: __dxlog_file17,
2227
3355
  L: 104,
2228
3356
  S: this,
2229
3357
  C: (f, a) => f(...a)
@@ -2253,10 +3381,10 @@ var SpaceManager = class {
2253
3381
  onMemberRolesChanged
2254
3382
  });
2255
3383
  this._spaces.set(space.key, space);
2256
- log11.trace("dxos.echo.space-manager.construct-space", trace4.end({
3384
+ log14.trace("dxos.echo.space-manager.construct-space", trace5.end({
2257
3385
  id: this._instanceId
2258
3386
  }), {
2259
- F: __dxlog_file13,
3387
+ F: __dxlog_file17,
2260
3388
  L: 135,
2261
3389
  S: this,
2262
3390
  C: (f, a) => f(...a)
@@ -2265,93 +3393,439 @@ var SpaceManager = class {
2265
3393
  }
2266
3394
  async requestSpaceAdmissionCredential(params) {
2267
3395
  const traceKey = "dxos.echo.space-manager.request-space-admission";
2268
- log11.trace(traceKey, trace4.begin({
3396
+ log14.trace(traceKey, trace5.begin({
2269
3397
  id: this._instanceId
2270
3398
  }), {
2271
- F: __dxlog_file13,
3399
+ F: __dxlog_file17,
2272
3400
  L: 141,
2273
3401
  S: this,
2274
3402
  C: (f, a) => f(...a)
2275
3403
  });
2276
- log11("requesting space admission credential...", {
3404
+ log14("requesting space admission credential...", {
2277
3405
  spaceKey: params.spaceKey
2278
3406
  }, {
2279
- F: __dxlog_file13,
2280
- L: 142,
3407
+ F: __dxlog_file17,
3408
+ L: 142,
3409
+ S: this,
3410
+ C: (f, a) => f(...a)
3411
+ });
3412
+ const onCredentialResolved = new Trigger3();
3413
+ const protocol = new SpaceProtocol({
3414
+ topic: params.spaceKey,
3415
+ swarmIdentity: params.swarmIdentity,
3416
+ networkManager: this._networkManager,
3417
+ onSessionAuth: (session) => {
3418
+ session.addExtension("dxos.mesh.teleport.admission-discovery", new CredentialRetrieverExtension({
3419
+ spaceKey: params.spaceKey,
3420
+ memberKey: params.identityKey
3421
+ }, onCredentialResolved));
3422
+ },
3423
+ onAuthFailure: (session) => session.close(),
3424
+ blobStore: this._blobStore
3425
+ });
3426
+ try {
3427
+ await protocol.start();
3428
+ const credential = await onCredentialResolved.wait({
3429
+ timeout: params.timeout
3430
+ });
3431
+ log14.trace(traceKey, trace5.end({
3432
+ id: this._instanceId
3433
+ }), {
3434
+ F: __dxlog_file17,
3435
+ L: 165,
3436
+ S: this,
3437
+ C: (f, a) => f(...a)
3438
+ });
3439
+ return credential;
3440
+ } catch (err) {
3441
+ log14.trace(traceKey, trace5.error({
3442
+ id: this._instanceId,
3443
+ error: err
3444
+ }), {
3445
+ F: __dxlog_file17,
3446
+ L: 168,
3447
+ S: this,
3448
+ C: (f, a) => f(...a)
3449
+ });
3450
+ throw err;
3451
+ } finally {
3452
+ await protocol.stop();
3453
+ }
3454
+ }
3455
+ };
3456
+ _ts_decorate8([
3457
+ synchronized4
3458
+ ], SpaceManager.prototype, "open", null);
3459
+ _ts_decorate8([
3460
+ synchronized4
3461
+ ], SpaceManager.prototype, "close", null);
3462
+ SpaceManager = _ts_decorate8([
3463
+ trackLeaks3("open", "close")
3464
+ ], SpaceManager);
3465
+
3466
+ // packages/core/echo/echo-pipeline/src/metadata/metadata-store.ts
3467
+ import CRC32 from "crc-32";
3468
+ import { Event as Event6, scheduleTaskInterval as scheduleTaskInterval2, synchronized as synchronized5 } from "@dxos/async";
3469
+ import { Context as Context6 } from "@dxos/context";
3470
+ import { invariant as invariant13 } from "@dxos/invariant";
3471
+ import { PublicKey as PublicKey9 } from "@dxos/keys";
3472
+ import { log as log15 } from "@dxos/log";
3473
+ import { DataCorruptionError, STORAGE_VERSION, schema as schema6 } from "@dxos/protocols";
3474
+ import { Invitation, SpaceState } from "@dxos/protocols/proto/dxos/client/services";
3475
+ import { ComplexMap as ComplexMap5, arrayToBuffer, forEachAsync, isNotNullOrUndefined } from "@dxos/util";
3476
+ function _ts_decorate9(decorators, target, key, desc) {
3477
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3478
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
3479
+ r = Reflect.decorate(decorators, target, key, desc);
3480
+ else
3481
+ for (var i = decorators.length - 1; i >= 0; i--)
3482
+ if (d = decorators[i])
3483
+ r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
3484
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
3485
+ }
3486
+ var __dxlog_file18 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/metadata/metadata-store.ts";
3487
+ var EXPIRED_INVITATION_CLEANUP_INTERVAL = 60 * 60 * 1e3;
3488
+ var emptyEchoMetadata = () => ({
3489
+ version: STORAGE_VERSION,
3490
+ spaces: [],
3491
+ created: /* @__PURE__ */ new Date(),
3492
+ updated: /* @__PURE__ */ new Date()
3493
+ });
3494
+ var emptyLargeSpaceMetadata = () => ({});
3495
+ var EchoMetadata = schema6.getCodecForType("dxos.echo.metadata.EchoMetadata");
3496
+ var LargeSpaceMetadata = schema6.getCodecForType("dxos.echo.metadata.LargeSpaceMetadata");
3497
+ var MetadataStore = class {
3498
+ constructor(directory) {
3499
+ this._metadata = emptyEchoMetadata();
3500
+ this._spaceLargeMetadata = new ComplexMap5(PublicKey9.hash);
3501
+ this._metadataFile = void 0;
3502
+ this.update = new Event6();
3503
+ this._invitationCleanupCtx = new Context6(void 0, {
3504
+ F: __dxlog_file18,
3505
+ L: 53
3506
+ });
3507
+ this._directory = directory;
3508
+ }
3509
+ get metadata() {
3510
+ return this._metadata;
3511
+ }
3512
+ get version() {
3513
+ return this._metadata.version ?? 0;
3514
+ }
3515
+ /**
3516
+ * Returns a list of currently saved spaces. The list and objects in it can be modified addSpace and
3517
+ * addSpaceFeed functions.
3518
+ */
3519
+ get spaces() {
3520
+ return this._metadata.spaces ?? [];
3521
+ }
3522
+ async _readFile(file, codec2) {
3523
+ try {
3524
+ const { size: fileLength } = await file.stat();
3525
+ if (fileLength < 8) {
3526
+ return;
3527
+ }
3528
+ const dataSize = fromBytesInt32(await file.read(0, 4));
3529
+ const checksum = fromBytesInt32(await file.read(4, 4));
3530
+ log15("loaded", {
3531
+ size: dataSize,
3532
+ checksum,
3533
+ name: file.filename
3534
+ }, {
3535
+ F: __dxlog_file18,
3536
+ L: 89,
3537
+ S: this,
3538
+ C: (f, a) => f(...a)
3539
+ });
3540
+ if (fileLength < dataSize + 8) {
3541
+ throw new DataCorruptionError("Metadata size is smaller than expected.", {
3542
+ fileLength,
3543
+ dataSize
3544
+ });
3545
+ }
3546
+ const data = await file.read(8, dataSize);
3547
+ const calculatedChecksum = CRC32.buf(data);
3548
+ if (calculatedChecksum !== checksum) {
3549
+ throw new DataCorruptionError("Metadata checksum is invalid.");
3550
+ }
3551
+ return codec2.decode(data);
3552
+ } finally {
3553
+ await file.close();
3554
+ }
3555
+ }
3556
+ /**
3557
+ * @internal
3558
+ */
3559
+ async _writeFile(file, codec2, data) {
3560
+ const encoded = arrayToBuffer(codec2.encode(data));
3561
+ const checksum = CRC32.buf(encoded);
3562
+ const result = Buffer.alloc(8 + encoded.length);
3563
+ result.writeInt32LE(encoded.length, 0);
3564
+ result.writeInt32LE(checksum, 4);
3565
+ encoded.copy(result, 8);
3566
+ await file.write(0, result);
3567
+ log15("saved", {
3568
+ size: encoded.length,
3569
+ checksum
3570
+ }, {
3571
+ F: __dxlog_file18,
3572
+ L: 124,
2281
3573
  S: this,
2282
3574
  C: (f, a) => f(...a)
2283
3575
  });
2284
- const onCredentialResolved = new Trigger2();
2285
- const protocol = new SpaceProtocol({
2286
- topic: params.spaceKey,
2287
- swarmIdentity: params.swarmIdentity,
2288
- networkManager: this._networkManager,
2289
- onSessionAuth: (session) => {
2290
- session.addExtension("dxos.mesh.teleport.admission-discovery", new CredentialRetrieverExtension({
2291
- spaceKey: params.spaceKey,
2292
- memberKey: params.identityKey
2293
- }, onCredentialResolved));
2294
- },
2295
- onAuthFailure: (session) => session.close(),
2296
- blobStore: this._blobStore
2297
- });
3576
+ }
3577
+ async close() {
3578
+ await this._invitationCleanupCtx.dispose();
3579
+ await this.flush();
3580
+ await this._metadataFile?.close();
3581
+ this._metadataFile = void 0;
3582
+ this._metadata = emptyEchoMetadata();
3583
+ this._spaceLargeMetadata.clear();
3584
+ }
3585
+ /**
3586
+ * Loads metadata from persistent storage.
3587
+ */
3588
+ async load() {
3589
+ if (!this._metadataFile || this._metadataFile.closed) {
3590
+ this._metadataFile = this._directory.getOrCreateFile("EchoMetadata");
3591
+ }
2298
3592
  try {
2299
- await protocol.start();
2300
- const credential = await onCredentialResolved.wait({
2301
- timeout: params.timeout
3593
+ const metadata = await this._readFile(this._metadataFile, EchoMetadata);
3594
+ if (metadata) {
3595
+ this._metadata = metadata;
3596
+ }
3597
+ this._metadata.spaces?.forEach((space) => {
3598
+ space.state ??= SpaceState.ACTIVE;
2302
3599
  });
2303
- log11.trace(traceKey, trace4.end({
2304
- id: this._instanceId
2305
- }), {
2306
- F: __dxlog_file13,
2307
- L: 165,
3600
+ } catch (err) {
3601
+ log15.error("failed to load metadata", {
3602
+ err
3603
+ }, {
3604
+ F: __dxlog_file18,
3605
+ L: 156,
2308
3606
  S: this,
2309
3607
  C: (f, a) => f(...a)
2310
3608
  });
2311
- return credential;
3609
+ this._metadata = emptyEchoMetadata();
3610
+ }
3611
+ await forEachAsync([
3612
+ this._metadata.identity?.haloSpace.key,
3613
+ ...this._metadata.spaces?.map((space) => space.key) ?? []
3614
+ ].filter(isNotNullOrUndefined), async (key) => {
3615
+ try {
3616
+ await this._loadSpaceLargeMetadata(key);
3617
+ } catch (err) {
3618
+ log15.error("failed to load space large metadata", {
3619
+ err
3620
+ }, {
3621
+ F: __dxlog_file18,
3622
+ L: 168,
3623
+ S: this,
3624
+ C: (f, a) => f(...a)
3625
+ });
3626
+ }
3627
+ });
3628
+ scheduleTaskInterval2(this._invitationCleanupCtx, async () => {
3629
+ for (const invitation of this._metadata.invitations ?? []) {
3630
+ if (hasInvitationExpired(invitation) || isLegacyInvitationFormat(invitation)) {
3631
+ await this.removeInvitation(invitation.invitationId);
3632
+ }
3633
+ }
3634
+ }, EXPIRED_INVITATION_CLEANUP_INTERVAL);
3635
+ }
3636
+ async _save() {
3637
+ const data = {
3638
+ ...this._metadata,
3639
+ version: STORAGE_VERSION,
3640
+ created: this._metadata.created ?? /* @__PURE__ */ new Date(),
3641
+ updated: /* @__PURE__ */ new Date()
3642
+ };
3643
+ this.update.emit(data);
3644
+ const file = this._directory.getOrCreateFile("EchoMetadata");
3645
+ await this._writeFile(file, EchoMetadata, data);
3646
+ }
3647
+ async _loadSpaceLargeMetadata(key) {
3648
+ const file = this._directory.getOrCreateFile(`space_${key.toHex()}_large`);
3649
+ try {
3650
+ const metadata = await this._readFile(file, LargeSpaceMetadata);
3651
+ if (metadata) {
3652
+ this._spaceLargeMetadata.set(key, metadata);
3653
+ }
2312
3654
  } catch (err) {
2313
- log11.trace(traceKey, trace4.error({
2314
- id: this._instanceId,
2315
- error: err
2316
- }), {
2317
- F: __dxlog_file13,
2318
- L: 168,
3655
+ log15.error("failed to load space large metadata", {
3656
+ err
3657
+ }, {
3658
+ F: __dxlog_file18,
3659
+ L: 210,
2319
3660
  S: this,
2320
3661
  C: (f, a) => f(...a)
2321
3662
  });
2322
- throw err;
2323
- } finally {
2324
- await protocol.stop();
2325
3663
  }
2326
3664
  }
3665
+ async _saveSpaceLargeMetadata(key) {
3666
+ const data = this._getLargeSpaceMetadata(key);
3667
+ const file = this._directory.getOrCreateFile(`space_${key.toHex()}_large`);
3668
+ await this._writeFile(file, LargeSpaceMetadata, data);
3669
+ }
3670
+ async flush() {
3671
+ await this._directory.flush();
3672
+ }
3673
+ _getSpace(spaceKey) {
3674
+ if (this._metadata.identity?.haloSpace.key.equals(spaceKey)) {
3675
+ return this._metadata.identity.haloSpace;
3676
+ }
3677
+ const space = this.spaces.find((space2) => space2.key === spaceKey);
3678
+ invariant13(space, "Space not found", {
3679
+ F: __dxlog_file18,
3680
+ L: 232,
3681
+ S: this,
3682
+ A: [
3683
+ "space",
3684
+ "'Space not found'"
3685
+ ]
3686
+ });
3687
+ return space;
3688
+ }
3689
+ _getLargeSpaceMetadata(key) {
3690
+ let entry = this._spaceLargeMetadata.get(key);
3691
+ if (entry) {
3692
+ return entry;
3693
+ }
3694
+ entry = emptyLargeSpaceMetadata();
3695
+ this._spaceLargeMetadata.set(key, entry);
3696
+ return entry;
3697
+ }
3698
+ /**
3699
+ * Clears storage - doesn't work for now.
3700
+ */
3701
+ async clear() {
3702
+ log15("clearing all metadata", void 0, {
3703
+ F: __dxlog_file18,
3704
+ L: 251,
3705
+ S: this,
3706
+ C: (f, a) => f(...a)
3707
+ });
3708
+ await this._directory.delete();
3709
+ this._metadata = emptyEchoMetadata();
3710
+ }
3711
+ getIdentityRecord() {
3712
+ return this._metadata.identity;
3713
+ }
3714
+ async setIdentityRecord(record) {
3715
+ invariant13(!this._metadata.identity, "Cannot overwrite existing identity in metadata", {
3716
+ F: __dxlog_file18,
3717
+ L: 261,
3718
+ S: this,
3719
+ A: [
3720
+ "!this._metadata.identity",
3721
+ "'Cannot overwrite existing identity in metadata'"
3722
+ ]
3723
+ });
3724
+ this._metadata.identity = record;
3725
+ await this._save();
3726
+ await this.flush();
3727
+ }
3728
+ getInvitations() {
3729
+ return this._metadata.invitations ?? [];
3730
+ }
3731
+ async addInvitation(invitation) {
3732
+ if (this._metadata.invitations?.find((i) => i.invitationId === invitation.invitationId)) {
3733
+ return;
3734
+ }
3735
+ (this._metadata.invitations ??= []).push(invitation);
3736
+ await this._save();
3737
+ await this.flush();
3738
+ }
3739
+ async removeInvitation(invitationId) {
3740
+ this._metadata.invitations = (this._metadata.invitations ?? []).filter((i) => i.invitationId !== invitationId);
3741
+ await this._save();
3742
+ await this.flush();
3743
+ }
3744
+ async addSpace(record) {
3745
+ invariant13(!(this._metadata.spaces ?? []).find((space) => space.key === record.key), "Cannot overwrite existing space in metadata", {
3746
+ F: __dxlog_file18,
3747
+ L: 289,
3748
+ S: this,
3749
+ A: [
3750
+ "!(this._metadata.spaces ?? []).find((space) => space.key === record.key)",
3751
+ "'Cannot overwrite existing space in metadata'"
3752
+ ]
3753
+ });
3754
+ (this._metadata.spaces ??= []).push(record);
3755
+ await this._save();
3756
+ await this.flush();
3757
+ }
3758
+ async setSpaceDataLatestTimeframe(spaceKey, timeframe) {
3759
+ this._getSpace(spaceKey).dataTimeframe = timeframe;
3760
+ await this._save();
3761
+ }
3762
+ async setSpaceControlLatestTimeframe(spaceKey, timeframe) {
3763
+ this._getSpace(spaceKey).controlTimeframe = timeframe;
3764
+ await this._save();
3765
+ await this.flush();
3766
+ }
3767
+ async setCache(spaceKey, cache) {
3768
+ this._getSpace(spaceKey).cache = cache;
3769
+ await this._save();
3770
+ }
3771
+ async setWritableFeedKeys(spaceKey, controlFeedKey, dataFeedKey) {
3772
+ const space = this._getSpace(spaceKey);
3773
+ space.controlFeedKey = controlFeedKey;
3774
+ space.dataFeedKey = dataFeedKey;
3775
+ await this._save();
3776
+ await this.flush();
3777
+ }
3778
+ async setSpaceState(spaceKey, state) {
3779
+ this._getSpace(spaceKey).state = state;
3780
+ await this._save();
3781
+ await this.flush();
3782
+ }
3783
+ getSpaceControlPipelineSnapshot(spaceKey) {
3784
+ return this._getLargeSpaceMetadata(spaceKey).controlPipelineSnapshot;
3785
+ }
3786
+ async setSpaceControlPipelineSnapshot(spaceKey, snapshot) {
3787
+ this._getLargeSpaceMetadata(spaceKey).controlPipelineSnapshot = snapshot;
3788
+ await this._saveSpaceLargeMetadata(spaceKey);
3789
+ await this.flush();
3790
+ }
3791
+ };
3792
+ _ts_decorate9([
3793
+ synchronized5
3794
+ ], MetadataStore.prototype, "load", null);
3795
+ _ts_decorate9([
3796
+ synchronized5
3797
+ ], MetadataStore.prototype, "_save", null);
3798
+ _ts_decorate9([
3799
+ synchronized5
3800
+ ], MetadataStore.prototype, "_saveSpaceLargeMetadata", null);
3801
+ var fromBytesInt32 = (buf) => buf.readInt32LE(0);
3802
+ var hasInvitationExpired = (invitation) => {
3803
+ return Boolean(invitation.created && invitation.lifetime && invitation.lifetime !== 0 && invitation.created.getTime() + invitation.lifetime * 1e3 < Date.now());
3804
+ };
3805
+ var isLegacyInvitationFormat = (invitation) => {
3806
+ return invitation.type === Invitation.Type.MULTIUSE;
2327
3807
  };
2328
- _ts_decorate7([
2329
- synchronized4
2330
- ], SpaceManager.prototype, "open", null);
2331
- _ts_decorate7([
2332
- synchronized4
2333
- ], SpaceManager.prototype, "close", null);
2334
- SpaceManager = _ts_decorate7([
2335
- trackLeaks3("open", "close")
2336
- ], SpaceManager);
2337
3808
 
2338
3809
  export {
2339
- Buffer,
2340
3810
  codec,
2341
3811
  valueEncoding,
2342
3812
  createMappedFeedWriter,
2343
3813
  SnapshotManager,
2344
3814
  SnapshotStore,
2345
3815
  DocumentsSynchronizer,
2346
- DataServiceImpl,
2347
- MetadataStore,
2348
- hasInvitationExpired,
3816
+ diffCollectionState,
3817
+ LevelDBStorageAdapter,
3818
+ encodingOptions,
3819
+ AutomergeHost,
3820
+ getSpaceKeyFromDoc,
3821
+ deriveCollectionIdFromSpaceId,
3822
+ getSpaceIdFromCollectionId,
3823
+ AuthExtension,
2349
3824
  mapTimeframeToFeedIndexes,
2350
3825
  mapFeedIndexesToTimeframe,
2351
3826
  startAfter,
2352
3827
  TimeframeClock,
2353
3828
  Pipeline,
2354
- AuthExtension,
2355
3829
  Space,
2356
3830
  createIdFromSpaceKey,
2357
3831
  CredentialRetrieverExtension,
@@ -2361,6 +3835,10 @@ export {
2361
3835
  SpaceProtocol,
2362
3836
  AuthStatus,
2363
3837
  SpaceProtocolSession,
2364
- SpaceManager
3838
+ SpaceManager,
3839
+ MeshEchoReplicator,
3840
+ DataServiceImpl,
3841
+ MetadataStore,
3842
+ hasInvitationExpired
2365
3843
  };
2366
- //# sourceMappingURL=chunk-UJQ5VS5V.mjs.map
3844
+ //# sourceMappingURL=chunk-PEE7MYCZ.mjs.map