@dxos/client-services 0.6.12 → 0.6.13-main.548ca8d

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/dist/lib/browser/{chunk-TOAILL4T.mjs → chunk-UEQIHAL2.mjs} +5838 -5151
  2. package/dist/lib/browser/chunk-UEQIHAL2.mjs.map +7 -0
  3. package/dist/lib/browser/index.mjs +3 -3
  4. package/dist/lib/browser/index.mjs.map +3 -3
  5. package/dist/lib/browser/meta.json +1 -1
  6. package/dist/lib/browser/testing/index.mjs +8 -7
  7. package/dist/lib/browser/testing/index.mjs.map +3 -3
  8. package/dist/lib/node/{chunk-H6C4XY6B.cjs → chunk-MA5EWTRH.cjs} +5858 -5175
  9. package/dist/lib/node/chunk-MA5EWTRH.cjs.map +7 -0
  10. package/dist/lib/node/index.cjs +46 -46
  11. package/dist/lib/node/index.cjs.map +3 -3
  12. package/dist/lib/node/meta.json +1 -1
  13. package/dist/lib/node/testing/index.cjs +14 -13
  14. package/dist/lib/node/testing/index.cjs.map +3 -3
  15. package/dist/lib/node-esm/chunk-AIBLDI4U.mjs +8403 -0
  16. package/dist/lib/node-esm/chunk-AIBLDI4U.mjs.map +7 -0
  17. package/dist/lib/node-esm/index.mjs +416 -0
  18. package/dist/lib/node-esm/index.mjs.map +7 -0
  19. package/dist/lib/node-esm/meta.json +1 -0
  20. package/dist/lib/node-esm/testing/index.mjs +420 -0
  21. package/dist/lib/node-esm/testing/index.mjs.map +7 -0
  22. package/dist/types/src/packlets/diagnostics/diagnostics-broadcast.d.ts.map +1 -1
  23. package/dist/types/src/packlets/identity/authenticator.d.ts.map +1 -1
  24. package/dist/types/src/packlets/identity/authenticator.node.test.d.ts +2 -0
  25. package/dist/types/src/packlets/identity/authenticator.node.test.d.ts.map +1 -0
  26. package/dist/types/src/packlets/identity/contacts-service.d.ts +1 -1
  27. package/dist/types/src/packlets/identity/contacts-service.d.ts.map +1 -1
  28. package/dist/types/src/packlets/identity/identity-manager.d.ts +19 -7
  29. package/dist/types/src/packlets/identity/identity-manager.d.ts.map +1 -1
  30. package/dist/types/src/packlets/identity/identity.d.ts +8 -1
  31. package/dist/types/src/packlets/identity/identity.d.ts.map +1 -1
  32. package/dist/types/src/packlets/invitations/edge-invitation-handler.d.ts +30 -0
  33. package/dist/types/src/packlets/invitations/edge-invitation-handler.d.ts.map +1 -0
  34. package/dist/types/src/packlets/invitations/invitation-guest-extenstion.d.ts +2 -1
  35. package/dist/types/src/packlets/invitations/invitation-guest-extenstion.d.ts.map +1 -1
  36. package/dist/types/src/packlets/invitations/invitation-host-extension.d.ts +2 -1
  37. package/dist/types/src/packlets/invitations/invitation-host-extension.d.ts.map +1 -1
  38. package/dist/types/src/packlets/invitations/invitation-state.d.ts +19 -0
  39. package/dist/types/src/packlets/invitations/invitation-state.d.ts.map +1 -0
  40. package/dist/types/src/packlets/invitations/invitations-handler.d.ts +8 -8
  41. package/dist/types/src/packlets/invitations/invitations-handler.d.ts.map +1 -1
  42. package/dist/types/src/packlets/invitations/space-invitation-protocol.d.ts.map +1 -1
  43. package/dist/types/src/packlets/services/service-context.d.ts +9 -9
  44. package/dist/types/src/packlets/services/service-context.d.ts.map +1 -1
  45. package/dist/types/src/packlets/services/service-host.d.ts +1 -0
  46. package/dist/types/src/packlets/services/service-host.d.ts.map +1 -1
  47. package/dist/types/src/packlets/spaces/data-space-manager.d.ts +6 -3
  48. package/dist/types/src/packlets/spaces/data-space-manager.d.ts.map +1 -1
  49. package/dist/types/src/packlets/spaces/data-space.d.ts +4 -3
  50. package/dist/types/src/packlets/spaces/data-space.d.ts.map +1 -1
  51. package/dist/types/src/packlets/spaces/edge-feed-replicator.d.ts +3 -0
  52. package/dist/types/src/packlets/spaces/edge-feed-replicator.d.ts.map +1 -1
  53. package/dist/types/src/packlets/spaces/edge-feed-replicator.test.d.ts +2 -0
  54. package/dist/types/src/packlets/spaces/edge-feed-replicator.test.d.ts.map +1 -0
  55. package/dist/types/src/packlets/spaces/epoch-migrations.d.ts +1 -1
  56. package/dist/types/src/packlets/spaces/epoch-migrations.d.ts.map +1 -1
  57. package/dist/types/src/packlets/spaces/notarization-plugin.d.ts +31 -6
  58. package/dist/types/src/packlets/spaces/notarization-plugin.d.ts.map +1 -1
  59. package/dist/types/src/packlets/spaces/spaces-service.d.ts +1 -1
  60. package/dist/types/src/packlets/spaces/spaces-service.d.ts.map +1 -1
  61. package/dist/types/src/packlets/storage/storage.d.ts.map +1 -1
  62. package/dist/types/src/packlets/testing/test-builder.d.ts +1 -2
  63. package/dist/types/src/packlets/testing/test-builder.d.ts.map +1 -1
  64. package/dist/types/src/packlets/worker/worker-runtime.d.ts.map +1 -1
  65. package/dist/types/src/testing/setup.d.ts +3 -0
  66. package/dist/types/src/testing/setup.d.ts.map +1 -0
  67. package/dist/types/src/version.d.ts +1 -1
  68. package/dist/types/src/version.d.ts.map +1 -1
  69. package/package.json +43 -39
  70. package/src/packlets/devices/devices-service.test.ts +4 -5
  71. package/src/packlets/diagnostics/diagnostics-broadcast.ts +1 -0
  72. package/src/packlets/identity/{authenticator.test.ts → authenticator.node.test.ts} +2 -3
  73. package/src/packlets/identity/authenticator.ts +5 -2
  74. package/src/packlets/identity/contacts-service.ts +1 -1
  75. package/src/packlets/identity/identity-manager.test.ts +5 -6
  76. package/src/packlets/identity/identity-manager.ts +35 -19
  77. package/src/packlets/identity/identity-service.test.ts +4 -8
  78. package/src/packlets/identity/identity.test.ts +128 -239
  79. package/src/packlets/identity/identity.ts +42 -8
  80. package/src/packlets/invitations/device-invitation-protocol.test.ts +7 -4
  81. package/src/packlets/invitations/edge-invitation-handler.ts +184 -0
  82. package/src/packlets/invitations/invitation-guest-extenstion.ts +8 -4
  83. package/src/packlets/invitations/invitation-host-extension.ts +8 -7
  84. package/src/packlets/invitations/invitation-state.ts +111 -0
  85. package/src/packlets/invitations/invitations-handler.test.ts +16 -9
  86. package/src/packlets/invitations/invitations-handler.ts +23 -92
  87. package/src/packlets/invitations/space-invitation-protocol.test.ts +4 -3
  88. package/src/packlets/invitations/space-invitation-protocol.ts +4 -0
  89. package/src/packlets/logging/logging.test.ts +1 -2
  90. package/src/packlets/network/network-service.test.ts +2 -3
  91. package/src/packlets/services/service-context.test.ts +3 -1
  92. package/src/packlets/services/service-context.ts +68 -31
  93. package/src/packlets/services/service-host.test.ts +8 -12
  94. package/src/packlets/services/service-host.ts +8 -6
  95. package/src/packlets/services/service-registry.test.ts +1 -2
  96. package/src/packlets/spaces/data-space-manager.test.ts +2 -2
  97. package/src/packlets/spaces/data-space-manager.ts +40 -5
  98. package/src/packlets/spaces/data-space.ts +34 -6
  99. package/src/packlets/spaces/edge-feed-replicator.test.ts +253 -0
  100. package/src/packlets/spaces/edge-feed-replicator.ts +80 -22
  101. package/src/packlets/spaces/epoch-migrations.ts +2 -2
  102. package/src/packlets/spaces/notarization-plugin.test.ts +10 -7
  103. package/src/packlets/spaces/notarization-plugin.ts +169 -29
  104. package/src/packlets/spaces/spaces-service.test.ts +5 -9
  105. package/src/packlets/spaces/spaces-service.ts +6 -1
  106. package/src/packlets/storage/storage.ts +0 -1
  107. package/src/packlets/system/system-service.test.ts +1 -2
  108. package/src/packlets/testing/test-builder.ts +3 -4
  109. package/src/packlets/worker/worker-runtime.ts +2 -2
  110. package/src/testing/setup.ts +11 -0
  111. package/src/version.ts +1 -5
  112. package/dist/lib/browser/chunk-TOAILL4T.mjs.map +0 -7
  113. package/dist/lib/node/chunk-H6C4XY6B.cjs.map +0 -7
  114. package/dist/types/src/packlets/identity/authenticator.test.d.ts +0 -2
  115. package/dist/types/src/packlets/identity/authenticator.test.d.ts.map +0 -1
  116. package/dist/types/src/packlets/services/automerge-host.test.d.ts +0 -2
  117. package/dist/types/src/packlets/services/automerge-host.test.d.ts.map +0 -1
  118. package/src/packlets/services/automerge-host.test.ts +0 -60
