@dxos/client-services 0.8.4-main.f5c0578 → 0.8.4-main.fcfe5033a5

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 (204) hide show
  1. package/dist/lib/browser/{chunk-WBMHLEAB.mjs → chunk-HYGNOM23.mjs} +2843 -2114
  2. package/dist/lib/browser/chunk-HYGNOM23.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 +512 -90
  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 +37 -19
  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-IPVFX6KI.mjs → chunk-GFT7MAQE.mjs} +2571 -1711
  23. package/dist/lib/node-esm/chunk-GFT7MAQE.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 +512 -90
  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 +37 -19
  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 +2 -1
  44. package/dist/types/src/packlets/agents/edge-agent-service.d.ts.map +1 -1
  45. package/dist/types/src/packlets/devtools/devtools.d.ts +2 -2
  46. package/dist/types/src/packlets/devtools/devtools.d.ts.map +1 -1
  47. package/dist/types/src/packlets/diagnostics/diagnostics.d.ts +1 -2
  48. package/dist/types/src/packlets/diagnostics/diagnostics.d.ts.map +1 -1
  49. package/dist/types/src/packlets/diagnostics/index.d.ts +1 -1
  50. package/dist/types/src/packlets/diagnostics/index.d.ts.map +1 -1
  51. package/dist/types/src/packlets/identity/authenticator.d.ts +2 -2
  52. package/dist/types/src/packlets/identity/authenticator.d.ts.map +1 -1
  53. package/dist/types/src/packlets/identity/contacts-service.d.ts.map +1 -1
  54. package/dist/types/src/packlets/identity/identity-manager.d.ts +6 -6
  55. package/dist/types/src/packlets/identity/identity-manager.d.ts.map +1 -1
  56. package/dist/types/src/packlets/identity/identity-recovery-manager.d.ts +7 -6
  57. package/dist/types/src/packlets/identity/identity-recovery-manager.d.ts.map +1 -1
  58. package/dist/types/src/packlets/identity/identity-service.d.ts +6 -10
  59. package/dist/types/src/packlets/identity/identity-service.d.ts.map +1 -1
  60. package/dist/types/src/packlets/identity/identity.d.ts +8 -11
  61. package/dist/types/src/packlets/identity/identity.d.ts.map +1 -1
  62. package/dist/types/src/packlets/invitations/device-invitation-protocol.d.ts +6 -5
  63. package/dist/types/src/packlets/invitations/device-invitation-protocol.d.ts.map +1 -1
  64. package/dist/types/src/packlets/invitations/edge-invitation-handler.d.ts +1 -1
  65. package/dist/types/src/packlets/invitations/edge-invitation-handler.d.ts.map +1 -1
  66. package/dist/types/src/packlets/invitations/invitation-guest-extenstion.d.ts.map +1 -1
  67. package/dist/types/src/packlets/invitations/invitation-host-extension.d.ts.map +1 -1
  68. package/dist/types/src/packlets/invitations/invitation-protocol.d.ts +7 -4
  69. package/dist/types/src/packlets/invitations/invitation-protocol.d.ts.map +1 -1
  70. package/dist/types/src/packlets/invitations/invitation-state.d.ts.map +1 -1
  71. package/dist/types/src/packlets/invitations/invitations-handler.d.ts +4 -4
  72. package/dist/types/src/packlets/invitations/invitations-handler.d.ts.map +1 -1
  73. package/dist/types/src/packlets/invitations/invitations-manager.d.ts +3 -3
  74. package/dist/types/src/packlets/invitations/invitations-manager.d.ts.map +1 -1
  75. package/dist/types/src/packlets/invitations/invitations-service.d.ts +3 -3
  76. package/dist/types/src/packlets/invitations/invitations-service.d.ts.map +1 -1
  77. package/dist/types/src/packlets/invitations/space-invitation-protocol.d.ts +4 -3
  78. package/dist/types/src/packlets/invitations/space-invitation-protocol.d.ts.map +1 -1
  79. package/dist/types/src/packlets/locks/index.d.ts +1 -1
  80. package/dist/types/src/packlets/locks/index.d.ts.map +1 -1
  81. package/dist/types/src/packlets/logging/logging-service.d.ts +4 -0
  82. package/dist/types/src/packlets/logging/logging-service.d.ts.map +1 -1
  83. package/dist/types/src/packlets/network/network-service.d.ts +5 -4
  84. package/dist/types/src/packlets/network/network-service.d.ts.map +1 -1
  85. package/dist/types/src/packlets/services/client-rpc-server.d.ts +5 -5
  86. package/dist/types/src/packlets/services/client-rpc-server.d.ts.map +1 -1
  87. package/dist/types/src/packlets/services/feed-syncer.d.ts +59 -0
  88. package/dist/types/src/packlets/services/feed-syncer.d.ts.map +1 -0
  89. package/dist/types/src/packlets/services/feed-syncer.test.d.ts +2 -0
  90. package/dist/types/src/packlets/services/feed-syncer.test.d.ts.map +1 -0
  91. package/dist/types/src/packlets/services/platform.d.ts.map +1 -1
  92. package/dist/types/src/packlets/services/service-context.d.ts +13 -8
  93. package/dist/types/src/packlets/services/service-context.d.ts.map +1 -1
  94. package/dist/types/src/packlets/services/service-host.d.ts +20 -7
  95. package/dist/types/src/packlets/services/service-host.d.ts.map +1 -1
  96. package/dist/types/src/packlets/space-export/archive-format.d.ts +9 -0
  97. package/dist/types/src/packlets/space-export/archive-format.d.ts.map +1 -0
  98. package/dist/types/src/packlets/space-export/index.d.ts +4 -1
  99. package/dist/types/src/packlets/space-export/index.d.ts.map +1 -1
  100. package/dist/types/src/packlets/space-export/serialized-space-reader.d.ts +23 -0
  101. package/dist/types/src/packlets/space-export/serialized-space-reader.d.ts.map +1 -0
  102. package/dist/types/src/packlets/space-export/serialized-space-writer.d.ts +36 -0
  103. package/dist/types/src/packlets/space-export/serialized-space-writer.d.ts.map +1 -0
  104. package/dist/types/src/packlets/space-export/space-archive-reader.d.ts +9 -1
  105. package/dist/types/src/packlets/space-export/space-archive-reader.d.ts.map +1 -1
  106. package/dist/types/src/packlets/space-export/space-archive-writer.d.ts +7 -1
  107. package/dist/types/src/packlets/space-export/space-archive-writer.d.ts.map +1 -1
  108. package/dist/types/src/packlets/space-export/space-archive.test.d.ts +2 -0
  109. package/dist/types/src/packlets/space-export/space-archive.test.d.ts.map +1 -0
  110. package/dist/types/src/packlets/spaces/data-space-manager.d.ts +28 -16
  111. package/dist/types/src/packlets/spaces/data-space-manager.d.ts.map +1 -1
  112. package/dist/types/src/packlets/spaces/data-space.d.ts +26 -9
  113. package/dist/types/src/packlets/spaces/data-space.d.ts.map +1 -1
  114. package/dist/types/src/packlets/spaces/edge-feed-replicator.d.ts +2 -2
  115. package/dist/types/src/packlets/spaces/edge-feed-replicator.d.ts.map +1 -1
  116. package/dist/types/src/packlets/spaces/genesis.d.ts +2 -1
  117. package/dist/types/src/packlets/spaces/genesis.d.ts.map +1 -1
  118. package/dist/types/src/packlets/spaces/notarization-plugin.d.ts +6 -6
  119. package/dist/types/src/packlets/spaces/notarization-plugin.d.ts.map +1 -1
  120. package/dist/types/src/packlets/spaces/spaces-service.d.ts +17 -7
  121. package/dist/types/src/packlets/spaces/spaces-service.d.ts.map +1 -1
  122. package/dist/types/src/packlets/testing/invitation-utils.d.ts +6 -3
  123. package/dist/types/src/packlets/testing/invitation-utils.d.ts.map +1 -1
  124. package/dist/types/src/packlets/testing/test-builder.d.ts +6 -5
  125. package/dist/types/src/packlets/testing/test-builder.d.ts.map +1 -1
  126. package/dist/types/src/packlets/worker/worker-runtime.d.ts +41 -4
  127. package/dist/types/src/packlets/worker/worker-runtime.d.ts.map +1 -1
  128. package/dist/types/src/packlets/worker/worker-session.d.ts +2 -4
  129. package/dist/types/src/packlets/worker/worker-session.d.ts.map +1 -1
  130. package/dist/types/src/version.d.ts +1 -1
  131. package/dist/types/src/version.d.ts.map +1 -1
  132. package/dist/types/tsconfig.tsbuildinfo +1 -1
  133. package/package.json +70 -48
  134. package/src/index.ts +1 -0
  135. package/src/packlets/agents/edge-agent-manager.ts +10 -7
  136. package/src/packlets/agents/edge-agent-service.ts +15 -3
  137. package/src/packlets/devices/devices-service.test.ts +0 -1
  138. package/src/packlets/devices/devices-service.ts +1 -1
  139. package/src/packlets/devtools/devtools.ts +2 -3
  140. package/src/packlets/diagnostics/diagnostics.ts +1 -2
  141. package/src/packlets/diagnostics/index.ts +1 -1
  142. package/src/packlets/identity/authenticator.ts +2 -2
  143. package/src/packlets/identity/contacts-service.ts +0 -1
  144. package/src/packlets/identity/identity-manager.test.ts +5 -5
  145. package/src/packlets/identity/identity-manager.ts +21 -18
  146. package/src/packlets/identity/identity-recovery-manager.ts +22 -18
  147. package/src/packlets/identity/identity-service.test.ts +6 -27
  148. package/src/packlets/identity/identity-service.ts +13 -81
  149. package/src/packlets/identity/identity.test.ts +6 -6
  150. package/src/packlets/identity/identity.ts +11 -34
  151. package/src/packlets/invitations/device-invitation-protocol.ts +8 -7
  152. package/src/packlets/invitations/edge-invitation-handler.ts +9 -5
  153. package/src/packlets/invitations/invitation-guest-extenstion.ts +6 -4
  154. package/src/packlets/invitations/invitation-host-extension.ts +6 -4
  155. package/src/packlets/invitations/invitation-protocol.ts +7 -4
  156. package/src/packlets/invitations/invitation-state.ts +0 -10
  157. package/src/packlets/invitations/invitations-handler.test.ts +4 -5
  158. package/src/packlets/invitations/invitations-handler.ts +69 -13
  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 +11 -16
  163. package/src/packlets/locks/index.ts +1 -1
  164. package/src/packlets/logging/logging-service.ts +5 -1
  165. package/src/packlets/network/network-service.test.ts +0 -1
  166. package/src/packlets/network/network-service.ts +10 -8
  167. package/src/packlets/services/client-rpc-server.ts +19 -16
  168. package/src/packlets/services/feed-syncer.test.ts +340 -0
  169. package/src/packlets/services/feed-syncer.ts +337 -0
  170. package/src/packlets/services/platform.ts +7 -1
  171. package/src/packlets/services/service-context.test.ts +3 -2
  172. package/src/packlets/services/service-context.ts +141 -55
  173. package/src/packlets/services/service-host.test.ts +8 -8
  174. package/src/packlets/services/service-host.ts +77 -32
  175. package/src/packlets/services/service-registry.test.ts +0 -1
  176. package/src/packlets/space-export/archive-format.ts +42 -0
  177. package/src/packlets/space-export/index.ts +4 -1
  178. package/src/packlets/space-export/serialized-space-reader.ts +111 -0
  179. package/src/packlets/space-export/serialized-space-writer.ts +246 -0
  180. package/src/packlets/space-export/space-archive-reader.ts +65 -4
  181. package/src/packlets/space-export/space-archive-writer.ts +43 -5
  182. package/src/packlets/space-export/space-archive.test.ts +461 -0
  183. package/src/packlets/spaces/data-space-manager.test.ts +79 -13
  184. package/src/packlets/spaces/data-space-manager.ts +122 -110
  185. package/src/packlets/spaces/data-space.ts +58 -33
  186. package/src/packlets/spaces/edge-feed-replicator.test.ts +2 -2
  187. package/src/packlets/spaces/edge-feed-replicator.ts +12 -10
  188. package/src/packlets/spaces/epoch-migrations.ts +5 -5
  189. package/src/packlets/spaces/genesis.ts +6 -1
  190. package/src/packlets/spaces/notarization-plugin.test.ts +2 -2
  191. package/src/packlets/spaces/notarization-plugin.ts +10 -9
  192. package/src/packlets/spaces/spaces-service.test.ts +18 -11
  193. package/src/packlets/spaces/spaces-service.ts +124 -25
  194. package/src/packlets/storage/storage.ts +4 -4
  195. package/src/packlets/testing/invitation-utils.ts +10 -6
  196. package/src/packlets/testing/test-builder.ts +36 -10
  197. package/src/packlets/worker/worker-runtime.ts +179 -17
  198. package/src/packlets/worker/worker-session.ts +12 -18
  199. package/src/version.ts +1 -1
  200. package/dist/lib/browser/chunk-WBMHLEAB.mjs.map +0 -7
  201. package/dist/lib/node-esm/chunk-IPVFX6KI.mjs.map +0 -7
  202. package/dist/types/src/packlets/identity/default-space-state-machine.d.ts +0 -19
  203. package/dist/types/src/packlets/identity/default-space-state-machine.d.ts.map +0 -1
  204. 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,13 +15,13 @@ import {
15
15
  createAdmissionCredentials,
16
16
  getCredentialAssertion,
17
17
  } from '@dxos/credentials';
