@dxos/echo-pipeline 0.6.12 → 0.6.13-main.548ca8d

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 (123) hide show
  1. package/dist/lib/browser/chunk-PESZVYAN.mjs +2050 -0
  2. package/dist/lib/browser/chunk-PESZVYAN.mjs.map +7 -0
  3. package/dist/lib/browser/index.mjs +3463 -17
  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 +3 -4
  7. package/dist/lib/browser/testing/index.mjs.map +3 -3
  8. package/dist/lib/node/{chunk-7HHYCGUR.cjs → chunk-6EZVIJNE.cjs} +89 -47
  9. package/dist/lib/node/chunk-6EZVIJNE.cjs.map +7 -0
  10. package/dist/lib/node/index.cjs +3440 -35
  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 +11 -12
  14. package/dist/lib/node/testing/index.cjs.map +3 -3
  15. package/dist/lib/{browser/chunk-UKXIJW43.mjs → node-esm/chunk-4LW7MDPZ.mjs} +76 -36
  16. package/dist/lib/node-esm/chunk-4LW7MDPZ.mjs.map +7 -0
  17. package/dist/lib/{browser/chunk-MPWFDDQK.mjs → node-esm/index.mjs} +1702 -335
  18. package/dist/lib/node-esm/index.mjs.map +7 -0
  19. package/dist/lib/node-esm/meta.json +1 -0
  20. package/dist/lib/node-esm/testing/index.mjs +551 -0
  21. package/dist/lib/node-esm/testing/index.mjs.map +7 -0
  22. package/dist/types/src/automerge/automerge-host.d.ts +24 -1
  23. package/dist/types/src/automerge/automerge-host.d.ts.map +1 -1
  24. package/dist/types/src/automerge/collection-synchronizer.d.ts +2 -0
  25. package/dist/types/src/automerge/collection-synchronizer.d.ts.map +1 -1
  26. package/dist/types/src/automerge/echo-network-adapter.d.ts.map +1 -1
  27. package/dist/types/src/automerge/echo-replicator.d.ts +3 -3
  28. package/dist/types/src/automerge/echo-replicator.d.ts.map +1 -1
  29. package/dist/types/src/automerge/mesh-echo-replicator-connection.d.ts +3 -3
  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.map +1 -1
  32. package/dist/types/src/automerge/space-collection.d.ts +3 -2
  33. package/dist/types/src/automerge/space-collection.d.ts.map +1 -1
  34. package/dist/types/src/db-host/automerge-metrics.d.ts +11 -0
  35. package/dist/types/src/db-host/automerge-metrics.d.ts.map +1 -0
  36. package/dist/types/src/db-host/data-service.d.ts +3 -2
  37. package/dist/types/src/db-host/data-service.d.ts.map +1 -1
  38. package/dist/types/src/db-host/database-root.d.ts +20 -0
  39. package/dist/types/src/db-host/database-root.d.ts.map +1 -0
  40. package/dist/types/src/db-host/documents-iterator.d.ts +7 -0
  41. package/dist/types/src/db-host/documents-iterator.d.ts.map +1 -0
  42. package/dist/types/src/db-host/echo-host.d.ts +73 -0
  43. package/dist/types/src/db-host/echo-host.d.ts.map +1 -0
  44. package/dist/types/src/db-host/index.d.ts +5 -0
  45. package/dist/types/src/db-host/index.d.ts.map +1 -1
  46. package/dist/types/src/db-host/migration.d.ts +8 -0
  47. package/dist/types/src/db-host/migration.d.ts.map +1 -0
  48. package/dist/types/src/db-host/query-service.d.ts +25 -0
  49. package/dist/types/src/db-host/query-service.d.ts.map +1 -0
  50. package/dist/types/src/db-host/query-state.d.ts +41 -0
  51. package/dist/types/src/db-host/query-state.d.ts.map +1 -0
  52. package/dist/types/src/db-host/space-state-manager.d.ts +23 -0
  53. package/dist/types/src/db-host/space-state-manager.d.ts.map +1 -0
  54. package/dist/types/src/edge/echo-edge-replicator.d.ts +23 -0
  55. package/dist/types/src/edge/echo-edge-replicator.d.ts.map +1 -0
  56. package/dist/types/src/edge/echo-edge-replicator.test.d.ts +2 -0
  57. package/dist/types/src/edge/echo-edge-replicator.test.d.ts.map +1 -0
  58. package/dist/types/src/edge/index.d.ts +2 -0
  59. package/dist/types/src/edge/index.d.ts.map +1 -0
  60. package/dist/types/src/index.d.ts +1 -0
  61. package/dist/types/src/index.d.ts.map +1 -1
  62. package/dist/types/src/metadata/metadata-store.d.ts +4 -1
  63. package/dist/types/src/metadata/metadata-store.d.ts.map +1 -1
  64. package/dist/types/src/testing/test-agent-builder.d.ts.map +1 -1
  65. package/dist/types/src/testing/test-replicator.d.ts +4 -4
  66. package/dist/types/src/testing/test-replicator.d.ts.map +1 -1
  67. package/package.json +40 -50
  68. package/src/automerge/automerge-host.test.ts +8 -9
  69. package/src/automerge/automerge-host.ts +46 -7
  70. package/src/automerge/automerge-repo.test.ts +18 -16
  71. package/src/automerge/collection-synchronizer.test.ts +10 -5
  72. package/src/automerge/collection-synchronizer.ts +17 -6
  73. package/src/automerge/echo-data-monitor.test.ts +1 -3
  74. package/src/automerge/echo-network-adapter.test.ts +4 -3
  75. package/src/automerge/echo-network-adapter.ts +5 -4
  76. package/src/automerge/echo-replicator.ts +3 -3
  77. package/src/automerge/mesh-echo-replicator-connection.ts +10 -9
  78. package/src/automerge/mesh-echo-replicator.ts +2 -1
  79. package/src/automerge/space-collection.ts +3 -2
  80. package/src/automerge/storage-adapter.test.ts +2 -3
  81. package/src/db-host/automerge-metrics.ts +38 -0
  82. package/src/db-host/data-service.ts +29 -14
  83. package/src/db-host/database-root.ts +86 -0
  84. package/src/db-host/documents-iterator.ts +73 -0
  85. package/src/db-host/documents-synchronizer.test.ts +2 -2
  86. package/src/db-host/echo-host.ts +257 -0
  87. package/src/db-host/index.ts +6 -1
  88. package/src/db-host/migration.ts +57 -0
  89. package/src/db-host/query-service.ts +208 -0
  90. package/src/db-host/query-state.ts +200 -0
  91. package/src/db-host/space-state-manager.ts +90 -0
  92. package/src/edge/echo-edge-replicator.test.ts +96 -0
  93. package/src/edge/echo-edge-replicator.ts +337 -0
  94. package/src/edge/index.ts +5 -0
  95. package/src/index.ts +1 -0
  96. package/src/metadata/metadata-store.ts +20 -0
  97. package/src/pipeline/pipeline-stress.test.ts +44 -47
  98. package/src/pipeline/pipeline.test.ts +3 -4
  99. package/src/space/control-pipeline.test.ts +2 -3
  100. package/src/space/control-pipeline.ts +10 -1
  101. package/src/space/replication.browser.test.ts +2 -8
  102. package/src/space/space-manager.browser.test.ts +6 -5
  103. package/src/space/space-protocol.browser.test.ts +29 -34
  104. package/src/space/space-protocol.test.ts +29 -27
  105. package/src/space/space.test.ts +28 -11
  106. package/src/testing/test-agent-builder.ts +2 -2
  107. package/src/testing/test-replicator.ts +3 -3
  108. package/dist/lib/browser/chunk-MPWFDDQK.mjs.map +0 -7
  109. package/dist/lib/browser/chunk-UKXIJW43.mjs.map +0 -7
  110. package/dist/lib/browser/chunk-XPCF2V5U.mjs +0 -31
  111. package/dist/lib/browser/chunk-XPCF2V5U.mjs.map +0 -7
  112. package/dist/lib/browser/light.mjs +0 -32
  113. package/dist/lib/browser/light.mjs.map +0 -7
  114. package/dist/lib/node/chunk-5DH4KR2S.cjs +0 -2148
  115. package/dist/lib/node/chunk-5DH4KR2S.cjs.map +0 -7
  116. package/dist/lib/node/chunk-7HHYCGUR.cjs.map +0 -7
  117. package/dist/lib/node/chunk-DZVH7HDD.cjs +0 -43
  118. package/dist/lib/node/chunk-DZVH7HDD.cjs.map +0 -7
  119. package/dist/lib/node/light.cjs +0 -52
  120. package/dist/lib/node/light.cjs.map +0 -7
  121. package/dist/types/src/light.d.ts +0 -4
  122. package/dist/types/src/light.d.ts.map +0 -1
  123. package/src/light.ts +0 -7
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,46 +17,3441 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
  var node_exports = {};
20
30
  __export(node_exports, {
21
- AuthExtension: () => import_chunk_7HHYCGUR.AuthExtension,
22
- AuthStatus: () => import_chunk_7HHYCGUR.AuthStatus,
23
- AutomergeHost: () => import_chunk_5DH4KR2S.AutomergeHost,
24
- CredentialRetrieverExtension: () => import_chunk_7HHYCGUR.CredentialRetrieverExtension,
25
- CredentialServerExtension: () => import_chunk_7HHYCGUR.CredentialServerExtension,
26
- DataServiceImpl: () => import_chunk_5DH4KR2S.DataServiceImpl,
27
- DocumentsSynchronizer: () => import_chunk_5DH4KR2S.DocumentsSynchronizer,
28
- EchoDataMonitor: () => import_chunk_5DH4KR2S.EchoDataMonitor,
29
- LevelDBStorageAdapter: () => import_chunk_5DH4KR2S.LevelDBStorageAdapter,
30
- MOCK_AUTH_PROVIDER: () => import_chunk_7HHYCGUR.MOCK_AUTH_PROVIDER,
31
- MOCK_AUTH_VERIFIER: () => import_chunk_7HHYCGUR.MOCK_AUTH_VERIFIER,
32
- MeshEchoReplicator: () => import_chunk_5DH4KR2S.MeshEchoReplicator,
33
- MetadataStore: () => import_chunk_7HHYCGUR.MetadataStore,
34
- Pipeline: () => import_chunk_7HHYCGUR.Pipeline,
35
- Space: () => import_chunk_7HHYCGUR.Space,
36
- SpaceManager: () => import_chunk_7HHYCGUR.SpaceManager,
37
- SpaceProtocol: () => import_chunk_7HHYCGUR.SpaceProtocol,
38
- SpaceProtocolSession: () => import_chunk_7HHYCGUR.SpaceProtocolSession,
39
- TimeframeClock: () => import_chunk_7HHYCGUR.TimeframeClock,
40
- codec: () => import_chunk_7HHYCGUR.codec,
41
- createIdFromSpaceKey: () => import_chunk_DZVH7HDD.createIdFromSpaceKey,
42
- createMappedFeedWriter: () => import_chunk_7HHYCGUR.createMappedFeedWriter,
43
- deriveCollectionIdFromSpaceId: () => import_chunk_5DH4KR2S.deriveCollectionIdFromSpaceId,
44
- diffCollectionState: () => import_chunk_5DH4KR2S.diffCollectionState,
45
- encodingOptions: () => import_chunk_5DH4KR2S.encodingOptions,
46
- getSpaceIdFromCollectionId: () => import_chunk_5DH4KR2S.getSpaceIdFromCollectionId,
47
- getSpaceKeyFromDoc: () => import_chunk_5DH4KR2S.getSpaceKeyFromDoc,
48
- hasInvitationExpired: () => import_chunk_7HHYCGUR.hasInvitationExpired,
49
- mapFeedIndexesToTimeframe: () => import_chunk_7HHYCGUR.mapFeedIndexesToTimeframe,
50
- mapTimeframeToFeedIndexes: () => import_chunk_7HHYCGUR.mapTimeframeToFeedIndexes,
51
- startAfter: () => import_chunk_7HHYCGUR.startAfter,
52
- valueEncoding: () => import_chunk_7HHYCGUR.valueEncoding
31
+ AuthExtension: () => import_chunk_6EZVIJNE.AuthExtension,
32
+ AuthStatus: () => import_chunk_6EZVIJNE.AuthStatus,
33
+ AutomergeHost: () => AutomergeHost,
34
+ CredentialRetrieverExtension: () => import_chunk_6EZVIJNE.CredentialRetrieverExtension,
35
+ CredentialServerExtension: () => import_chunk_6EZVIJNE.CredentialServerExtension,
36
+ DataServiceImpl: () => DataServiceImpl,
37
+ DatabaseRoot: () => DatabaseRoot,
38
+ DocumentsSynchronizer: () => DocumentsSynchronizer,
39
+ EchoDataMonitor: () => EchoDataMonitor,
40
+ EchoEdgeReplicator: () => EchoEdgeReplicator,
41
+ EchoHost: () => EchoHost,
42
+ LevelDBStorageAdapter: () => LevelDBStorageAdapter,
43
+ MOCK_AUTH_PROVIDER: () => import_chunk_6EZVIJNE.MOCK_AUTH_PROVIDER,
44
+ MOCK_AUTH_VERIFIER: () => import_chunk_6EZVIJNE.MOCK_AUTH_VERIFIER,
45
+ MeshEchoReplicator: () => MeshEchoReplicator,
46
+ MetadataStore: () => import_chunk_6EZVIJNE.MetadataStore,
47
+ Pipeline: () => import_chunk_6EZVIJNE.Pipeline,
48
+ QueryServiceImpl: () => QueryServiceImpl,
49
+ QueryState: () => QueryState,
50
+ Space: () => import_chunk_6EZVIJNE.Space,
51
+ SpaceManager: () => import_chunk_6EZVIJNE.SpaceManager,
52
+ SpaceProtocol: () => import_chunk_6EZVIJNE.SpaceProtocol,
53
+ SpaceProtocolSession: () => import_chunk_6EZVIJNE.SpaceProtocolSession,
54
+ TimeframeClock: () => import_chunk_6EZVIJNE.TimeframeClock,
55
+ codec: () => import_chunk_6EZVIJNE.codec,
56
+ convertLegacyReferences: () => convertLegacyReferences,
57
+ convertLegacySpaceRootDoc: () => convertLegacySpaceRootDoc,
58
+ createIdFromSpaceKey: () => import_chunk_6EZVIJNE.createIdFromSpaceKey,
59
+ createMappedFeedWriter: () => import_chunk_6EZVIJNE.createMappedFeedWriter,
60
+ deriveCollectionIdFromSpaceId: () => deriveCollectionIdFromSpaceId,
61
+ diffCollectionState: () => diffCollectionState,
62
+ encodingOptions: () => encodingOptions,
63
+ findInlineObjectOfType: () => findInlineObjectOfType,
64
+ getSpaceIdFromCollectionId: () => getSpaceIdFromCollectionId,
65
+ getSpaceKeyFromDoc: () => getSpaceKeyFromDoc,
66
+ hasInvitationExpired: () => import_chunk_6EZVIJNE.hasInvitationExpired,
67
+ mapFeedIndexesToTimeframe: () => import_chunk_6EZVIJNE.mapFeedIndexesToTimeframe,
68
+ mapTimeframeToFeedIndexes: () => import_chunk_6EZVIJNE.mapTimeframeToFeedIndexes,
69
+ startAfter: () => import_chunk_6EZVIJNE.startAfter,
70
+ valueEncoding: () => import_chunk_6EZVIJNE.valueEncoding
53
71
  });
54
72
  module.exports = __toCommonJS(node_exports);
