@dxos/client-services 0.8.4-main.72ec0f3 → 0.8.4-main.74a063c4e0

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 (181) hide show
  1. package/dist/lib/browser/{chunk-HJH6BNTN.mjs → chunk-3LSLNVKQ.mjs} +2102 -1870
  2. package/dist/lib/browser/chunk-3LSLNVKQ.mjs.map +7 -0
  3. package/dist/lib/browser/chunk-NQSC7HOE.mjs +22 -0
  4. package/dist/lib/browser/chunk-NQSC7HOE.mjs.map +7 -0
  5. package/dist/lib/browser/chunk-QCWEHHJW.mjs +24 -0
  6. package/dist/lib/browser/chunk-QCWEHHJW.mjs.map +7 -0
  7. package/dist/lib/browser/index.mjs +451 -67
  8. package/dist/lib/browser/index.mjs.map +4 -4
  9. package/dist/lib/browser/meta.json +1 -1
  10. package/dist/lib/browser/packlets/diagnostics/browser-diagnostics-broadcast.mjs +93 -0
  11. package/dist/lib/browser/packlets/diagnostics/browser-diagnostics-broadcast.mjs.map +7 -0
  12. package/dist/lib/browser/packlets/diagnostics/diagnostics-broadcast.mjs +11 -0
  13. package/dist/lib/browser/packlets/diagnostics/diagnostics-broadcast.mjs.map +7 -0
  14. package/dist/lib/browser/packlets/locks/browser.mjs +126 -0
  15. package/dist/lib/browser/packlets/locks/browser.mjs.map +7 -0
  16. package/dist/lib/browser/packlets/locks/node.mjs +66 -0
  17. package/dist/lib/browser/packlets/locks/node.mjs.map +7 -0
  18. package/dist/lib/browser/testing/index.mjs +36 -17
  19. package/dist/lib/browser/testing/index.mjs.map +3 -3
  20. package/dist/lib/node-esm/chunk-2SZHAWBN.mjs +24 -0
  21. package/dist/lib/node-esm/chunk-2SZHAWBN.mjs.map +7 -0
  22. package/dist/lib/node-esm/{chunk-ONQM6RQH.mjs → chunk-5S7PIHLS.mjs} +1942 -1579
  23. package/dist/lib/node-esm/chunk-5S7PIHLS.mjs.map +7 -0
  24. package/dist/lib/node-esm/chunk-PKEGMOQ4.mjs +22 -0
  25. package/dist/lib/node-esm/chunk-PKEGMOQ4.mjs.map +7 -0
  26. package/dist/lib/node-esm/index.mjs +451 -67
  27. package/dist/lib/node-esm/index.mjs.map +4 -4
  28. package/dist/lib/node-esm/meta.json +1 -1
  29. package/dist/lib/node-esm/packlets/diagnostics/browser-diagnostics-broadcast.mjs +93 -0
  30. package/dist/lib/node-esm/packlets/diagnostics/browser-diagnostics-broadcast.mjs.map +7 -0
  31. package/dist/lib/node-esm/packlets/diagnostics/diagnostics-broadcast.mjs +11 -0
  32. package/dist/lib/node-esm/packlets/diagnostics/diagnostics-broadcast.mjs.map +7 -0
  33. package/dist/lib/node-esm/packlets/locks/browser.mjs +126 -0
  34. package/dist/lib/node-esm/packlets/locks/browser.mjs.map +7 -0
  35. package/dist/lib/node-esm/packlets/locks/node.mjs +66 -0
  36. package/dist/lib/node-esm/packlets/locks/node.mjs.map +7 -0
  37. package/dist/lib/node-esm/testing/index.mjs +36 -17
  38. package/dist/lib/node-esm/testing/index.mjs.map +3 -3
  39. package/dist/types/src/index.d.ts +1 -0
  40. package/dist/types/src/index.d.ts.map +1 -1
  41. package/dist/types/src/packlets/agents/edge-agent-manager.d.ts +3 -2
  42. package/dist/types/src/packlets/agents/edge-agent-manager.d.ts.map +1 -1
  43. package/dist/types/src/packlets/agents/edge-agent-service.d.ts.map +1 -1
  44. package/dist/types/src/packlets/devtools/devtools.d.ts +2 -2
  45. package/dist/types/src/packlets/devtools/devtools.d.ts.map +1 -1
  46. package/dist/types/src/packlets/diagnostics/index.d.ts +1 -1
  47. package/dist/types/src/packlets/diagnostics/index.d.ts.map +1 -1
  48. package/dist/types/src/packlets/identity/authenticator.d.ts +2 -2
  49. package/dist/types/src/packlets/identity/authenticator.d.ts.map +1 -1
  50. package/dist/types/src/packlets/identity/contacts-service.d.ts.map +1 -1
  51. package/dist/types/src/packlets/identity/identity-manager.d.ts +6 -6
  52. package/dist/types/src/packlets/identity/identity-manager.d.ts.map +1 -1
  53. package/dist/types/src/packlets/identity/identity-recovery-manager.d.ts +7 -6
  54. package/dist/types/src/packlets/identity/identity-recovery-manager.d.ts.map +1 -1
  55. package/dist/types/src/packlets/identity/identity-service.d.ts +1 -6
  56. package/dist/types/src/packlets/identity/identity-service.d.ts.map +1 -1
  57. package/dist/types/src/packlets/identity/identity.d.ts +8 -11
  58. package/dist/types/src/packlets/identity/identity.d.ts.map +1 -1
  59. package/dist/types/src/packlets/invitations/device-invitation-protocol.d.ts +4 -4
  60. package/dist/types/src/packlets/invitations/device-invitation-protocol.d.ts.map +1 -1
  61. package/dist/types/src/packlets/invitations/edge-invitation-handler.d.ts.map +1 -1
  62. package/dist/types/src/packlets/invitations/invitation-guest-extenstion.d.ts.map +1 -1
  63. package/dist/types/src/packlets/invitations/invitation-host-extension.d.ts.map +1 -1
  64. package/dist/types/src/packlets/invitations/invitation-protocol.d.ts +2 -3
  65. package/dist/types/src/packlets/invitations/invitation-protocol.d.ts.map +1 -1
  66. package/dist/types/src/packlets/invitations/invitations-handler.d.ts +4 -4
  67. package/dist/types/src/packlets/invitations/invitations-handler.d.ts.map +1 -1
  68. package/dist/types/src/packlets/invitations/invitations-manager.d.ts +3 -3
  69. package/dist/types/src/packlets/invitations/invitations-manager.d.ts.map +1 -1
  70. package/dist/types/src/packlets/invitations/space-invitation-protocol.d.ts +2 -2
  71. package/dist/types/src/packlets/invitations/space-invitation-protocol.d.ts.map +1 -1
  72. package/dist/types/src/packlets/locks/index.d.ts +1 -1
  73. package/dist/types/src/packlets/locks/index.d.ts.map +1 -1
  74. package/dist/types/src/packlets/logging/logging-service.d.ts +4 -0
  75. package/dist/types/src/packlets/logging/logging-service.d.ts.map +1 -1
  76. package/dist/types/src/packlets/network/network-service.d.ts.map +1 -1
  77. package/dist/types/src/packlets/services/client-rpc-server.d.ts +2 -2
  78. package/dist/types/src/packlets/services/client-rpc-server.d.ts.map +1 -1
  79. package/dist/types/src/packlets/services/feed-syncer.d.ts +59 -0
  80. package/dist/types/src/packlets/services/feed-syncer.d.ts.map +1 -0
  81. package/dist/types/src/packlets/services/feed-syncer.test.d.ts +2 -0
  82. package/dist/types/src/packlets/services/feed-syncer.test.d.ts.map +1 -0
  83. package/dist/types/src/packlets/services/platform.d.ts.map +1 -1
  84. package/dist/types/src/packlets/services/service-context.d.ts +13 -8
  85. package/dist/types/src/packlets/services/service-context.d.ts.map +1 -1
  86. package/dist/types/src/packlets/services/service-host.d.ts +20 -6
  87. package/dist/types/src/packlets/services/service-host.d.ts.map +1 -1
  88. package/dist/types/src/packlets/space-export/space-archive-reader.d.ts +9 -1
  89. package/dist/types/src/packlets/space-export/space-archive-reader.d.ts.map +1 -1
  90. package/dist/types/src/packlets/space-export/space-archive-writer.d.ts +6 -0
  91. package/dist/types/src/packlets/space-export/space-archive-writer.d.ts.map +1 -1
  92. package/dist/types/src/packlets/space-export/space-archive.test.d.ts +2 -0
  93. package/dist/types/src/packlets/space-export/space-archive.test.d.ts.map +1 -0
  94. package/dist/types/src/packlets/spaces/data-space-manager.d.ts +27 -15
  95. package/dist/types/src/packlets/spaces/data-space-manager.d.ts.map +1 -1
  96. package/dist/types/src/packlets/spaces/data-space.d.ts +24 -8
  97. package/dist/types/src/packlets/spaces/data-space.d.ts.map +1 -1
  98. package/dist/types/src/packlets/spaces/edge-feed-replicator.d.ts +2 -2
  99. package/dist/types/src/packlets/spaces/edge-feed-replicator.d.ts.map +1 -1
  100. package/dist/types/src/packlets/spaces/genesis.d.ts +2 -1
  101. package/dist/types/src/packlets/spaces/genesis.d.ts.map +1 -1
  102. package/dist/types/src/packlets/spaces/notarization-plugin.d.ts +6 -6
  103. package/dist/types/src/packlets/spaces/notarization-plugin.d.ts.map +1 -1
  104. package/dist/types/src/packlets/spaces/spaces-service.d.ts +2 -2
  105. package/dist/types/src/packlets/spaces/spaces-service.d.ts.map +1 -1
  106. package/dist/types/src/packlets/testing/invitation-utils.d.ts +6 -3
  107. package/dist/types/src/packlets/testing/invitation-utils.d.ts.map +1 -1
  108. package/dist/types/src/packlets/testing/test-builder.d.ts +6 -5
  109. package/dist/types/src/packlets/testing/test-builder.d.ts.map +1 -1
  110. package/dist/types/src/packlets/worker/worker-runtime.d.ts +31 -4
  111. package/dist/types/src/packlets/worker/worker-runtime.d.ts.map +1 -1
  112. package/dist/types/src/packlets/worker/worker-session.d.ts +2 -2
  113. package/dist/types/src/packlets/worker/worker-session.d.ts.map +1 -1
  114. package/dist/types/src/version.d.ts +1 -1
  115. package/dist/types/src/version.d.ts.map +1 -1
  116. package/dist/types/tsconfig.tsbuildinfo +1 -1
  117. package/package.json +70 -48
  118. package/src/index.ts +1 -0
  119. package/src/packlets/agents/edge-agent-manager.ts +8 -5
  120. package/src/packlets/agents/edge-agent-service.ts +2 -1
  121. package/src/packlets/devices/devices-service.test.ts +0 -1
  122. package/src/packlets/devtools/devtools.ts +2 -3
  123. package/src/packlets/diagnostics/index.ts +1 -1
  124. package/src/packlets/identity/authenticator.ts +2 -2
  125. package/src/packlets/identity/contacts-service.ts +0 -1
  126. package/src/packlets/identity/identity-manager.test.ts +5 -5
  127. package/src/packlets/identity/identity-manager.ts +21 -18
  128. package/src/packlets/identity/identity-recovery-manager.ts +22 -18
  129. package/src/packlets/identity/identity-service.test.ts +6 -27
  130. package/src/packlets/identity/identity-service.ts +5 -76
  131. package/src/packlets/identity/identity.test.ts +2 -2
  132. package/src/packlets/identity/identity.ts +9 -32
  133. package/src/packlets/invitations/device-invitation-protocol.ts +5 -6
  134. package/src/packlets/invitations/edge-invitation-handler.ts +4 -3
  135. package/src/packlets/invitations/invitation-guest-extenstion.ts +6 -4
  136. package/src/packlets/invitations/invitation-host-extension.ts +6 -4
  137. package/src/packlets/invitations/invitation-protocol.ts +2 -3
  138. package/src/packlets/invitations/invitations-handler.test.ts +4 -5
  139. package/src/packlets/invitations/invitations-handler.ts +10 -10
  140. package/src/packlets/invitations/invitations-manager.ts +37 -14
  141. package/src/packlets/invitations/invitations-service.ts +4 -4
  142. package/src/packlets/invitations/space-invitation-protocol.test.ts +17 -16
  143. package/src/packlets/invitations/space-invitation-protocol.ts +10 -15
  144. package/src/packlets/locks/index.ts +1 -1
  145. package/src/packlets/logging/logging-service.ts +4 -0
  146. package/src/packlets/network/network-service.test.ts +0 -1
  147. package/src/packlets/network/network-service.ts +5 -4
  148. package/src/packlets/services/client-rpc-server.ts +4 -4
  149. package/src/packlets/services/feed-syncer.test.ts +340 -0
  150. package/src/packlets/services/feed-syncer.ts +337 -0
  151. package/src/packlets/services/platform.ts +7 -1
  152. package/src/packlets/services/service-context.test.ts +3 -2
  153. package/src/packlets/services/service-context.ts +129 -44
  154. package/src/packlets/services/service-host.test.ts +8 -8
  155. package/src/packlets/services/service-host.ts +63 -22
  156. package/src/packlets/services/service-registry.test.ts +0 -1
  157. package/src/packlets/space-export/space-archive-reader.ts +64 -3
  158. package/src/packlets/space-export/space-archive-writer.ts +39 -2
  159. package/src/packlets/space-export/space-archive.test.ts +287 -0
  160. package/src/packlets/spaces/data-space-manager.test.ts +79 -13
  161. package/src/packlets/spaces/data-space-manager.ts +97 -107
  162. package/src/packlets/spaces/data-space.ts +52 -29
  163. package/src/packlets/spaces/edge-feed-replicator.test.ts +1 -1
  164. package/src/packlets/spaces/edge-feed-replicator.ts +10 -9
  165. package/src/packlets/spaces/epoch-migrations.ts +5 -5
  166. package/src/packlets/spaces/genesis.ts +6 -1
  167. package/src/packlets/spaces/notarization-plugin.test.ts +2 -2
  168. package/src/packlets/spaces/notarization-plugin.ts +10 -9
  169. package/src/packlets/spaces/spaces-service.test.ts +9 -7
  170. package/src/packlets/spaces/spaces-service.ts +40 -16
  171. package/src/packlets/storage/storage.ts +4 -4
  172. package/src/packlets/testing/invitation-utils.ts +10 -6
  173. package/src/packlets/testing/test-builder.ts +36 -10
  174. package/src/packlets/worker/worker-runtime.ts +150 -13
  175. package/src/packlets/worker/worker-session.ts +8 -8
  176. package/src/version.ts +1 -1
  177. package/dist/lib/browser/chunk-HJH6BNTN.mjs.map +0 -7
  178. package/dist/lib/node-esm/chunk-ONQM6RQH.mjs.map +0 -7
  179. package/dist/types/src/packlets/identity/default-space-state-machine.d.ts +0 -19
  180. package/dist/types/src/packlets/identity/default-space-state-machine.d.ts.map +0 -1
  181. package/src/packlets/identity/default-space-state-machine.ts +0 -44