@@ -2,14 +2,18 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import { DeferredTask, Event, scheduleTask, sleep, TimeoutError, Trigger } from '@dxos/async';
6
- import { Context, rejectOnDispose } from '@dxos/context';
7
- import { type CredentialProcessor } from '@dxos/credentials';
5
+ import { DeferredTask, Event, scheduleTask, sleep, TimeoutError, Trigger, scheduleMicroTask } from '@dxos/async';
6
+ import { type Context, rejectOnDispose, Resource } from '@dxos/context';
7
+ import { type CredentialProcessor, verifyCredential } from '@dxos/credentials';
8
+ import { type EdgeHttpClient } from '@dxos/edge-client';
8
9
  import { type FeedWriter } from '@dxos/feed-store';
9
10
  import { invariant } from '@dxos/invariant';
10
11
  import { PublicKey } from '@dxos/keys';
11
- import { log } from '@dxos/log';
12
+ import { type SpaceId } from '@dxos/keys';
13
+ import { logInfo, log } from '@dxos/log';
14
+ import { EdgeCallFailedError } from '@dxos/protocols';
12
15
  import { schema } from '@dxos/protocols/proto';
16
+ import { type Runtime } from '@dxos/protocols/proto/dxos/config';
13
17
  import { type Credential } from '@dxos/protocols/proto/dxos/halo/credentials';