55
- var import_chunk_5DH4KR2S = require("./chunk-5DH4KR2S.cjs");
56
- var import_chunk_7HHYCGUR = require("./chunk-7HHYCGUR.cjs");
57
- var import_chunk_DZVH7HDD = require("./chunk-DZVH7HDD.cjs");
73
+ var import_chunk_6EZVIJNE = require("./chunk-6EZVIJNE.cjs");
74
+ var import_async = require("@dxos/async");
75
+ var import_stream = require("@dxos/codec-protobuf/stream");
76
+ var import_invariant = require("@dxos/invariant");
77
+ var import_keys = require("@dxos/keys");
78
+ var import_log = require("@dxos/log");
79
+ var import_async2 = require("@dxos/async");
80
+ var import_automerge = require("@dxos/automerge/automerge");
81
+ var import_context = require("@dxos/context");
82
+ var import_invariant2 = require("@dxos/invariant");
83
+ var import_log2 = require("@dxos/log");
84
+ var import_async3 = require("@dxos/async");
85
+ var import_automerge2 = require("@dxos/automerge/automerge");
86
+ var import_automerge_repo = require("@dxos/automerge/automerge-repo");
87
+ var import_context2 = require("@dxos/context");
88
+ var import_invariant3 = require("@dxos/invariant");
89
+ var import_keys2 = require("@dxos/keys");
90
+ var import_log3 = require("@dxos/log");
91
+ var import_protocols = require("@dxos/protocols");
92
+ var import_tracing = require("@dxos/tracing");
93
+ var import_async4 = require("@dxos/async");
94
+ var import_automerge3 = require("@dxos/automerge/automerge");
95
+ var import_context3 = require("@dxos/context");
96
+ var import_log4 = require("@dxos/log");
97
+ var import_util = require("@dxos/util");
98
+ var import_async5 = require("@dxos/async");
99
+ var import_automerge_repo2 = require("@dxos/automerge/automerge-repo");
100
+ var import_context4 = require("@dxos/context");
101
+ var import_invariant4 = require("@dxos/invariant");
102
+ var import_log5 = require("@dxos/log");
103
+ var import_util2 = require("@dxos/util");
104
+ var import_protocols2 = require("@dxos/protocols");
105
+ var import_indexing = require("@dxos/indexing");
106
+ var import_context5 = require("@dxos/context");
107
+ var import_invariant5 = require("@dxos/invariant");
108
+ var import_keys3 = require("@dxos/keys");
109
+ var import_log6 = require("@dxos/log");
110
+ var import_util3 = require("@dxos/util");
111
+ var A2 = __toESM(require("@dxos/automerge/automerge"));
112
+ var import_automerge_repo3 = require("@dxos/automerge/automerge-repo");
113
+ var import_context6 = require("@dxos/context");
114
+ var import_invariant6 = require("@dxos/invariant");
115
+ var import_log7 = require("@dxos/log");
116
+ var import_teleport_extension_automerge_replicator = require("@dxos/teleport-extension-automerge-replicator");
117
+ var import_invariant7 = require("@dxos/invariant");
118
+ var import_keys4 = require("@dxos/keys");
119
+ var import_tracing2 = require("@dxos/tracing");
120
+ var import_util4 = require("@dxos/util");
121
+ var import_context7 = require("@dxos/context");
122
+ var import_debug = require("@dxos/debug");
123
+ var import_echo_protocol = require("@dxos/echo-protocol");
124
+ var import_indexing2 = require("@dxos/indexing");
125
+ var import_invariant8 = require("@dxos/invariant");
126
+ var import_indexing3 = require("@dxos/protocols/proto/dxos/echo/indexing");
127
+ var import_tracing3 = require("@dxos/tracing");
128
+ var A3 = __toESM(require("@dxos/automerge/automerge"));
129
+ var import_context8 = require("@dxos/context");
130
+ var import_echo_protocol2 = require("@dxos/echo-protocol");
131
+ var import_invariant9 = require("@dxos/invariant");
132
+ var import_log8 = require("@dxos/log");
133
+ var import_protocols3 = require("@dxos/protocols");
134
+ var import_async6 = require("@dxos/async");
135
+ var import_automerge4 = require("@dxos/automerge/automerge");
136
+ var import_codec_protobuf = require("@dxos/codec-protobuf");
137
+ var import_context9 = require("@dxos/context");
138
+ var import_log9 = require("@dxos/log");
139
+ var import_protocols4 = require("@dxos/protocols");
140
+ var import_tracing4 = require("@dxos/tracing");
141
+ var import_context10 = require("@dxos/context");
142
+ var import_echo_protocol3 = require("@dxos/echo-protocol");
143
+ var import_invariant10 = require("@dxos/invariant");
144
+ var import_keys5 = require("@dxos/keys");
145
+ var import_protocols5 = require("@dxos/protocols");
146
+ var import_tracing5 = require("@dxos/tracing");
147
+ var import_util5 = require("@dxos/util");
148
+ var import_lodash = __toESM(require("lodash.isequal"));
149
+ var import_async7 = require("@dxos/async");
150
+ var import_automerge_repo4 = require("@dxos/automerge/automerge-repo");
151
+ var import_context11 = require("@dxos/context");
152
+ var import_echo_protocol4 = require("@dxos/echo-protocol");
153
+ var import_invariant11 = require("@dxos/invariant");
154
+ var A4 = __toESM(require("@dxos/automerge/automerge"));
155
+ var import_log10 = require("@dxos/log");
156
+ var import_echo_protocol5 = require("@dxos/echo-protocol");
157
+ var import_echo_protocol6 = require("@dxos/echo-protocol");
158
+ var import_echo_schema = require("@dxos/echo-schema");
159
+ var import_util6 = require("@dxos/util");
160
+ var import_async8 = require("@dxos/async");
161
+ var A5 = __toESM(require("@dxos/automerge/automerge"));
162
+ var import_automerge_repo5 = require("@dxos/automerge/automerge-repo");
163
+ var import_context12 = require("@dxos/context");
164
+ var import_crypto = require("@dxos/crypto");
165
+ var import_invariant12 = require("@dxos/invariant");
166
+ var import_log11 = require("@dxos/log");
167
+ var import_protocols6 = require("@dxos/protocols");
168
+ var import_buf = require("@dxos/protocols/buf");
169
+ var import_messenger_pb = require("@dxos/protocols/buf/dxos/edge/messenger_pb");
170
+ var import_util7 = require("@dxos/util");
171
+ var __dxlog_file = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/db-host/documents-synchronizer.ts";
172
+ var MAX_UPDATE_FREQ = 10;
173
+ var DocumentsSynchronizer = class extends import_context.Resource {
174
+ constructor(_params) {
175
+ super();
176
+ this._params = _params;
177
+ this._syncStates = /* @__PURE__ */ new Map();
178
+ this._pendingUpdates = /* @__PURE__ */ new Set();
179
+ this._sendUpdatesJob = void 0;
180
+ }
181
+ addDocuments(documentIds, retryCounter = 0) {
182
+ if (retryCounter > 3) {
183
+ import_log2.log.warn("Failed to load document, retry limit reached", {
184
+ documentIds
185
+ }, {
186
+ F: __dxlog_file,
187
+ L: 49,
188
+ S: this,
189
+ C: (f, a) => f(...a)
190
+ });
191
+ return;
192
+ }
193
+ for (const documentId of documentIds) {
194
+ const doc = this._params.repo.find(documentId);
195
+ doc.whenReady().then(() => {
196
+ this._startSync(doc);
197
+ this._pendingUpdates.add(doc.documentId);
198
+ this._sendUpdatesJob.trigger();
199
+ }).catch((error) => {
200
+ import_log2.log.warn("Failed to load document, wraparound", {
201
+ documentId,
202
+ error
203
+ }, {
204
+ F: __dxlog_file,
205
+ L: 63,
206
+ S: this,
207
+ C: (f, a) => f(...a)
208
+ });
209
+ this.addDocuments([
210
+ documentId
211
+ ], retryCounter + 1);
212
+ });
213
+ }
214
+ }
215
+ removeDocuments(documentIds) {
216
+ for (const documentId of documentIds) {
217
+ this._syncStates.get(documentId)?.clearSubscriptions?.();
218
+ this._syncStates.delete(documentId);
219
+ this._pendingUpdates.delete(documentId);
220
+ }
221
+ }
222
+ async _open() {
223
+ this._sendUpdatesJob = new import_async2.UpdateScheduler(this._ctx, this._checkAndSendUpdates.bind(this), {
224
+ maxFrequency: MAX_UPDATE_FREQ
225
+ });
226
+ }
227
+ async _close() {
228
+ await this._sendUpdatesJob.join();
229
+ this._syncStates.clear();
230
+ }
231
+ update(updates) {
232
+ for (const { documentId, mutation, isNew } of updates) {
233
+ if (isNew) {
234
+ const doc = this._params.repo.find(documentId);
235
+ doc.update((doc2) => import_automerge.next.loadIncremental(doc2, mutation));
236
+ this._startSync(doc);
237
+ } else {
238
+ this._writeMutation(documentId, mutation);
239
+ }
240
+ }
241
+ }
242
+ _startSync(doc) {
243
+ if (this._syncStates.has(doc.documentId)) {
244
+ import_log2.log.info("Document already being synced", {
245
+ documentId: doc.documentId
246
+ }, {
247
+ F: __dxlog_file,
248
+ L: 102,
249
+ S: this,
250
+ C: (f, a) => f(...a)
251
+ });
252
+ return;
253
+ }
254
+ const syncState = {
255
+ handle: doc
256
+ };
257
+ this._subscribeForChanges(syncState);
258
+ this._syncStates.set(doc.documentId, syncState);
259
+ }
260
+ _subscribeForChanges(syncState) {
261
+ const handler = () => {
262
+ this._pendingUpdates.add(syncState.handle.documentId);
263
+ this._sendUpdatesJob.trigger();
264
+ };
265
+ syncState.handle.on("heads-changed", handler);
266
+ syncState.clearSubscriptions = () => syncState.handle.off("heads-changed", handler);
267
+ }
268
+ async _checkAndSendUpdates() {
269
+ const updates = [];
270
+ const docsWithPendingUpdates = Array.from(this._pendingUpdates);
271
+ this._pendingUpdates.clear();
272
+ for (const documentId of docsWithPendingUpdates) {
273
+ const update = this._getPendingChanges(documentId);
274
+ if (update) {
275
+ updates.push({
276
+ documentId,
277
+ mutation: update
278
+ });
279
+ }
280
+ }
281
+ if (updates.length > 0) {
282
+ this._params.sendUpdates({
283
+ updates
284
+ });
285
+ }
286
+ }
287
+ _getPendingChanges(documentId) {
288
+ const syncState = this._syncStates.get(documentId);
289
+ (0, import_invariant2.invariant)(syncState, "Sync state for document not found", {
290
+ F: __dxlog_file,
291
+ L: 143,
292
+ S: this,
293
+ A: [
294
+ "syncState",
295
+ "'Sync state for document not found'"
296
+ ]
297
+ });
298
+ const doc = syncState.handle.docSync();
299
+ if (!doc) {
300
+ return;
301
+ }
302
+ const mutation = syncState.lastSentHead ? import_automerge.next.saveSince(doc, syncState.lastSentHead) : import_automerge.next.save(doc);
303
+ if (mutation.length === 0) {
304
+ return;
305
+ }
306
+ syncState.lastSentHead = import_automerge.next.getHeads(doc);
307
+ return mutation;
308
+ }
309
+ _writeMutation(documentId, mutation) {
310
+ const syncState = this._syncStates.get(documentId);
311
+ (0, import_invariant2.invariant)(syncState, "Sync state for document not found", {
312
+ F: __dxlog_file,
313
+ L: 158,
314
+ S: this,
315
+ A: [
316
+ "syncState",
317
+ "'Sync state for document not found'"
318
+ ]
319
+ });
320
+ syncState.handle.update((doc) => {
321
+ const headsBefore = import_automerge.next.getHeads(doc);
322
+ const newDoc = import_automerge.next.loadIncremental(doc, mutation);
323
+ if (import_automerge.next.equals(headsBefore, syncState.lastSentHead)) {
324
+ syncState.lastSentHead = import_automerge.next.getHeads(newDoc);
325
+ }
326
+ return newDoc;
327
+ });
328
+ }
329
+ };
330
+ var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/automerge/collection-synchronizer.ts";
331
+ var MIN_QUERY_INTERVAL = 5e3;
332
+ var POLL_INTERVAL = 3e4;
333
+ var CollectionSynchronizer = class extends import_context3.Resource {
334
+ constructor(params) {
335
+ super();
336
+ this._perCollectionStates = /* @__PURE__ */ new Map();
337
+ this._connectedPeers = /* @__PURE__ */ new Set();
338
+ this.remoteStateUpdated = new import_async4.Event();
339
+ this._sendCollectionState = params.sendCollectionState;
340
+ this._queryCollectionState = params.queryCollectionState;
341
+ this._shouldSyncCollection = params.shouldSyncCollection;
342
+ }
343
+ async _open(ctx) {
344
+ (0, import_async4.scheduleTaskInterval)(this._ctx, async () => {
345
+ for (const collectionId of this._perCollectionStates.keys()) {
346
+ this.refreshCollection(collectionId);
347
+ await (0, import_async4.asyncReturn)();
348
+ }
349
+ }, POLL_INTERVAL);
350
+ }
351
+ getRegisteredCollectionIds() {
352
+ return [
353
+ ...this._perCollectionStates.keys()
354
+ ];
355
+ }
356
+ getLocalCollectionState(collectionId) {
357
+ return this._getPerCollectionState(collectionId).localState;
358
+ }
359
+ setLocalCollectionState(collectionId, state) {
360
+ (0, import_log4.log)("setLocalCollectionState", {
361
+ collectionId,
362
+ state
363
+ }, {
364
+ F: __dxlog_file2,
365
+ L: 68,
366
+ S: this,
367
+ C: (f, a) => f(...a)
368
+ });
369
+ this._getPerCollectionState(collectionId).localState = state;
370
+ queueMicrotask(async () => {
371
+ if (!this._ctx.disposed) {
372
+ this._refreshInterestedPeers(collectionId);
373
+ this.refreshCollection(collectionId);
374
+ }
375
+ });
376
+ }
377
+ getRemoteCollectionStates(collectionId) {
378
+ return this._getPerCollectionState(collectionId).remoteStates;
379
+ }
380
+ refreshCollection(collectionId) {
381
+ let scheduleAnotherRefresh = false;
382
+ const state = this._getPerCollectionState(collectionId);
383
+ for (const peerId of this._connectedPeers) {
384
+ if (state.interestedPeers.has(peerId)) {
385
+ const lastQueried = state.lastQueried.get(peerId) ?? 0;
386
+ if (Date.now() - lastQueried > MIN_QUERY_INTERVAL) {
387
+ state.lastQueried.set(peerId, Date.now());
388
+ this._queryCollectionState(collectionId, peerId);
389
+ } else {
390
+ scheduleAnotherRefresh = true;
391
+ }
392
+ }
393
+ }
394
+ if (scheduleAnotherRefresh) {
395
+ (0, import_async4.scheduleTask)(this._ctx, () => this.refreshCollection(collectionId), MIN_QUERY_INTERVAL);
396
+ }
397
+ }
398
+ /**
399
+ * Callback when a connection to a peer is established.
400
+ */
401
+ onConnectionOpen(peerId) {
402
+ this._connectedPeers.add(peerId);
403
+ queueMicrotask(async () => {
404
+ if (this._ctx.disposed) {
405
+ return;
406
+ }
407
+ for (const [collectionId, state] of this._perCollectionStates.entries()) {
408
+ if (this._shouldSyncCollection(collectionId, peerId)) {
409
+ state.interestedPeers.add(peerId);
410
+ state.lastQueried.set(peerId, Date.now());
411
+ this._queryCollectionState(collectionId, peerId);
412
+ }
413
+ }
414
+ });
415
+ }
416
+ /**
417
+ * Callback when a connection to a peer is closed.
418
+ */
419
+ onConnectionClosed(peerId) {
420
+ this._connectedPeers.delete(peerId);
421
+ for (const perCollectionState of this._perCollectionStates.values()) {
422
+ perCollectionState.remoteStates.delete(peerId);
423
+ }
424
+ }
425
+ /**
426
+ * Callback when a peer queries the state of a collection.
427
+ */
428
+ onCollectionStateQueried(collectionId, peerId) {
429
+ const perCollectionState = this._getPerCollectionState(collectionId);
430
+ if (perCollectionState.localState) {
431
+ this._sendCollectionState(collectionId, peerId, perCollectionState.localState);
432
+ }
433
+ }
434
+ /**
435
+ * Callback when a peer sends the state of a collection.
436
+ */
437
+ onRemoteStateReceived(collectionId, peerId, state) {
438
+ (0, import_log4.log)("onRemoteStateReceived", {
439
+ collectionId,
440
+ peerId,
441
+ state
442
+ }, {
443
+ F: __dxlog_file2,
444
+ L: 148,
445
+ S: this,
446
+ C: (f, a) => f(...a)
447
+ });
448
+ validateCollectionState(state);
449
+ const perCollectionState = this._getPerCollectionState(collectionId);
450
+ perCollectionState.remoteStates.set(peerId, state);
451
+ this.remoteStateUpdated.emit({
452
+ peerId,
453
+ collectionId
454
+ });
455
+ }
456
+ _getPerCollectionState(collectionId) {
457
+ return (0, import_util.defaultMap)(this._perCollectionStates, collectionId, () => ({
458
+ localState: void 0,
459
+ remoteStates: /* @__PURE__ */ new Map(),
460
+ interestedPeers: /* @__PURE__ */ new Set(),
461
+ lastQueried: /* @__PURE__ */ new Map()
462
+ }));
463
+ }
464
+ _refreshInterestedPeers(collectionId) {
465
+ for (const peerId of this._connectedPeers) {
466
+ if (this._shouldSyncCollection(collectionId, peerId)) {
467
+ this._getPerCollectionState(collectionId).interestedPeers.add(peerId);
468
+ } else {
469
+ this._getPerCollectionState(collectionId).interestedPeers.delete(peerId);
470
+ }
471
+ }
472
+ }
473
+ };
474
+ var diffCollectionState = (local, remote) => {
475
+ const allDocuments = /* @__PURE__ */ new Set([
476
+ ...Object.keys(local.documents),
477
+ ...Object.keys(remote.documents)
478
+ ]);
479
+ const missingOnRemote = [];
480
+ const missingOnLocal = [];
481
+ const different = [];
482
+ for (const documentId of allDocuments) {
483
+ if (!local.documents[documentId]) {
484
+ missingOnLocal.push(documentId);
485
+ } else if (!remote.documents[documentId]) {
486
+ missingOnRemote.push(documentId);
487
+ } else if (!import_automerge3.next.equals(local.documents[documentId], remote.documents[documentId])) {
488
+ different.push(documentId);
489
+ }
490
+ }
491
+ return {
492
+ missingOnRemote,
493
+ missingOnLocal,
494
+ different
495
+ };
496
+ };
497
+ var validateCollectionState = (state) => {
498
+ Object.entries(state.documents).forEach(([documentId, heads]) => {
499
+ if (!isValidDocumentId(documentId)) {
500
+ throw new Error(`Invalid documentId: ${documentId}`);
501
+ }
502
+ if (Array.isArray(heads) && heads.some((head) => typeof head !== "string")) {
503
+ throw new Error(`Invalid heads: ${heads}`);
504
+ }
505
+ });
506
+ };
507
+ var isValidDocumentId = (documentId) => {
508
+ return typeof documentId === "string" && !documentId.includes(":");
509
+ };
510
+ var isCollectionQueryMessage = (message) => message.type === import_protocols2.MESSAGE_TYPE_COLLECTION_QUERY;
511
+ var isCollectionStateMessage = (message) => message.type === import_protocols2.MESSAGE_TYPE_COLLECTION_STATE;
512
+ function _ts_decorate(decorators, target, key, desc) {
513
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
514
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
515
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
516
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
517
+ }
518
+ var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/automerge/echo-network-adapter.ts";
519
+ var EchoNetworkAdapter = class extends import_automerge_repo2.NetworkAdapter {
520
+ constructor(_params) {
521
+ super();
522
+ this._params = _params;
523
+ this._replicators = /* @__PURE__ */ new Set();
524
+ this._connections = /* @__PURE__ */ new Map();
525
+ this._lifecycleState = import_context4.LifecycleState.CLOSED;
526
+ this._connected = new import_async5.Trigger();
527
+ }
528
+ connect(peerId, peerMetadata) {
529
+ this.peerId = peerId;
530
+ this.peerMetadata = peerMetadata;
531
+ this._connected.wake();
532
+ }
533
+ send(message) {
534
+ this._send(message);
535
+ }
536
+ disconnect() {
537
+ }
538
+ async open() {
539
+ if (this._lifecycleState === import_context4.LifecycleState.OPEN) {
540
+ return;
541
+ }
542
+ this._lifecycleState = import_context4.LifecycleState.OPEN;
543
+ (0, import_log5.log)("emit ready", void 0, {
544
+ F: __dxlog_file3,
545
+ L: 82,
546
+ S: this,
547
+ C: (f, a) => f(...a)
548
+ });
549
+ this.emit("ready", {
550
+ network: this
551
+ });
552
+ }
553
+ async close() {
554
+ if (this._lifecycleState === import_context4.LifecycleState.CLOSED) {
555
+ return this;
556
+ }
557
+ for (const replicator of this._replicators) {
558
+ await replicator.disconnect();
559
+ }
560
+ this._replicators.clear();
561
+ this._lifecycleState = import_context4.LifecycleState.CLOSED;
562
+ }
563
+ async whenConnected() {
564
+ await this._connected.wait({
565
+ timeout: 1e4
566
+ });
567
+ }
568
+ async addReplicator(replicator) {
569
+ (0, import_invariant4.invariant)(this._lifecycleState === import_context4.LifecycleState.OPEN, void 0, {
570
+ F: __dxlog_file3,
571
+ L: 108,
572
+ S: this,
573
+ A: [
574
+ "this._lifecycleState === LifecycleState.OPEN",
575
+ ""
576
+ ]
577
+ });
578
+ (0, import_invariant4.invariant)(this.peerId, void 0, {
579
+ F: __dxlog_file3,
580
+ L: 109,
581
+ S: this,
582
+ A: [
583
+ "this.peerId",
584
+ ""
585
+ ]
586
+ });
587
+ (0, import_invariant4.invariant)(!this._replicators.has(replicator), void 0, {
588
+ F: __dxlog_file3,
589
+ L: 110,
590
+ S: this,
591
+ A: [
592
+ "!this._replicators.has(replicator)",
593
+ ""
594
+ ]
595
+ });
596
+ this._replicators.add(replicator);
597
+ await replicator.connect({
598
+ peerId: this.peerId,
599
+ onConnectionOpen: this._onConnectionOpen.bind(this),
600
+ onConnectionClosed: this._onConnectionClosed.bind(this),
601
+ onConnectionAuthScopeChanged: this._onConnectionAuthScopeChanged.bind(this),
602
+ isDocumentInRemoteCollection: this._params.isDocumentInRemoteCollection,
603
+ getContainingSpaceForDocument: this._params.getContainingSpaceForDocument,
604
+ getContainingSpaceIdForDocument: async (documentId) => {
605
+ const key = await this._params.getContainingSpaceForDocument(documentId);
606
+ return key ? (0, import_chunk_6EZVIJNE.createIdFromSpaceKey)(key) : null;
607
+ }
608
+ });
609
+ }
610
+ async removeReplicator(replicator) {
611
+ (0, import_invariant4.invariant)(this._lifecycleState === import_context4.LifecycleState.OPEN, void 0, {
612
+ F: __dxlog_file3,
613
+ L: 129,
614
+ S: this,
615
+ A: [
616
+ "this._lifecycleState === LifecycleState.OPEN",
617
+ ""
618
+ ]
619
+ });
620
+ (0, import_invariant4.invariant)(this._replicators.has(replicator), void 0, {
621
+ F: __dxlog_file3,
622
+ L: 130,
623
+ S: this,
624
+ A: [
625
+ "this._replicators.has(replicator)",
626
+ ""
627
+ ]
628
+ });
629
+ await replicator.disconnect();
630
+ this._replicators.delete(replicator);
631
+ }
632
+ async shouldAdvertise(peerId, params) {
633
+ const connection = this._connections.get(peerId);
634
+ if (!connection) {
635
+ return false;
636
+ }
637
+ return connection.connection.shouldAdvertise(params);
638
+ }
639
+ shouldSyncCollection(peerId, params) {
640
+ const connection = this._connections.get(peerId);
641
+ if (!connection) {
642
+ return false;
643
+ }
644
+ return connection.connection.shouldSyncCollection(params);
645
+ }
646
+ queryCollectionState(collectionId, targetId) {
647
+ const message = {
648
+ type: "collection-query",
649
+ senderId: this.peerId,
650
+ targetId,
651
+ collectionId
652
+ };
653
+ this._send(message);
654
+ }
655
+ sendCollectionState(collectionId, targetId, state) {
656
+ const message = {
657
+ type: "collection-state",
658
+ senderId: this.peerId,
659
+ targetId,
660
+ collectionId,
661
+ state
662
+ };
663
+ this._send(message);
664
+ }
665
+ _send(message) {
666
+ const connectionEntry = this._connections.get(message.targetId);
667
+ if (!connectionEntry) {
668
+ throw new Error("Connection not found.");
669
+ }
670
+ const writeStart = Date.now();
671
+ connectionEntry.writer.write(message).then(() => {
672
+ const durationMs = Date.now() - writeStart;
673
+ this._params.monitor?.recordMessageSent(message, durationMs);
674
+ }).catch((err) => {
675
+ if (connectionEntry.isOpen) {
676
+ import_log5.log.catch(err, void 0, {
677
+ F: __dxlog_file3,
678
+ L: 190,
679
+ S: this,
680
+ C: (f, a) => f(...a)
681
+ });
682
+ }
683
+ this._params.monitor?.recordMessageSendingFailed(message);
684
+ });
685
+ }
686
+ // TODO(dmaretskyi): Remove.
687
+ getPeersInterestedInCollection(collectionId) {
688
+ return Array.from(this._connections.values()).map((connection) => {
689
+ return connection.connection.shouldSyncCollection({
690
+ collectionId
691
+ }) ? connection.connection.peerId : null;
692
+ }).filter(import_util2.nonNullable);
693
+ }
694
+ _onConnectionOpen(connection) {
695
+ (0, import_log5.log)("Connection opened", {
696
+ peerId: connection.peerId
697
+ }, {
698
+ F: __dxlog_file3,
699
+ L: 208,
700
+ S: this,
701
+ C: (f, a) => f(...a)
702
+ });
703
+ (0, import_invariant4.invariant)(!this._connections.has(connection.peerId), void 0, {
704
+ F: __dxlog_file3,
705
+ L: 209,
706
+ S: this,
707
+ A: [
708
+ "!this._connections.has(connection.peerId as PeerId)",
709
+ ""
710
+ ]
711
+ });
712
+ const reader = connection.readable.getReader();
713
+ const writer = connection.writable.getWriter();
714
+ const connectionEntry = {
715
+ connection,
716
+ reader,
717
+ writer,
718
+ isOpen: true
719
+ };
720
+ this._connections.set(connection.peerId, connectionEntry);
721
+ queueMicrotask(async () => {
722
+ try {
723
+ while (true) {
724
+ const { done, value } = await reader.read();
725
+ if (done) {
726
+ break;
727
+ }
728
+ this._onMessage(value);
729
+ }
730
+ } catch (err) {
731
+ if (connectionEntry.isOpen) {
732
+ import_log5.log.catch(err, void 0, {
733
+ F: __dxlog_file3,
734
+ L: 228,
735
+ S: this,
736
+ C: (f, a) => f(...a)
737
+ });
738
+ }
739
+ }
740
+ });
741
+ (0, import_log5.log)("emit peer-candidate", {
742
+ peerId: connection.peerId
743
+ }, {
744
+ F: __dxlog_file3,
745
+ L: 233,
746
+ S: this,
747
+ C: (f, a) => f(...a)
748
+ });
749
+ this._emitPeerCandidate(connection);
750
+ this._params.monitor?.recordPeerConnected(connection.peerId);
751
+ }
752
+ _onMessage(message) {
753
+ if (isCollectionQueryMessage(message)) {
754
+ this._params.onCollectionStateQueried(message.collectionId, message.senderId);
755
+ } else if (isCollectionStateMessage(message)) {
756
+ this._params.onCollectionStateReceived(message.collectionId, message.senderId, message.state);
757
+ } else {
758
+ this.emit("message", message);
759
+ }
760
+ this._params.monitor?.recordMessageReceived(message);
761
+ }
762
+ /**
763
+ * Trigger doc-synchronizer shared documents set recalculation. Happens on peer-candidate.
764
+ * TODO(y): replace with a proper API call when sharePolicy update becomes supported by automerge-repo
765
+ */
766
+ _onConnectionAuthScopeChanged(connection) {
767
+ (0, import_log5.log)("Connection auth scope changed", {
768
+ peerId: connection.peerId
769
+ }, {
770
+ F: __dxlog_file3,
771
+ L: 254,
772
+ S: this,
773
+ C: (f, a) => f(...a)
774
+ });
775
+ const entry = this._connections.get(connection.peerId);
776
+ (0, import_invariant4.invariant)(entry, void 0, {
777
+ F: __dxlog_file3,
778
+ L: 256,
779
+ S: this,
780
+ A: [
781
+ "entry",
782
+ ""
783
+ ]
784
+ });
785
+ this.emit("peer-disconnected", {
786
+ peerId: connection.peerId
787
+ });
788
+ this._emitPeerCandidate(connection);
789
+ }
790
+ _onConnectionClosed(connection) {
791
+ (0, import_log5.log)("Connection closed", {
792
+ peerId: connection.peerId
793
+ }, {
794
+ F: __dxlog_file3,
795
+ L: 262,
796
+ S: this,
797
+ C: (f, a) => f(...a)
798
+ });
799
+ const entry = this._connections.get(connection.peerId);
800
+ (0, import_invariant4.invariant)(entry, void 0, {
801
+ F: __dxlog_file3,
802
+ L: 264,
803
+ S: this,
804
+ A: [
805
+ "entry",
806
+ ""
807
+ ]
808
+ });
809
+ entry.isOpen = false;
810
+ this.emit("peer-disconnected", {
811
+ peerId: connection.peerId
812
+ });
813
+ this._params.monitor?.recordPeerDisconnected(connection.peerId);
814
+ void entry.reader.cancel().catch((err) => import_log5.log.catch(err, void 0, {
815
+ F: __dxlog_file3,
816
+ L: 270,
817
+ S: this,
818
+ C: (f, a) => f(...a)
819
+ }));
820
+ void entry.writer.abort().catch((err) => import_log5.log.catch(err, void 0, {
821
+ F: __dxlog_file3,
822
+ L: 271,
823
+ S: this,
824
+ C: (f, a) => f(...a)
825
+ }));
826
+ this._connections.delete(connection.peerId);
827
+ }
828
+ _emitPeerCandidate(connection) {
829
+ this.emit("peer-candidate", {
830
+ peerId: connection.peerId,
831
+ peerMetadata: createEchoPeerMetadata()
832
+ });
833
+ }
834
+ };
835
+ _ts_decorate([
836
+ import_async5.synchronized
837
+ ], EchoNetworkAdapter.prototype, "open", null);
838
+ _ts_decorate([
839
+ import_async5.synchronized
840
+ ], EchoNetworkAdapter.prototype, "close", null);
841
+ _ts_decorate([
842
+ import_async5.synchronized
843
+ ], EchoNetworkAdapter.prototype, "addReplicator", null);
844
+ _ts_decorate([
845
+ import_async5.synchronized
846
+ ], EchoNetworkAdapter.prototype, "removeReplicator", null);
847
+ var createEchoPeerMetadata = () => ({
848
+ // TODO(dmaretskyi): Refactor this.
849
+ dxos_peerSource: "EchoNetworkAdapter"
850
+ });
851
+ var isEchoPeerMetadata = (metadata) => metadata?.dxos_peerSource === "EchoNetworkAdapter";
852
+ var HeadsStore = class {
853
+ constructor({ db }) {
854
+ this._db = db;
855
+ }
856
+ setHeads(documentId, heads, batch) {
857
+ batch.put(documentId, heads, {
858
+ sublevel: this._db,
859
+ keyEncoding: "utf8",
860
+ valueEncoding: import_indexing.headsEncoding
861
+ });
862
+ }
863
+ // TODO(dmaretskyi): Make batched.
864
+ async getHeads(documentIds) {
865
+ return this._db.getMany(documentIds, {
866
+ keyEncoding: "utf8",
867
+ valueEncoding: import_indexing.headsEncoding
868
+ });
869
+ }
870
+ };
871
+ var LevelDBStorageAdapter = class extends import_context5.Resource {
872
+ constructor(_params) {
873
+ super();
874
+ this._params = _params;
875
+ }
876
+ async load(keyArray) {
877
+ try {
878
+ if (this._lifecycleState !== import_context5.LifecycleState.OPEN) {
879
+ return void 0;
880
+ }
881
+ const startMs = Date.now();
882
+ const chunk = await this._params.db.get(keyArray, {
883
+ ...encodingOptions
884
+ });
885
+ this._params.monitor?.recordBytesLoaded(chunk.byteLength);
886
+ this._params.monitor?.recordLoadDuration(Date.now() - startMs);
887
+ return chunk;
888
+ } catch (err) {
889
+ if (isLevelDbNotFoundError(err)) {
890
+ return void 0;
891
+ }
892
+ throw err;
893
+ }
894
+ }
895
+ async save(keyArray, binary) {
896
+ if (this._lifecycleState !== import_context5.LifecycleState.OPEN) {
897
+ return void 0;
898
+ }
899
+ const startMs = Date.now();
900
+ const batch = this._params.db.batch();
901
+ await this._params.callbacks?.beforeSave?.({
902
+ path: keyArray,
903
+ batch
904
+ });
905
+ batch.put(keyArray, Buffer.from(binary), {
906
+ ...encodingOptions
907
+ });
908
+ await batch.write();
909
+ this._params.monitor?.recordBytesStored(binary.byteLength);
910
+ await this._params.callbacks?.afterSave?.(keyArray);
911
+ this._params.monitor?.recordStoreDuration(Date.now() - startMs);
912
+ }
913
+ async remove(keyArray) {
914
+ if (this._lifecycleState !== import_context5.LifecycleState.OPEN) {
915
+ return void 0;
916
+ }
917
+ await this._params.db.del(keyArray, {
918
+ ...encodingOptions
919
+ });
920
+ }
921
+ async loadRange(keyPrefix) {
922
+ if (this._lifecycleState !== import_context5.LifecycleState.OPEN) {
923
+ return [];
924
+ }
925
+ const startMs = Date.now();
926
+ const result = [];
927
+ for await (const [key, value] of this._params.db.iterator({
928
+ gte: keyPrefix,
929
+ lte: [
930
+ ...keyPrefix,
931
+ "\uFFFF"
932
+ ],
933
+ ...encodingOptions
934
+ })) {
935
+ result.push({
936
+ key,
937
+ data: value
938
+ });
939
+ this._params.monitor?.recordBytesLoaded(value.byteLength);
940
+ }
941
+ this._params.monitor?.recordLoadDuration(Date.now() - startMs);
942
+ return result;
943
+ }
944
+ async removeRange(keyPrefix) {
945
+ if (this._lifecycleState !== import_context5.LifecycleState.OPEN) {
946
+ return void 0;
947
+ }
948
+ const batch = this._params.db.batch();
949
+ for await (const [key] of this._params.db.iterator({
950
+ gte: keyPrefix,
951
+ lte: [
952
+ ...keyPrefix,
953
+ "\uFFFF"
954
+ ],
955
+ ...encodingOptions
956
+ })) {
957
+ batch.del(key, {
958
+ ...encodingOptions
959
+ });
960
+ }
961
+ await batch.write();
962
+ }
963
+ };
964
+ var keyEncoder = {
965
+ encode: (key) => Buffer.from(key.map((k) => k.replaceAll("%", "%25").replaceAll("-", "%2D")).join("-")),
966
+ decode: (key) => Buffer.from(key).toString().split("-").map((k) => k.replaceAll("%2D", "-").replaceAll("%25", "%")),
967
+ format: "buffer"
968
+ };
969
+ var encodingOptions = {
970
+ keyEncoding: keyEncoder,
971
+ valueEncoding: "buffer"
972
+ };
973
+ var isLevelDbNotFoundError = (err) => err.code === "LEVEL_NOT_FOUND";
974
+ function _ts_decorate2(decorators, target, key, desc) {
975
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
976
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
977
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
978
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
979
+ }
980
+ var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/automerge/automerge-host.ts";
981
+ var AutomergeHost = class extends import_context2.Resource {
982
+ constructor({ db, indexMetadataStore, dataMonitor }) {
983
+ super();
984
+ this._collectionSynchronizer = new CollectionSynchronizer({
985
+ queryCollectionState: this._queryCollectionState.bind(this),
986
+ sendCollectionState: this._sendCollectionState.bind(this),
987
+ shouldSyncCollection: this._shouldSyncCollection.bind(this)
988
+ });
989
+ this.collectionStateUpdated = new import_async3.Event();
990
+ this._db = db;
991
+ this._storage = new LevelDBStorageAdapter({
992
+ db: db.sublevel("automerge"),
993
+ callbacks: {
994
+ beforeSave: async (params) => this._beforeSave(params),
995
+ afterSave: async (key) => this._afterSave(key)
996
+ },
997
+ monitor: dataMonitor
998
+ });
999
+ this._echoNetworkAdapter = new EchoNetworkAdapter({
1000
+ getContainingSpaceForDocument: this._getContainingSpaceForDocument.bind(this),
1001
+ isDocumentInRemoteCollection: this._isDocumentInRemoteCollection.bind(this),
1002
+ onCollectionStateQueried: this._onCollectionStateQueried.bind(this),
1003
+ onCollectionStateReceived: this._onCollectionStateReceived.bind(this),
1004
+ monitor: dataMonitor
1005
+ });
1006
+ this._headsStore = new HeadsStore({
1007
+ db: db.sublevel("heads")
1008
+ });
1009
+ this._indexMetadataStore = indexMetadataStore;
1010
+ }
1011
+ async _open() {
1012
+ this._peerId = `host-${import_keys2.PublicKey.random().toHex()}`;
1013
+ await this._storage.open?.();
1014
+ this._repo = new import_automerge_repo.Repo({
1015
+ peerId: this._peerId,
1016
+ sharePolicy: this._sharePolicy.bind(this),
1017
+ storage: this._storage,
1018
+ network: [
1019
+ // Upstream swarm.
1020
+ this._echoNetworkAdapter
1021
+ ]
1022
+ });
1023
+ import_async3.Event.wrap(this._echoNetworkAdapter, "peer-candidate").on(this._ctx, (e) => this._onPeerConnected(e.peerId));
1024
+ import_async3.Event.wrap(this._echoNetworkAdapter, "peer-disconnected").on(this._ctx, (e) => this._onPeerDisconnected(e.peerId));
1025
+ this._collectionSynchronizer.remoteStateUpdated.on(this._ctx, ({ collectionId, peerId }) => {
1026
+ this._onRemoteCollectionStateUpdated(collectionId, peerId);
1027
+ this.collectionStateUpdated.emit({
1028
+ collectionId
1029
+ });
1030
+ });
1031
+ await this._echoNetworkAdapter.open();
1032
+ await this._collectionSynchronizer.open();
1033
+ await this._echoNetworkAdapter.open();
1034
+ await this._echoNetworkAdapter.whenConnected();
1035
+ }
1036
+ async _close() {
1037
+ await this._collectionSynchronizer.close();
1038
+ await this._storage.close?.();
1039
+ await this._echoNetworkAdapter.close();
1040
+ await this._ctx.dispose();
1041
+ }
1042
+ /**
1043
+ * @deprecated To be abstracted away.
1044
+ */
1045
+ get repo() {
1046
+ return this._repo;
1047
+ }
1048
+ get peerId() {
1049
+ return this._peerId;
1050
+ }
1051
+ get loadedDocsCount() {
1052
+ return Object.keys(this._repo.handles).length;
1053
+ }
1054
+ async addReplicator(replicator) {
1055
+ await this._echoNetworkAdapter.addReplicator(replicator);
1056
+ }
1057
+ async removeReplicator(replicator) {
1058
+ await this._echoNetworkAdapter.removeReplicator(replicator);
1059
+ }
1060
+ /**
1061
+ * Loads the document handle from the repo and waits for it to be ready.
1062
+ */
1063
+ async loadDoc(ctx, documentId, opts) {
1064
+ let handle;
1065
+ if (typeof documentId === "string") {
1066
+ handle = this._repo.handles[documentId];
1067
+ }
1068
+ if (!handle) {
1069
+ handle = this._repo.find(documentId);
1070
+ }
1071
+ if (!handle.isReady()) {
1072
+ if (!opts?.timeout) {
1073
+ await (0, import_context2.cancelWithContext)(ctx, handle.whenReady());
1074
+ } else {
1075
+ await (0, import_context2.cancelWithContext)(ctx, (0, import_async3.asyncTimeout)(handle.whenReady(), opts.timeout));
1076
+ }
1077
+ }
1078
+ return handle;
1079
+ }
1080
+ /**
1081
+ * Create new persisted document.
1082
+ */
1083
+ createDoc(initialValue, opts) {
1084
+ if (opts?.preserveHistory) {
1085
+ if (!(0, import_automerge2.isAutomerge)(initialValue)) {
1086
+ throw new TypeError("Initial value must be an Automerge document");
1087
+ }
1088
+ return this._repo.import((0, import_automerge2.save)(initialValue));
1089
+ } else {
1090
+ return this._repo.create(initialValue);
1091
+ }
1092
+ }
1093
+ async waitUntilHeadsReplicated(heads) {
1094
+ const entries = heads.entries;
1095
+ if (!entries?.length) {
1096
+ return;
1097
+ }
1098
+ const documentIds = entries.map((entry) => entry.documentId);
1099
+ const documentHeads = await this.getHeads(documentIds);
1100
+ const headsToWait = entries.filter((entry, index) => {
1101
+ const targetHeads = entry.heads;
1102
+ if (!targetHeads || targetHeads.length === 0) {
1103
+ return false;
1104
+ }
1105
+ const currentHeads = documentHeads[index];
1106
+ return !(currentHeads !== null && (0, import_automerge2.equals)(currentHeads, targetHeads));
1107
+ });
1108
+ if (headsToWait.length > 0) {
1109
+ await Promise.all(headsToWait.map(async (entry, index) => {
1110
+ const handle = await this.loadDoc(import_context2.Context.default(void 0, {
1111
+ F: __dxlog_file4,
1112
+ L: 230
1113
+ }), entry.documentId);
1114
+ await waitForHeads(handle, entry.heads);
1115
+ }));
1116
+ }
1117
+ await this._repo.flush(documentIds.filter((documentId) => !!this._repo.handles[documentId]));
1118
+ }
1119
+ async reIndexHeads(documentIds) {
1120
+ for (const documentId of documentIds) {
1121
+ import_log3.log.info("re-indexing heads for document", {
1122
+ documentId
1123
+ }, {
1124
+ F: __dxlog_file4,
1125
+ L: 242,
1126
+ S: this,
1127
+ C: (f, a) => f(...a)
1128
+ });
1129
+ const handle = this._repo.find(documentId);
1130
+ await handle.whenReady([
1131
+ "ready",
1132
+ "requesting"
1133
+ ]);
1134
+ if (handle.inState([
1135
+ "requesting"
1136
+ ])) {
1137
+ import_log3.log.warn("document is not available locally, skipping", {
1138
+ documentId
1139
+ }, {
1140
+ F: __dxlog_file4,
1141
+ L: 246,
1142
+ S: this,
1143
+ C: (f, a) => f(...a)
1144
+ });
1145
+ continue;
1146
+ }
1147
+ const doc = handle.docSync();
1148
+ (0, import_invariant3.invariant)(doc, void 0, {
1149
+ F: __dxlog_file4,
1150
+ L: 251,
1151
+ S: this,
1152
+ A: [
1153
+ "doc",
1154
+ ""
1155
+ ]
1156
+ });
1157
+ const heads = (0, import_automerge2.getHeads)(doc);
1158
+ const batch = this._db.batch();
1159
+ this._headsStore.setHeads(documentId, heads, batch);
1160
+ await batch.write();
1161
+ }
1162
+ import_log3.log.info("done re-indexing heads", void 0, {
1163
+ F: __dxlog_file4,
1164
+ L: 258,
1165
+ S: this,
1166
+ C: (f, a) => f(...a)
1167
+ });
1168
+ }
1169
+ // TODO(dmaretskyi): Share based on HALO permissions and space affinity.
1170
+ // Hosts, running in the worker, don't share documents unless requested by other peers.
1171
+ // NOTE: If both peers return sharePolicy=false the replication will not happen
1172
+ // https://github.com/automerge/automerge-repo/pull/292
1173
+ async _sharePolicy(peerId, documentId) {
1174
+ if (peerId.startsWith("client-")) {
1175
+ return false;
1176
+ }
1177
+ if (!documentId) {
1178
+ return false;
1179
+ }
1180
+ const peerMetadata = this.repo.peerMetadataByPeerId[peerId];
1181
+ if (isEchoPeerMetadata(peerMetadata)) {
1182
+ return this._echoNetworkAdapter.shouldAdvertise(peerId, {
1183
+ documentId
1184
+ });
1185
+ }
1186
+ return false;
1187
+ }
1188
+ async _beforeSave({ path, batch }) {
1189
+ const handle = this._repo.handles[path[0]];
1190
+ if (!handle) {
1191
+ return;
1192
+ }
1193
+ const doc = handle.docSync();
1194
+ if (!doc) {
1195
+ return;
1196
+ }
1197
+ const heads = (0, import_automerge2.getHeads)(doc);
1198
+ this._headsStore.setHeads(handle.documentId, heads, batch);
1199
+ const spaceKey = getSpaceKeyFromDoc(doc) ?? void 0;
1200
+ const objectIds = Object.keys(doc.objects ?? {});
1201
+ const encodedIds = objectIds.map((objectId) => import_protocols.objectPointerCodec.encode({
1202
+ documentId: handle.documentId,
1203
+ objectId,
1204
+ spaceKey
1205
+ }));
1206
+ const idToLastHash = new Map(encodedIds.map((id) => [
1207
+ id,
1208
+ heads
1209
+ ]));
1210
+ this._indexMetadataStore.markDirty(idToLastHash, batch);
1211
+ }
1212
+ _shouldSyncCollection(collectionId, peerId) {
1213
+ const peerMetadata = this._repo.peerMetadataByPeerId[peerId];
1214
+ if (isEchoPeerMetadata(peerMetadata)) {
1215
+ return this._echoNetworkAdapter.shouldSyncCollection(peerId, {
1216
+ collectionId
1217
+ });
1218
+ }
1219
+ return false;
1220
+ }
1221
+ /**
1222
+ * Called by AutomergeStorageAdapter after levelDB batch commit.
1223
+ */
1224
+ async _afterSave(path) {
1225
+ this._indexMetadataStore.notifyMarkedDirty();
1226
+ const documentId = path[0];
1227
+ const document = this._repo.handles[documentId]?.docSync();
1228
+ if (document) {
1229
+ const heads = (0, import_automerge2.getHeads)(document);
1230
+ this._onHeadsChanged(documentId, heads);
1231
+ }
1232
+ }
1233
+ _automergePeers() {
1234
+ return this._repo.peers;
1235
+ }
1236
+ async _isDocumentInRemoteCollection(params) {
1237
+ for (const collectionId of this._collectionSynchronizer.getRegisteredCollectionIds()) {
1238
+ const remoteCollections = this._collectionSynchronizer.getRemoteCollectionStates(collectionId);
1239
+ const remotePeerDocs = remoteCollections.get(params.peerId)?.documents;
1240
+ if (remotePeerDocs && params.documentId in remotePeerDocs) {
1241
+ return true;
1242
+ }
1243
+ }
1244
+ return false;
1245
+ }
1246
+ async _getContainingSpaceForDocument(documentId) {
1247
+ const doc = this._repo.handles[documentId]?.docSync();
1248
+ if (!doc) {
1249
+ return null;
1250
+ }
1251
+ const spaceKeyHex = getSpaceKeyFromDoc(doc);
1252
+ if (!spaceKeyHex) {
1253
+ return null;
1254
+ }
1255
+ return import_keys2.PublicKey.from(spaceKeyHex);
1256
+ }
1257
+ /**
1258
+ * Flush documents to disk.
1259
+ */
1260
+ async flush({ documentIds } = {}) {
1261
+ const loadedDocuments = documentIds?.filter((documentId) => !!this._repo.handles[documentId]);
1262
+ await this._repo.flush(loadedDocuments);
1263
+ }
1264
+ async getHeads(documentIds) {
1265
+ const result = [];
1266
+ const storeRequestIds = [];
1267
+ const storeResultIndices = [];
1268
+ for (const documentId of documentIds) {
1269
+ const doc = this._repo.handles[documentId]?.docSync();
1270
+ if (doc) {
1271
+ result.push((0, import_automerge2.getHeads)(doc));
1272
+ } else {
1273
+ storeRequestIds.push(documentId);
1274
+ storeResultIndices.push(result.length);
1275
+ result.push(void 0);
1276
+ }
1277
+ }
1278
+ if (storeRequestIds.length > 0) {
1279
+ const storedHeads = await this._headsStore.getHeads(storeRequestIds);
1280
+ for (let i = 0; i < storedHeads.length; i++) {
1281
+ result[storeResultIndices[i]] = storedHeads[i];
1282
+ }
1283
+ }
1284
+ return result;
1285
+ }
1286
+ //
1287
+ // Collection sync.
1288
+ //
1289
+ getLocalCollectionState(collectionId) {
1290
+ return this._collectionSynchronizer.getLocalCollectionState(collectionId);
1291
+ }
1292
+ getRemoteCollectionStates(collectionId) {
1293
+ return this._collectionSynchronizer.getRemoteCollectionStates(collectionId);
1294
+ }
1295
+ refreshCollection(collectionId) {
1296
+ this._collectionSynchronizer.refreshCollection(collectionId);
1297
+ }
1298
+ async getCollectionSyncState(collectionId) {
1299
+ const result = {
1300
+ peers: []
1301
+ };
1302
+ const localState = this.getLocalCollectionState(collectionId);
1303
+ const remoteState = this.getRemoteCollectionStates(collectionId);
1304
+ if (!localState) {
1305
+ return result;
1306
+ }
1307
+ for (const [peerId, state] of remoteState) {
1308
+ const diff = diffCollectionState(localState, state);
1309
+ result.peers.push({
1310
+ peerId,
1311
+ missingOnRemote: diff.missingOnRemote.length,
1312
+ missingOnLocal: diff.missingOnLocal.length,
1313
+ differentDocuments: diff.different.length,
1314
+ localDocumentCount: Object.keys(localState.documents).length,
1315
+ remoteDocumentCount: Object.keys(state.documents).length
1316
+ });
1317
+ }
1318
+ return result;
1319
+ }
1320
+ /**
1321
+ * Update the local collection state based on the locally stored document heads.
1322
+ */
1323
+ async updateLocalCollectionState(collectionId, documentIds) {
1324
+ const heads = await this.getHeads(documentIds);
1325
+ const documents = Object.fromEntries(heads.map((heads2, index) => [
1326
+ documentIds[index],
1327
+ heads2 ?? []
1328
+ ]));
1329
+ this._collectionSynchronizer.setLocalCollectionState(collectionId, {
1330
+ documents
1331
+ });
1332
+ }
1333
+ _onCollectionStateQueried(collectionId, peerId) {
1334
+ this._collectionSynchronizer.onCollectionStateQueried(collectionId, peerId);
1335
+ }
1336
+ _onCollectionStateReceived(collectionId, peerId, state) {
1337
+ this._collectionSynchronizer.onRemoteStateReceived(collectionId, peerId, decodeCollectionState(state));
1338
+ }
1339
+ _queryCollectionState(collectionId, peerId) {
1340
+ this._echoNetworkAdapter.queryCollectionState(collectionId, peerId);
1341
+ }
1342
+ _sendCollectionState(collectionId, peerId, state) {
1343
+ this._echoNetworkAdapter.sendCollectionState(collectionId, peerId, encodeCollectionState(state));
1344
+ }
1345
+ _onPeerConnected(peerId) {
1346
+ this._collectionSynchronizer.onConnectionOpen(peerId);
1347
+ }
1348
+ _onPeerDisconnected(peerId) {
1349
+ this._collectionSynchronizer.onConnectionClosed(peerId);
1350
+ }
1351
+ _onRemoteCollectionStateUpdated(collectionId, peerId) {
1352
+ const localState = this._collectionSynchronizer.getLocalCollectionState(collectionId);
1353
+ const remoteState = this._collectionSynchronizer.getRemoteCollectionStates(collectionId).get(peerId);
1354
+ if (!localState || !remoteState) {
1355
+ return;
1356
+ }
1357
+ const { different, missingOnLocal, missingOnRemote } = diffCollectionState(localState, remoteState);
1358
+ const toReplicate = [
1359
+ ...missingOnLocal,
1360
+ ...missingOnRemote,
1361
+ ...different
1362
+ ];
1363
+ if (toReplicate.length === 0) {
1364
+ return;
1365
+ }
1366
+ import_log3.log.info("replication documents after collection sync", {
1367
+ count: toReplicate.length
1368
+ }, {
1369
+ F: __dxlog_file4,
1370
+ L: 486,
1371
+ S: this,
1372
+ C: (f, a) => f(...a)
1373
+ });
1374
+ for (const documentId of toReplicate) {
1375
+ this._repo.find(documentId);
1376
+ }
1377
+ }
1378
+ _onHeadsChanged(documentId, heads) {
1379
+ const collectionsChanged = /* @__PURE__ */ new Set();
1380
+ for (const collectionId of this._collectionSynchronizer.getRegisteredCollectionIds()) {
1381
+ const state = this._collectionSynchronizer.getLocalCollectionState(collectionId);
1382
+ if (state?.documents[documentId]) {
1383
+ const newState = structuredClone(state);
1384
+ newState.documents[documentId] = heads;
1385
+ this._collectionSynchronizer.setLocalCollectionState(collectionId, newState);
1386
+ collectionsChanged.add(collectionId);
1387
+ }
1388
+ }
1389
+ for (const collectionId of collectionsChanged) {
1390
+ this.collectionStateUpdated.emit({
1391
+ collectionId
1392
+ });
1393
+ }
1394
+ }
1395
+ };
1396
+ _ts_decorate2([
1397
+ import_tracing.trace.info()
1398
+ ], AutomergeHost.prototype, "_peerId", void 0);
1399
+ _ts_decorate2([
1400
+ import_tracing.trace.info({
1401
+ depth: null
1402
+ })
1403
+ ], AutomergeHost.prototype, "_automergePeers", null);
1404
+ _ts_decorate2([
1405
+ import_tracing.trace.span({
1406
+ showInBrowserTimeline: true
1407
+ })
1408
+ ], AutomergeHost.prototype, "flush", null);
1409
+ AutomergeHost = _ts_decorate2([
1410
+ import_tracing.trace.resource()
1411
+ ], AutomergeHost);
1412
+ var getSpaceKeyFromDoc = (doc) => {
1413
+ const rawSpaceKey = doc.access?.spaceKey ?? doc.experimental_spaceKey;
1414
+ if (rawSpaceKey == null) {
1415
+ return null;
1416
+ }
1417
+ return String(rawSpaceKey);
1418
+ };
1419
+ var waitForHeads = async (handle, heads) => {
1420
+ const unavailableHeads = new Set(heads);
1421
+ await handle.whenReady();
1422
+ await import_async3.Event.wrap(handle, "change").waitForCondition(() => {
1423
+ for (const changeHash of unavailableHeads.values()) {
1424
+ if (changeIsPresentInDoc(handle.docSync(), changeHash)) {
1425
+ unavailableHeads.delete(changeHash);
1426
+ }
1427
+ }
1428
+ return unavailableHeads.size === 0;
1429
+ });
1430
+ };
1431
+ var changeIsPresentInDoc = (doc, changeHash) => {
1432
+ return !!(0, import_automerge2.getBackend)(doc).getChangeByHash(changeHash);
1433
+ };
1434
+ var decodeCollectionState = (state) => {
1435
+ (0, import_invariant3.invariant)(typeof state === "object" && state !== null, "Invalid state", {
1436
+ F: __dxlog_file4,
1437
+ L: 544,
1438
+ S: void 0,
1439
+ A: [
1440
+ "typeof state === 'object' && state !== null",
1441
+ "'Invalid state'"
1442
+ ]
1443
+ });
1444
+ return state;
1445
+ };
1446
+ var encodeCollectionState = (state) => {
1447
+ return state;
1448
+ };
1449
+ var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/automerge/mesh-echo-replicator-connection.ts";
1450
+ var DEFAULT_FACTORY = (params) => new import_teleport_extension_automerge_replicator.AutomergeReplicator(...params);
1451
+ var MeshReplicatorConnection = class extends import_context6.Resource {
1452
+ constructor(_params) {
1453
+ super();
1454
+ this._params = _params;
1455
+ this.remoteDeviceKey = null;
1456
+ this._remotePeerId = null;
1457
+ this._isEnabled = false;
1458
+ let readableStreamController;
1459
+ this.readable = new ReadableStream({
1460
+ start: (controller) => {
1461
+ readableStreamController = controller;
1462
+ this._ctx.onDispose(() => controller.close());
1463
+ }
1464
+ });
1465
+ this.writable = new WritableStream({
1466
+ write: async (message, controller) => {
1467
+ (0, import_invariant6.invariant)(this._isEnabled, "Writing to a disabled connection", {
1468
+ F: __dxlog_file5,
1469
+ L: 50,
1470
+ S: this,
1471
+ A: [
1472
+ "this._isEnabled",
1473
+ "'Writing to a disabled connection'"
1474
+ ]
1475
+ });
1476
+ try {
1477
+ logSendSync(message);
1478
+ await this.replicatorExtension.sendSyncMessage({
1479
+ payload: import_automerge_repo3.cbor.encode(message)
1480
+ });
1481
+ } catch (err) {
1482
+ controller.error(err);
1483
+ this._disconnectIfEnabled();
1484
+ }
1485
+ }
1486
+ });
1487
+ const createAutomergeReplicator = this._params.replicatorFactory ?? DEFAULT_FACTORY;
1488
+ this.replicatorExtension = createAutomergeReplicator([
1489
+ {
1490
+ peerId: this._params.ownPeerId
1491
+ },
1492
+ {
1493
+ onStartReplication: async (info, remotePeerId) => {
1494
+ this.remoteDeviceKey = remotePeerId;
1495
+ this._remotePeerId = info.id;
1496
+ (0, import_log7.log)("onStartReplication", {
1497
+ id: info.id,
1498
+ thisPeerId: this.peerId,
1499
+ remotePeerId: remotePeerId.toHex()
1500
+ }, {
1501
+ F: __dxlog_file5,
1502
+ L: 85,
1503
+ S: this,
1504
+ C: (f, a) => f(...a)
1505
+ });
1506
+ this._params.onRemoteConnected();
1507
+ },
1508
+ onSyncMessage: async ({ payload }) => {
1509
+ if (!this._isEnabled) {
1510
+ return;
1511
+ }
1512
+ const message = import_automerge_repo3.cbor.decode(payload);
1513
+ readableStreamController.enqueue(message);
1514
+ },
1515
+ onClose: async () => {
1516
+ this._disconnectIfEnabled();
1517
+ }
1518
+ }
1519
+ ]);
1520
+ }
1521
+ _disconnectIfEnabled() {
1522
+ if (this._isEnabled) {
1523
+ this._params.onRemoteDisconnected();
1524
+ }
1525
+ }
1526
+ get peerId() {
1527
+ (0, import_invariant6.invariant)(this._remotePeerId != null, "Remote peer has not connected yet.", {
1528
+ F: __dxlog_file5,
1529
+ L: 111,
1530
+ S: this,
1531
+ A: [
1532
+ "this._remotePeerId != null",
1533
+ "'Remote peer has not connected yet.'"
1534
+ ]
1535
+ });
1536
+ return this._remotePeerId;
1537
+ }
1538
+ async shouldAdvertise(params) {
1539
+ return this._params.shouldAdvertise(params);
1540
+ }
1541
+ shouldSyncCollection(params) {
1542
+ return this._params.shouldSyncCollection(params);
1543
+ }
1544
+ /**
1545
+ * Start exchanging messages with the remote peer.
1546
+ * Call after the remote peer has connected.
1547
+ */
1548
+ enable() {
1549
+ (0, import_invariant6.invariant)(this._remotePeerId != null, "Remote peer has not connected yet.", {
1550
+ F: __dxlog_file5,
1551
+ L: 128,
1552
+ S: this,
1553
+ A: [
1554
+ "this._remotePeerId != null",
1555
+ "'Remote peer has not connected yet.'"
1556
+ ]
1557
+ });
1558
+ this._isEnabled = true;
1559
+ }
1560
+ /**
1561
+ * Stop exchanging messages with the remote peer.
1562
+ */
1563
+ disable() {
1564
+ this._isEnabled = false;
1565
+ }
1566
+ };
1567
+ var logSendSync = (message) => {
1568
+ (0, import_log7.log)("sendSyncMessage", () => {
1569
+ const decodedSyncMessage = message.type === "sync" && message.data ? A2.decodeSyncMessage(message.data) : void 0;
1570
+ return {
1571
+ sync: decodedSyncMessage && {
1572
+ headsLength: decodedSyncMessage.heads.length,
1573
+ requesting: decodedSyncMessage.need.length > 0,
1574
+ sendingChanges: decodedSyncMessage.changes.length > 0
1575
+ },
1576
+ type: message.type,
1577
+ from: message.senderId,
1578
+ to: message.targetId
1579
+ };
1580
+ }, {
1581
+ F: __dxlog_file5,
1582
+ L: 141,
1583
+ S: void 0,
1584
+ C: (f, a) => f(...a)
1585
+ });
1586
+ };
1587
+ var __dxlog_file6 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/automerge/space-collection.ts";
1588
+ var deriveCollectionIdFromSpaceId = (spaceId) => `space:${spaceId}`;
1589
+ var getSpaceIdFromCollectionId = (collectionId) => {
1590
+ const spaceId = collectionId.replace(/^space:/, "");
1591
+ (0, import_invariant7.invariant)(import_keys4.SpaceId.isValid(spaceId), void 0, {
1592
+ F: __dxlog_file6,
1593
+ L: 13,
1594
+ S: void 0,
1595
+ A: [
1596
+ "SpaceId.isValid(spaceId)",
1597
+ ""
1598
+ ]
1599
+ });
1600
+ return spaceId;
1601
+ };
1602
+ var __dxlog_file7 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/automerge/mesh-echo-replicator.ts";
1603
+ var MeshEchoReplicator = class {
1604
+ constructor() {
1605
+ this._connections = /* @__PURE__ */ new Set();
1606
+ this._connectionsPerPeer = /* @__PURE__ */ new Map();
1607
+ this._authorizedDevices = /* @__PURE__ */ new Map();
1608
+ this._context = null;
1609
+ }
1610
+ async connect(context) {
1611
+ this._context = context;
1612
+ }
1613
+ async disconnect() {
1614
+ for (const connection of this._connectionsPerPeer.values()) {
1615
+ this._context?.onConnectionClosed(connection);
1616
+ }
1617
+ for (const connection of this._connections) {
1618
+ await connection.close();
1619
+ }
1620
+ this._connections.clear();
1621
+ this._connectionsPerPeer.clear();
1622
+ this._context = null;
1623
+ }
1624
+ createExtension(extensionFactory) {
1625
+ (0, import_invariant5.invariant)(this._context, void 0, {
1626
+ F: __dxlog_file7,
1627
+ L: 57,
1628
+ S: this,
1629
+ A: [
1630
+ "this._context",
1631
+ ""
1632
+ ]
1633
+ });
1634
+ const connection = new MeshReplicatorConnection({
1635
+ ownPeerId: this._context.peerId,
1636
+ replicatorFactory: extensionFactory,
1637
+ onRemoteConnected: async () => {
1638
+ (0, import_log6.log)("onRemoteConnected", {
1639
+ peerId: connection.peerId
1640
+ }, {
1641
+ F: __dxlog_file7,
1642
+ L: 63,
1643
+ S: this,
1644
+ C: (f, a) => f(...a)
1645
+ });
1646
+ (0, import_invariant5.invariant)(this._context, void 0, {
1647
+ F: __dxlog_file7,
1648
+ L: 64,
1649
+ S: this,
1650
+ A: [
1651
+ "this._context",
1652
+ ""
1653
+ ]
1654
+ });
1655
+ if (this._connectionsPerPeer.has(connection.peerId)) {
1656
+ this._context.onConnectionAuthScopeChanged(connection);
1657
+ } else {
1658
+ this._connectionsPerPeer.set(connection.peerId, connection);
1659
+ this._context.onConnectionOpen(connection);
1660
+ connection.enable();
1661
+ }
1662
+ },
1663
+ onRemoteDisconnected: async () => {
1664
+ (0, import_log6.log)("onRemoteDisconnected", {
1665
+ peerId: connection.peerId
1666
+ }, {
1667
+ F: __dxlog_file7,
1668
+ L: 75,
1669
+ S: this,
1670
+ C: (f, a) => f(...a)
1671
+ });
1672
+ this._context?.onConnectionClosed(connection);
1673
+ this._connectionsPerPeer.delete(connection.peerId);
1674
+ connection.disable();
1675
+ this._connections.delete(connection);
1676
+ },
1677
+ shouldAdvertise: async (params) => {
1678
+ (0, import_log6.log)("shouldAdvertise", {
1679
+ peerId: connection.peerId,
1680
+ documentId: params.documentId
1681
+ }, {
1682
+ F: __dxlog_file7,
1683
+ L: 82,
1684
+ S: this,
1685
+ C: (f, a) => f(...a)
1686
+ });
1687
+ (0, import_invariant5.invariant)(this._context, void 0, {
1688
+ F: __dxlog_file7,
1689
+ L: 83,
1690
+ S: this,
1691
+ A: [
1692
+ "this._context",
1693
+ ""
1694
+ ]
1695
+ });
1696
+ try {
1697
+ const spaceKey = await this._context.getContainingSpaceForDocument(params.documentId);
1698
+ if (!spaceKey) {
1699
+ const remoteDocumentExists = await this._context.isDocumentInRemoteCollection({
1700
+ documentId: params.documentId,
1701
+ peerId: connection.peerId
1702
+ });
1703
+ (0, import_log6.log)("document not found locally for share policy check, accepting the remote document", {
1704
+ peerId: connection.peerId,
1705
+ documentId: params.documentId,
1706
+ remoteDocumentExists
1707
+ }, {
1708
+ F: __dxlog_file7,
1709
+ L: 91,
1710
+ S: this,
1711
+ C: (f, a) => f(...a)
1712
+ });
1713
+ return remoteDocumentExists;
1714
+ }
1715
+ const spaceId = await (0, import_chunk_6EZVIJNE.createIdFromSpaceKey)(spaceKey);
1716
+ const authorizedDevices = this._authorizedDevices.get(spaceId);
1717
+ if (!connection.remoteDeviceKey) {
1718
+ (0, import_log6.log)("device key not found for share policy check", {
1719
+ peerId: connection.peerId,
1720
+ documentId: params.documentId
1721
+ }, {
1722
+ F: __dxlog_file7,
1723
+ L: 107,
1724
+ S: this,
1725
+ C: (f, a) => f(...a)
1726
+ });
1727
+ return false;
1728
+ }
1729
+ const isAuthorized = authorizedDevices?.has(connection.remoteDeviceKey) ?? false;
1730
+ (0, import_log6.log)("share policy check", {
1731
+ localPeer: this._context.peerId,
1732
+ remotePeer: connection.peerId,
1733
+ documentId: params.documentId,
1734
+ deviceKey: connection.remoteDeviceKey,
1735
+ spaceKey,
1736
+ isAuthorized
1737
+ }, {
1738
+ F: __dxlog_file7,
1739
+ L: 115,
1740
+ S: this,
1741
+ C: (f, a) => f(...a)
1742
+ });
1743
+ return isAuthorized;
1744
+ } catch (err) {
1745
+ import_log6.log.catch(err, void 0, {
1746
+ F: __dxlog_file7,
1747
+ L: 125,
1748
+ S: this,
1749
+ C: (f, a) => f(...a)
1750
+ });
1751
+ return false;
1752
+ }
1753
+ },
1754
+ shouldSyncCollection: ({ collectionId }) => {
1755
+ const spaceId = getSpaceIdFromCollectionId(collectionId);
1756
+ const authorizedDevices = this._authorizedDevices.get(spaceId);
1757
+ if (!connection.remoteDeviceKey) {
1758
+ (0, import_log6.log)("device key not found for collection sync check", {
1759
+ peerId: connection.peerId,
1760
+ collectionId
1761
+ }, {
1762
+ F: __dxlog_file7,
1763
+ L: 135,
1764
+ S: this,
1765
+ C: (f, a) => f(...a)
1766
+ });
1767
+ return false;
1768
+ }
1769
+ const isAuthorized = authorizedDevices?.has(connection.remoteDeviceKey) ?? false;
1770
+ return isAuthorized;
1771
+ }
1772
+ });
1773
+ this._connections.add(connection);
1774
+ return connection.replicatorExtension;
1775
+ }
1776
+ async authorizeDevice(spaceKey, deviceKey) {
1777
+ (0, import_log6.log)("authorizeDevice", {
1778
+ spaceKey,
1779
+ deviceKey
1780
+ }, {
1781
+ F: __dxlog_file7,
1782
+ L: 152,
1783
+ S: this,
1784
+ C: (f, a) => f(...a)
1785
+ });
1786
+ const spaceId = await (0, import_chunk_6EZVIJNE.createIdFromSpaceKey)(spaceKey);
1787
+ (0, import_util3.defaultMap)(this._authorizedDevices, spaceId, () => new import_util3.ComplexSet(import_keys3.PublicKey.hash)).add(deviceKey);
1788
+ for (const connection of this._connections) {
1789
+ if (connection.remoteDeviceKey && connection.remoteDeviceKey.equals(deviceKey)) {
1790
+ if (this._connectionsPerPeer.has(connection.peerId)) {
1791
+ this._context?.onConnectionAuthScopeChanged(connection);
1792
+ }
1793
+ }
1794
+ }
1795
+ }
1796
+ };
1797
+ function _ts_decorate3(decorators, target, key, desc) {
1798
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1799
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1800
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1801
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
1802
+ }
1803
+ var PER_SECOND_RATE_AVG_WINDOW_SIZE = 5;
1804
+ var DEFAULT_AVG_WINDOW_SIZE = 25;
1805
+ var EchoDataMonitor = class {
1806
+ constructor(_params = {
1807
+ timeSeriesLength: 30
1808
+ }) {
1809
+ this._params = _params;
1810
+ this._lastTick = 0;
1811
+ this._activeCounters = createLocalCounters();
1812
+ this._localTimeSeries = createLocalTimeSeries();
1813
+ this._storageAverages = createStorageAverages();
1814
+ this._replicationAverages = createNetworkAverages();
1815
+ this._sizeByMessageType = {};
1816
+ this._lastReceivedMessages = new import_util4.CircularBuffer(100);
1817
+ this._lastSentMessages = new import_util4.CircularBuffer(100);
1818
+ this._connectionsCount = 0;
1819
+ }
1820
+ tick(timeMs) {
1821
+ this._advanceTimeWindow(timeMs - this._lastTick);
1822
+ this._lastTick = timeMs;
1823
+ }
1824
+ computeStats() {
1825
+ return {
1826
+ meta: {
1827
+ rateAverageOverSeconds: PER_SECOND_RATE_AVG_WINDOW_SIZE
1828
+ },
1829
+ storage: {
1830
+ reads: {
1831
+ payloadSize: this._storageAverages.loadedChunkSize.average(),
1832
+ opDuration: this._storageAverages.loadDuration.average(),
1833
+ countPerSecond: this._storageAverages.loadsPerSecond.average()
1834
+ },
1835
+ writes: {
1836
+ payloadSize: this._storageAverages.storedChunkSize.average(),
1837
+ opDuration: this._storageAverages.storeDuration.average(),
1838
+ countPerSecond: this._storageAverages.storesPerSecond.average()
1839
+ }
1840
+ },
1841
+ replicator: {
1842
+ connections: this._connectionsCount,
1843
+ receivedMessages: {
1844
+ payloadSize: this._replicationAverages.receivedMessageSize.average(),
1845
+ countPerSecond: this._replicationAverages.receivedPerSecond.average()
1846
+ },
1847
+ sentMessages: {
1848
+ payloadSize: this._replicationAverages.sentMessageSize.average(),
1849
+ opDuration: this._replicationAverages.sendDuration.average(),
1850
+ countPerSecond: this._replicationAverages.sentPerSecond.average(),
1851
+ failedPerSecond: this._replicationAverages.sendsFailedPerSecond.average()
1852
+ },
1853
+ countByMessageType: this._computeMessageHistogram("type"),
1854
+ avgSizeByMessageType: (0, import_util4.mapValues)(this._sizeByMessageType, (summary) => summary.average())
1855
+ }
1856
+ };
1857
+ }
1858
+ get connectionsCount() {
1859
+ return this._connectionsCount;
1860
+ }
1861
+ /**
1862
+ * @internal
1863
+ */
1864
+ get lastPerSecondStats() {
1865
+ return this._lastCompleteCounters;
1866
+ }
1867
+ /**
1868
+ * @internal
1869
+ */
1870
+ get timeSeries() {
1871
+ return {
1872
+ ...this._localTimeSeries.storage,
1873
+ ...this._localTimeSeries.replication
1874
+ };
1875
+ }
1876
+ /**
1877
+ * @internal
1878
+ */
1879
+ get messagesByPeerId() {
1880
+ return this._computeMessageHistogram("peerId");
1881
+ }
1882
+ _advanceTimeWindow(millisPassed) {
1883
+ const oldMetrics = Object.freeze(this._activeCounters);
1884
+ this._activeCounters = createLocalCounters();
1885
+ this._lastCompleteCounters = oldMetrics;
1886
+ for (const peerId of Object.keys(oldMetrics.byPeerId)) {
1887
+ this._activeCounters.byPeerId[peerId] = createMessageCounter();
1888
+ }
1889
+ this._addToTimeSeries(oldMetrics.replication, this._localTimeSeries.replication);
1890
+ this._addToTimeSeries(oldMetrics.storage, this._localTimeSeries.storage);
1891
+ if (Math.abs(millisPassed - 1e3) < 100) {
1892
+ this._reportPerSecondRate(oldMetrics);
1893
+ }
1894
+ }
1895
+ _addToTimeSeries(values, timeSeries) {
1896
+ for (const [key, value] of Object.entries(values)) {
1897
+ const values2 = timeSeries[key];
1898
+ values2.push(value);
1899
+ if (values2.length > this._params.timeSeriesLength) {
1900
+ values2.shift();
1901
+ }
1902
+ }
1903
+ }
1904
+ _reportPerSecondRate(metrics) {
1905
+ const toReport = [
1906
+ [
1907
+ "storage.load",
1908
+ metrics.storage.loadedChunks,
1909
+ this._storageAverages.loadsPerSecond
1910
+ ],
1911
+ [
1912
+ "storage.store",
1913
+ metrics.storage.storedChunks,
1914
+ this._storageAverages.storesPerSecond
1915
+ ],
1916
+ [
1917
+ "network.receive",
1918
+ metrics.replication.received,
1919
+ this._replicationAverages.receivedPerSecond
1920
+ ],
1921
+ [
1922
+ "network.send",
1923
+ metrics.replication.sent,
1924
+ this._replicationAverages.sentPerSecond
1925
+ ]
1926
+ ];
1927
+ for (const [metricName, metric, summary] of toReport) {
1928
+ summary.record(metric);
1929
+ if (metric > 0) {
1930
+ import_tracing2.trace.metrics.distribution(`dxos.echo.${metricName}-rate`, metric);
1931
+ import_tracing2.trace.metrics.increment(`dxos.echo.${metricName}`, 1, {
1932
+ tags: {
1933
+ status: "busy"
1934
+ }
1935
+ });
1936
+ } else {
1937
+ import_tracing2.trace.metrics.increment(`dxos.echo.${metricName}`, 1, {
1938
+ tags: {
1939
+ status: "idle"
1940
+ }
1941
+ });
1942
+ }
1943
+ }
1944
+ this._replicationAverages.sendsFailedPerSecond.record(metrics.replication.failed);
1945
+ }
1946
+ recordPeerConnected(peerId) {
1947
+ this._activeCounters.byPeerId[peerId] = createMessageCounter();
1948
+ this._connectionsCount++;
1949
+ }
1950
+ recordPeerDisconnected(peerId) {
1951
+ this._connectionsCount--;
1952
+ delete this._activeCounters.byPeerId[peerId];
1953
+ }
1954
+ recordBytesStored(count) {
1955
+ this._activeCounters.storage.storedChunks++;
1956
+ this._activeCounters.storage.storedBytes += count;
1957
+ this._storageAverages.storedChunkSize.record(count);
1958
+ import_tracing2.trace.metrics.distribution("dxos.echo.storage.bytes-stored", count, {
1959
+ unit: "bytes"
1960
+ });
1961
+ }
1962
+ recordLoadDuration(durationMs) {
1963
+ this._storageAverages.loadDuration.record(durationMs);
1964
+ }
1965
+ recordStoreDuration(durationMs) {
1966
+ this._storageAverages.storeDuration.record(durationMs);
1967
+ }
1968
+ recordBytesLoaded(count) {
1969
+ this._activeCounters.storage.loadedChunks++;
1970
+ this._activeCounters.storage.loadedBytes += count;
1971
+ this._storageAverages.loadedChunkSize.record(count);
1972
+ import_tracing2.trace.metrics.distribution("dxos.echo.storage.bytes-loaded", count, {
1973
+ unit: "bytes"
1974
+ });
1975
+ }
1976
+ recordMessageSent(message, duration) {
1977
+ let metricsGroupName;
1978
+ const bytes = getByteCount(message);
1979
+ const tags = {
1980
+ type: message.type
1981
+ };
1982
+ if (isAutomergeProtocolMessage(message)) {
1983
+ this._activeCounters.replication.sent++;
1984
+ this._replicationAverages.sendDuration.record(duration);
1985
+ this._replicationAverages.sentMessageSize.record(bytes);
1986
+ metricsGroupName = "replication";
1987
+ } else {
1988
+ metricsGroupName = "collection-sync";
1989
+ }
1990
+ import_tracing2.trace.metrics.distribution(`dxos.echo.${metricsGroupName}.bytes-sent`, bytes, {
1991
+ unit: "bytes",
1992
+ tags
1993
+ });
1994
+ import_tracing2.trace.metrics.distribution(`dxos.echo.${metricsGroupName}.send-duration`, duration, {
1995
+ unit: "millisecond",
1996
+ tags
1997
+ });
1998
+ import_tracing2.trace.metrics.increment(`dxos.echo.${metricsGroupName}.send-status`, 1, {
1999
+ tags: {
2000
+ ...tags,
2001
+ success: true
2002
+ }
2003
+ });
2004
+ const { messageSize, messageCounts } = this._getStatsForType(message);
2005
+ messageSize.record(bytes);
2006
+ messageCounts.sent++;
2007
+ this._lastSentMessages.push({
2008
+ type: message.type,
2009
+ peerId: message.targetId
2010
+ });
2011
+ }
2012
+ recordMessageReceived(message) {
2013
+ const bytes = getByteCount(message);
2014
+ const tags = {
2015
+ type: message.type
2016
+ };
2017
+ if (isAutomergeProtocolMessage(message)) {
2018
+ this._activeCounters.replication.received++;
2019
+ this._replicationAverages.receivedMessageSize.record(bytes);
2020
+ import_tracing2.trace.metrics.distribution("dxos.echo.replication.bytes-received", bytes, {
2021
+ unit: "bytes",
2022
+ tags
2023
+ });
2024
+ } else {
2025
+ import_tracing2.trace.metrics.distribution("dxos.echo.collection-sync.bytes-received", bytes, {
2026
+ unit: "bytes",
2027
+ tags
2028
+ });
2029
+ }
2030
+ const { messageSize, messageCounts } = this._getStatsForType(message);
2031
+ messageSize.record(bytes);
2032
+ messageCounts.received++;
2033
+ this._lastReceivedMessages.push({
2034
+ type: message.type,
2035
+ peerId: message.senderId
2036
+ });
2037
+ }
2038
+ recordMessageSendingFailed(message) {
2039
+ const tags = {
2040
+ type: message.type,
2041
+ success: false
2042
+ };
2043
+ if (isAutomergeProtocolMessage(message)) {
2044
+ this._activeCounters.replication.failed++;
2045
+ import_tracing2.trace.metrics.increment("dxos.echo.replication.send-status", 1, {
2046
+ unit: "bytes",
2047
+ tags
2048
+ });
2049
+ } else {
2050
+ import_tracing2.trace.metrics.increment("dxos.echo.collection-sync.send-status", 1, {
2051
+ unit: "bytes",
2052
+ tags
2053
+ });
2054
+ }
2055
+ const { messageCounts } = this._getStatsForType(message);
2056
+ messageCounts.failed++;
2057
+ }
2058
+ _getStatsForType(message) {
2059
+ const messageSize = this._sizeByMessageType[message.type] ??= createSlidingWindow();
2060
+ const messageCounts = this._activeCounters.byType[message.type] ??= createMessageCounter();
2061
+ return {
2062
+ messageCounts,
2063
+ messageSize
2064
+ };
2065
+ }
2066
+ _computeMessageHistogram(groupKey) {
2067
+ const result = {};
2068
+ for (const receivedMessage of this._lastReceivedMessages) {
2069
+ const counters = result[receivedMessage[groupKey]] ??= {
2070
+ received: 0,
2071
+ sent: 0
2072
+ };
2073
+ counters.received++;
2074
+ }
2075
+ for (const receivedMessage of this._lastSentMessages) {
2076
+ const counters = result[receivedMessage[groupKey]] ??= {
2077
+ received: 0,
2078
+ sent: 0
2079
+ };
2080
+ counters.sent++;
2081
+ }
2082
+ return result;
2083
+ }
2084
+ };
2085
+ EchoDataMonitor = _ts_decorate3([
2086
+ import_tracing2.trace.resource()
2087
+ ], EchoDataMonitor);
2088
+ var isAutomergeProtocolMessage = (message) => {
2089
+ return !(isCollectionQueryMessage(message) || isCollectionStateMessage(message));
2090
+ };
2091
+ var createSlidingWindow = (overrides) => new import_util4.SlidingWindowSummary({
2092
+ dataPoints: DEFAULT_AVG_WINDOW_SIZE,
2093
+ precision: 2,
2094
+ ...overrides
2095
+ });
2096
+ var createLocalCounters = () => ({
2097
+ storage: {
2098
+ loadedBytes: 0,
2099
+ storedBytes: 0,
2100
+ storedChunks: 0,
2101
+ loadedChunks: 0
2102
+ },
2103
+ replication: createMessageCounter(),
2104
+ byPeerId: {},
2105
+ byType: {}
2106
+ });
2107
+ var createLocalTimeSeries = () => ({
2108
+ storage: {
2109
+ loadedBytes: [],
2110
+ storedBytes: [],
2111
+ storedChunks: [],
2112
+ loadedChunks: []
2113
+ },
2114
+ replication: {
2115
+ sent: [],
2116
+ failed: [],
2117
+ received: []
2118
+ }
2119
+ });
2120
+ var createMessageCounter = () => ({
2121
+ sent: 0,
2122
+ received: 0,
2123
+ failed: 0
2124
+ });
2125
+ var createNetworkAverages = () => ({
2126
+ receivedMessageSize: createSlidingWindow(),
2127
+ sentMessageSize: createSlidingWindow(),
2128
+ sendDuration: createSlidingWindow(),
2129
+ receivedPerSecond: createSlidingWindow({
2130
+ dataPoints: PER_SECOND_RATE_AVG_WINDOW_SIZE
2131
+ }),
2132
+ sentPerSecond: createSlidingWindow({
2133
+ dataPoints: PER_SECOND_RATE_AVG_WINDOW_SIZE
2134
+ }),
2135
+ sendsFailedPerSecond: createSlidingWindow({
2136
+ dataPoints: PER_SECOND_RATE_AVG_WINDOW_SIZE
2137
+ })
2138
+ });
2139
+ var createStorageAverages = () => ({
2140
+ storedChunkSize: createSlidingWindow(),
2141
+ loadedChunkSize: createSlidingWindow(),
2142
+ loadDuration: createSlidingWindow(),
2143
+ storeDuration: createSlidingWindow(),
2144
+ loadsPerSecond: createSlidingWindow({
2145
+ dataPoints: PER_SECOND_RATE_AVG_WINDOW_SIZE
2146
+ }),
2147
+ storesPerSecond: createSlidingWindow({
2148
+ dataPoints: PER_SECOND_RATE_AVG_WINDOW_SIZE
2149
+ })
2150
+ });
2151
+ var getByteCount = (message) => {
2152
+ return message.type.length + message.senderId.length + message.targetId.length + (message.data?.byteLength ?? 0) + (message.documentId?.length ?? 0);
2153
+ };
2154
+ var __dxlog_file8 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/db-host/data-service.ts";
2155
+ var DataServiceImpl = class {
2156
+ constructor(params) {
2157
+ this._subscriptions = /* @__PURE__ */ new Map();
2158
+ this._automergeHost = params.automergeHost;
2159
+ this._updateIndexes = params.updateIndexes;
2160
+ }
2161
+ subscribe(request) {
2162
+ return new import_stream.Stream(({ next, ready }) => {
2163
+ const synchronizer = new DocumentsSynchronizer({
2164
+ repo: this._automergeHost.repo,
2165
+ sendUpdates: (updates) => next(updates)
2166
+ });
2167
+ synchronizer.open().then(() => {
2168
+ this._subscriptions.set(request.subscriptionId, synchronizer);
2169
+ ready();
2170
+ }).catch((err) => import_log.log.catch(err, void 0, {
2171
+ F: __dxlog_file8,
2172
+ L: 66,
2173
+ S: this,
2174
+ C: (f, a) => f(...a)
2175
+ }));
2176
+ return () => synchronizer.close();
2177
+ });
2178
+ }
2179
+ async updateSubscription(request) {
2180
+ const synchronizer = this._subscriptions.get(request.subscriptionId);
2181
+ (0, import_invariant.invariant)(synchronizer, "Subscription not found", {
2182
+ F: __dxlog_file8,
2183
+ L: 73,
2184
+ S: this,
2185
+ A: [
2186
+ "synchronizer",
2187
+ "'Subscription not found'"
2188
+ ]
2189
+ });
2190
+ if (request.addIds?.length) {
2191
+ await synchronizer.addDocuments(request.addIds);
2192
+ }
2193
+ if (request.removeIds?.length) {
2194
+ await synchronizer.removeDocuments(request.removeIds);
2195
+ }
2196
+ }
2197
+ async update(request) {
2198
+ if (!request.updates) {
2199
+ return;
2200
+ }
2201
+ const synchronizer = this._subscriptions.get(request.subscriptionId);
2202
+ (0, import_invariant.invariant)(synchronizer, "Subscription not found", {
2203
+ F: __dxlog_file8,
2204
+ L: 88,
2205
+ S: this,
2206
+ A: [
2207
+ "synchronizer",
2208
+ "'Subscription not found'"
2209
+ ]
2210
+ });
2211
+ synchronizer.update(request.updates);
2212
+ }
2213
+ async flush(request) {
2214
+ await this._automergeHost.flush(request);
2215
+ }
2216
+ async getDocumentHeads(request) {
2217
+ const documentIds = request.documentIds;
2218
+ if (!documentIds) {
2219
+ return {
2220
+ heads: {
2221
+ entries: []
2222
+ }
2223
+ };
2224
+ }
2225
+ const heads = await this._automergeHost.getHeads(documentIds);
2226
+ return {
2227
+ heads: {
2228
+ entries: heads.map((heads2, idx) => ({
2229
+ documentId: documentIds[idx],
2230
+ heads: heads2
2231
+ }))
2232
+ }
2233
+ };
2234
+ }
2235
+ async waitUntilHeadsReplicated(request, options) {
2236
+ await this._automergeHost.waitUntilHeadsReplicated(request.heads);
2237
+ }
2238
+ async reIndexHeads(request, options) {
2239
+ await this._automergeHost.reIndexHeads(request.documentIds ?? []);
2240
+ }
2241
+ async updateIndexes() {
2242
+ await this._updateIndexes();
2243
+ }
2244
+ subscribeSpaceSyncState(request) {
2245
+ return new import_stream.Stream(({ ctx, next, ready }) => {
2246
+ (0, import_invariant.invariant)(import_keys.SpaceId.isValid(request.spaceId), void 0, {
2247
+ F: __dxlog_file8,
2248
+ L: 127,
2249
+ S: this,
2250
+ A: [
2251
+ "SpaceId.isValid(request.spaceId)",
2252
+ ""
2253
+ ]
2254
+ });
2255
+ const collectionId = deriveCollectionIdFromSpaceId(request.spaceId);
2256
+ const scheduler = new import_async.UpdateScheduler(ctx, async () => {
2257
+ const state = await this._automergeHost.getCollectionSyncState(collectionId);
2258
+ next({
2259
+ peers: state.peers.map((peer) => ({
2260
+ peerId: peer.peerId,
2261
+ missingOnRemote: peer.missingOnRemote,
2262
+ missingOnLocal: peer.missingOnLocal,
2263
+ differentDocuments: peer.differentDocuments,
2264
+ localDocumentCount: peer.localDocumentCount,
2265
+ remoteDocumentCount: peer.remoteDocumentCount
2266
+ }))
2267
+ });
2268
+ });
2269
+ this._automergeHost.collectionStateUpdated.on(ctx, (e) => {
2270
+ if (e.collectionId === collectionId) {
2271
+ scheduler.trigger();
2272
+ }
2273
+ });
2274
+ scheduler.trigger();
2275
+ });
2276
+ }
2277
+ };
2278
+ var __dxlog_file9 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/db-host/documents-iterator.ts";
2279
+ var LOG_VIEW_OPERATION_THRESHOLD = 300;
2280
+ var createSelectedDocumentsIterator = (automergeHost) => (
2281
+ /**
2282
+ * Get object data blobs from Automerge Repo by ids.
2283
+ */
2284
+ // TODO(mykola): Unload automerge handles after usage.
2285
+ async function* loadDocuments(objects) {
2286
+ for (const [id, heads] of objects.entries()) {
2287
+ try {
2288
+ const { documentId, objectId } = import_protocols3.objectPointerCodec.decode(id);
2289
+ const handle = await automergeHost.loadDoc(import_context8.Context.default(void 0, {
2290
+ F: __dxlog_file9,
2291
+ L: 30
2292
+ }), documentId);
2293
+ let doc = handle.docSync();
2294
+ (0, import_invariant9.invariant)(doc, void 0, {
2295
+ F: __dxlog_file9,
2296
+ L: 33,
2297
+ S: this,
2298
+ A: [
2299
+ "doc",
2300
+ ""
2301
+ ]
2302
+ });
2303
+ const currentHeads = A3.getHeads(doc);
2304
+ if (!A3.equals(currentHeads, heads)) {
2305
+ const begin = Date.now();
2306
+ doc = A3.view(doc, heads);
2307
+ const end = Date.now();
2308
+ if (end - begin > LOG_VIEW_OPERATION_THRESHOLD) {
2309
+ import_log8.log.info("Checking out document version is taking too long", {
2310
+ duration: end - begin,
2311
+ requestedHeads: heads,
2312
+ originalHeads: currentHeads
2313
+ }, {
2314
+ F: __dxlog_file9,
2315
+ L: 44,
2316
+ S: this,
2317
+ C: (f, a) => f(...a)
2318
+ });
2319
+ }
2320
+ }
2321
+ if (doc.version !== import_echo_protocol2.SpaceDocVersion.CURRENT) {
2322
+ continue;
2323
+ }
2324
+ if (!doc.objects?.[objectId]) {
2325
+ continue;
2326
+ }
2327
+ let newId = id;
2328
+ if (import_protocols3.objectPointerCodec.getVersion(id) === import_protocols3.ObjectPointerVersion.V0) {
2329
+ const spaceKey = getSpaceKeyFromDoc(doc) ?? void 0;
2330
+ newId = import_protocols3.objectPointerCodec.encode({
2331
+ documentId,
2332
+ objectId,
2333
+ spaceKey
2334
+ });
2335
+ }
2336
+ yield [
2337
+ {
2338
+ id: newId,
2339
+ object: doc.objects[objectId],
2340
+ heads
2341
+ }
2342
+ ];
2343
+ } catch (error) {
2344
+ import_log8.log.error("Error loading document", {
2345
+ heads,
2346
+ id,
2347
+ error
2348
+ }, {
2349
+ F: __dxlog_file9,
2350
+ L: 70,
2351
+ S: this,
2352
+ C: (f, a) => f(...a)
2353
+ });
2354
+ }
2355
+ }
2356
+ }
2357
+ );
2358
+ function _ts_decorate4(decorators, target, key, desc) {
2359
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2360
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
2361
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
2362
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
2363
+ }
2364
+ var __dxlog_file10 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/db-host/query-state.ts";
2365
+ var QueryState = class extends import_context10.Resource {
2366
+ get active() {
2367
+ return this._lifecycleState === import_context10.LifecycleState.OPEN;
2368
+ }
2369
+ constructor(_params) {
2370
+ super();
2371
+ this._params = _params;
2372
+ this._results = [];
2373
+ this._firstRun = true;
2374
+ this.metrics = {
2375
+ objectsReturned: 0,
2376
+ objectsReturnedFromIndex: 0,
2377
+ documentsLoaded: 0,
2378
+ executionTime: 0,
2379
+ indexQueryTime: 0,
2380
+ documentLoadTime: 0
2381
+ };
2382
+ this.filter = _params.request.filter;
2383
+ }
2384
+ getResults() {
2385
+ return this._results;
2386
+ }
2387
+ // https://github.com/open-telemetry/semantic-conventions/blob/main/docs/attributes-registry/db.md#generic-database-attributes
2388
+ async execQuery() {
2389
+ const filter = this._params.request.filter;
2390
+ const beginQuery = performance.now();
2391
+ const hits = filter.objectIds && filter.objectIds?.length > 0 ? [] : await this._params.indexer.execQuery(filterToIndexQuery(filter));
2392
+ if (this._firstRun) {
2393
+ this.metrics.indexQueryTime = performance.now() - beginQuery;
2394
+ }
2395
+ const beginFilter = performance.now();
2396
+ const results = (await Promise.all(hits.map(async (result) => {
2397
+ if (this._firstRun) {
2398
+ this.metrics.objectsReturnedFromIndex++;
2399
+ }
2400
+ const { objectId, documentId, spaceKey: spaceKeyInIndex } = import_protocols5.objectPointerCodec.decode(result.id);
2401
+ let spaceKey;
2402
+ if (spaceKeyInIndex !== void 0) {
2403
+ spaceKey = spaceKeyInIndex;
2404
+ } else {
2405
+ if (this._firstRun) {
2406
+ this.metrics.documentsLoaded++;
2407
+ }
2408
+ const handle = await this._params.automergeHost.loadDoc(import_context10.Context.default(void 0, {
2409
+ F: __dxlog_file10,
2410
+ L: 116
2411
+ }), documentId);
2412
+ if (this._ctx.disposed) {
2413
+ return;
2414
+ }
2415
+ spaceKey = getSpaceKeyFromDoc(handle.docSync());
2416
+ }
2417
+ if (!spaceKey) {
2418
+ return;
2419
+ }
2420
+ if (this._params.request.filter.options?.spaces?.length && !this._params.request.filter.options.spaces.some((key) => key.equals(spaceKey))) {
2421
+ return;
2422
+ }
2423
+ if (this._firstRun) {
2424
+ this.metrics.objectsReturned++;
2425
+ }
2426
+ return {
2427
+ id: objectId,
2428
+ documentId,
2429
+ spaceId: await (0, import_echo_protocol3.createIdFromSpaceKey)(import_keys5.PublicKey.from(spaceKey)),
2430
+ spaceKey: import_keys5.PublicKey.from(spaceKey),
2431
+ rank: result.rank
2432
+ };
2433
+ }))).filter(import_util5.nonNullable);
2434
+ if (this._firstRun) {
2435
+ this.metrics.documentLoadTime = performance.now() - beginFilter;
2436
+ }
2437
+ if (this._ctx.disposed) {
2438
+ return {
2439
+ changed: false
2440
+ };
2441
+ }
2442
+ const areResultsUnchanged = !this._firstRun && this._results.length === results.length && this._results.every((oldResult) => results.some((result) => result.id === oldResult.id)) && results.every((result) => this._results.some((oldResult) => oldResult.id === result.id));
2443
+ if (this._firstRun) {
2444
+ this.metrics.executionTime = performance.now() - beginQuery;
2445
+ }
2446
+ this._firstRun = false;
2447
+ if (areResultsUnchanged) {
2448
+ return {
2449
+ changed: false
2450
+ };
2451
+ }
2452
+ this._results = results;
2453
+ return {
2454
+ changed: true
2455
+ };
2456
+ }
2457
+ };
2458
+ _ts_decorate4([
2459
+ import_tracing5.trace.info({
2460
+ depth: null
2461
+ })
2462
+ ], QueryState.prototype, "filter", void 0);
2463
+ _ts_decorate4([
2464
+ import_tracing5.trace.info()
2465
+ ], QueryState.prototype, "metrics", void 0);
2466
+ _ts_decorate4([
2467
+ import_tracing5.trace.info()
2468
+ ], QueryState.prototype, "active", null);
2469
+ _ts_decorate4([
2470
+ import_tracing5.trace.span({
2471
+ showInBrowserTimeline: true,
2472
+ op: "db.query",
2473
+ attributes: {
2474
+ "db.system": "echo"
2475
+ }
2476
+ })
2477
+ ], QueryState.prototype, "execQuery", null);
2478
+ QueryState = _ts_decorate4([
2479
+ import_tracing5.trace.resource()
2480
+ ], QueryState);
2481
+ var filterToIndexQuery = (filter) => {
2482
+ (0, import_invariant10.invariant)(!(filter.type && (filter.or ?? []).length > 0), "Cannot mix type and or filters.", {
2483
+ F: __dxlog_file10,
2484
+ L: 181,
2485
+ S: void 0,
2486
+ A: [
2487
+ "!(filter.type && (filter.or ?? []).length > 0)",
2488
+ "'Cannot mix type and or filters.'"
2489
+ ]
2490
+ });
2491
+ (0, import_invariant10.invariant)((filter.or ?? []).every((subFilter) => !(subFilter.type && (subFilter.or ?? []).length > 0)), "Cannot mix type and or filters.", {
2492
+ F: __dxlog_file10,
2493
+ L: 182,
2494
+ S: void 0,
2495
+ A: [
2496
+ "(filter.or ?? []).every((subFilter) => !(subFilter.type && (subFilter.or ?? []).length > 0))",
2497
+ "'Cannot mix type and or filters.'"
2498
+ ]
2499
+ });
2500
+ if (filter.type || (filter.or ?? []).length > 0 && (filter.or ?? []).every((subFilter) => !subFilter.not && subFilter.type)) {
2501
+ return {
2502
+ typenames: filter.type?.objectId ? [
2503
+ filter.type.objectId
2504
+ ] : (filter.or ?? []).map((f) => f.type?.objectId).filter(import_util5.nonNullable),
2505
+ inverted: filter.not
2506
+ };
2507
+ } else {
2508
+ return {
2509
+ typenames: []
2510
+ };
2511
+ }
2512
+ };
2513
+ var __dxlog_file11 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/db-host/query-service.ts";
2514
+ var QueryServiceImpl = class extends import_context9.Resource {
2515
+ // TODO(burdon): OK for options, but not params. Pass separately and type readonly here.
2516
+ constructor(_params) {
2517
+ super();
2518
+ this._params = _params;
2519
+ this._queries = /* @__PURE__ */ new Set();
2520
+ this._updateQueries = new import_async6.DeferredTask(this._ctx, async () => {
2521
+ await Promise.all(Array.from(this._queries).map(async (query) => {
2522
+ try {
2523
+ const { changed } = await query.state.execQuery();
2524
+ if (changed) {
2525
+ query.sendResults(query.state.getResults());
2526
+ }
2527
+ } catch (err) {
2528
+ import_log9.log.catch(err, void 0, {
2529
+ F: __dxlog_file11,
2530
+ L: 52,
2531
+ S: this,
2532
+ C: (f, a) => f(...a)
2533
+ });
2534
+ }
2535
+ }));
2536
+ });
2537
+ import_tracing4.trace.diagnostic({
2538
+ id: "active-queries",
2539
+ name: "Active Queries",
2540
+ fetch: () => {
2541
+ return Array.from(this._queries).map((query) => {
2542
+ return {
2543
+ filter: JSON.stringify(query.state.filter),
2544
+ metrics: query.state.metrics
2545
+ };
2546
+ });
2547
+ }
2548
+ });
2549
+ }
2550
+ async _open() {
2551
+ this._params.indexer.updated.on(this._ctx, () => this._updateQueries.schedule());
2552
+ }
2553
+ async _close() {
2554
+ await Promise.all(Array.from(this._queries).map((query) => query.close()));
2555
+ }
2556
+ async setConfig(config) {
2557
+ if (this._params.indexer.initialized) {
2558
+ import_log9.log.warn("Indexer already initialized.", void 0, {
2559
+ F: __dxlog_file11,
2560
+ L: 86,
2561
+ S: this,
2562
+ C: (f, a) => f(...a)
2563
+ });
2564
+ return;
2565
+ }
2566
+ this._params.indexer.setConfig(config);
2567
+ }
2568
+ execQuery(request) {
2569
+ return new import_codec_protobuf.Stream(({ next, close, ctx }) => {
2570
+ const query = {
2571
+ state: new QueryState({
2572
+ indexer: this._params.indexer,
2573
+ automergeHost: this._params.automergeHost,
2574
+ request
2575
+ }),
2576
+ sendResults: (results) => {
2577
+ if (ctx.disposed) {
2578
+ return;
2579
+ }
2580
+ next({
2581
+ queryId: request.queryId,
2582
+ results
2583
+ });
2584
+ },
2585
+ close: async () => {
2586
+ close();
2587
+ await query.state.close();
2588
+ this._queries.delete(query);
2589
+ }
2590
+ };
2591
+ this._queries.add(query);
2592
+ queueMicrotask(async () => {
2593
+ await query.state.open();
2594
+ try {
2595
+ const { changed } = await query.state.execQuery();
2596
+ if (changed) {
2597
+ query.sendResults(query.state.getResults());
2598
+ }
2599
+ } catch (error) {
2600
+ import_log9.log.catch(error, void 0, {
2601
+ F: __dxlog_file11,
2602
+ L: 123,
2603
+ S: this,
2604
+ C: (f, a) => f(...a)
2605
+ });
2606
+ }
2607
+ });
2608
+ return query.close;
2609
+ });
2610
+ }
2611
+ /**
2612
+ * Re-index all loaded documents.
2613
+ */
2614
+ async reindex() {
2615
+ import_log9.log.info("Reindexing all documents...", void 0, {
2616
+ F: __dxlog_file11,
2617
+ L: 135,
2618
+ S: this,
2619
+ C: (f, a) => f(...a)
2620
+ });
2621
+ const iterator = createDocumentsIterator(this._params.automergeHost);
2622
+ const ids = /* @__PURE__ */ new Map();
2623
+ for await (const documents of iterator()) {
2624
+ for (const { id, heads } of documents) {
2625
+ ids.set(id, heads);
2626
+ }
2627
+ if (ids.size % 100 === 0) {
2628
+ import_log9.log.info("Collected documents...", {
2629
+ count: ids.size
2630
+ }, {
2631
+ F: __dxlog_file11,
2632
+ L: 143,
2633
+ S: this,
2634
+ C: (f, a) => f(...a)
2635
+ });
2636
+ }
2637
+ }
2638
+ import_log9.log.info("Marking all documents as dirty...", {
2639
+ count: ids.size
2640
+ }, {
2641
+ F: __dxlog_file11,
2642
+ L: 147,
2643
+ S: this,
2644
+ C: (f, a) => f(...a)
2645
+ });
2646
+ await this._params.indexer.reindex(ids);
2647
+ }
2648
+ };
2649
+ var createDocumentsIterator = (automergeHost) => (
2650
+ /**
2651
+ * Recursively get all object data blobs from loaded documents from Automerge Repo.
2652
+ */
2653
+ // TODO(mykola): Unload automerge handles after usage.
2654
+ async function* getAllDocuments() {
2655
+ const visited = /* @__PURE__ */ new Set();
2656
+ async function* getObjectsFromHandle(handle) {
2657
+ if (visited.has(handle.documentId)) {
2658
+ return;
2659
+ }
2660
+ const doc = handle.docSync();
2661
+ const spaceKey = getSpaceKeyFromDoc(doc) ?? void 0;
2662
+ if (doc.objects) {
2663
+ yield Object.entries(doc.objects).map(([objectId, object]) => {
2664
+ return {
2665
+ id: import_protocols4.objectPointerCodec.encode({
2666
+ documentId: handle.documentId,
2667
+ objectId,
2668
+ spaceKey
2669
+ }),
2670
+ object,
2671
+ heads: (0, import_automerge4.getHeads)(doc)
2672
+ };
2673
+ });
2674
+ }
2675
+ if (doc.links) {
2676
+ for (const id of Object.values(doc.links)) {
2677
+ if (visited.has(id)) {
2678
+ continue;
2679
+ }
2680
+ const linkHandle = await automergeHost.loadDoc(import_context9.Context.default(void 0, {
2681
+ F: __dxlog_file11,
2682
+ L: 188
2683
+ }), id);
2684
+ for await (const result of getObjectsFromHandle(linkHandle)) {
2685
+ yield result;
2686
+ }
2687
+ }
2688
+ }
2689
+ visited.add(handle.documentId);
2690
+ }
2691
+ for (const handle of Object.values(automergeHost.repo.handles)) {
2692
+ if (visited.has(handle.documentId)) {
2693
+ continue;
2694
+ }
2695
+ for await (const result of getObjectsFromHandle(handle)) {
2696
+ yield result;
2697
+ }
2698
+ visited.add(handle.documentId);
2699
+ }
2700
+ }
2701
+ );
2702
+ var __dxlog_file12 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/db-host/automerge-metrics.ts";
2703
+ var measureDocMetrics = (doc) => {
2704
+ const snapshot = A4.save(doc);
2705
+ const start = Date.now();
2706
+ const temp = A4.load(snapshot);
2707
+ const end = Date.now();
2708
+ A4.free(temp);
2709
+ const getAllChangesStart = Date.now();
2710
+ const mutationCount = A4.getAllChanges(doc).length;
2711
+ const getAllChangesEnd = Date.now();
2712
+ if (getAllChangesEnd - getAllChangesStart > 300) {
2713
+ import_log10.log.warn("getAllChanges took too long", {
2714
+ elapsed: getAllChangesEnd - getAllChangesStart
2715
+ }, {
2716
+ F: __dxlog_file12,
2717
+ L: 30,
2718
+ S: void 0,
2719
+ C: (f, a) => f(...a)
2720
+ });
2721
+ }
2722
+ return {
2723
+ compressedByteSize: snapshot.byteLength,
2724
+ loadTime: end - start,
2725
+ mutationCount
2726
+ };
2727
+ };
2728
+ var __dxlog_file13 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/db-host/database-root.ts";
2729
+ var DatabaseRoot = class {
2730
+ constructor(_rootHandle) {
2731
+ this._rootHandle = _rootHandle;
2732
+ }
2733
+ get documentId() {
2734
+ return this._rootHandle.documentId;
2735
+ }
2736
+ get url() {
2737
+ return this._rootHandle.url;
2738
+ }
2739
+ get isLoaded() {
2740
+ return !!this._rootHandle.docSync();
2741
+ }
2742
+ get handle() {
2743
+ return this._rootHandle;
2744
+ }
2745
+ docSync() {
2746
+ return this._rootHandle.docSync();
2747
+ }
2748
+ getVersion() {
2749
+ const doc = this.docSync();
2750
+ if (!doc) {
2751
+ return null;
2752
+ }
2753
+ return doc.version ?? import_echo_protocol4.SpaceDocVersion.LEGACY;
2754
+ }
2755
+ getSpaceKey() {
2756
+ const doc = this.docSync();
2757
+ if (!doc) {
2758
+ return null;
2759
+ }
2760
+ return getSpaceKeyFromDoc(doc);
2761
+ }
2762
+ getInlineObjectCount() {
2763
+ const doc = this.docSync();
2764
+ if (!doc) {
2765
+ return null;
2766
+ }
2767
+ return Object.keys(doc.objects ?? {}).length;
2768
+ }
2769
+ getLinkedObjectCount() {
2770
+ const doc = this.docSync();
2771
+ if (!doc) {
2772
+ return null;
2773
+ }
2774
+ return Object.keys(doc.links ?? {}).length;
2775
+ }
2776
+ getAllLinkedDocuments() {
2777
+ const doc = this.docSync();
2778
+ (0, import_invariant11.invariant)(doc, void 0, {
2779
+ F: __dxlog_file13,
2780
+ L: 74,
2781
+ S: this,
2782
+ A: [
2783
+ "doc",
2784
+ ""
2785
+ ]
2786
+ });
2787
+ return Object.values(doc.links ?? {});
2788
+ }
2789
+ measureMetrics() {
2790
+ const doc = this.docSync();
2791
+ if (!doc) {
2792
+ return null;
2793
+ }
2794
+ return measureDocMetrics(doc);
2795
+ }
2796
+ };
2797
+ var __dxlog_file14 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/db-host/space-state-manager.ts";
2798
+ var SpaceStateManager = class extends import_context11.Resource {
2799
+ constructor() {
2800
+ super(...arguments);
2801
+ this._roots = /* @__PURE__ */ new Map();
2802
+ this._rootBySpace = /* @__PURE__ */ new Map();
2803
+ this._perRootContext = /* @__PURE__ */ new Map();
2804
+ this._lastSpaceDocumentList = /* @__PURE__ */ new Map();
2805
+ this.spaceDocumentListUpdated = new import_async7.Event();
2806
+ }
2807
+ async _close(ctx) {
2808
+ for (const [_, rootCtx] of this._perRootContext) {
2809
+ await rootCtx.dispose();
2810
+ }
2811
+ this._roots.clear();
2812
+ }
2813
+ get roots() {
2814
+ return this._roots;
2815
+ }
2816
+ getRootByDocumentId(documentId) {
2817
+ return this._roots.get(documentId);
2818
+ }
2819
+ async assignRootToSpace(spaceId, handle) {
2820
+ let root;
2821
+ if (this._roots.has(handle.documentId)) {
2822
+ root = this._roots.get(handle.documentId);
2823
+ } else {
2824
+ root = new DatabaseRoot(handle);
2825
+ this._roots.set(handle.documentId, root);
2826
+ }
2827
+ if (this._rootBySpace.get(spaceId) === root.handle.documentId) {
2828
+ return root;
2829
+ }
2830
+ const prevRootId = this._rootBySpace.get(spaceId);
2831
+ if (prevRootId) {
2832
+ void this._perRootContext.get(prevRootId)?.dispose();
2833
+ this._perRootContext.delete(prevRootId);
2834
+ }
2835
+ this._rootBySpace.set(spaceId, root.handle.documentId);
2836
+ const ctx = new import_context11.Context(void 0, {
2837
+ F: __dxlog_file14,
2838
+ L: 58
2839
+ });
2840
+ this._perRootContext.set(root.handle.documentId, ctx);
2841
+ await root.handle.whenReady();
2842
+ const documentListCheckScheduler = new import_async7.UpdateScheduler(ctx, async () => {
2843
+ const documentIds = [
2844
+ root.documentId,
2845
+ ...root.getAllLinkedDocuments().map((url) => (0, import_automerge_repo4.interpretAsDocumentId)(url))
2846
+ ];
2847
+ if (!(0, import_lodash.default)(documentIds, this._lastSpaceDocumentList.get(spaceId))) {
2848
+ this._lastSpaceDocumentList.set(spaceId, documentIds);
2849
+ this.spaceDocumentListUpdated.emit(new SpaceDocumentListUpdatedEvent(spaceId, documentIds));
2850
+ }
2851
+ }, {
2852
+ maxFrequency: 50
2853
+ });
2854
+ const triggerCheckOnChange = () => documentListCheckScheduler.trigger();
2855
+ root.handle.addListener("change", triggerCheckOnChange);
2856
+ ctx.onDispose(() => root.handle.removeListener("change", triggerCheckOnChange));
2857
+ documentListCheckScheduler.trigger();
2858
+ return root;
2859
+ }
2860
+ };
2861
+ var SpaceDocumentListUpdatedEvent = class {
2862
+ constructor(spaceId, documentIds) {
2863
+ this.spaceId = spaceId;
2864
+ this.documentIds = documentIds;
2865
+ }
2866
+ };
2867
+ var __dxlog_file15 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/db-host/echo-host.ts";
2868
+ var INDEXER_CONFIG = {
2869
+ enabled: true,
2870
+ indexes: [
2871
+ {
2872
+ kind: import_indexing3.IndexKind.Kind.SCHEMA_MATCH
2873
+ }
2874
+ ]
2875
+ };
2876
+ var EchoHost = class extends import_context7.Resource {
2877
+ constructor({ kv }) {
2878
+ super();
2879
+ this._spaceStateManager = new SpaceStateManager();
2880
+ this._indexMetadataStore = new import_indexing2.IndexMetadataStore({
2881
+ db: kv.sublevel("index-metadata")
2882
+ });
2883
+ this._echoDataMonitor = new EchoDataMonitor();
2884
+ this._automergeHost = new AutomergeHost({
2885
+ db: kv,
2886
+ dataMonitor: this._echoDataMonitor,
2887
+ indexMetadataStore: this._indexMetadataStore
2888
+ });
2889
+ this._indexer = new import_indexing2.Indexer({
2890
+ db: kv,
2891
+ indexStore: new import_indexing2.IndexStore({
2892
+ db: kv.sublevel("index-storage")
2893
+ }),
2894
+ metadataStore: this._indexMetadataStore,
2895
+ loadDocuments: createSelectedDocumentsIterator(this._automergeHost),
2896
+ indexCooldownTime: process.env.NODE_ENV === "test" ? 0 : void 0
2897
+ });
2898
+ this._indexer.setConfig(INDEXER_CONFIG);
2899
+ this._queryService = new QueryServiceImpl({
2900
+ automergeHost: this._automergeHost,
2901
+ indexer: this._indexer
2902
+ });
2903
+ this._dataService = new DataServiceImpl({
2904
+ automergeHost: this._automergeHost,
2905
+ updateIndexes: async () => {
2906
+ await this._indexer.updateIndexes();
2907
+ }
2908
+ });
2909
+ import_tracing3.trace.diagnostic({
2910
+ id: "echo-stats",
2911
+ name: "Echo Stats",
2912
+ fetch: async () => {
2913
+ return {
2914
+ dataStats: this._echoDataMonitor.computeStats(),
2915
+ loadedDocsCount: this._automergeHost.loadedDocsCount
2916
+ };
2917
+ }
2918
+ });
2919
+ import_tracing3.trace.diagnostic({
2920
+ id: "database-roots",
2921
+ name: "Database Roots",
2922
+ fetch: async () => {
2923
+ return Array.from(this._spaceStateManager.roots.values()).map((root) => ({
2924
+ url: root.url,
2925
+ isLoaded: root.isLoaded,
2926
+ spaceKey: root.getSpaceKey(),
2927
+ inlineObjects: root.getInlineObjectCount(),
2928
+ linkedObjects: root.getLinkedObjectCount()
2929
+ }));
2930
+ }
2931
+ });
2932
+ import_tracing3.trace.diagnostic({
2933
+ id: "database-root-metrics",
2934
+ name: "Database Roots (with metrics)",
2935
+ fetch: async () => {
2936
+ return Array.from(this._spaceStateManager.roots.values()).map((root) => ({
2937
+ url: root.url,
2938
+ isLoaded: root.isLoaded,
2939
+ spaceKey: root.getSpaceKey(),
2940
+ inlineObjects: root.getInlineObjectCount(),
2941
+ linkedObjects: root.getLinkedObjectCount(),
2942
+ ...root.measureMetrics() ?? {}
2943
+ }));
2944
+ }
2945
+ });
2946
+ }
2947
+ get queryService() {
2948
+ return this._queryService;
2949
+ }
2950
+ get dataService() {
2951
+ return this._dataService;
2952
+ }
2953
+ /**
2954
+ * @deprecated To be abstracted away.
2955
+ */
2956
+ get automergeRepo() {
2957
+ return this._automergeHost.repo;
2958
+ }
2959
+ get roots() {
2960
+ return this._spaceStateManager.roots;
2961
+ }
2962
+ async _open(ctx) {
2963
+ await this._automergeHost.open();
2964
+ await this._indexer.open(ctx);
2965
+ await this._queryService.open(ctx);
2966
+ await this._spaceStateManager.open(ctx);
2967
+ this._spaceStateManager.spaceDocumentListUpdated.on(this._ctx, (e) => {
2968
+ void this._automergeHost.updateLocalCollectionState(deriveCollectionIdFromSpaceId(e.spaceId), e.documentIds);
2969
+ });
2970
+ }
2971
+ async _close(ctx) {
2972
+ await this._spaceStateManager.close();
2973
+ await this._queryService.close(ctx);
2974
+ await this._indexer.close(ctx);
2975
+ await this._automergeHost.close();
2976
+ }
2977
+ /**
2978
+ * Flush all pending writes to the underlying storage.
2979
+ */
2980
+ async flush() {
2981
+ await this._automergeHost.repo.flush();
2982
+ }
2983
+ /**
2984
+ * Perform any pending index updates.
2985
+ */
2986
+ async updateIndexes() {
2987
+ await this._indexer.updateIndexes();
2988
+ }
2989
+ /**
2990
+ * Loads the document handle from the repo and waits for it to be ready.
2991
+ */
2992
+ async loadDoc(ctx, documentId, opts) {
2993
+ return await this._automergeHost.loadDoc(ctx, documentId, opts);
2994
+ }
2995
+ /**
2996
+ * Create new persisted document.
2997
+ */
2998
+ createDoc(initialValue, opts) {
2999
+ return this._automergeHost.createDoc(initialValue, opts);
3000
+ }
3001
+ /**
3002
+ * Create new space root.
3003
+ */
3004
+ async createSpaceRoot(spaceKey) {
3005
+ (0, import_invariant8.invariant)(this._lifecycleState === import_context7.LifecycleState.OPEN, void 0, {
3006
+ F: __dxlog_file15,
3007
+ L: 206,
3008
+ S: this,
3009
+ A: [
3010
+ "this._lifecycleState === LifecycleState.OPEN",
3011
+ ""
3012
+ ]
3013
+ });
3014
+ const spaceId = await (0, import_echo_protocol.createIdFromSpaceKey)(spaceKey);
3015
+ const automergeRoot = this._automergeHost.createDoc({
3016
+ version: import_echo_protocol.SpaceDocVersion.CURRENT,
3017
+ access: {
3018
+ spaceKey: spaceKey.toHex()
3019
+ }
3020
+ });
3021
+ await this._automergeHost.flush({
3022
+ documentIds: [
3023
+ automergeRoot.documentId
3024
+ ]
3025
+ });
3026
+ return await this.openSpaceRoot(spaceId, automergeRoot.url);
3027
+ }
3028
+ // TODO(dmaretskyi): Change to document id.
3029
+ async openSpaceRoot(spaceId, automergeUrl) {
3030
+ (0, import_invariant8.invariant)(this._lifecycleState === import_context7.LifecycleState.OPEN, void 0, {
3031
+ F: __dxlog_file15,
3032
+ L: 221,
3033
+ S: this,
3034
+ A: [
3035
+ "this._lifecycleState === LifecycleState.OPEN",
3036
+ ""
3037
+ ]
3038
+ });
3039
+ const handle = this._automergeHost.repo.find(automergeUrl);
3040
+ return this._spaceStateManager.assignRootToSpace(spaceId, handle);
3041
+ }
3042
+ // TODO(dmaretskyi): Change to document id.
3043
+ async closeSpaceRoot(automergeUrl) {
3044
+ (0, import_debug.todo)();
3045
+ }
3046
+ /**
3047
+ * Install data replicator.
3048
+ */
3049
+ async addReplicator(replicator) {
3050
+ await this._automergeHost.addReplicator(replicator);
3051
+ }
3052
+ /**
3053
+ * Remove data replicator.
3054
+ */
3055
+ async removeReplicator(replicator) {
3056
+ await this._automergeHost.removeReplicator(replicator);
3057
+ }
3058
+ async getSpaceSyncState(spaceId) {
3059
+ const collectionId = deriveCollectionIdFromSpaceId(spaceId);
3060
+ return this._automergeHost.getCollectionSyncState(collectionId);
3061
+ }
3062
+ };
3063
+ var convertLegacyReferences = async (doc) => {
3064
+ const newDoc = await (0, import_util6.deepMapValuesAsync)(doc, async (value, recurse) => {
3065
+ if ((0, import_echo_protocol6.isLegacyReference)(value)) {
3066
+ return (0, import_echo_protocol5.convertLegacyReference)(value);
3067
+ }
3068
+ return recurse(value);
3069
+ });
3070
+ newDoc.version = import_echo_protocol6.SpaceDocVersion.CURRENT;
3071
+ return newDoc;
3072
+ };
3073
+ var convertLegacySpaceRootDoc = async (root) => {
3074
+ const newDoc = await convertLegacyReferences(root);
3075
+ const properties = findInlineObjectOfType(newDoc, import_echo_protocol6.LEGACY_TYPE_PROPERTIES);
3076
+ if (properties) {
3077
+ const [_, obj] = properties;
3078
+ obj.system.type = (0, import_echo_protocol6.encodeReference)(import_echo_protocol6.Reference.fromLegacyTypename(import_echo_schema.TYPE_PROPERTIES));
3079
+ }
3080
+ return newDoc;
3081
+ };
3082
+ var findInlineObjectOfType = (spaceDoc, typename) => {
3083
+ for (const id in spaceDoc.objects ?? {}) {
3084
+ const obj = spaceDoc.objects[id];
3085
+ if (obj.system.type && (0, import_echo_protocol6.decodeReference)(obj.system.type).objectId === typename) {
3086
+ return [
3087
+ id,
3088
+ obj
3089
+ ];
3090
+ }
3091
+ }
3092
+ return void 0;
3093
+ };
3094
+ function _using_ctx() {
3095
+ var _disposeSuppressedError = typeof SuppressedError === "function" ? SuppressedError : function(error, suppressed) {
3096
+ var err = new Error();
3097
+ err.name = "SuppressedError";
3098
+ err.suppressed = suppressed;
3099
+ err.error = error;
3100
+ return err;
3101
+ }, empty = {}, stack = [];
3102
+ function using(isAwait, value) {
3103
+ if (value != null) {
3104
+ if (Object(value) !== value) {
3105
+ throw new TypeError("using declarations can only be used with objects, functions, null, or undefined.");
3106
+ }
3107
+ if (isAwait) {
3108
+ var dispose = value[Symbol.asyncDispose || Symbol.for("Symbol.asyncDispose")];
3109
+ }
3110
+ if (dispose == null) {
3111
+ dispose = value[Symbol.dispose || Symbol.for("Symbol.dispose")];
3112
+ }
3113
+ if (typeof dispose !== "function") {
3114
+ throw new TypeError(`Property [Symbol.dispose] is not a function.`);
3115
+ }
3116
+ stack.push({
3117
+ v: value,
3118
+ d: dispose,
3119
+ a: isAwait
3120
+ });
3121
+ } else if (isAwait) {
3122
+ stack.push({
3123
+ d: value,
3124
+ a: isAwait
3125
+ });
3126
+ }
3127
+ return value;
3128
+ }
3129
+ return {
3130
+ e: empty,
3131
+ u: using.bind(null, false),
3132
+ a: using.bind(null, true),
3133
+ d: function() {
3134
+ var error = this.e;
3135
+ function next() {
3136
+ while (resource = stack.pop()) {
3137
+ try {
3138
+ var resource, disposalResult = resource.d && resource.d.call(resource.v);
3139
+ if (resource.a) {
3140
+ return Promise.resolve(disposalResult).then(next, err);
3141
+ }
3142
+ } catch (e) {
3143
+ return err(e);
3144
+ }
3145
+ }
3146
+ if (error !== empty) throw error;
3147
+ }
3148
+ function err(e) {
3149
+ error = error !== empty ? new _disposeSuppressedError(error, e) : e;
3150
+ return next();
3151
+ }
3152
+ return next();
3153
+ }
3154
+ };
3155
+ }
3156
+ var __dxlog_file16 = "/home/runner/work/dxos/dxos/packages/core/echo/echo-pipeline/src/edge/echo-edge-replicator.ts";
3157
+ var RESTART_DELAY = 500;
3158
+ var EchoEdgeReplicator = class {
3159
+ constructor({ edgeConnection, disableSharePolicy }) {
3160
+ this._mutex = new import_async8.Mutex();
3161
+ this._ctx = void 0;
3162
+ this._context = null;
3163
+ this._connectedSpaces = /* @__PURE__ */ new Set();
3164
+ this._connections = /* @__PURE__ */ new Map();
3165
+ this._sharePolicyEnabled = true;
3166
+ this._edgeConnection = edgeConnection;
3167
+ this._sharePolicyEnabled = !disableSharePolicy;
3168
+ }
3169
+ async connect(context) {
3170
+ import_log11.log.info("connect", {
3171
+ peerId: context.peerId,
3172
+ connectedSpaces: this._connectedSpaces.size
3173
+ }, {
3174
+ F: __dxlog_file16,
3175
+ L: 58,
3176
+ S: this,
3177
+ C: (f, a) => f(...a)
3178
+ });
3179
+ this._context = context;
3180
+ this._ctx = import_context12.Context.default(void 0, {
3181
+ F: __dxlog_file16,
3182
+ L: 61
3183
+ });
3184
+ this._edgeConnection.reconnect.on(this._ctx, async () => {
3185
+ try {
3186
+ var _usingCtx = _using_ctx();
3187
+ const _guard = _usingCtx.u(await this._mutex.acquire());
3188
+ const spaces = [
3189
+ ...this._connectedSpaces
3190
+ ];
3191
+ for (const connection of this._connections.values()) {
3192
+ await connection.close();
3193
+ }
3194
+ this._connections.clear();
3195
+ if (this._context !== null) {
3196
+ for (const spaceId of spaces) {
3197
+ await this._openConnection(spaceId);
3198
+ }
3199
+ }
3200
+ } catch (_) {
3201
+ _usingCtx.e = _;
3202
+ } finally {
3203
+ _usingCtx.d();
3204
+ }
3205
+ });
3206
+ for (const spaceId of this._connectedSpaces) {
3207
+ await this._openConnection(spaceId);
3208
+ }
3209
+ }
3210
+ async disconnect() {
3211
+ try {
3212
+ var _usingCtx = _using_ctx();
3213
+ const _guard = _usingCtx.u(await this._mutex.acquire());
3214
+ await this._ctx?.dispose();
3215
+ for (const connection of this._connections.values()) {
3216
+ await connection.close();
3217
+ }
3218
+ this._connections.clear();
3219
+ } catch (_) {
3220
+ _usingCtx.e = _;
3221
+ } finally {
3222
+ _usingCtx.d();
3223
+ }
3224
+ }
3225
+ async connectToSpace(spaceId) {
3226
+ try {
3227
+ var _usingCtx = _using_ctx();
3228
+ const _guard = _usingCtx.u(await this._mutex.acquire());
3229
+ this._connectedSpaces.add(spaceId);
3230
+ if (this._context !== null) {
3231
+ await this._openConnection(spaceId);
3232
+ }
3233
+ } catch (_) {
3234
+ _usingCtx.e = _;
3235
+ } finally {
3236
+ _usingCtx.d();
3237
+ }
3238
+ }
3239
+ async disconnectFromSpace(spaceId) {
3240
+ try {
3241
+ var _usingCtx = _using_ctx();
3242
+ const _guard = _usingCtx.u(await this._mutex.acquire());
3243
+ this._connectedSpaces.delete(spaceId);
3244
+ const connection = this._connections.get(spaceId);
3245
+ if (connection) {
3246
+ await connection.close();
3247
+ this._connections.delete(spaceId);
3248
+ }
3249
+ } catch (_) {
3250
+ _usingCtx.e = _;
3251
+ } finally {
3252
+ _usingCtx.d();
3253
+ }
3254
+ }
3255
+ async _openConnection(spaceId) {
3256
+ (0, import_invariant12.invariant)(this._context, void 0, {
3257
+ F: __dxlog_file16,
3258
+ L: 117,
3259
+ S: this,
3260
+ A: [
3261
+ "this._context",
3262
+ ""
3263
+ ]
3264
+ });
3265
+ (0, import_invariant12.invariant)(!this._connections.has(spaceId), void 0, {
3266
+ F: __dxlog_file16,
3267
+ L: 118,
3268
+ S: this,
3269
+ A: [
3270
+ "!this._connections.has(spaceId)",
3271
+ ""
3272
+ ]
3273
+ });
3274
+ const connection = new EdgeReplicatorConnection({
3275
+ edgeConnection: this._edgeConnection,
3276
+ ownPeerId: this._context.peerId,
3277
+ spaceId,
3278
+ context: this._context,
3279
+ sharedPolicyEnabled: this._sharePolicyEnabled,
3280
+ onRemoteConnected: async () => {
3281
+ this._context?.onConnectionOpen(connection);
3282
+ },
3283
+ onRemoteDisconnected: async () => {
3284
+ this._context?.onConnectionClosed(connection);
3285
+ },
3286
+ onRestartRequested: async () => {
3287
+ try {
3288
+ var _usingCtx = _using_ctx();
3289
+ const _guard = _usingCtx.u(await this._mutex.acquire());
3290
+ const ctx = this._ctx;
3291
+ await connection.close();
3292
+ this._connections.delete(spaceId);
3293
+ if (ctx?.disposed) {
3294
+ return;
3295
+ }
3296
+ await this._openConnection(spaceId);
3297
+ } catch (_) {
3298
+ _usingCtx.e = _;
3299
+ } finally {
3300
+ _usingCtx.d();
3301
+ }
3302
+ }
3303
+ });
3304
+ this._connections.set(spaceId, connection);
3305
+ await connection.open();
3306
+ }
3307
+ };
3308
+ var EdgeReplicatorConnection = class extends import_context12.Resource {
3309
+ constructor({ edgeConnection, ownPeerId, spaceId, context, sharedPolicyEnabled, onRemoteConnected, onRemoteDisconnected, onRestartRequested }) {
3310
+ super();
3311
+ this._remotePeerId = null;
3312
+ this._restartScheduled = false;
3313
+ this._edgeConnection = edgeConnection;
3314
+ this._ownPeerId = ownPeerId;
3315
+ this._spaceId = spaceId;
3316
+ this._context = context;
3317
+ this._remotePeerId = `${import_protocols6.EdgeService.AUTOMERGE_REPLICATOR}:${spaceId}-${(0, import_crypto.randomUUID)()}`;
3318
+ this._targetServiceId = `${import_protocols6.EdgeService.AUTOMERGE_REPLICATOR}:${spaceId}`;
3319
+ this._sharedPolicyEnabled = sharedPolicyEnabled;
3320
+ this._onRemoteConnected = onRemoteConnected;
3321
+ this._onRemoteDisconnected = onRemoteDisconnected;
3322
+ this._onRestartRequested = onRestartRequested;
3323
+ this.readable = new ReadableStream({
3324
+ start: (controller) => {
3325
+ this._readableStreamController = controller;
3326
+ }
3327
+ });
3328
+ this.writable = new WritableStream({
3329
+ write: async (message, controller) => {
3330
+ await this._sendMessage(message);
3331
+ }
3332
+ });
3333
+ }
3334
+ async _open(ctx) {
3335
+ (0, import_log11.log)("open", void 0, {
3336
+ F: __dxlog_file16,
3337
+ L: 219,
3338
+ S: this,
3339
+ C: (f, a) => f(...a)
3340
+ });
3341
+ this._ctx.onDispose(this._edgeConnection.addListener((msg) => {
3342
+ this._onMessage(msg);
3343
+ }));
3344
+ await this._onRemoteConnected();
3345
+ }
3346
+ async _close() {
3347
+ (0, import_log11.log)("close", void 0, {
3348
+ F: __dxlog_file16,
3349
+ L: 231,
3350
+ S: this,
3351
+ C: (f, a) => f(...a)
3352
+ });
3353
+ this._readableStreamController.close();
3354
+ await this._onRemoteDisconnected();
3355
+ }
3356
+ get peerId() {
3357
+ (0, import_invariant12.invariant)(this._remotePeerId, "Not connected", {
3358
+ F: __dxlog_file16,
3359
+ L: 237,
3360
+ S: this,
3361
+ A: [
3362
+ "this._remotePeerId",
3363
+ "'Not connected'"
3364
+ ]
3365
+ });
3366
+ return this._remotePeerId;
3367
+ }
3368
+ async shouldAdvertise(params) {
3369
+ if (!this._sharedPolicyEnabled) {
3370
+ return true;
3371
+ }
3372
+ const spaceId = await this._context.getContainingSpaceIdForDocument(params.documentId);
3373
+ if (!spaceId) {
3374
+ return true;
3375
+ }
3376
+ return spaceId === this._spaceId;
3377
+ }
3378
+ shouldSyncCollection(params) {
3379
+ if (!this._sharedPolicyEnabled) {
3380
+ return true;
3381
+ }
3382
+ const spaceId = getSpaceIdFromCollectionId(params.collectionId);
3383
+ return spaceId === this._spaceId;
3384
+ }
3385
+ _onMessage(message) {
3386
+ if (message.serviceId !== this._targetServiceId) {
3387
+ return;
3388
+ }
3389
+ const payload = import_automerge_repo5.cbor.decode(message.payload.value);
3390
+ (0, import_log11.log)("recv", () => {
3391
+ const decodedData = payload.type === "sync" && payload.data ? A5.decodeSyncMessage(payload.data) : payload.type === "collection-state" ? payload.state : payload;
3392
+ return {
3393
+ from: message.serviceId,
3394
+ type: payload.type,
3395
+ decodedData
3396
+ };
3397
+ }, {
3398
+ F: __dxlog_file16,
3399
+ L: 268,
3400
+ S: this,
3401
+ C: (f, a) => f(...a)
3402
+ });
3403
+ payload.senderId = this._remotePeerId;
3404
+ this._processMessage(payload);
3405
+ }
3406
+ _processMessage(message) {
3407
+ if (isForbiddenErrorMessage(message)) {
3408
+ if (!this._restartScheduled) {
3409
+ import_log11.log.warn("Forbidden error received, replicator will restart the connection", {
3410
+ spaceId: this._spaceId,
3411
+ delayMs: RESTART_DELAY,
3412
+ remotePeerId: this._remotePeerId
3413
+ }, {
3414
+ F: __dxlog_file16,
3415
+ L: 288,
3416
+ S: this,
3417
+ C: (f, a) => f(...a)
3418
+ });
3419
+ this._restartScheduled = true;
3420
+ (0, import_async8.scheduleTask)(this._ctx, async () => {
3421
+ await this._onRestartRequested();
3422
+ }, RESTART_DELAY);
3423
+ }
3424
+ return;
3425
+ }
3426
+ this._readableStreamController.enqueue(message);
3427
+ }
3428
+ async _sendMessage(message) {
3429
+ message.targetId = this._targetServiceId;
3430
+ (0, import_log11.log)("send", {
3431
+ type: message.type,
3432
+ senderId: message.senderId,
3433
+ targetId: message.targetId,
3434
+ documentId: message.documentId
3435
+ }, {
3436
+ F: __dxlog_file16,
3437
+ L: 312,
3438
+ S: this,
3439
+ C: (f, a) => f(...a)
3440
+ });
3441
+ const encoded = import_automerge_repo5.cbor.encode(message);
3442
+ await this._edgeConnection.send(import_buf.buf.create(import_messenger_pb.MessageSchema, {
3443
+ serviceId: this._targetServiceId,
3444
+ source: {
3445
+ identityKey: this._edgeConnection.identityKey,
3446
+ peerKey: this._edgeConnection.peerKey
3447
+ },
3448
+ payload: {
3449
+ value: (0, import_util7.bufferToArray)(encoded)
3450
+ }
3451
+ }));
3452
+ }
3453
+ };
3454
+ var isForbiddenErrorMessage = (message) => message.type === "error" && message.message === "Forbidden";
58
3455
  // Annotate the CommonJS export names for ESM import in node:
59
3456
  0 && (module.exports = {
60
3457
  AuthExtension,
@@ -63,25 +3460,33 @@ var import_chunk_DZVH7HDD = require("./chunk-DZVH7HDD.cjs");
63
3460
  CredentialRetrieverExtension,
64
3461
  CredentialServerExtension,
65
3462
  DataServiceImpl,
3463
+ DatabaseRoot,
66
3464
  DocumentsSynchronizer,
67
3465
  EchoDataMonitor,
3466
+ EchoEdgeReplicator,
3467
+ EchoHost,
68
3468
  LevelDBStorageAdapter,
69
3469
  MOCK_AUTH_PROVIDER,
70
3470
  MOCK_AUTH_VERIFIER,
71
3471
  MeshEchoReplicator,
72
3472
  MetadataStore,
73
3473
  Pipeline,
3474
+ QueryServiceImpl,
3475
+ QueryState,
74
3476
  Space,
75
3477
  SpaceManager,
76
3478
  SpaceProtocol,
77
3479
  SpaceProtocolSession,
78
3480
  TimeframeClock,
79
3481
  codec,
3482
+ convertLegacyReferences,
3483
+ convertLegacySpaceRootDoc,
80
3484
  createIdFromSpaceKey,
81
3485
  createMappedFeedWriter,
82
3486
  deriveCollectionIdFromSpaceId,
83
3487
  diffCollectionState,
84
3488
  encodingOptions,
3489
+ findInlineObjectOfType,
85
3490
  getSpaceIdFromCollectionId,
86
3491
  getSpaceKeyFromDoc,
87
3492
  hasInvitationExpired,