@@ -5,6 +5,7 @@
5
5
  import { describe, expect, test } from 'vitest';
6
6
 
7
7
  import { asyncTimeout, latch } from '@dxos/async';
8
+ import { Context } from '@dxos/context';
8
9
  import { createAdmissionCredentials } from '@dxos/credentials';
9
10
  import { AuthStatus } from '@dxos/echo-pipeline';
10
11
  import { writeMessages } from '@dxos/feed-store';
@@ -22,7 +23,7 @@ describe('DataSpaceManager', () => {
22
23
  await peer.createIdentity();
23
24
  await openAndClose(peer.echoHost, peer.dataSpaceManager);
24
25
 
25
- const space = await peer.dataSpaceManager.createSpace();
26
+ const space = await peer.dataSpaceManager.createSpace(new Context());
26
27
 
27
28
  // Process all written mutations.
28
29
  await space.inner.controlPipeline.state.waitUntilTimeframe(space.inner.controlPipeline.state.endTimeframe);
@@ -45,7 +46,7 @@ describe('DataSpaceManager', () => {
45
46
  await openAndClose(peer1.echoHost, peer1.dataSpaceManager, peer2.echoHost, peer2.dataSpaceManager);
46
47
  await connectReplicators([peer1, peer2]);
47
48
 
48
- const space1 = await peer1.dataSpaceManager.createSpace();
49
+ const space1 = await peer1.dataSpaceManager.createSpace(new Context());
49
50
  await space1.inner.controlPipeline.state.waitUntilTimeframe(space1.inner.controlPipeline.state.endTimeframe);
50
51
 
51
52
  // Admit peer2 to space1.
@@ -60,7 +61,7 @@ describe('DataSpaceManager', () => {
60
61
  );
61
62
 
62
63
  // Accept must be called after admission so that the peer can authenticate for notarization.
63
- const space2 = await peer2.dataSpaceManager.acceptSpace({
64
+ const space2 = await peer2.dataSpaceManager.acceptSpace(new Context(), {
64
65
  spaceKey: space1.key,
65
66
  genesisFeedKey: space1.inner.genesisFeedKey,
66
67
  });
@@ -115,7 +116,7 @@ describe('DataSpaceManager', () => {
115
116
  await openAndClose(peer1.echoHost, peer1.dataSpaceManager, peer2.echoHost, peer2.dataSpaceManager);
116
117
  await connectReplicators([peer1, peer2]);
117
118
 
118
- const space1 = await peer1.dataSpaceManager.createSpace();
119
+ const space1 = await peer1.dataSpaceManager.createSpace(new Context());
119
120
  await space1.inner.controlPipeline.state.waitUntilTimeframe(space1.inner.controlPipeline.state.endTimeframe);
120
121
 
121
122
  // Admit peer2 to space1.
@@ -130,7 +131,7 @@ describe('DataSpaceManager', () => {
130
131
  );
131
132
 
132
133
  // Accept must be called after admission so that the peer can authenticate for notarization.
133
- const space2 = await peer2.dataSpaceManager.acceptSpace({
134
+ const space2 = await peer2.dataSpaceManager.acceptSpace(new Context(), {
134
135
  spaceKey: space1.key,
135
136
  genesisFeedKey: space1.inner.genesisFeedKey,
136
137
  });
@@ -149,6 +150,71 @@ describe('DataSpaceManager', () => {
149
150
  await receivedMessage();
150
151
  });
151
152
 
153
+ test('create space with tags', async () => {
154
+ const builder = new TestBuilder();
155
+ const peer = builder.createPeer();
156
+ await peer.createIdentity();
157
+ await openAndClose(peer.echoHost, peer.dataSpaceManager);
158
+
159
+ const space = await peer.dataSpaceManager.createSpace(new Context(), { tags: ['personal', 'test'] });
160
+ await space.inner.controlPipeline.state.waitUntilTimeframe(space.inner.controlPipeline.state.endTimeframe);
161
+
162
+ expect(space.inner.spaceState.tags).toEqual(['personal', 'test']);
163
+ expect(space.inner.spaceState.genesisCredential).to.exist;
164
+ });
165
+
166
+ test('create space without tags has empty tags', async () => {
167
+ const builder = new TestBuilder();
168
+ const peer = builder.createPeer();
169
+ await peer.createIdentity();
170
+ await openAndClose(peer.echoHost, peer.dataSpaceManager);
171
+
172
+ const space = await peer.dataSpaceManager.createSpace(new Context());
173
+ await space.inner.controlPipeline.state.waitUntilTimeframe(space.inner.controlPipeline.state.endTimeframe);
174
+
175
+ expect(space.inner.spaceState.tags).toEqual([]);
176
+ });
177
+
178
+ test('tags propagate through peer admission', async () => {
179
+ const builder = new TestBuilder();
180
+ const peer1 = builder.createPeer();
181
+ await peer1.createIdentity();
182
+ const peer2 = builder.createPeer();
183
+ await peer2.createIdentity();
184
+
185
+ await openAndClose(peer1.echoHost, peer1.dataSpaceManager, peer2.echoHost, peer2.dataSpaceManager);
186
+ await connectReplicators([peer1, peer2]);
187
+
188
+ const space1 = await peer1.dataSpaceManager.createSpace(new Context(), { tags: ['personal'] });
189
+ await space1.inner.controlPipeline.state.waitUntilTimeframe(space1.inner.controlPipeline.state.endTimeframe);
190
+
191
+ // Admit peer2 to space1.
192
+ await writeMessages(
193
+ space1.inner.controlPipeline.writer,
194
+ await createAdmissionCredentials(
195
+ peer1.identity.credentialSigner,
196
+ peer2.identity.identityKey,
197
+ space1.key,
198
+ space1.inner.genesisFeedKey,
199
+ undefined, // role (default ADMIN)
200
+ undefined, // membershipChainHeads
201
+ undefined, // profile
202
+ undefined, // invitationCredentialId
203
+ space1.inner.spaceState.tags, // tags
204
+ ),
205
+ );
206
+
207
+ const space2 = await peer2.dataSpaceManager.acceptSpace(new Context(), {
208
+ spaceKey: space1.key,
209
+ genesisFeedKey: space1.inner.genesisFeedKey,
210
+ tags: space1.inner.spaceState.tags,
211
+ });
212
+ await peer2.dataSpaceManager.waitUntilSpaceReady(space2.key);
213
+
214
+ // Peer2's space should have the same tags.
215
+ expect(space2.inner.spaceState.tags).toEqual(['personal']);
216
+ });
217
+
152
218
  describe('activation', () => {
153
219
  test('can activate and deactivate a space', async () => {
154
220
  const builder = new TestBuilder();
@@ -157,14 +223,14 @@ describe('DataSpaceManager', () => {
157
223
  await peer.createIdentity();
158
224
  await openAndClose(peer.echoHost, peer.dataSpaceManager);
159
225
 
160
- const space = await peer.dataSpaceManager.createSpace();
226
+ const space = await peer.dataSpaceManager.createSpace(new Context());
161
227
  await space.inner.controlPipeline.state.waitUntilTimeframe(space.inner.controlPipeline.state.endTimeframe);
162
228
  expect(space.state).to.equal(SpaceState.SPACE_READY);
163
229
 
164
- await space.deactivate();
230
+ await space.deactivate(new Context());
165
231
  expect(space.state).to.equal(SpaceState.SPACE_INACTIVE);
166
232
 
167
- await space.activate();
233
+ await space.activate(new Context());
168
234
  await asyncTimeout(
169
235
  space.stateUpdate.waitForCondition(() => space.state === SpaceState.SPACE_READY),
170
236
  500,
@@ -178,12 +244,12 @@ describe('DataSpaceManager', () => {
178
244
  await peer.createIdentity();
179
245
  await openAndClose(peer.echoHost, peer.dataSpaceManager);
180
246
 
181
- await peer.dataSpaceManager.createSpace();
247
+ await peer.dataSpaceManager.createSpace(new Context());
182
248
  await reloadDataSpaces(peer);
183
249
 
184
250
  const space = getFirstSpace(peer);
185
251
  expect(space.state).to.equal(SpaceState.SPACE_CLOSED);
186
- await space.activate();
252
+ await space.activate(new Context());
187
253
  await asyncTimeout(
188
254
  space.stateUpdate.waitForCondition(() => space.state === SpaceState.SPACE_READY),
189
255
  500,
@@ -197,10 +263,10 @@ describe('DataSpaceManager', () => {
197
263
  await peer.createIdentity();
198
264
  await openAndClose(peer.echoHost, peer.dataSpaceManager);
199
265
 
200
- await peer.dataSpaceManager.createSpace();
266
+ await peer.dataSpaceManager.createSpace(new Context());
201
267
  await reloadDataSpaces(peer);
202
268
 
203
- await getFirstSpace(peer).deactivate();
269
+ await getFirstSpace(peer).deactivate(new Context());
204
270
 
205
271
  await reloadDataSpaces(peer);
206
272
 
@@ -209,7 +275,7 @@ describe('DataSpaceManager', () => {
209
275
  });
210
276
 
211
277
  const connectReplicators = (peers: TestPeer[]) => {
212
- return Promise.all(peers.map((peer) => peer.echoHost.addReplicator(peer.meshEchoReplicator)));
278
+ return Promise.all(peers.map((peer) => peer.echoHost.addReplicator(Context.default(), peer.meshEchoReplicator)));
213
279
  };
214
280
 
215
281
  const reloadDataSpaces = async (peer: TestPeer) => {
@@ -3,10 +3,10 @@
3
3
  //
4
4
 
5
5
  import { type Doc } from '@automerge/automerge';
6
- import { type AutomergeUrl, type DocHandle, type DocumentId, interpretAsDocumentId } from '@automerge/automerge-repo';
6
+ import { type AutomergeUrl, type DocumentId, interpretAsDocumentId } from '@automerge/automerge-repo';
7
7
 
8
8
  import { Event, synchronized, trackLeaks } from '@dxos/async';
9
- import { PropertiesType, TYPE_PROPERTIES } from '@dxos/client-protocol';
9
+ import { LegacySpaceProperties, SpaceProperties } from '@dxos/client-protocol';
10
10
  import { Context, LifecycleState, Resource, cancelWithContext } from '@dxos/context';
11
11
  import {
12
12
  type CredentialSigner,
@@ -15,14 +15,13 @@ import {
15
15
  createAdmissionCredentials,
16
16
  getCredentialAssertion,
17
17
  } from '@dxos/credentials';
18
- import { ObjectId, getTypeReference } from '@dxos/echo/internal';
18
+ import { Type } from '@dxos/echo';
19
19
  import {
20
20
  AuthStatus,
21
21
  CredentialServerExtension,
22
22
  DatabaseRoot,
23
23
  type EchoEdgeReplicator,
24
24
  type EchoHost,
25
- FIND_PARAMS,
26
25
  type MeshEchoReplicator,
27
26
  type MetadataStore,
28
27
  type Space,
@@ -31,13 +30,7 @@ import {
31
30
  type SpaceProtocolSession,
32
31
  findInlineObjectOfType,
33
32
  } from '@dxos/echo-pipeline';
34
- import {
35
- type DatabaseDirectory,
36
- type ObjectStructure,
37
- SpaceDocVersion,
38
- createIdFromSpaceKey,
39
- encodeReference,
40
- } from '@dxos/echo-protocol';
33
+ import { type DatabaseDirectory, createIdFromSpaceKey } from '@dxos/echo-protocol';
41
34
  import type { EdgeConnection, EdgeHttpClient } from '@dxos/edge-client';
42
35
  import { type FeedStore, writeMessages } from '@dxos/feed-store';
43
36
  import { assertArgument, assertState, failedInvariant, invariant } from '@dxos/invariant';
@@ -49,27 +42,28 @@ import { Invitation, SpaceState } from '@dxos/protocols/proto/dxos/client/servic
49
42
  import { type Runtime } from '@dxos/protocols/proto/dxos/config';
50
43
  import { type FeedMessage } from '@dxos/protocols/proto/dxos/echo/feed';
51
44
  import { EdgeReplicationSetting, type SpaceMetadata } from '@dxos/protocols/proto/dxos/echo/metadata';
52
- import { type Credential, type ProfileDocument, SpaceMember } from '@dxos/protocols/proto/dxos/halo/credentials';
45
+ import {
46
+ type Credential,
47
+ MembershipPolicy,
48
+ type ProfileDocument,
49
+ SpaceMember,
50
+ } from '@dxos/protocols/proto/dxos/halo/credentials';
53
51
  import { type DelegateSpaceInvitation } from '@dxos/protocols/proto/dxos/halo/invitations';
54
52
  import { type PeerState } from '@dxos/protocols/proto/dxos/mesh/presence';
55
53
  import { type Teleport } from '@dxos/teleport';
56
54
  import { Gossip, Presence } from '@dxos/teleport-extension-gossip';
57
55
  import { type Timeframe } from '@dxos/timeframe';
58
56
  import { trace } from '@dxos/tracing';
59
- import { ComplexMap, deferFunction, forEachAsync, setDeep } from '@dxos/util';
57
+ import { ComplexMap, deferFunction, forEachAsync } from '@dxos/util';
60
58
 
61
59
  import { createAuthProvider } from '../identity';
62
60
  import { type InvitationsManager } from '../invitations';
63
-
64
61
  import { DataSpace } from './data-space';
65
62
  import { spaceGenesis } from './genesis';
66
63
 
67
64
  const PRESENCE_ANNOUNCE_INTERVAL = 10_000;
68
65
  const PRESENCE_OFFLINE_TIMEOUT = 20_000;
69
66
 
70
- // Space properties key for default metadata.
71
- const DEFAULT_SPACE_KEY = '__DEFAULT__';
72
-
73
67
  export interface SigningContext {
74
68
  identityKey: PublicKey;
75
69
  deviceKey: PublicKey;
@@ -94,6 +88,9 @@ export type AcceptSpaceOptions = {
94
88
  * We will try to catch up to this timeframe before initializing the database.
95
89
  */
96
90
  dataTimeframe?: Timeframe;
91
+
92
+ /** Tags assigned to the space member. */
93
+ tags?: string[];
97
94
  };
98
95
 
99
96
  export type AdmitMemberOptions = {
@@ -102,9 +99,10 @@ export type AdmitMemberOptions = {
102
99
  role: SpaceMember.Role;
103
100
  profile?: ProfileDocument;
104
101
  delegationCredentialId?: PublicKey;
102
+ tags?: string[];
105
103
  };
106
104
 
107
- export type DataSpaceManagerParams = {
105
+ export type DataSpaceManagerProps = {
108
106
  spaceManager: SpaceManager;
109
107
  metadataStore: MetadataStore;
110
108
  keyring: Keyring;
@@ -116,23 +114,31 @@ export type DataSpaceManagerParams = {
116
114
  edgeHttpClient?: EdgeHttpClient;
117
115
  meshReplicator?: MeshEchoReplicator;
118
116
  echoEdgeReplicator?: EchoEdgeReplicator;
119
- runtimeParams?: DataSpaceManagerRuntimeParams;
117
+ runtimeProps?: DataSpaceManagerRuntimeProps;
120
118
  edgeFeatures?: Runtime.Client.EdgeFeatures;
121
119
  };
122
120
 
123
- export type DataSpaceManagerRuntimeParams = {
121
+ export type DataSpaceManagerRuntimeProps = {
124
122
  spaceMemberPresenceAnnounceInterval?: number;
125
123
  spaceMemberPresenceOfflineTimeout?: number;
126
124
  activeEdgeNotarizationPollingInterval?: number;
127
125
  disableP2pReplication?: boolean;
126
+ /**
127
+ * If true, spaces that were previously SPACE_ACTIVE will be automatically activated on startup.
128
+ * This is used in dedicated worker mode to restore space state after leader changeover.
129
+ */
130
+ autoActivateSpaces?: boolean;
128
131
  };
129
132
 
130
133
  export type CreateSpaceOptions = {
131
134
  rootUrl?: AutomergeUrl;
132
135
  documents?: Record<DocumentId, Uint8Array>;
136
+ tags?: string[];
137
+ membershipPolicy?: MembershipPolicy;
133
138
  };
134
139
 
135
140
  @trackLeaks('open', 'close')
141
+ @trace.resource()
136
142
  export class DataSpaceManager extends Resource {
137
143
  public readonly updated = new Event();
138
144
 
@@ -152,9 +158,9 @@ export class DataSpaceManager extends Resource {
152
158
  private readonly _edgeFeatures?: Runtime.Client.EdgeFeatures = undefined;
153
159
  private readonly _meshReplicator?: MeshEchoReplicator = undefined;
154
160
  private readonly _echoEdgeReplicator?: EchoEdgeReplicator = undefined;
155
- private readonly _runtimeParams?: DataSpaceManagerRuntimeParams = undefined;
161
+ private readonly _runtimeProps?: DataSpaceManagerRuntimeProps = undefined;
156
162
 
157
- constructor(params: DataSpaceManagerParams) {
163
+ constructor(params: DataSpaceManagerProps) {
158
164
  super();
159
165
 
160
166
  this._spaceManager = params.spaceManager;
@@ -169,7 +175,7 @@ export class DataSpaceManager extends Resource {
169
175
  this._edgeFeatures = params.edgeFeatures;
170
176
  this._echoEdgeReplicator = params.echoEdgeReplicator;
171
177
  this._edgeHttpClient = params.edgeHttpClient;
172
- this._runtimeParams = params.runtimeParams;
178
+ this._runtimeProps = params.runtimeProps;
173
179
 
174
180
  trace.diagnostic({
175
181
  id: 'spaces',
@@ -179,12 +185,15 @@ export class DataSpaceManager extends Resource {
179
185
  Array.from(this._spaces.values()).map(async (space) => {
180
186
  const rootUrl = space.automergeSpaceState.rootUrl;
181
187
  const rootHandle = rootUrl
182
- ? await this._echoHost.automergeRepo.find<Doc<DatabaseDirectory>>(rootUrl as AutomergeUrl, FIND_PARAMS)
188
+ ? await this._echoHost.loadDoc<Doc<DatabaseDirectory>>(this._ctx, rootUrl as AutomergeUrl)
183
189
  : undefined;
184
190
  await rootHandle?.whenReady();
185
191
  const rootDoc = rootHandle?.doc();
186
192
 
187
- const properties = rootDoc && findInlineObjectOfType(rootDoc, TYPE_PROPERTIES);
193
+ const properties =
194
+ rootDoc &&
195
+ (findInlineObjectOfType(rootDoc, Type.getTypename(SpaceProperties)) ??
196
+ findInlineObjectOfType(rootDoc, Type.getTypename(LegacySpaceProperties)));
188
197
 
189
198
  return {
190
199
  key: space.key.toHex(),
@@ -217,25 +226,38 @@ export class DataSpaceManager extends Resource {
217
226
  log.trace('dxos.echo.data-space-manager.open', Trace.begin({ id: this._instanceId }));
218
227
  log('metadata loaded', { spaces: this._metadataStore.spaces.length });
219
228
 
229
+ const spacesToActivate: DataSpace[] = [];
220
230
  await forEachAsync(this._metadataStore.spaces, async (spaceMetadata) => {
221
231
  try {
222
232
  log('load space', { spaceMetadata });
223
- await this._constructSpace(spaceMetadata);
233
+ const space = await this._constructSpace(this._ctx, spaceMetadata);
234
+ // Track spaces that were previously active for auto-activation (used in dedicated worker mode).
235
+ if (this._runtimeProps?.autoActivateSpaces && spaceMetadata.state === SpaceState.SPACE_ACTIVE) {
236
+ spacesToActivate.push(space);
237
+ }
224
238
  } catch (err) {
225
239
  log.error('Error loading space', { spaceMetadata, err });
226
240
  }
227
241
  });
228
242
 
243
+ // Auto-activate spaces that were previously active (used in dedicated worker mode after leader changeover).
244
+ for (const space of spacesToActivate) {
245
+ log('auto-activating space', { spaceKey: space.key });
246
+ space.activate(this._ctx).catch((err) => {
247
+ log.error('Error auto-activating space', { spaceKey: space.key, err });
248
+ });
249
+ }
250
+
229
251
  this.updated.emit();
230
252
 
231
253
  log.trace('dxos.echo.data-space-manager.open', Trace.end({ id: this._instanceId }));
232
254
  }
233
255
 
234
256
  @synchronized
235
- protected override async _close(): Promise<void> {
257
+ protected override async _close(ctx: Context): Promise<void> {
236
258
  log('close');
237
259
  for (const space of this._spaces.values()) {
238
- await space.close();
260
+ await space.close(ctx);
239
261
  }
240
262
  this._spaces.clear();
241
263
  }
@@ -244,7 +266,8 @@ export class DataSpaceManager extends Resource {
244
266
  * Creates a new space writing the genesis credentials to the control feed.
245
267
  */
246
268
  @synchronized
247
- async createSpace(options: CreateSpaceOptions = {}): Promise<DataSpace> {
269
+ @trace.span({ showInBrowserTimeline: true })
270
+ async createSpace(ctx: Context, options: CreateSpaceOptions = {}): Promise<DataSpace> {
248
271
  assertArgument(
249
272
  !!options.rootUrl === !!options.documents,
250
273
  'options',
@@ -252,6 +275,8 @@ export class DataSpaceManager extends Resource {
252
275
  );
253
276
 
254
277
  assertState(this._lifecycleState === LifecycleState.OPEN, 'Not open.');
278
+
279
+ const tags = options.tags ? Array.from(options.tags) : [];
255
280
  const spaceKey = await this._keyring.createKey();
256
281
  const controlFeedKey = await this._keyring.createKey();
257
282
  const dataFeedKey = await this._keyring.createKey();
@@ -264,6 +289,7 @@ export class DataSpaceManager extends Resource {
264
289
  controlFeedKey,
265
290
  dataFeedKey,
266
291
  state: SpaceState.SPACE_ACTIVE,
292
+ tags,
267
293
  };
268
294
 
269
295
  log('creating space...', { spaceId, spaceKey });
@@ -301,30 +327,37 @@ export class DataSpaceManager extends Resource {
301
327
  let root: DatabaseRoot;
302
328
  if (options.rootUrl) {
303
329
  const newRootDocId = documentIdMapping[interpretAsDocumentId(options.rootUrl)] ?? failedInvariant();
304
- const rootDocHandle = await this._echoHost.loadDoc<DatabaseDirectory>(Context.default(), newRootDocId);
330
+ const rootDocHandle = await this._echoHost.loadDoc<DatabaseDirectory>(ctx, newRootDocId);
305
331
  DatabaseRoot.mapLinks(rootDocHandle, documentIdMapping);
306
332
 
307
- root = await this._echoHost.openSpaceRoot(spaceId, `automerge:${newRootDocId}` as AutomergeUrl);
333
+ root = await this._echoHost.openSpaceRoot(ctx, spaceId, `automerge:${newRootDocId}` as AutomergeUrl);
308
334
  } else {
309
- root = await this._echoHost.createSpaceRoot(spaceKey);
335
+ root = await this._echoHost.createSpaceRoot(ctx, spaceKey);
310
336
  }
311
- await this._echoHost.flush();
337
+ await this._echoHost.flush(ctx);
312
338
 
313
339
  log('constructing space...', { spaceKey });
314
340
 
315
- const space = await this._constructSpace(metadata);
316
- await space.open();
341
+ const space = await this._constructSpace(ctx, metadata);
342
+ await space.open(ctx);
317
343
 
318
344
  log('adding space...', { spaceKey });
319
345
 
320
- const credentials = await spaceGenesis(this._keyring, this._signingContext, space.inner, root.url);
346
+ const credentials = await spaceGenesis(
347
+ this._keyring,
348
+ this._signingContext,
349
+ space.inner,
350
+ root.url,
351
+ tags,
352
+ options.membershipPolicy,
353
+ );
321
354
  await this._metadataStore.addSpace(metadata);
322
355
 
323
356
  const memberCredential = credentials[1];
324
357
  invariant(getCredentialAssertion(memberCredential)['@type'] === 'dxos.halo.credentials.SpaceMember');
325
358
  await this._signingContext.recordCredential(memberCredential);
326
359
 
327
- await space.initializeDataPipeline();
360
+ await space.initializeDataPipeline(ctx);
328
361
 
329
362
  log('space ready.', { spaceId, spaceKey });
330
363
 
@@ -332,79 +365,30 @@ export class DataSpaceManager extends Resource {
332
365
  return space;
333
366
  }
334
367
 
335
- async isDefaultSpace(space: DataSpace): Promise<boolean> {
336
- if (!space.databaseRoot) {
337
- return false;
338
- }
339
- switch (space.databaseRoot.getVersion()) {
340
- case SpaceDocVersion.CURRENT: {
341
- if (!space.databaseRoot.handle.isReady()) {
342
- log.warn('waiting for space root to be ready', { spaceId: space.id });
343
- await space.databaseRoot.handle.whenReady();
344
- }
345
- const [_, properties] = findInlineObjectOfType(space.databaseRoot.doc()!, TYPE_PROPERTIES) ?? [];
346
- return properties?.data?.[DEFAULT_SPACE_KEY] === this._signingContext.identityKey.toHex();
347
- }
348
- case SpaceDocVersion.LEGACY: {
349
- throw new Error('Legacy space version is not supported');
350
- }
351
-
352
- default:
353
- log.warn('unknown space version', { version: space.databaseRoot.getVersion(), spaceId: space.id });
354
- return false;
355
- }
356
- }
357
-
358
- async createDefaultSpace(): Promise<DataSpace> {
359
- const space = await this.createSpace();
360
- const document = await this._getSpaceRootDocument(space);
361
-
362
- // TODO(dmaretskyi): Better API for low-level data access.
363
- const properties: ObjectStructure = {
364
- system: {
365
- type: encodeReference(getTypeReference(PropertiesType)!),
366
- },
367
- data: {
368
- [DEFAULT_SPACE_KEY]: this._signingContext.identityKey.toHex(),
369
- },
370
- meta: {
371
- keys: [],
372
- },
373
- };
374
-
375
- const propertiesId = ObjectId.random();
376
- document.change((doc: DatabaseDirectory) => {
377
- setDeep(doc, ['objects', propertiesId], properties);
378
- });
379
-
380
- await this._echoHost.flush();
381
- return space;
382
- }
383
-
384
- private async _getSpaceRootDocument(space: DataSpace): Promise<DocHandle<DatabaseDirectory>> {
385
- const automergeIndex = space.automergeSpaceState.rootUrl;
386
- invariant(automergeIndex);
387
- const document = await this._echoHost.automergeRepo.find<DatabaseDirectory>(automergeIndex as any, FIND_PARAMS);
388
- await document.whenReady();
389
- return document;
390
- }
391
-
368
+ /**
369
+ * Accepts an existing space by joining its swarm and initializing the data pipeline.
370
+ * @param ctx - Caller context for cancellation and tracing.
371
+ * @param opts - Space keys and optional timeframes for catch-up.
372
+ */
392
373
  // TODO(burdon): Rename join space.
393
374
  @synchronized
394
- async acceptSpace(opts: AcceptSpaceOptions): Promise<DataSpace> {
375
+ @trace.span({ showInBrowserTimeline: true })
376
+ async acceptSpace(ctx: Context, opts: AcceptSpaceOptions): Promise<DataSpace> {
395
377
  log('accept space', { opts });
396
378
  invariant(this._lifecycleState === LifecycleState.OPEN, 'Not open.');
397
379
  invariant(!this._spaces.has(opts.spaceKey), 'Space already exists.');
398
380
 
381
+ const tags = opts.tags ? Array.from(opts.tags) : [];
399
382
  const metadata: SpaceMetadata = {
400
383
  key: opts.spaceKey,
401
384
  genesisFeedKey: opts.genesisFeedKey,
402
385
  controlTimeframe: opts.controlTimeframe,
403
386
  dataTimeframe: opts.dataTimeframe,
387
+ tags,
404
388
  };
405
389
 
406
- const space = await this._constructSpace(metadata);
407
- await space.open();
390
+ const space = await this._constructSpace(ctx, metadata);
391
+ await space.open(ctx);
408
392
  await this._metadataStore.addSpace(metadata);
409
393
  space.initializeDataPipelineAsync();
410
394
 
@@ -430,6 +414,7 @@ export class DataSpaceManager extends Resource {
430
414
  space.spaceState.membershipChainHeads,
431
415
  options.profile,
432
416
  options.delegationCredentialId,
417
+ space.spaceState.tags,
433
418
  );
434
419
 
435
420
  // TODO(dmaretskyi): Refactor.
@@ -456,8 +441,8 @@ export class DataSpaceManager extends Resource {
456
441
  );
457
442
  }
458
443
 
459
- public async requestSpaceAdmissionCredential(spaceKey: PublicKey): Promise<Credential> {
460
- return this._spaceManager.requestSpaceAdmissionCredential({
444
+ public async requestSpaceAdmissionCredential(ctx: Context, spaceKey: PublicKey): Promise<Credential> {
445
+ return this._spaceManager.requestSpaceAdmissionCredential(ctx, {
461
446
  spaceKey,
462
447
  identityKey: this._signingContext.identityKey,
463
448
  timeout: 15_000,
@@ -470,7 +455,11 @@ export class DataSpaceManager extends Resource {
470
455
  });
471
456
  }
472
457
 
473
- async setSpaceEdgeReplicationSetting(spaceKey: PublicKey, setting: EdgeReplicationSetting): Promise<void> {
458
+ async setSpaceEdgeReplicationSetting(
459
+ ctx: Context,
460
+ spaceKey: PublicKey,
461
+ setting: EdgeReplicationSetting,
462
+ ): Promise<void> {
474
463
  const space = this._spaces.get(spaceKey);
475
464
  invariant(space, 'Space not found.');
476
465
 
@@ -482,7 +471,7 @@ export class DataSpaceManager extends Resource {
482
471
  await this._echoEdgeReplicator?.disconnectFromSpace(space.id);
483
472
  break;
484
473
  case EdgeReplicationSetting.ENABLED:
485
- await this._echoEdgeReplicator?.connectToSpace(space.id);
474
+ await this._echoEdgeReplicator?.connectToSpace(ctx, space.id);
486
475
  break;
487
476
  }
488
477
  }
@@ -490,14 +479,14 @@ export class DataSpaceManager extends Resource {
490
479
  space.stateUpdate.emit();
491
480
  }
492
481
 
493
- private async _constructSpace(metadata: SpaceMetadata): Promise<DataSpace> {
482
+ private async _constructSpace(ctx: Context, metadata: SpaceMetadata): Promise<DataSpace> {
494
483
  log('construct space', { metadata });
495
484
  const gossip = new Gossip({
496
485
  localPeerId: this._signingContext.deviceKey,
497
486
  });
498
487
  const presence = new Presence({
499
- announceInterval: this._runtimeParams?.spaceMemberPresenceAnnounceInterval ?? PRESENCE_ANNOUNCE_INTERVAL,
500
- offlineTimeout: this._runtimeParams?.spaceMemberPresenceOfflineTimeout ?? PRESENCE_OFFLINE_TIMEOUT,
488
+ announceInterval: this._runtimeProps?.spaceMemberPresenceAnnounceInterval ?? PRESENCE_ANNOUNCE_INTERVAL,
489
+ offlineTimeout: this._runtimeProps?.spaceMemberPresenceOfflineTimeout ?? PRESENCE_OFFLINE_TIMEOUT,
501
490
  identityKey: this._signingContext.identityKey,
502
491
  gossip,
503
492
  });
@@ -580,15 +569,16 @@ export class DataSpaceManager extends Resource {
580
569
  },
581
570
  },
582
571
  cache: metadata.cache,
572
+ tags: metadata.tags,
583
573
  edgeConnection: this._edgeConnection,
584
574
  edgeHttpClient: this._edgeHttpClient,
585
575
  edgeFeatures: this._edgeFeatures,
586
- activeEdgeNotarizationPollingInterval: this._runtimeParams?.activeEdgeNotarizationPollingInterval,
576
+ activeEdgeNotarizationPollingInterval: this._runtimeProps?.activeEdgeNotarizationPollingInterval,
587
577
  });
588
578
  dataSpace.postOpen.append(async () => {
589
579
  const setting = dataSpace.getEdgeReplicationSetting();
590
580
  if (!setting || setting === EdgeReplicationSetting.ENABLED) {
591
- await this._echoEdgeReplicator?.connectToSpace(dataSpace.id);
581
+ await this._echoEdgeReplicator?.connectToSpace(ctx, dataSpace.id);
592
582
  } else if (this._echoEdgeReplicator) {
593
583
  log('not connecting EchoEdgeReplicator because of EdgeReplicationSetting', { spaceId: dataSpace.id });
594
584
  }
@@ -685,7 +675,7 @@ export class DataSpaceManager extends Resource {
685
675
  invitations: Array<[PublicKey, DelegateSpaceInvitation]>,
686
676
  ): Promise<void> {
687
677
  const tasks = invitations.map(([credentialId, invitation]) => {
688
- return this._invitationsManager.createInvitation({
678
+ return this._invitationsManager.createInvitation(this._ctx, {
689
679
  type: Invitation.Type.DELEGATED,
690
680
  kind: Invitation.Kind.SPACE,
691
681
  spaceKey: space.key,