@dxos/client-services 0.8.4-main.c85a9c8dae → 0.8.4-main.cb12b3f963

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 (199) hide show
  1. package/dist/lib/browser/{chunk-MQ6PWJ76.mjs → chunk-TZQLGPMV.mjs} +1596 -3099
  2. package/dist/lib/browser/chunk-TZQLGPMV.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-GUAL4U7S.mjs → chunk-6DX3BNLB.mjs} +1596 -3099
  18. package/dist/lib/node-esm/chunk-6DX3BNLB.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 +2 -2
  85. package/dist/types/src/packlets/services/feed-syncer.d.ts.map +1 -1
  86. package/dist/types/src/packlets/services/service-context.d.ts +2 -3
  87. package/dist/types/src/packlets/services/service-context.d.ts.map +1 -1
  88. package/dist/types/src/packlets/services/service-host.d.ts +2 -3
  89. package/dist/types/src/packlets/services/service-host.d.ts.map +1 -1
  90. package/dist/types/src/packlets/services/service-registry.d.ts.map +1 -1
  91. package/dist/types/src/packlets/services/util.d.ts.map +1 -1
  92. package/dist/types/src/packlets/space-export/archive-format.d.ts +9 -0
  93. package/dist/types/src/packlets/space-export/archive-format.d.ts.map +1 -0
  94. package/dist/types/src/packlets/space-export/index.d.ts +4 -1
  95. package/dist/types/src/packlets/space-export/index.d.ts.map +1 -1
  96. package/dist/types/src/packlets/space-export/serialized-space-reader.d.ts +23 -0
  97. package/dist/types/src/packlets/space-export/serialized-space-reader.d.ts.map +1 -0
  98. package/dist/types/src/packlets/space-export/serialized-space-writer.d.ts +36 -0
  99. package/dist/types/src/packlets/space-export/serialized-space-writer.d.ts.map +1 -0
  100. package/dist/types/src/packlets/space-export/space-archive-reader.d.ts +9 -1
  101. package/dist/types/src/packlets/space-export/space-archive-reader.d.ts.map +1 -1
  102. package/dist/types/src/packlets/space-export/space-archive-writer.d.ts +7 -1
  103. package/dist/types/src/packlets/space-export/space-archive-writer.d.ts.map +1 -1
  104. package/dist/types/src/packlets/space-export/space-archive.test.d.ts +2 -0
  105. package/dist/types/src/packlets/space-export/space-archive.test.d.ts.map +1 -0
  106. package/dist/types/src/packlets/spaces/automerge-space-state.d.ts.map +1 -1
  107. package/dist/types/src/packlets/spaces/data-space-manager.d.ts +18 -12
  108. package/dist/types/src/packlets/spaces/data-space-manager.d.ts.map +1 -1
  109. package/dist/types/src/packlets/spaces/data-space.d.ts +24 -7
  110. package/dist/types/src/packlets/spaces/data-space.d.ts.map +1 -1
  111. package/dist/types/src/packlets/spaces/edge-feed-replicator.d.ts.map +1 -1
  112. package/dist/types/src/packlets/spaces/epoch-migrations.d.ts.map +1 -1
  113. package/dist/types/src/packlets/spaces/genesis.d.ts +2 -1
  114. package/dist/types/src/packlets/spaces/genesis.d.ts.map +1 -1
  115. package/dist/types/src/packlets/spaces/notarization-plugin.d.ts +1 -4
  116. package/dist/types/src/packlets/spaces/notarization-plugin.d.ts.map +1 -1
  117. package/dist/types/src/packlets/spaces/spaces-service.d.ts +10 -7
  118. package/dist/types/src/packlets/spaces/spaces-service.d.ts.map +1 -1
  119. package/dist/types/src/packlets/storage/level.d.ts.map +1 -1
  120. package/dist/types/src/packlets/storage/profile-archive.d.ts.map +1 -1
  121. package/dist/types/src/packlets/storage/storage.d.ts.map +1 -1
  122. package/dist/types/src/packlets/storage/util.d.ts.map +1 -1
  123. package/dist/types/src/packlets/system/system-service.d.ts +1 -1
  124. package/dist/types/src/packlets/system/system-service.d.ts.map +1 -1
  125. package/dist/types/src/packlets/testing/credential-utils.d.ts.map +1 -1
  126. package/dist/types/src/packlets/testing/invitation-utils.d.ts.map +1 -1
  127. package/dist/types/src/packlets/testing/test-builder.d.ts.map +1 -1
  128. package/dist/types/src/packlets/worker/worker-runtime.d.ts +11 -1
  129. package/dist/types/src/packlets/worker/worker-runtime.d.ts.map +1 -1
  130. package/dist/types/src/packlets/worker/worker-session.d.ts +0 -2
  131. package/dist/types/src/packlets/worker/worker-session.d.ts.map +1 -1
  132. package/dist/types/src/testing/setup.d.ts.map +1 -1
  133. package/dist/types/src/version.d.ts +1 -1
  134. package/dist/types/tsconfig.tsbuildinfo +1 -1
  135. package/package.json +42 -49
  136. package/src/index.ts +1 -0
  137. package/src/packlets/agents/edge-agent-manager.ts +8 -5
  138. package/src/packlets/agents/edge-agent-service.ts +4 -2
  139. package/src/packlets/devices/devices-service.test.ts +0 -1
  140. package/src/packlets/devtools/devtools.ts +0 -1
  141. package/src/packlets/diagnostics/diagnostics.ts +1 -2
  142. package/src/packlets/identity/contacts-service.ts +0 -1
  143. package/src/packlets/identity/identity-manager.test.ts +5 -5
  144. package/src/packlets/identity/identity-manager.ts +18 -17
  145. package/src/packlets/identity/identity-recovery-manager.ts +20 -16
  146. package/src/packlets/identity/identity-service.test.ts +6 -27
  147. package/src/packlets/identity/identity-service.ts +13 -81
  148. package/src/packlets/identity/identity.test.ts +2 -2
  149. package/src/packlets/identity/identity.ts +9 -32
  150. package/src/packlets/invitations/device-invitation-protocol.ts +3 -2
  151. package/src/packlets/invitations/edge-invitation-handler.ts +9 -5
  152. package/src/packlets/invitations/invitation-host-extension.ts +7 -10
  153. package/src/packlets/invitations/invitation-protocol.ts +5 -1
  154. package/src/packlets/invitations/invitation-state.ts +1 -15
  155. package/src/packlets/invitations/invitations-handler.test.ts +4 -5
  156. package/src/packlets/invitations/invitations-handler.ts +67 -15
  157. package/src/packlets/invitations/invitations-manager.ts +40 -15
  158. package/src/packlets/invitations/invitations-service.ts +9 -9
  159. package/src/packlets/invitations/space-invitation-protocol.test.ts +17 -16
  160. package/src/packlets/invitations/space-invitation-protocol.ts +4 -3
  161. package/src/packlets/logging/logging-service.ts +15 -15
  162. package/src/packlets/network/network-service.test.ts +0 -1
  163. package/src/packlets/network/network-service.ts +10 -8
  164. package/src/packlets/services/client-rpc-server.ts +15 -12
  165. package/src/packlets/services/feed-syncer.test.ts +3 -3
  166. package/src/packlets/services/feed-syncer.ts +24 -17
  167. package/src/packlets/services/service-context.test.ts +3 -2
  168. package/src/packlets/services/service-context.ts +91 -40
  169. package/src/packlets/services/service-host.test.ts +8 -8
  170. package/src/packlets/services/service-host.ts +17 -24
  171. package/src/packlets/services/service-registry.test.ts +0 -1
  172. package/src/packlets/space-export/archive-format.ts +42 -0
  173. package/src/packlets/space-export/index.ts +4 -1
  174. package/src/packlets/space-export/serialized-space-reader.ts +111 -0
  175. package/src/packlets/space-export/serialized-space-writer.ts +252 -0
  176. package/src/packlets/space-export/space-archive-reader.ts +64 -3
  177. package/src/packlets/space-export/space-archive-writer.ts +38 -2
  178. package/src/packlets/space-export/space-archive.test.ts +461 -0
  179. package/src/packlets/spaces/data-space-manager.test.ts +79 -13
  180. package/src/packlets/spaces/data-space-manager.ts +75 -111
  181. package/src/packlets/spaces/data-space.ts +49 -29
  182. package/src/packlets/spaces/edge-feed-replicator.test.ts +1 -1
  183. package/src/packlets/spaces/edge-feed-replicator.ts +9 -7
  184. package/src/packlets/spaces/epoch-migrations.ts +3 -3
  185. package/src/packlets/spaces/genesis.ts +6 -1
  186. package/src/packlets/spaces/notarization-plugin.ts +2 -1
  187. package/src/packlets/spaces/spaces-service.test.ts +18 -11
  188. package/src/packlets/spaces/spaces-service.ts +113 -17
  189. package/src/packlets/testing/invitation-utils.ts +3 -2
  190. package/src/packlets/worker/worker-runtime.ts +39 -6
  191. package/src/packlets/worker/worker-session.ts +4 -10
  192. package/src/version.ts +1 -1
  193. package/dist/lib/browser/chunk-MQ6PWJ76.mjs.map +0 -7
  194. package/dist/lib/node-esm/chunk-GUAL4U7S.mjs.map +0 -7
  195. package/dist/types/src/packlets/identity/default-space-state-machine.d.ts +0 -19
  196. package/dist/types/src/packlets/identity/default-space-state-machine.d.ts.map +0 -1
  197. package/src/packlets/identity/default-space-state-machine.ts +0 -44
  198. /package/dist/lib/browser/{chunk-NQSC7HOE.mjs.map → chunk-XJRPB3GA.mjs.map} +0 -0
  199. /package/dist/lib/node-esm/{chunk-PKEGMOQ4.mjs.map → chunk-2DT3MZRL.mjs.map} +0 -0