18
+ import { Type } from '@dxos/echo';
18
19
  import {
19
20
  AuthStatus,
20
21
  CredentialServerExtension,
21
22
  DatabaseRoot,
22
23
  type EchoEdgeReplicator,
23
24
  type EchoHost,
24
- FIND_PARAMS,
25
25
  type MeshEchoReplicator,
26
26
  type MetadataStore,
27
27
  type Space,
@@ -30,14 +30,7 @@ import {
30
30
  type SpaceProtocolSession,
31
31
  findInlineObjectOfType,
32
32
  } from '@dxos/echo-pipeline';
33
- import {
34
- type DatabaseDirectory,
35
- type ObjectStructure,
36
- SpaceDocVersion,
37
- createIdFromSpaceKey,
38
- encodeReference,
39
- } from '@dxos/echo-protocol';
40
- import { ObjectId, getTypeReference } from '@dxos/echo-schema';
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({ lifecycle: true })
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(),
@@ -212,30 +221,44 @@ export class DataSpaceManager extends Resource {
212
221
  }
213
222
 
214
223
  @synchronized
215
- protected override async _open(): Promise<void> {
224
+ @trace.span({ showInBrowserTimeline: true, op: 'lifecycle' })
225
+ protected override async _open(ctx: Context): Promise<void> {
216
226
  log('open');
217
227
  log.trace('dxos.echo.data-space-manager.open', Trace.begin({ id: this._instanceId }));
218
228
  log('metadata loaded', { spaces: this._metadataStore.spaces.length });
219
229
 
230
+ const spacesToActivate: DataSpace[] = [];
220
231
  await forEachAsync(this._metadataStore.spaces, async (spaceMetadata) => {
221
232
  try {
222
233
  log('load space', { spaceMetadata });
223
- await this._constructSpace(spaceMetadata);
234
+ const space = await this._constructSpace(ctx, spaceMetadata);
235
+ // Track spaces that were previously active for auto-activation (used in dedicated worker mode).
236
+ if (this._runtimeProps?.autoActivateSpaces && spaceMetadata.state === SpaceState.SPACE_ACTIVE) {
237
+ spacesToActivate.push(space);
238
+ }
224
239
  } catch (err) {
225
240
  log.error('Error loading space', { spaceMetadata, err });
226
241
  }
227
242
  });
228
243
 
244
+ // Auto-activate spaces that were previously active (used in dedicated worker mode after leader changeover).
245
+ for (const space of spacesToActivate) {
246
+ log('auto-activating space', { spaceKey: space.key });
247
+ space.activate(ctx).catch((err) => {
248
+ log.error('Error auto-activating space', { spaceKey: space.key, err });
249
+ });
250
+ }
251
+
229
252
  this.updated.emit();
230
253
 
231
254
  log.trace('dxos.echo.data-space-manager.open', Trace.end({ id: this._instanceId }));
232
255
  }
233
256
 
234
257
  @synchronized
235
- protected override async _close(): Promise<void> {
258
+ protected override async _close(ctx: Context): Promise<void> {
236
259
  log('close');
237
260
  for (const space of this._spaces.values()) {
238
- await space.close();
261
+ await space.close(ctx);
239
262
  }
240
263
  this._spaces.clear();
241
264
  }
@@ -244,10 +267,17 @@ export class DataSpaceManager extends Resource {
244
267
  * Creates a new space writing the genesis credentials to the control feed.
245
268
  */
246
269
  @synchronized
247
- async createSpace(options: CreateSpaceOptions = {}): Promise<DataSpace> {
248
- assertArgument(!!options.rootUrl === !!options.documents, 'root url must be required when providing documents');
270
+ @trace.span({ showInBrowserTimeline: true, op: 'lifecycle' })
271
+ async createSpace(ctx: Context, options: CreateSpaceOptions = {}): Promise<DataSpace> {
272
+ assertArgument(
273
+ !!options.rootUrl === !!options.documents,
274
+ 'options',
275
+ 'root url must be required when providing documents',
276
+ );
249
277
 
250
278
  assertState(this._lifecycleState === LifecycleState.OPEN, 'Not open.');
279
+
280
+ const tags = options.tags ? Array.from(options.tags) : [];
251
281
  const spaceKey = await this._keyring.createKey();
252
282
  const controlFeedKey = await this._keyring.createKey();
253
283
  const dataFeedKey = await this._keyring.createKey();
@@ -260,6 +290,7 @@ export class DataSpaceManager extends Resource {
260
290
  controlFeedKey,
261
291
  dataFeedKey,
262
292
  state: SpaceState.SPACE_ACTIVE,
293
+ tags,
263
294
  };
264
295
 
265
296
  log('creating space...', { spaceId, spaceKey });
@@ -275,7 +306,18 @@ export class DataSpaceManager extends Resource {
275
306
  await Promise.all(
276
307
  Object.entries(options.documents).map(async ([documentId, data]) => {
277
308
  log('creating document...', { documentId });
278
- const newDoc = await this._echoHost.createDoc(data, { preserveHistory: true });
309
+ // TODO(dmaretskyi): Broken types -- the bytes get interpreted as CRDT data.
310
+ const newDoc = await this._echoHost.createDoc(data as any as DatabaseDirectory, {
311
+ preserveHistory: true,
312
+ });
313
+
314
+ // The archived documents might have the spaceKey from the space they were expored from, we need to update it to the new spaceKey.
315
+ if (newDoc.doc().access !== undefined && newDoc.doc().access!.spaceKey !== spaceKey.toHex()) {
316
+ newDoc.change((doc) => {
317
+ doc.access!.spaceKey = spaceKey.toHex();
318
+ });
319
+ }
320
+
279
321
  documentIdMapping[documentId as DocumentId] = newDoc.documentId;
280
322
  }),
281
323
  );
@@ -286,29 +328,37 @@ export class DataSpaceManager extends Resource {
286
328
  let root: DatabaseRoot;
287
329
  if (options.rootUrl) {
288
330
  const newRootDocId = documentIdMapping[interpretAsDocumentId(options.rootUrl)] ?? failedInvariant();
289
- const rootDocHandle = await this._echoHost.loadDoc<DatabaseDirectory>(Context.default(), newRootDocId);
331
+ const rootDocHandle = await this._echoHost.loadDoc<DatabaseDirectory>(ctx, newRootDocId);
290
332
  DatabaseRoot.mapLinks(rootDocHandle, documentIdMapping);
291
333
 
292
- root = await this._echoHost.openSpaceRoot(spaceId, `automerge:${newRootDocId}` as AutomergeUrl);
334
+ root = await this._echoHost.openSpaceRoot(ctx, spaceId, `automerge:${newRootDocId}` as AutomergeUrl);
293
335
  } else {
294
- root = await this._echoHost.createSpaceRoot(spaceKey);
336
+ root = await this._echoHost.createSpaceRoot(ctx, spaceKey);
295
337
  }
338
+ await this._echoHost.flush(ctx);
296
339
 
297
340
  log('constructing space...', { spaceKey });
298
341
 
299
- const space = await this._constructSpace(metadata);
300
- await space.open();
342
+ const space = await this._constructSpace(ctx, metadata);
343
+ await space.open(ctx);
301
344
 
302
345
  log('adding space...', { spaceKey });
303
346
 
304
- const credentials = await spaceGenesis(this._keyring, this._signingContext, space.inner, root.url);
347
+ const credentials = await spaceGenesis(
348
+ this._keyring,
349
+ this._signingContext,
350
+ space.inner,
351
+ root.url,
352
+ tags,
353
+ options.membershipPolicy,
354
+ );
305
355
  await this._metadataStore.addSpace(metadata);
306
356
 
307
357
  const memberCredential = credentials[1];
308
358
  invariant(getCredentialAssertion(memberCredential)['@type'] === 'dxos.halo.credentials.SpaceMember');
309
359
  await this._signingContext.recordCredential(memberCredential);
310
360
 
311
- await space.initializeDataPipeline();
361
+ await space.initializeDataPipeline(ctx);
312
362
 
313
363
  log('space ready.', { spaceId, spaceKey });
314
364
 
@@ -316,81 +366,36 @@ export class DataSpaceManager extends Resource {
316
366
  return space;
317
367
  }
318
368
 
319
- async isDefaultSpace(space: DataSpace): Promise<boolean> {
320
- if (!space.databaseRoot) {
321
- return false;
322
- }
323
- switch (space.databaseRoot.getVersion()) {
324
- case SpaceDocVersion.CURRENT: {
325
- if (!space.databaseRoot.handle.isReady()) {
326
- log.warn('waiting for space root to be ready', { spaceId: space.id });
327
- await space.databaseRoot.handle.whenReady();
328
- }
329
- const [_, properties] = findInlineObjectOfType(space.databaseRoot.doc()!, TYPE_PROPERTIES) ?? [];
330
- return properties?.data?.[DEFAULT_SPACE_KEY] === this._signingContext.identityKey.toHex();
331
- }
332
- case SpaceDocVersion.LEGACY: {
333
- throw new Error('Legacy space version is not supported');
334
- }
335
-
336
- default:
337
- log.warn('unknown space version', { version: space.databaseRoot.getVersion(), spaceId: space.id });
338
- return false;
339
- }
340
- }
341
-
342
- async createDefaultSpace(): Promise<DataSpace> {
343
- const space = await this.createSpace();
344
- const document = await this._getSpaceRootDocument(space);
345
-
346
- // TODO(dmaretskyi): Better API for low-level data access.
347
- const properties: ObjectStructure = {
348
- system: {
349
- type: encodeReference(getTypeReference(PropertiesType)!),
350
- },
351
- data: {
352
- [DEFAULT_SPACE_KEY]: this._signingContext.identityKey.toHex(),
353
- },
354
- meta: {
355
- keys: [],
356
- },
357
- };
358
-
359
- const propertiesId = ObjectId.random();
360
- document.change((doc: DatabaseDirectory) => {
361
- setDeep(doc, ['objects', propertiesId], properties);
362
- });
363
-
364
- await this._echoHost.flush();
365
- return space;
366
- }
367
-
368
- private async _getSpaceRootDocument(space: DataSpace): Promise<DocHandle<DatabaseDirectory>> {
369
- const automergeIndex = space.automergeSpaceState.rootUrl;
370
- invariant(automergeIndex);
371
- const document = await this._echoHost.automergeRepo.find<DatabaseDirectory>(automergeIndex as any, FIND_PARAMS);
372
- await document.whenReady();
373
- return document;
374
- }
375
-
369
+ /**
370
+ * Accepts an existing space by joining its swarm and initializing the data pipeline.
371
+ * @param ctx - Caller context for cancellation and tracing.
372
+ * @param opts - Space keys and optional timeframes for catch-up.
373
+ */
376
374
  // TODO(burdon): Rename join space.
377
375
  @synchronized
378
- async acceptSpace(opts: AcceptSpaceOptions): Promise<DataSpace> {
376
+ @trace.span({ showInBrowserTimeline: true, op: 'lifecycle' })
377
+ async acceptSpace(ctx: Context, opts: AcceptSpaceOptions): Promise<DataSpace> {
379
378
  log('accept space', { opts });
380
379
  invariant(this._lifecycleState === LifecycleState.OPEN, 'Not open.');
381
380
  invariant(!this._spaces.has(opts.spaceKey), 'Space already exists.');
382
381
 
382
+ const tags = opts.tags ? Array.from(opts.tags) : [];
383
383
  const metadata: SpaceMetadata = {
384
384
  key: opts.spaceKey,
385
385
  genesisFeedKey: opts.genesisFeedKey,
386
386
  controlTimeframe: opts.controlTimeframe,
387
387
  dataTimeframe: opts.dataTimeframe,
388
+ tags,
388
389
  };
389
390
 
390
- const space = await this._constructSpace(metadata);
391
- await space.open();
391
+ const space = await this._constructSpace(ctx, metadata);
392
+ await space.open(ctx);
392
393
  await this._metadataStore.addSpace(metadata);
393
- space.initializeDataPipelineAsync();
394
+ // Use DSM lifecycle ctx: the invitation accept flow disposes `ctx` as soon as
395
+ // `acceptSpace` returns (guardedState.complete -> ctx.dispose). Detached data-pipeline
396
+ // initialization must outlive the invitation flow, and its span must be parented to a
397
+ // long-lived context.
398
+ space.initializeDataPipelineAsync(this._ctx);
394
399
 
395
400
  this.updated.emit();
396
401
  return space;
@@ -414,6 +419,7 @@ export class DataSpaceManager extends Resource {
414
419
  space.spaceState.membershipChainHeads,
415
420
  options.profile,
416
421
  options.delegationCredentialId,
422
+ space.spaceState.tags,
417
423
  );
418
424
 
419
425
  // TODO(dmaretskyi): Refactor.
@@ -440,8 +446,8 @@ export class DataSpaceManager extends Resource {
440
446
  );
441
447
  }
442
448
 
443
- public async requestSpaceAdmissionCredential(spaceKey: PublicKey): Promise<Credential> {
444
- return this._spaceManager.requestSpaceAdmissionCredential({
449
+ public async requestSpaceAdmissionCredential(ctx: Context, spaceKey: PublicKey): Promise<Credential> {
450
+ return this._spaceManager.requestSpaceAdmissionCredential(ctx, {
445
451
  spaceKey,
446
452
  identityKey: this._signingContext.identityKey,
447
453
  timeout: 15_000,
@@ -454,7 +460,11 @@ export class DataSpaceManager extends Resource {
454
460
  });
455
461
  }
456
462
 
457
- async setSpaceEdgeReplicationSetting(spaceKey: PublicKey, setting: EdgeReplicationSetting): Promise<void> {
463
+ async setSpaceEdgeReplicationSetting(
464
+ ctx: Context,
465
+ spaceKey: PublicKey,
466
+ setting: EdgeReplicationSetting,
467
+ ): Promise<void> {
458
468
  const space = this._spaces.get(spaceKey);
459
469
  invariant(space, 'Space not found.');
460
470
 
@@ -466,7 +476,7 @@ export class DataSpaceManager extends Resource {
466
476
  await this._echoEdgeReplicator?.disconnectFromSpace(space.id);
467
477
  break;
468
478
  case EdgeReplicationSetting.ENABLED:
469
- await this._echoEdgeReplicator?.connectToSpace(space.id);
479
+ await this._echoEdgeReplicator?.connectToSpace(ctx, space.id);
470
480
  break;
471
481
  }
472
482
  }
@@ -474,14 +484,14 @@ export class DataSpaceManager extends Resource {
474
484
  space.stateUpdate.emit();
475
485
  }
476
486
 
477
- private async _constructSpace(metadata: SpaceMetadata): Promise<DataSpace> {
487
+ private async _constructSpace(ctx: Context, metadata: SpaceMetadata): Promise<DataSpace> {
478
488
  log('construct space', { metadata });
479
489
  const gossip = new Gossip({
480
490
  localPeerId: this._signingContext.deviceKey,
481
491
  });
482
492
  const presence = new Presence({
483
- announceInterval: this._runtimeParams?.spaceMemberPresenceAnnounceInterval ?? PRESENCE_ANNOUNCE_INTERVAL,
484
- offlineTimeout: this._runtimeParams?.spaceMemberPresenceOfflineTimeout ?? PRESENCE_OFFLINE_TIMEOUT,
493
+ announceInterval: this._runtimeProps?.spaceMemberPresenceAnnounceInterval ?? PRESENCE_ANNOUNCE_INTERVAL,
494
+ offlineTimeout: this._runtimeProps?.spaceMemberPresenceOfflineTimeout ?? PRESENCE_OFFLINE_TIMEOUT,
485
495
  identityKey: this._signingContext.identityKey,
486
496
  gossip,
487
497
  });
@@ -564,15 +574,17 @@ export class DataSpaceManager extends Resource {
564
574
  },
565
575
  },
566
576
  cache: metadata.cache,
577
+ tags: metadata.tags,
567
578
  edgeConnection: this._edgeConnection,
568
579
  edgeHttpClient: this._edgeHttpClient,
569
580
  edgeFeatures: this._edgeFeatures,
570
- activeEdgeNotarizationPollingInterval: this._runtimeParams?.activeEdgeNotarizationPollingInterval,
581
+ activeEdgeNotarizationPollingInterval: this._runtimeProps?.activeEdgeNotarizationPollingInterval,
571
582
  });
572
583
  dataSpace.postOpen.append(async () => {
573
584
  const setting = dataSpace.getEdgeReplicationSetting();
574
585
  if (!setting || setting === EdgeReplicationSetting.ENABLED) {
575
- await this._echoEdgeReplicator?.connectToSpace(dataSpace.id);
586
+ // Use lifecycle ctx: the caller ctx from _constructSpace may be disposed by the time postOpen fires.
587
+ await this._echoEdgeReplicator?.connectToSpace(this._ctx, dataSpace.id);
576
588
  } else if (this._echoEdgeReplicator) {
577
589
  log('not connecting EchoEdgeReplicator because of EdgeReplicationSetting', { spaceId: dataSpace.id });
578
590
  }
@@ -669,7 +681,7 @@ export class DataSpaceManager extends Resource {
669
681
  invitations: Array<[PublicKey, DelegateSpaceInvitation]>,
670
682
  ): Promise<void> {
671
683
  const tasks = invitations.map(([credentialId, invitation]) => {
672
- return this._invitationsManager.createInvitation({
684
+ return this._invitationsManager.createInvitation(this._ctx, {
673
685
  type: Invitation.Type.DELEGATED,
674
686
  kind: Invitation.Kind.SPACE,
675
687
  spaceKey: space.key,