@dxos/client-services 0.8.4-main.3eb6e50203 → 0.8.4-main.3fbcb4aa9b

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 (201) hide show
  1. package/dist/lib/browser/{chunk-675YEKTQ.mjs → chunk-OBWND4JJ.mjs} +1701 -3078
  2. package/dist/lib/browser/chunk-OBWND4JJ.mjs.map +7 -0
  3. package/dist/lib/browser/{chunk-NQSC7HOE.mjs → chunk-XJRPB3GA.mjs} +1 -1
  4. package/dist/lib/browser/index.mjs +106 -194
  5. package/dist/lib/browser/index.mjs.map +3 -3
  6. package/dist/lib/browser/meta.json +1 -1
  7. package/dist/lib/browser/packlets/diagnostics/browser-diagnostics-broadcast.mjs +1 -6
  8. package/dist/lib/browser/packlets/diagnostics/browser-diagnostics-broadcast.mjs.map +2 -2
  9. package/dist/lib/browser/packlets/diagnostics/diagnostics-broadcast.mjs +1 -1
  10. package/dist/lib/browser/packlets/locks/browser.mjs +9 -49
  11. package/dist/lib/browser/packlets/locks/browser.mjs.map +2 -2
  12. package/dist/lib/browser/packlets/locks/node.mjs +4 -22
  13. package/dist/lib/browser/packlets/locks/node.mjs.map +2 -2
  14. package/dist/lib/browser/testing/index.mjs +9 -22
  15. package/dist/lib/browser/testing/index.mjs.map +3 -3
  16. package/dist/lib/node-esm/{chunk-PKEGMOQ4.mjs → chunk-2DT3MZRL.mjs} +1 -1
  17. package/dist/lib/node-esm/{chunk-RUUAV2BD.mjs → chunk-PB2SRGWV.mjs} +1701 -3078
  18. package/dist/lib/node-esm/chunk-PB2SRGWV.mjs.map +7 -0
  19. package/dist/lib/node-esm/index.mjs +106 -194
  20. package/dist/lib/node-esm/index.mjs.map +3 -3
  21. package/dist/lib/node-esm/meta.json +1 -1
  22. package/dist/lib/node-esm/packlets/diagnostics/browser-diagnostics-broadcast.mjs +1 -6
  23. package/dist/lib/node-esm/packlets/diagnostics/browser-diagnostics-broadcast.mjs.map +2 -2
  24. package/dist/lib/node-esm/packlets/diagnostics/diagnostics-broadcast.mjs +1 -1
  25. package/dist/lib/node-esm/packlets/locks/browser.mjs +9 -49
  26. package/dist/lib/node-esm/packlets/locks/browser.mjs.map +2 -2
  27. package/dist/lib/node-esm/packlets/locks/node.mjs +4 -22
  28. package/dist/lib/node-esm/packlets/locks/node.mjs.map +2 -2
  29. package/dist/lib/node-esm/testing/index.mjs +9 -22
  30. package/dist/lib/node-esm/testing/index.mjs.map +3 -3
  31. package/dist/types/src/index.d.ts +1 -0
  32. package/dist/types/src/index.d.ts.map +1 -1
  33. package/dist/types/src/packlets/agents/edge-agent-manager.d.ts +3 -2
  34. package/dist/types/src/packlets/agents/edge-agent-manager.d.ts.map +1 -1
  35. package/dist/types/src/packlets/agents/edge-agent-service.d.ts +2 -1
  36. package/dist/types/src/packlets/agents/edge-agent-service.d.ts.map +1 -1
  37. package/dist/types/src/packlets/devices/devices-service.d.ts.map +1 -1
  38. package/dist/types/src/packlets/devtools/devtools.d.ts.map +1 -1
  39. package/dist/types/src/packlets/devtools/feeds.d.ts.map +1 -1
  40. package/dist/types/src/packlets/devtools/keys.d.ts.map +1 -1
  41. package/dist/types/src/packlets/devtools/metadata.d.ts.map +1 -1
  42. package/dist/types/src/packlets/devtools/network.d.ts.map +1 -1
  43. package/dist/types/src/packlets/devtools/spaces.d.ts.map +1 -1
  44. package/dist/types/src/packlets/diagnostics/browser-diagnostics-broadcast.d.ts.map +1 -1
  45. package/dist/types/src/packlets/diagnostics/diagnostics-broadcast.d.ts.map +1 -1
  46. package/dist/types/src/packlets/diagnostics/diagnostics-collector.d.ts.map +1 -1
  47. package/dist/types/src/packlets/diagnostics/diagnostics.d.ts +2 -3
  48. package/dist/types/src/packlets/diagnostics/diagnostics.d.ts.map +1 -1
  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 +3 -3
  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 +6 -5
  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 +6 -10
  56. package/dist/types/src/packlets/identity/identity-service.d.ts.map +1 -1
  57. package/dist/types/src/packlets/identity/identity.d.ts +6 -9
  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 +2 -1
  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 +1 -1
  62. package/dist/types/src/packlets/invitations/edge-invitation-handler.d.ts.map +1 -1
  63. package/dist/types/src/packlets/invitations/invitation-guest-extenstion.d.ts.map +1 -1
  64. package/dist/types/src/packlets/invitations/invitation-host-extension.d.ts.map +1 -1
  65. package/dist/types/src/packlets/invitations/invitation-protocol.d.ts +5 -1
  66. package/dist/types/src/packlets/invitations/invitation-protocol.d.ts.map +1 -1
  67. package/dist/types/src/packlets/invitations/invitation-state.d.ts.map +1 -1
  68. package/dist/types/src/packlets/invitations/invitation-topology.d.ts.map +1 -1
  69. package/dist/types/src/packlets/invitations/invitations-handler.d.ts.map +1 -1
  70. package/dist/types/src/packlets/invitations/invitations-manager.d.ts +3 -3
  71. package/dist/types/src/packlets/invitations/invitations-manager.d.ts.map +1 -1
  72. package/dist/types/src/packlets/invitations/invitations-service.d.ts +3 -3
  73. package/dist/types/src/packlets/invitations/invitations-service.d.ts.map +1 -1
  74. package/dist/types/src/packlets/invitations/space-invitation-protocol.d.ts +2 -1
  75. package/dist/types/src/packlets/invitations/space-invitation-protocol.d.ts.map +1 -1
  76. package/dist/types/src/packlets/invitations/utils.d.ts.map +1 -1
  77. package/dist/types/src/packlets/locks/browser.d.ts.map +1 -1
  78. package/dist/types/src/packlets/locks/node.d.ts.map +1 -1
  79. package/dist/types/src/packlets/logging/logging-service.d.ts.map +1 -1
  80. package/dist/types/src/packlets/network/network-service.d.ts +5 -4
  81. package/dist/types/src/packlets/network/network-service.d.ts.map +1 -1
  82. package/dist/types/src/packlets/services/client-rpc-server.d.ts +3 -3
  83. package/dist/types/src/packlets/services/client-rpc-server.d.ts.map +1 -1
  84. package/dist/types/src/packlets/services/feed-syncer.d.ts +22 -4
  85. package/dist/types/src/packlets/services/feed-syncer.d.ts.map +1 -1
  86. package/dist/types/src/packlets/services/feed-syncer.test.d.ts +2 -0
  87. package/dist/types/src/packlets/services/feed-syncer.test.d.ts.map +1 -0
  88. package/dist/types/src/packlets/services/service-context.d.ts +2 -4
  89. package/dist/types/src/packlets/services/service-context.d.ts.map +1 -1
  90. package/dist/types/src/packlets/services/service-host.d.ts +2 -3
  91. package/dist/types/src/packlets/services/service-host.d.ts.map +1 -1
  92. package/dist/types/src/packlets/services/service-registry.d.ts.map +1 -1
  93. package/dist/types/src/packlets/services/util.d.ts.map +1 -1
  94. package/dist/types/src/packlets/space-export/archive-format.d.ts +9 -0
  95. package/dist/types/src/packlets/space-export/archive-format.d.ts.map +1 -0
  96. package/dist/types/src/packlets/space-export/index.d.ts +4 -1
  97. package/dist/types/src/packlets/space-export/index.d.ts.map +1 -1
  98. package/dist/types/src/packlets/space-export/serialized-space-reader.d.ts +23 -0
  99. package/dist/types/src/packlets/space-export/serialized-space-reader.d.ts.map +1 -0
  100. package/dist/types/src/packlets/space-export/serialized-space-writer.d.ts +36 -0
  101. package/dist/types/src/packlets/space-export/serialized-space-writer.d.ts.map +1 -0
  102. package/dist/types/src/packlets/space-export/space-archive-reader.d.ts +9 -1
  103. package/dist/types/src/packlets/space-export/space-archive-reader.d.ts.map +1 -1
  104. package/dist/types/src/packlets/space-export/space-archive-writer.d.ts +7 -1
  105. package/dist/types/src/packlets/space-export/space-archive-writer.d.ts.map +1 -1
  106. package/dist/types/src/packlets/space-export/space-archive.test.d.ts +2 -0
  107. package/dist/types/src/packlets/space-export/space-archive.test.d.ts.map +1 -0
  108. package/dist/types/src/packlets/spaces/automerge-space-state.d.ts.map +1 -1
  109. package/dist/types/src/packlets/spaces/data-space-manager.d.ts +18 -12
  110. package/dist/types/src/packlets/spaces/data-space-manager.d.ts.map +1 -1
  111. package/dist/types/src/packlets/spaces/data-space.d.ts +24 -7
  112. package/dist/types/src/packlets/spaces/data-space.d.ts.map +1 -1
  113. package/dist/types/src/packlets/spaces/edge-feed-replicator.d.ts.map +1 -1
  114. package/dist/types/src/packlets/spaces/epoch-migrations.d.ts.map +1 -1
  115. package/dist/types/src/packlets/spaces/genesis.d.ts +2 -1
  116. package/dist/types/src/packlets/spaces/genesis.d.ts.map +1 -1
  117. package/dist/types/src/packlets/spaces/notarization-plugin.d.ts +1 -4
  118. package/dist/types/src/packlets/spaces/notarization-plugin.d.ts.map +1 -1
  119. package/dist/types/src/packlets/spaces/spaces-service.d.ts +10 -7
  120. package/dist/types/src/packlets/spaces/spaces-service.d.ts.map +1 -1
  121. package/dist/types/src/packlets/storage/level.d.ts.map +1 -1
  122. package/dist/types/src/packlets/storage/profile-archive.d.ts.map +1 -1
  123. package/dist/types/src/packlets/storage/storage.d.ts.map +1 -1
  124. package/dist/types/src/packlets/storage/util.d.ts.map +1 -1
  125. package/dist/types/src/packlets/system/system-service.d.ts +1 -1
  126. package/dist/types/src/packlets/system/system-service.d.ts.map +1 -1
  127. package/dist/types/src/packlets/testing/credential-utils.d.ts.map +1 -1
  128. package/dist/types/src/packlets/testing/invitation-utils.d.ts.map +1 -1
  129. package/dist/types/src/packlets/testing/test-builder.d.ts.map +1 -1
  130. package/dist/types/src/packlets/worker/worker-runtime.d.ts +11 -1
  131. package/dist/types/src/packlets/worker/worker-runtime.d.ts.map +1 -1
  132. package/dist/types/src/packlets/worker/worker-session.d.ts +0 -2
  133. package/dist/types/src/packlets/worker/worker-session.d.ts.map +1 -1
  134. package/dist/types/src/testing/setup.d.ts.map +1 -1
  135. package/dist/types/src/version.d.ts +1 -1
  136. package/dist/types/tsconfig.tsbuildinfo +1 -1
  137. package/package.json +42 -49
  138. package/src/index.ts +1 -0
  139. package/src/packlets/agents/edge-agent-manager.ts +8 -5
  140. package/src/packlets/agents/edge-agent-service.ts +4 -2
  141. package/src/packlets/devices/devices-service.test.ts +0 -1
  142. package/src/packlets/devtools/devtools.ts +0 -1
  143. package/src/packlets/diagnostics/diagnostics.ts +1 -2
  144. package/src/packlets/identity/contacts-service.ts +0 -1
  145. package/src/packlets/identity/identity-manager.test.ts +5 -5
  146. package/src/packlets/identity/identity-manager.ts +18 -17
  147. package/src/packlets/identity/identity-recovery-manager.ts +20 -16
  148. package/src/packlets/identity/identity-service.test.ts +6 -27
  149. package/src/packlets/identity/identity-service.ts +13 -81
  150. package/src/packlets/identity/identity.test.ts +2 -2
  151. package/src/packlets/identity/identity.ts +9 -32
  152. package/src/packlets/invitations/device-invitation-protocol.ts +3 -2
  153. package/src/packlets/invitations/edge-invitation-handler.ts +9 -5
  154. package/src/packlets/invitations/invitation-host-extension.ts +7 -10
  155. package/src/packlets/invitations/invitation-protocol.ts +5 -1
  156. package/src/packlets/invitations/invitation-state.ts +1 -15
  157. package/src/packlets/invitations/invitations-handler.test.ts +4 -5
  158. package/src/packlets/invitations/invitations-handler.ts +67 -15
  159. package/src/packlets/invitations/invitations-manager.ts +40 -15
  160. package/src/packlets/invitations/invitations-service.ts +9 -9
  161. package/src/packlets/invitations/space-invitation-protocol.test.ts +17 -16
  162. package/src/packlets/invitations/space-invitation-protocol.ts +4 -3
  163. package/src/packlets/logging/logging-service.ts +15 -15
  164. package/src/packlets/network/network-service.test.ts +0 -1
  165. package/src/packlets/network/network-service.ts +10 -8
  166. package/src/packlets/services/client-rpc-server.ts +15 -12
  167. package/src/packlets/services/feed-syncer.test.ts +340 -0
  168. package/src/packlets/services/feed-syncer.ts +175 -25
  169. package/src/packlets/services/service-context.test.ts +3 -2
  170. package/src/packlets/services/service-context.ts +98 -41
  171. package/src/packlets/services/service-host.test.ts +8 -8
  172. package/src/packlets/services/service-host.ts +17 -27
  173. package/src/packlets/services/service-registry.test.ts +0 -1
  174. package/src/packlets/space-export/archive-format.ts +42 -0
  175. package/src/packlets/space-export/index.ts +4 -1
  176. package/src/packlets/space-export/serialized-space-reader.ts +111 -0
  177. package/src/packlets/space-export/serialized-space-writer.ts +252 -0
  178. package/src/packlets/space-export/space-archive-reader.ts +64 -3
  179. package/src/packlets/space-export/space-archive-writer.ts +38 -2
  180. package/src/packlets/space-export/space-archive.test.ts +461 -0
  181. package/src/packlets/spaces/data-space-manager.test.ts +79 -13
  182. package/src/packlets/spaces/data-space-manager.ts +75 -111
  183. package/src/packlets/spaces/data-space.ts +49 -29
  184. package/src/packlets/spaces/edge-feed-replicator.test.ts +1 -1
  185. package/src/packlets/spaces/edge-feed-replicator.ts +9 -7
  186. package/src/packlets/spaces/epoch-migrations.ts +3 -3
  187. package/src/packlets/spaces/genesis.ts +6 -1
  188. package/src/packlets/spaces/notarization-plugin.ts +2 -1
  189. package/src/packlets/spaces/spaces-service.test.ts +18 -11
  190. package/src/packlets/spaces/spaces-service.ts +113 -17
  191. package/src/packlets/testing/invitation-utils.ts +3 -2
  192. package/src/packlets/worker/worker-runtime.ts +39 -6
  193. package/src/packlets/worker/worker-session.ts +8 -14
  194. package/src/version.ts +1 -1
  195. package/dist/lib/browser/chunk-675YEKTQ.mjs.map +0 -7
  196. package/dist/lib/node-esm/chunk-RUUAV2BD.mjs.map +0 -7
  197. package/dist/types/src/packlets/identity/default-space-state-machine.d.ts +0 -19
  198. package/dist/types/src/packlets/identity/default-space-state-machine.d.ts.map +0 -1
  199. package/src/packlets/identity/default-space-state-machine.ts +0 -44
  200. /package/dist/lib/browser/{chunk-NQSC7HOE.mjs.map → chunk-XJRPB3GA.mjs.map} +0 -0
  201. /package/dist/lib/node-esm/{chunk-PKEGMOQ4.mjs.map → chunk-2DT3MZRL.mjs.map} +0 -0