14
18
  import { type NotarizationService, type NotarizeRequest } from '@dxos/protocols/proto/dxos/mesh/teleport/notarization';
15
19
  import { type ExtensionContext, RpcExtension } from '@dxos/teleport';
@@ -21,8 +25,18 @@ const DEFAULT_SUCCESS_DELAY = 1_000;
21
25
 
22
26
  const DEFAULT_NOTARIZE_TIMEOUT = 10_000;
23
27
 
28
+ const MAX_EDGE_RETRIES = 2;
29
+
24
30
  const WRITER_NOT_SET_ERROR_CODE = 'WRITER_NOT_SET';
25
31
 
32
+ const credentialCodec = schema.getCodecForType('dxos.halo.credentials.Credential');
33
+
34
+ export type NotarizationPluginParams = {
35
+ spaceId: SpaceId;
36
+ edgeClient?: EdgeHttpClient;
37
+ edgeFeatures?: Runtime.Client.EdgeFeatures;
38
+ };
39
+
26
40
  export type NotarizeParams = {
27
41
  /**
28
42
  * For cancellation.
@@ -53,13 +67,17 @@ export type NotarizeParams = {
53
67
  * @default {@link DEFAULT_SUCCESS_DELAY}
54
68
  */
55
69
  successDelay?: number;
70
+
71
+ /**
72
+ * A random amount of time before making or retrying an edge request to help prevent large bursts of requests.
73
+ */
74
+ edgeRetryJitter?: number;
56
75
  };