@@ -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,
@@ -24,7 +24,7 @@ import { failedInvariant, invariant } from '@dxos/invariant';
24
24
  import { type Keyring } from '@dxos/keyring';
25
25
  import { PublicKey } from '@dxos/keys';
26
26
  import { log } from '@dxos/log';
27
- import { CancelledError, SystemError } from '@dxos/protocols';
27
+ import { CancelledError, type FeedProtocol, SystemError } from '@dxos/protocols';
28
28
  import {
29
29
  type CreateEpochRequest,
30
30
  type Space as SpaceProto,
@@ -37,6 +37,7 @@ import {
37
37
  AdmittedFeed,
38
38
  type Credential,
39
39
  type Epoch,
40
+ MembershipPolicy,
40
41
  type ProfileDocument,
41
42
  SpaceMember,
42
43
  } from '@dxos/protocols/proto/dxos/halo/credentials';
@@ -47,7 +48,6 @@ import { trace } from '@dxos/tracing';
47
48
  import { type AsyncCallback, CallbackCollection, ComplexSet } from '@dxos/util';
48
49
 
49
50
  import { TrustedKeySetAuthVerifier } from '../identity';
50
-
51
51
  import { AutomergeSpaceState } from './automerge-space-state';
52
52
  import { type SigningContext } from './data-space-manager';
53
53
  import { EdgeFeedReplicator } from './edge-feed-replicator';
@@ -83,6 +83,7 @@ export type DataSpaceProps = {
83
83
  signingContext: SigningContext;
84
84
  callbacks?: DataSpaceCallbacks;
85
85
  cache?: SpaceCache;
86
+ tags?: string[];
86
87
  edgeConnection?: EdgeConnection;
87
88
  edgeHttpClient?: EdgeHttpClient;
88
89
  edgeFeatures?: Runtime.Client.EdgeFeatures;
@@ -120,6 +121,9 @@ export class DataSpace {
120
121
 
121
122
  private _state = SpaceState.SPACE_CLOSED;
122
123
 
124
+ /** Immutable tags from space metadata, available immediately. */
125
+ readonly tags: string[];
126
+
123
127
  private _databaseRoot: DatabaseRoot | null = null;
124
128
 
125
129
  /**
@@ -167,6 +171,7 @@ export class DataSpace {
167
171
  });
168
172
 
169
173
  this._cache = params.cache;
174
+ this.tags = params.tags ?? [];
170
175
 
171
176
  if (params.edgeConnection && params.edgeFeatures?.feedReplicator) {
172
177
  this._edgeFeedReplicator = new EdgeFeedReplicator({ messenger: params.edgeConnection, spaceId: this.id });
@@ -212,6 +217,11 @@ export class DataSpace {
212
217
  return this._cache;
213
218
  }
214
219
 
220
+ /** Membership policy from the genesis credential, defaults to INVITE. */
221
+ get membershipPolicy(): MembershipPolicy {
222
+ return this._inner.spaceState.genesisCredential ? this._inner.spaceState.membershipPolicy : MembershipPolicy.INVITE;
223
+ }
224
+
215
225
  get automergeSpaceState() {
216
226
  return this._automergeSpaceState;
217
227
  }
@@ -229,13 +239,14 @@ export class DataSpace {
229
239
  }
230
240
 
231
241
  @synchronized
232
- async open(): Promise<void> {
242
+ @trace.span({ showInBrowserTimeline: true, op: 'lifecycle' })
243
+ async open(ctx: Context): Promise<void> {
233
244
  if (this._state === SpaceState.SPACE_CLOSED) {
234
- await this._open();
245
+ await this._open(ctx);
235
246
  }
236
247
  }
237
248
 
238
- private async _open(): Promise<void> {
249
+ private async _open(ctx: Context): Promise<void> {
239
250
  await this._presence.open();
240
251
  await this._gossip.open();
241
252
  await this._notarizationPlugin.open();
@@ -247,8 +258,8 @@ export class DataSpace {
247
258
  this.inner.protocol.feedAdded.append(this._onFeedAdded);
248
259
  }
249
260
 
250
- await this._inner.open(new Context());
251
- await this._inner.startProtocol();
261
+ await this._inner.open(ctx);
262
+ await this._inner.startProtocol(ctx);
252
263
 
253
264
  await this._edgeFeedReplicator?.open();
254
265
 
@@ -262,11 +273,12 @@ export class DataSpace {
262
273
  }
263
274
 
264
275
  @synchronized
265
- async close(): Promise<void> {
266
- await this._close();
276
+ @trace.span({ showInBrowserTimeline: true, op: 'lifecycle' })
277
+ async close(ctx: Context): Promise<void> {
278
+ await this._close(ctx);
267
279
  }
268
280
 
269
- private async _close(): Promise<void> {
281
+ private async _close(ctx: Context): Promise<void> {
270
282
  await this._callbacks.beforeClose?.();
271
283
 
272
284
  await this.preClose.callSerial();
@@ -284,7 +296,7 @@ export class DataSpace {
284
296
 
285
297
  await this.authVerifier.close();
286
298
 
287
- await this._inner.close();
299
+ await this._inner.close(ctx);
288
300
  await this._inner.spaceState.removeCredentialProcessor(this._automergeSpaceState);
289
301
  await this._automergeSpaceState.close();
290
302
  await this._inner.spaceState.removeCredentialProcessor(this._notarizationPlugin);
@@ -304,12 +316,14 @@ export class DataSpace {
304
316
 
305
317
  /**
306
318
  * Initialize the data pipeline in a separate task.
319
+ * @param callerCtx - Trace context from the caller (e.g., activate or createSpace) for span parenting.
307
320
  */
308
- initializeDataPipelineAsync(): void {
321
+ initializeDataPipelineAsync(callerCtx?: Context): void {
322
+ const traceCtx = callerCtx ?? this._ctx;
309
323
  scheduleTask(this._ctx, async () => {
310
324
  try {
311
325
  this.metrics.pipelineInitBegin = new Date();
312
- await this.initializeDataPipeline();
326
+ await this.initializeDataPipeline(traceCtx);
313
327
  } catch (err) {
314
328
  if (err instanceof CancelledError || err instanceof ContextDisposedError) {
315
329
  log('data pipeline initialization cancelled', err);
@@ -327,8 +341,8 @@ export class DataSpace {
327
341
  });
328
342
  }
329
343
 
330
- @trace.span({ showInBrowserTimeline: true })
331
- async initializeDataPipeline(): Promise<void> {
344
+ @trace.span({ showInBrowserTimeline: true, op: 'lifecycle' })
345
+ async initializeDataPipeline(ctx: Context): Promise<void> {
332
346
  if (this._state !== SpaceState.SPACE_CONTROL_ONLY) {
333
347
  throw new SystemError({ message: 'Invalid operation' });
334
348
  }
@@ -337,7 +351,7 @@ export class DataSpace {
337
351
  log('new state', { state: SpaceState[this._state] });
338
352
 
339
353
  log('initializing control pipeline');
340
- await this._initializeAndReadControlPipeline();
354
+ await this._initializeAndReadControlPipeline(ctx);
341
355
 
342
356
  // Allow other tasks to run before loading the data pipeline.
343
357
  await sleep(1);
@@ -360,11 +374,19 @@ export class DataSpace {
360
374
  yield [this._databaseRoot.documentId, root];
361
375
 
362
376
  for (const documentUrl of this._databaseRoot.getAllLinkedDocuments()) {
363
- const data = await this._echoHost.exportDoc(Context.default(), documentUrl);
377
+ const data = await this._echoHost.exportDoc(documentUrl);
364
378
  yield [documentUrl.replace(/^automerge:/, ''), data];
365
379
  }
366
380
  }
367
381
 
382
+ /**
383
+ * Get all feeds and their blocks for this space.
384
+ * Used for space archive export.
385
+ */
386
+ async getAllFeeds(): Promise<Array<{ feedId: string; feedNamespace: string; blocks: FeedProtocol.Block[] }>> {
387
+ return this._echoHost.getAllFeedsForSpace(this.id);
388
+ }
389
+
368
390
  private async _enterReadyState(): Promise<void> {
369
391
  await this._callbacks.beforeReady?.();
370
392
 
@@ -375,10 +397,10 @@ export class DataSpace {
375
397
  await this._callbacks.afterReady?.();
376
398
  }
377
399
 
378
- @trace.span({ showInBrowserTimeline: true })
379
- private async _initializeAndReadControlPipeline(): Promise<void> {
400
+ @trace.span({ showInBrowserTimeline: true, op: 'lifecycle' })
401
+ private async _initializeAndReadControlPipeline(ctx: Context): Promise<void> {
380
402
  await this._inner.controlPipeline.state.waitUntilReachedTargetTimeframe({
381
- ctx: this._ctx,
403
+ ctx,
382
404
  timeout: 10_000,
383
405
  breakOnStall: false,
384
406
  });
@@ -473,11 +495,10 @@ export class DataSpace {
473
495
  await warnAfterTimeout(5_000, 'Automerge root doc load timeout (DataSpace)', async () => {
474
496
  handle = await cancelWithContext(
475
497
  this._ctx,
476
- this._echoHost.loadDoc<DatabaseDirectory>(Context.default(), rootUrl as AutomergeUrl, {
498
+ this._echoHost.loadDoc<DatabaseDirectory>(this._ctx, rootUrl as AutomergeUrl, {
477
499
  fetchFromNetwork: true,
478
500
  }),
479
501
  );
480
- await cancelWithContext(this._ctx, handle.whenReady());
481
502
  });
482
503
  if (this._ctx.disposed) {
483
504
  return;
@@ -496,7 +517,7 @@ export class DataSpace {
496
517
 
497
518
  // TODO(dmaretskyi): Close roots.
498
519
  // TODO(dmaretskyi): How do we handle changing to the next EPOCH?
499
- const root = await this._echoHost.openSpaceRoot(this.id, handle.url);
520
+ const root = await this._echoHost.openSpaceRoot(this._ctx, this.id, handle.url);
500
521
 
501
522
  // NOTE: Make sure this assignment happens synchronously together with the state change.
502
523
  this._databaseRoot = root;
@@ -573,25 +594,24 @@ export class DataSpace {
573
594
  }
574
595
 
575
596
  @synchronized
576
- async activate(): Promise<void> {
597
+ async activate(ctx: Context): Promise<void> {
577
598
  if (![SpaceState.SPACE_CLOSED, SpaceState.SPACE_INACTIVE].includes(this._state)) {
578
599
  return;
579
600
  }
580
601
 
581
602
  await this._metadataStore.setSpaceState(this.key, SpaceState.SPACE_ACTIVE);
582
- await this._open();
583
- this.initializeDataPipelineAsync();
603
+ await this._open(ctx);
604
+ this.initializeDataPipelineAsync(ctx);
584
605
  }
585
606
 
586
607
  @synchronized
587
- async deactivate(): Promise<void> {
608
+ async deactivate(ctx: Context): Promise<void> {
588
609
  if (this._state === SpaceState.SPACE_INACTIVE) {
589
610
  return;
590
611
  }
591
- // Unregister from data service.
592
612
  await this._metadataStore.setSpaceState(this.key, SpaceState.SPACE_INACTIVE);
593
613
  if (this._state !== SpaceState.SPACE_CLOSED) {
594
- await this._close();
614
+ await this._close(ctx);
595
615
  }
596
616
  this._state = SpaceState.SPACE_INACTIVE;
597
617
  log('new state', { state: SpaceState[this._state] });
@@ -107,7 +107,7 @@ describe('EdgeFeedReplicator', () => {
107
107
  const { feed } = await attachReplicator(messenger);
108
108
  await appendMessage(feed);
109
109
 
110
- sendSpy.mockImplementationOnce(async (request: any) => {
110
+ sendSpy.mockImplementationOnce(async (_ctx: any, request: any) => {
111
111
  sendResponseMessage(request, encodeCbor({ type: 'metadata', feedKey: feed.key.toHex(), length: 0 }));
112
112
  return Promise.resolve();
113
113
  });