@@ -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,7 +3,7 @@
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
9
  import { SpaceProperties } from '@dxos/client-protocol';
@@ -16,7 +16,6 @@ import {
16
16
  getCredentialAssertion,
17
17
  } from '@dxos/credentials';
18
18
  import { Type } from '@dxos/echo';
19
- import { getSchemaDXN } from '@dxos/echo/internal';
20
19
  import {
21
20
  AuthStatus,
22
21
  CredentialServerExtension,
@@ -31,45 +30,40 @@ import {
31
30
  type SpaceProtocolSession,
32
31
  findInlineObjectOfType,
33
32
  } from '@dxos/echo-pipeline';
34
- import {
35
- type DatabaseDirectory,
36
- EncodedReference,
37
- type ObjectStructure,
38
- SpaceDocVersion,
39
- createIdFromSpaceKey,
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';
44
37
  import { type Keyring } from '@dxos/keyring';
45
- import { ObjectId, PublicKey, type SpaceId } from '@dxos/keys';
38
+ import { PublicKey, type SpaceId } from '@dxos/keys';
46
39
  import { log } from '@dxos/log';
47
- import { AlreadyJoinedError, trace as Trace } from '@dxos/protocols';
40
+ import { AlreadyJoinedError } from '@dxos/protocols';
48
41
  import { Invitation, SpaceState } from '@dxos/protocols/proto/dxos/client/services';
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,6 +99,7 @@ export type AdmitMemberOptions = {
102
99
  role: SpaceMember.Role;
103
100
  profile?: ProfileDocument;
104
101
  delegationCredentialId?: PublicKey;
102
+ tags?: string[];
105
103
  };
106
104
 
107
105
  export type DataSpaceManagerProps = {
@@ -135,16 +133,17 @@ export type DataSpaceManagerRuntimeProps = {
135
133
  export type CreateSpaceOptions = {
136
134
  rootUrl?: AutomergeUrl;
137
135
  documents?: Record<DocumentId, Uint8Array>;
136
+ tags?: string[];
137
+ membershipPolicy?: MembershipPolicy;
138
138
  };
139
139
 
140
140
  @trackLeaks('open', 'close')
141
+ @trace.resource({ lifecycle: true })
141
142
  export class DataSpaceManager extends Resource {
142
143
  public readonly updated = new Event();
143
144
 
144
145
  private readonly _spaces = new ComplexMap<PublicKey, DataSpace>(PublicKey.hash);
145
146
 
146
- private readonly _instanceId = PublicKey.random().toHex();
147
-
148
147
  private readonly _spaceManager: SpaceManager;
149
148
  private readonly _metadataStore: MetadataStore;
150
149
  private readonly _keyring: Keyring;
@@ -184,9 +183,8 @@ export class DataSpaceManager extends Resource {
184
183
  Array.from(this._spaces.values()).map(async (space) => {
185
184
  const rootUrl = space.automergeSpaceState.rootUrl;
186
185
  const rootHandle = rootUrl
187
- ? await this._echoHost.loadDoc<Doc<DatabaseDirectory>>(Context.default(), rootUrl as AutomergeUrl)
186
+ ? await this._echoHost.loadDoc<Doc<DatabaseDirectory>>(this._ctx, rootUrl as AutomergeUrl)
188
187
  : undefined;
189
- await rootHandle?.whenReady();
190
188
  const rootDoc = rootHandle?.doc();
191
189
 
192
190
  const properties = rootDoc && findInlineObjectOfType(rootDoc, Type.getTypename(SpaceProperties));
@@ -217,16 +215,16 @@ export class DataSpaceManager extends Resource {
217
215
  }
218
216
 
219
217
  @synchronized
220
- protected override async _open(): Promise<void> {
218
+ @trace.span({ showInBrowserTimeline: true, op: 'lifecycle' })
219
+ protected override async _open(ctx: Context): Promise<void> {
221
220
  log('open');
222
- log.trace('dxos.echo.data-space-manager.open', Trace.begin({ id: this._instanceId }));
223
221
  log('metadata loaded', { spaces: this._metadataStore.spaces.length });
224
222
 
225
223
  const spacesToActivate: DataSpace[] = [];
226
224
  await forEachAsync(this._metadataStore.spaces, async (spaceMetadata) => {
227
225
  try {
228
226
  log('load space', { spaceMetadata });
229
- const space = await this._constructSpace(spaceMetadata);
227
+ const space = await this._constructSpace(ctx, spaceMetadata);
230
228
  // Track spaces that were previously active for auto-activation (used in dedicated worker mode).
231
229
  if (this._runtimeProps?.autoActivateSpaces && spaceMetadata.state === SpaceState.SPACE_ACTIVE) {
232
230
  spacesToActivate.push(space);
@@ -239,21 +237,19 @@ export class DataSpaceManager extends Resource {
239
237
  // Auto-activate spaces that were previously active (used in dedicated worker mode after leader changeover).
240
238
  for (const space of spacesToActivate) {
241
239
  log('auto-activating space', { spaceKey: space.key });
242
- space.activate().catch((err) => {
240
+ space.activate(ctx).catch((err) => {
243
241
  log.error('Error auto-activating space', { spaceKey: space.key, err });
244
242
  });
245
243
  }
246
244
 
247
245
  this.updated.emit();
248
-
249
- log.trace('dxos.echo.data-space-manager.open', Trace.end({ id: this._instanceId }));
250
246
  }
251
247
 
252
248
  @synchronized
253
- protected override async _close(): Promise<void> {
249
+ protected override async _close(ctx: Context): Promise<void> {
254
250
  log('close');
255
251
  for (const space of this._spaces.values()) {
256
- await space.close();
252
+ await space.close(ctx);
257
253
  }
258
254
  this._spaces.clear();
259
255
  }
@@ -262,7 +258,8 @@ export class DataSpaceManager extends Resource {
262
258
  * Creates a new space writing the genesis credentials to the control feed.
263
259
  */
264
260
  @synchronized
265
- async createSpace(options: CreateSpaceOptions = {}): Promise<DataSpace> {
261
+ @trace.span({ showInBrowserTimeline: true, op: 'lifecycle' })
262
+ async createSpace(ctx: Context, options: CreateSpaceOptions = {}): Promise<DataSpace> {
266
263
  assertArgument(
267
264
  !!options.rootUrl === !!options.documents,
268
265
  'options',
@@ -270,6 +267,8 @@ export class DataSpaceManager extends Resource {
270
267
  );
271
268
 
272
269
  assertState(this._lifecycleState === LifecycleState.OPEN, 'Not open.');
270
+
271
+ const tags = options.tags ? Array.from(options.tags) : [];
273
272
  const spaceKey = await this._keyring.createKey();
274
273
  const controlFeedKey = await this._keyring.createKey();
275
274
  const dataFeedKey = await this._keyring.createKey();
@@ -282,6 +281,7 @@ export class DataSpaceManager extends Resource {
282
281
  controlFeedKey,
283
282
  dataFeedKey,
284
283
  state: SpaceState.SPACE_ACTIVE,
284
+ tags,
285
285
  };
286
286
 
287
287
  log('creating space...', { spaceId, spaceKey });
@@ -319,30 +319,37 @@ export class DataSpaceManager extends Resource {
319
319
  let root: DatabaseRoot;
320
320
  if (options.rootUrl) {
321
321
  const newRootDocId = documentIdMapping[interpretAsDocumentId(options.rootUrl)] ?? failedInvariant();
322
- const rootDocHandle = await this._echoHost.loadDoc<DatabaseDirectory>(Context.default(), newRootDocId);
322
+ const rootDocHandle = await this._echoHost.loadDoc<DatabaseDirectory>(ctx, newRootDocId);
323
323
  DatabaseRoot.mapLinks(rootDocHandle, documentIdMapping);
324
324
 
325
- root = await this._echoHost.openSpaceRoot(spaceId, `automerge:${newRootDocId}` as AutomergeUrl);
325
+ root = await this._echoHost.openSpaceRoot(ctx, spaceId, `automerge:${newRootDocId}` as AutomergeUrl);
326
326
  } else {
327
- root = await this._echoHost.createSpaceRoot(spaceKey);
327
+ root = await this._echoHost.createSpaceRoot(ctx, spaceKey);
328
328
  }
329
- await this._echoHost.flush();
329
+ await this._echoHost.flush(ctx);
330
330
 
331
331
  log('constructing space...', { spaceKey });
332
332
 
333
- const space = await this._constructSpace(metadata);
334
- await space.open();
333
+ const space = await this._constructSpace(ctx, metadata);
334
+ await space.open(ctx);
335
335
 
336
336
  log('adding space...', { spaceKey });
337
337
 
338
- const credentials = await spaceGenesis(this._keyring, this._signingContext, space.inner, root.url);
338
+ const credentials = await spaceGenesis(
339
+ this._keyring,
340
+ this._signingContext,
341
+ space.inner,
342
+ root.url,
343
+ tags,
344
+ options.membershipPolicy,
345
+ );
339
346
  await this._metadataStore.addSpace(metadata);
340
347
 
341
348
  const memberCredential = credentials[1];
342
349
  invariant(getCredentialAssertion(memberCredential)['@type'] === 'dxos.halo.credentials.SpaceMember');
343
350
  await this._signingContext.recordCredential(memberCredential);
344
351
 
345
- await space.initializeDataPipeline();
352
+ await space.initializeDataPipeline(ctx);
346
353
 
347
354
  log('space ready.', { spaceId, spaceKey });
348
355
 
@@ -350,86 +357,36 @@ export class DataSpaceManager extends Resource {
350
357
  return space;
351
358
  }
352
359
 
353
- async isDefaultSpace(space: DataSpace): Promise<boolean> {
354
- if (!space.databaseRoot) {
355
- return false;
356
- }
357
- switch (space.databaseRoot.getVersion()) {
358
- case SpaceDocVersion.CURRENT: {
359
- if (!space.databaseRoot.handle.isReady()) {
360
- log.warn('waiting for space root to be ready', { spaceId: space.id });
361
- await space.databaseRoot.handle.whenReady();
362
- }
363
-
364
- const [_, properties] =
365
- findInlineObjectOfType(space.databaseRoot.doc()!, Type.getTypename(SpaceProperties)) ?? [];
366
- return properties?.data?.[DEFAULT_SPACE_KEY] === this._signingContext.identityKey.toHex();
367
- }
368
-
369
- case SpaceDocVersion.LEGACY: {
370
- throw new Error('Legacy space version is not supported');
371
- }
372
-
373
- default:
374
- log.warn('unknown space version', { version: space.databaseRoot.getVersion(), spaceId: space.id });
375
- return false;
376
- }
377
- }
378
-
379
- async createDefaultSpace(): Promise<DataSpace> {
380
- const space = await this.createSpace();
381
- const document = await this._getSpaceRootDocument(space);
382
-
383
- // TODO(dmaretskyi): Better API for low-level data access.
384
- const properties: ObjectStructure = {
385
- system: {
386
- type: EncodedReference.fromDXN(getSchemaDXN(SpaceProperties)!),
387
- },
388
- data: {
389
- [DEFAULT_SPACE_KEY]: this._signingContext.identityKey.toHex(),
390
- },
391
- meta: {
392
- keys: [],
393
- },
394
- };
395
-
396
- const propertiesId = ObjectId.random();
397
- document.change((doc: DatabaseDirectory) => {
398
- setDeep(doc, ['objects', propertiesId], properties);
399
- });
400
-
401
- await this._echoHost.flush();
402
- return space;
403
- }
404
-
405
- private async _getSpaceRootDocument(space: DataSpace): Promise<DocHandle<DatabaseDirectory>> {
406
- const automergeIndex = space.automergeSpaceState.rootUrl;
407
- invariant(automergeIndex);
408
- const document = await this._echoHost.loadDoc<DatabaseDirectory>(Context.default(), automergeIndex as any, {
409
- fetchFromNetwork: true,
410
- });
411
- await document.whenReady();
412
- return document;
413
- }
414
-
360
+ /**
361
+ * Accepts an existing space by joining its swarm and initializing the data pipeline.
362
+ * @param ctx - Caller context for cancellation and tracing.
363
+ * @param opts - Space keys and optional timeframes for catch-up.
364
+ */
415
365
  // TODO(burdon): Rename join space.
416
366
  @synchronized
417
- async acceptSpace(opts: AcceptSpaceOptions): Promise<DataSpace> {
367
+ @trace.span({ showInBrowserTimeline: true, op: 'lifecycle' })
368
+ async acceptSpace(ctx: Context, opts: AcceptSpaceOptions): Promise<DataSpace> {
418
369
  log('accept space', { opts });
419
370
  invariant(this._lifecycleState === LifecycleState.OPEN, 'Not open.');
420
371
  invariant(!this._spaces.has(opts.spaceKey), 'Space already exists.');
421
372
 
373
+ const tags = opts.tags ? Array.from(opts.tags) : [];
422
374
  const metadata: SpaceMetadata = {
423
375
  key: opts.spaceKey,
424
376
  genesisFeedKey: opts.genesisFeedKey,
425
377
  controlTimeframe: opts.controlTimeframe,
426
378
  dataTimeframe: opts.dataTimeframe,
379
+ tags,
427
380
  };
428
381
 
429
- const space = await this._constructSpace(metadata);
430
- await space.open();
382
+ const space = await this._constructSpace(ctx, metadata);
383
+ await space.open(ctx);
431
384
  await this._metadataStore.addSpace(metadata);
432
- space.initializeDataPipelineAsync();
385
+ // Use DSM lifecycle ctx: the invitation accept flow disposes `ctx` as soon as
386
+ // `acceptSpace` returns (guardedState.complete -> ctx.dispose). Detached data-pipeline
387
+ // initialization must outlive the invitation flow, and its span must be parented to a
388
+ // long-lived context.
389
+ space.initializeDataPipelineAsync(this._ctx);
433
390
 
434
391
  this.updated.emit();
435
392
  return space;
@@ -453,6 +410,7 @@ export class DataSpaceManager extends Resource {
453
410
  space.spaceState.membershipChainHeads,
454
411
  options.profile,
455
412
  options.delegationCredentialId,
413
+ space.spaceState.tags,
456
414
  );
457
415
 
458
416
  // TODO(dmaretskyi): Refactor.
@@ -479,8 +437,8 @@ export class DataSpaceManager extends Resource {
479
437
  );
480
438
  }
481
439
 
482
- public async requestSpaceAdmissionCredential(spaceKey: PublicKey): Promise<Credential> {
483
- return this._spaceManager.requestSpaceAdmissionCredential({
440
+ public async requestSpaceAdmissionCredential(ctx: Context, spaceKey: PublicKey): Promise<Credential> {
441
+ return this._spaceManager.requestSpaceAdmissionCredential(ctx, {
484
442
  spaceKey,
485
443
  identityKey: this._signingContext.identityKey,
486
444
  timeout: 15_000,
@@ -493,7 +451,11 @@ export class DataSpaceManager extends Resource {
493
451
  });
494
452
  }
495
453
 
496
- async setSpaceEdgeReplicationSetting(spaceKey: PublicKey, setting: EdgeReplicationSetting): Promise<void> {
454
+ async setSpaceEdgeReplicationSetting(
455
+ ctx: Context,
456
+ spaceKey: PublicKey,
457
+ setting: EdgeReplicationSetting,
458
+ ): Promise<void> {
497
459
  const space = this._spaces.get(spaceKey);
498
460
  invariant(space, 'Space not found.');
499
461
 
@@ -505,7 +467,7 @@ export class DataSpaceManager extends Resource {
505
467
  await this._echoEdgeReplicator?.disconnectFromSpace(space.id);
506
468
  break;
507
469
  case EdgeReplicationSetting.ENABLED:
508
- await this._echoEdgeReplicator?.connectToSpace(space.id);
470
+ await this._echoEdgeReplicator?.connectToSpace(ctx, space.id);
509
471
  break;
510
472
  }
511
473
  }
@@ -513,7 +475,7 @@ export class DataSpaceManager extends Resource {
513
475
  space.stateUpdate.emit();
514
476
  }
515
477
 
516
- private async _constructSpace(metadata: SpaceMetadata): Promise<DataSpace> {
478
+ private async _constructSpace(ctx: Context, metadata: SpaceMetadata): Promise<DataSpace> {
517
479
  log('construct space', { metadata });
518
480
  const gossip = new Gossip({
519
481
  localPeerId: this._signingContext.deviceKey,
@@ -603,6 +565,7 @@ export class DataSpaceManager extends Resource {
603
565
  },
604
566
  },
605
567
  cache: metadata.cache,
568
+ tags: metadata.tags,
606
569
  edgeConnection: this._edgeConnection,
607
570
  edgeHttpClient: this._edgeHttpClient,
608
571
  edgeFeatures: this._edgeFeatures,
@@ -611,7 +574,8 @@ export class DataSpaceManager extends Resource {
611
574
  dataSpace.postOpen.append(async () => {
612
575
  const setting = dataSpace.getEdgeReplicationSetting();
613
576
  if (!setting || setting === EdgeReplicationSetting.ENABLED) {
614
- await this._echoEdgeReplicator?.connectToSpace(dataSpace.id);
577
+ // Use lifecycle ctx: the caller ctx from _constructSpace may be disposed by the time postOpen fires.
578
+ await this._echoEdgeReplicator?.connectToSpace(this._ctx, dataSpace.id);
615
579
  } else if (this._echoEdgeReplicator) {
616
580
  log('not connecting EchoEdgeReplicator because of EdgeReplicationSetting', { spaceId: dataSpace.id });
617
581
  }
@@ -708,7 +672,7 @@ export class DataSpaceManager extends Resource {
708
672
  invitations: Array<[PublicKey, DelegateSpaceInvitation]>,
709
673
  ): Promise<void> {
710
674
  const tasks = invitations.map(([credentialId, invitation]) => {
711
- return this._invitationsManager.createInvitation({
675
+ return this._invitationsManager.createInvitation(this._ctx, {
712
676
  type: Invitation.Type.DELEGATED,
713
677
  kind: Invitation.Kind.SPACE,
714
678
  spaceKey: space.key,