57
76
 
58
77
  /**
59
78
  * See NotarizationService proto.
60
79
  */
61
- export class NotarizationPlugin implements CredentialProcessor {
62
- private readonly _ctx = new Context();
80
+ export class NotarizationPlugin extends Resource implements CredentialProcessor {
63
81
  private readonly _extensionOpened = new Event();
64
82
 
65
83
  private _writer: FeedWriter<Credential> | undefined;
@@ -67,13 +85,30 @@ export class NotarizationPlugin implements CredentialProcessor {
67
85
  private readonly _processedCredentials = new ComplexSet<PublicKey>(PublicKey.hash);
68
86
  private readonly _processCredentialsTriggers = new ComplexMap<PublicKey, Trigger>(PublicKey.hash);
69
87
 
88
+ @logInfo
89
+ private readonly _spaceId: SpaceId;
90
+
91
+ private readonly _edgeClient: EdgeHttpClient | undefined;
92
+
93
+ constructor(params: NotarizationPluginParams) {
94
+ super();
95
+ this._spaceId = params.spaceId;
96
+ if (params.edgeClient && params.edgeFeatures?.feedReplicator) {
97
+ this._edgeClient = params.edgeClient;
98
+ }
99
+ }
100
+
70
101
  get hasWriter() {
71
102
  return !!this._writer;
72
103
  }
73
104
 
74
- async open() {}
105
+ protected override async _open() {
106
+ if (this._edgeClient && this._writer) {
107
+ this._notarizePendingEdgeCredentials(this._edgeClient, this._writer);
108
+ }
109
+ }
75
110
 
76
- async close() {
111
+ protected override async _close() {
77
112
  await this._ctx.dispose();
78
113
  }
79
114
 
@@ -86,6 +121,7 @@ export class NotarizationPlugin implements CredentialProcessor {
86
121
  timeout = DEFAULT_NOTARIZE_TIMEOUT,
87
122
  retryTimeout = DEFAULT_RETRY_TIMEOUT,
88
123
  successDelay = DEFAULT_SUCCESS_DELAY,
124
+ edgeRetryJitter,
89
125
  }: NotarizeParams) {
90
126
  log('notarize', { credentials });
91
127
  invariant(
@@ -103,24 +139,35 @@ export class NotarizationPlugin implements CredentialProcessor {
103
139
  });
104
140
  opCtx?.onDispose(() => ctx.dispose());
105
141
 
106
- // Timeout/
107
142
  if (timeout !== 0) {
108
- scheduleTask(
109
- ctx,
110
- () => {
111
- log.warn('Notarization timeout', {
112
- timeout,
113
- peers: Array.from(this._extensions).map((extension) => extension.remotePeerId),
114
- });
115
- void ctx.dispose();
116
- errors.throw(new TimeoutError(timeout, 'Notarization timed out'));
117
- },
118
- timeout,
119
- );
143
+ this._scheduleTimeout(ctx, errors, timeout);
120
144
  }
121
145
 
122
146
  const allNotarized = Promise.all(credentials.map((credential) => this._waitUntilProcessed(credential.id!)));
123
147
 
148
+ this._tryNotarizeCredentialsWithPeers(ctx, credentials, { retryTimeout, successDelay });
149
+
150
+ if (this._edgeClient) {
151
+ this._tryNotarizeCredentialsWithEdge(ctx, this._edgeClient, credentials, {
152
+ retryTimeout,
153
+ successDelay,
154
+ jitter: edgeRetryJitter,
155
+ });
156
+ }
157
+
158
+ try {
159
+ await Promise.race([rejectOnDispose(ctx), allNotarized, errors.wait()]);
160
+ log('done');
161
+ } finally {
162
+ await ctx.dispose();
163
+ }
164
+ }
165
+
166
+ private _tryNotarizeCredentialsWithPeers(
167
+ ctx: Context,
168
+ credentials: Credential[],
169
+ { retryTimeout, successDelay }: NotarizationTimeouts,
170
+ ) {
124
171
  const peersTried = new Set<NotarizationTeleportExtension>();
125
172
 
126
173
  // Repeatable task that tries to notarize credentials with one of the available peers.
@@ -145,6 +192,7 @@ export class NotarizationPlugin implements CredentialProcessor {
145
192
  credentials: credentials.filter((credential) => !this._processedCredentials.has(credential.id!)),
146
193
  });
147
194
  log('success');
195
+
148
196
  await sleep(successDelay); // wait before trying with a new peer
149
197
  } catch (err: any) {
150
198
  if (!ctx.disposed && !err.message.includes(WRITER_NOT_SET_ERROR_CODE)) {
@@ -156,13 +204,31 @@ export class NotarizationPlugin implements CredentialProcessor {
156
204
 
157
205
  notarizeTask.schedule();
158
206
  this._extensionOpened.on(ctx, () => notarizeTask.schedule());
207
+ }
159
208
 
160
- try {
161
- await Promise.race([rejectOnDispose(ctx), allNotarized, errors.wait()]);
162
- log('done');
163
- } finally {
164
- await ctx.dispose();
165
- }
209
+ private _tryNotarizeCredentialsWithEdge(
210
+ ctx: Context,
211
+ client: EdgeHttpClient,
212
+ credentials: Credential[],
213
+ timeouts: NotarizationTimeouts & { jitter?: number },
214
+ ) {
215
+ const encodedCredentials = credentials.map((credential) => {
216
+ const binary = credentialCodec.encode(credential);
217
+ return Buffer.from(binary).toString('base64');
218
+ });
219
+ scheduleTask(ctx, async () => {
220
+ try {
221
+ await client.notarizeCredentials(
222
+ this._spaceId,
223
+ { credentials: encodedCredentials },
224
+ { retry: { count: MAX_EDGE_RETRIES, timeout: timeouts.retryTimeout, jitter: timeouts.jitter } },
225
+ );
226
+
227
+ log('edge notarization success');
228
+ } catch (error: any) {
229
+ handleEdgeError(error);
230
+ }
231
+ });
166
232
  }
167
233
 
168
234
  /**
@@ -180,6 +246,44 @@ export class NotarizationPlugin implements CredentialProcessor {
180
246
  setWriter(writer: FeedWriter<Credential>) {
181
247
  invariant(!this._writer, 'Writer already set.');
182
248
  this._writer = writer;
249
+ if (this._edgeClient) {
250
+ this._notarizePendingEdgeCredentials(this._edgeClient, writer);
251
+ }
252
+ }
253
+
254
+ /**
255
+ * The method is used only for adding agent feeds to spaces.
256
+ * When an agent is created we can admit them into all the existing spaces. In case the operation fails
257
+ * this method will fix it on the next space open.
258
+ * Given how rarely this happens there's no need to poll the endpoint.
259
+ */
260
+ private _notarizePendingEdgeCredentials(client: EdgeHttpClient, writer: FeedWriter<Credential>) {
261
+ scheduleMicroTask(this._ctx, async () => {
262
+ try {
263
+ const response = await client.getCredentialsForNotarization(this._spaceId, {
264
+ retry: { count: MAX_EDGE_RETRIES },
265
+ });
266
+
267
+ const credentials = response.awaitingNotarization.credentials;
268
+ if (!credentials.length) {
269
+ log('edge did not return credentials for notarization');
270
+ return;
271
+ }
272
+
273
+ log('got edge credentials for notarization', { count: credentials.length });
274
+
275
+ const decodedCredentials = credentials.map((credential) => {
276
+ const binary = Buffer.from(credential, 'base64');
277
+ return credentialCodec.decode(binary);
278
+ });
279
+
280
+ await this._notarizeCredentials(writer, decodedCredentials);
281
+
282
+ log.info('notarized edge credentials', { count: decodedCredentials.length });
283
+ } catch (error: any) {
284
+ handleEdgeError(error);
285
+ }
286
+ });
183
287
  }
184
288
 
185
289
  private async _waitUntilProcessed(id: PublicKey) {
@@ -196,12 +300,20 @@ export class NotarizationPlugin implements CredentialProcessor {
196
300
  if (!this._writer) {
197
301
  throw new Error(WRITER_NOT_SET_ERROR_CODE);
198
302
  }
199
- for (const credential of request.credentials ?? []) {
303
+ await this._notarizeCredentials(this._writer, request.credentials ?? []);
304
+ }
305
+
306
+ private async _notarizeCredentials(writer: FeedWriter<Credential>, credentials: Credential[]) {
307
+ for (const credential of credentials) {
200
308
  invariant(credential.id, 'Credential must have an id');
201
309
  if (this._processedCredentials.has(credential.id)) {
202
310
  continue;
203
311
  }
204
- await this._writer.write(credential);
312
+ const verificationResult = await verifyCredential(credential);
313
+ if (verificationResult.kind === 'fail') {
314
+ throw new Error(`Credential verification failed: ${verificationResult.errors.join('\n')}.`);
315
+ }
316
+ await writer.write(credential);
205
317
  }
206
318
  }
207
319
 
@@ -220,8 +332,31 @@ export class NotarizationPlugin implements CredentialProcessor {
220
332
  });
221
333
  return extension;
222
334
  }
335
+
336
+ private _scheduleTimeout(ctx: Context, errors: Trigger, timeout: number) {
337
+ scheduleTask(
338
+ ctx,
339
+ () => {
340
+ log.warn('Notarization timeout', {
341
+ timeout,
342
+ peers: Array.from(this._extensions).map((extension) => extension.remotePeerId),
343
+ });
344
+ void ctx.dispose();
345
+ errors.throw(new TimeoutError(timeout, 'Notarization timed out'));
346
+ },
347
+ timeout,
348
+ );
349
+ }
223
350
  }
224
351
 
352
+ const handleEdgeError = (error: any) => {
353
+ if (!(error instanceof EdgeCallFailedError) || error.errorData) {
354
+ log.catch(error);
355
+ } else {
356
+ log.info('Edge notarization failure', { reason: error.reason });
357
+ }
358
+ };
359
+
225
360
  export type NotarizationTeleportExtensionParams = {
226
361
  onOpen: () => Promise<void>;
227
362
  onClose: () => Promise<void>;
@@ -261,6 +396,11 @@ export class NotarizationTeleportExtension extends RpcExtension<Services, Servic
261
396
  }
262
397
  }
263
398
 
399
+ type NotarizationTimeouts = {
400
+ retryTimeout: number;
401
+ successDelay: number;
402
+ };
403
+
264
404
  type Services = {
265
405
  NotarizationService: NotarizationService;
266
406
  };
@@ -2,21 +2,17 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import chai, { expect } from 'chai';
6
- import chaiAsPromised from 'chai-as-promised';
5
+ import { afterEach, onTestFinished, beforeEach, describe, expect, test } from 'vitest';
7
6
 
8
7
  import { Trigger } from '@dxos/async';
9
8
  import { Context } from '@dxos/context';
10
9
  import { PublicKey } from '@dxos/keys';
11
10
  import { type Space, type SpacesService } from '@dxos/protocols/proto/dxos/client/services';
12
- import { afterEach, afterTest, beforeEach, describe, test } from '@dxos/test';
13
11
 
14
12
  import { SpacesServiceImpl } from './spaces-service';
15
13
  import { type ServiceContext } from '../services';
16
14
  import { createServiceContext } from '../testing';
17
15
 
18
- chai.use(chaiAsPromised);
19
-
20
16
  describe('SpacesService', () => {
21
17
  let serviceContext: ServiceContext;
22
18
  let spacesService: SpacesService;
@@ -36,7 +32,7 @@ describe('SpacesService', () => {
36
32
 
37
33
  describe('createSpace', () => {
38
34
  test('fails if no identity is available', async () => {
39
- await expect(spacesService.createSpace()).to.be.rejectedWith();
35
+ await expect(spacesService.createSpace()).rejects.toBeInstanceOf(Error);
40
36
  });
41
37
 
42
38
  test('creates a new space', async () => {
@@ -56,7 +52,7 @@ describe('SpacesService', () => {
56
52
  query.subscribe(({ spaces }) => {
57
53
  result.wake(spaces);
58
54
  });
59
- afterTest(() => query.close());
55
+ onTestFinished(() => query.close());
60
56
  expect(await result.wait()).to.be.length(0);
61
57
  });
62
58
 
@@ -73,7 +69,7 @@ describe('SpacesService', () => {
73
69
  query.subscribe(({ spaces }) => {
74
70
  result.wake(spaces);
75
71
  });
76
- afterTest(() => query.close());
72
+ onTestFinished(() => query.close());
77
73
 
78
74
  const spaces = await result.wait();
79
75
  expect(spaces).to.be.length(3);
@@ -87,7 +83,7 @@ describe('SpacesService', () => {
87
83
  query.subscribe(({ spaces }) => {
88
84
  result.wake(spaces);
89
85
  });
90
- afterTest(() => query.close());
86
+ onTestFinished(() => query.close());
91
87
  expect(await result.wait()).to.be.length(0);
92
88
 
93
89
  result.reset();
@@ -60,7 +60,7 @@ export class SpacesServiceImpl implements SpacesService {
60
60
  return this._serializeSpace(space);
61
61
  }
62
62
 
63
- async updateSpace({ spaceKey, state }: UpdateSpaceRequest) {
63
+ async updateSpace({ spaceKey, state, edgeReplication }: UpdateSpaceRequest) {
64
64
  const dataSpaceManager = await this._getDataSpaceManager();
65
65
  const space = dataSpaceManager.spaces.get(spaceKey) ?? raise(new SpaceNotFoundError(spaceKey));
66
66
 
@@ -77,6 +77,10 @@ export class SpacesServiceImpl implements SpacesService {
77
77
  throw new ApiError('Invalid space state');
78
78
  }
79
79
  }
80
+
81
+ if (edgeReplication !== undefined) {
82
+ await dataSpaceManager.setSpaceEdgeReplicationSetting(spaceKey, edgeReplication);
83
+ }
80
84
  }
81
85
 
82
86
  async updateMemberRole(request: UpdateMemberRoleRequest): Promise<void> {
@@ -308,6 +312,7 @@ export class SpacesServiceImpl implements SpacesService {
308
312
  creator: space.inner.spaceState.creator?.key,
309
313
  cache: space.cache,
310
314
  metrics: space.metrics,
315
+ edgeReplication: space.getEdgeReplicationSetting(),
311
316
  };
312
317
  }
313
318
 
@@ -14,7 +14,6 @@ import { getRootPath } from './util';
14
14
  // TODO(burdon): Factor out.
15
15
  export const createStorageObjects = (config: Runtime.Client.Storage) => {
16
16
  const { persistent = false, keyStore, dataStore } = config ?? {};
17
-
18
17
  if (persistent && dataStore === StorageDriver.RAM) {
19
18
  throw new InvalidConfigError('RAM storage cannot be used in persistent mode.');
20
19
  }
@@ -2,12 +2,11 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import { expect } from 'chai';
5
+ import { beforeEach, describe, expect, test } from 'vitest';
6
6
 
7
7
  import { Event, Trigger } from '@dxos/async';
8
8
  import { Config } from '@dxos/config';
9
9
  import { type SystemService, SystemStatus, type QueryStatusResponse } from '@dxos/protocols/proto/dxos/client/services';
10
- import { beforeEach, describe, test } from '@dxos/test';
11
10
 
12
11
  import { SystemServiceImpl } from './system-service';
13
12
 
@@ -6,8 +6,7 @@ import { type Config } from '@dxos/config';
6
6
  import { Context } from '@dxos/context';
7
7
  import { createCredentialSignerWithChain, CredentialGenerator } from '@dxos/credentials';
8
8
  import { failUndefined } from '@dxos/debug';
9
- import { EchoHost } from '@dxos/echo-db';
10
- import { MetadataStore, SpaceManager, valueEncoding, MeshEchoReplicator } from '@dxos/echo-pipeline';
9
+ import { EchoHost, MetadataStore, SpaceManager, valueEncoding, MeshEchoReplicator } from '@dxos/echo-pipeline';
11
10
  import { FeedFactory, FeedStore } from '@dxos/feed-store';
12
11
  import { Keyring } from '@dxos/keyring';
13
12
  import { type LevelDB } from '@dxos/kv-store';
@@ -54,8 +53,8 @@ export const createServiceContext = async ({
54
53
  const level = createTestLevel();
55
54
  await level.open();
56
55
 
57
- return new ServiceContext(storage, level, networkManager, signalManager, undefined, {
58
- invitationConnectionDefaultParams: { controlHeartbeatInterval: 200 },
56
+ return new ServiceContext(storage, level, networkManager, signalManager, undefined, undefined, {
57
+ invitationConnectionDefaultParams: { teleport: { controlHeartbeatInterval: 200 } },
59
58
  ...runtimeParams,
60
59
  });
61
60
  };
@@ -14,7 +14,7 @@ import {
14
14
  WebsocketSignalManager,
15
15
  setIdentityTags,
16
16
  } from '@dxos/messaging';
17
- import { SimplePeerTransportProxyFactory } from '@dxos/network-manager';
17
+ import { RtcTransportProxyFactory } from '@dxos/network-manager';
18
18
  import { type RpcPort } from '@dxos/rpc';
19
19
  import { type MaybePromise } from '@dxos/util';
20
20
 
@@ -46,7 +46,7 @@ export class WorkerRuntime {
46
46
  private readonly _acquireLock: () => Promise<void>;
47
47
  private readonly _releaseLock: () => void;
48
48
  private readonly _onStop?: () => Promise<void>;
49
- private readonly _transportFactory = new SimplePeerTransportProxyFactory();
49
+ private readonly _transportFactory = new RtcTransportProxyFactory();
50
50
  private readonly _ready = new Trigger<Error | undefined>();
51
51
  private readonly _sessions = new Set<WorkerSession>();
52
52
  private readonly _clientServices!: ClientServicesHost;
@@ -0,0 +1,11 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import { runTestSignalServer } from '@dxos/signal';
6
+
7
+ const port = 12004;
8
+
9
+ export default async () => {
10
+ await runTestSignalServer({ port, killExisting: true });
11
+ };
package/src/version.ts CHANGED
@@ -1,5 +1 @@
1
- //
2
- // Copyright 2023 DXOS.org
3
- //
4
-
5
- export const DXOS_VERSION = '0.6.12'; // {x-release-please-version}
1
+ export const DXOS_VERSION = "0.6.13-main.548ca8d";