@dxos/client-services 0.5.1-next.260c093 → 0.5.1-next.26de2f3

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 (61) hide show
  1. package/dist/lib/browser/{chunk-55TEJXLO.mjs → chunk-LPIE23D2.mjs} +1263 -907
  2. package/dist/lib/browser/chunk-LPIE23D2.mjs.map +7 -0
  3. package/dist/lib/browser/index.mjs +31 -2
  4. package/dist/lib/browser/index.mjs.map +3 -3
  5. package/dist/lib/browser/meta.json +1 -1
  6. package/dist/lib/browser/packlets/testing/index.mjs +28 -8
  7. package/dist/lib/browser/packlets/testing/index.mjs.map +3 -3
  8. package/dist/lib/node/{chunk-3ZUQNDPT.cjs → chunk-MVAX2L2C.cjs} +1417 -1069
  9. package/dist/lib/node/chunk-MVAX2L2C.cjs.map +7 -0
  10. package/dist/lib/node/index.cjs +73 -44
  11. package/dist/lib/node/index.cjs.map +3 -3
  12. package/dist/lib/node/meta.json +1 -1
  13. package/dist/lib/node/packlets/testing/index.cjs +34 -14
  14. package/dist/lib/node/packlets/testing/index.cjs.map +3 -3
  15. package/dist/types/src/packlets/invitations/device-invitation-protocol.d.ts +2 -1
  16. package/dist/types/src/packlets/invitations/device-invitation-protocol.d.ts.map +1 -1
  17. package/dist/types/src/packlets/invitations/invitation-guest-extenstion.d.ts +39 -0
  18. package/dist/types/src/packlets/invitations/invitation-guest-extenstion.d.ts.map +1 -0
  19. package/dist/types/src/packlets/invitations/{invitation-extension.d.ts → invitation-host-extension.d.ts} +17 -31
  20. package/dist/types/src/packlets/invitations/invitation-host-extension.d.ts.map +1 -0
  21. package/dist/types/src/packlets/invitations/invitation-protocol.d.ts +6 -1
  22. package/dist/types/src/packlets/invitations/invitation-protocol.d.ts.map +1 -1
  23. package/dist/types/src/packlets/invitations/invitation-topology.d.ts +37 -0
  24. package/dist/types/src/packlets/invitations/invitation-topology.d.ts.map +1 -0
  25. package/dist/types/src/packlets/invitations/invitations-handler.d.ts +19 -10
  26. package/dist/types/src/packlets/invitations/invitations-handler.d.ts.map +1 -1
  27. package/dist/types/src/packlets/invitations/invitations-handler.test.d.ts +2 -0
  28. package/dist/types/src/packlets/invitations/invitations-handler.test.d.ts.map +1 -0
  29. package/dist/types/src/packlets/invitations/invitations-manager.d.ts +2 -1
  30. package/dist/types/src/packlets/invitations/invitations-manager.d.ts.map +1 -1
  31. package/dist/types/src/packlets/invitations/space-invitation-protocol.d.ts +1 -0
  32. package/dist/types/src/packlets/invitations/space-invitation-protocol.d.ts.map +1 -1
  33. package/dist/types/src/packlets/invitations/utils.d.ts +6 -0
  34. package/dist/types/src/packlets/invitations/utils.d.ts.map +1 -0
  35. package/dist/types/src/packlets/services/service-context.d.ts +6 -3
  36. package/dist/types/src/packlets/services/service-context.d.ts.map +1 -1
  37. package/dist/types/src/packlets/testing/invitation-utils.d.ts +2 -1
  38. package/dist/types/src/packlets/testing/invitation-utils.d.ts.map +1 -1
  39. package/dist/types/src/packlets/testing/test-builder.d.ts.map +1 -1
  40. package/dist/types/src/packlets/vault/shell-runtime.d.ts +10 -2
  41. package/dist/types/src/packlets/vault/shell-runtime.d.ts.map +1 -1
  42. package/dist/types/src/version.d.ts +1 -1
  43. package/package.json +36 -36
  44. package/src/packlets/invitations/device-invitation-protocol.ts +5 -1
  45. package/src/packlets/invitations/invitation-guest-extenstion.ts +126 -0
  46. package/src/packlets/invitations/{invitation-extension.ts → invitation-host-extension.ts} +99 -105
  47. package/src/packlets/invitations/invitation-protocol.ts +7 -1
  48. package/src/packlets/invitations/invitation-topology.ts +87 -0
  49. package/src/packlets/invitations/invitations-handler.test.ts +361 -0
  50. package/src/packlets/invitations/invitations-handler.ts +246 -149
  51. package/src/packlets/invitations/invitations-manager.ts +42 -3
  52. package/src/packlets/invitations/space-invitation-protocol.ts +19 -1
  53. package/src/packlets/invitations/utils.ts +27 -0
  54. package/src/packlets/services/service-context.ts +5 -3
  55. package/src/packlets/testing/invitation-utils.ts +23 -3
  56. package/src/packlets/testing/test-builder.ts +3 -1
  57. package/src/packlets/vault/shell-runtime.ts +40 -2
  58. package/src/version.ts +1 -1
  59. package/dist/lib/browser/chunk-55TEJXLO.mjs.map +0 -7
  60. package/dist/lib/node/chunk-3ZUQNDPT.cjs.map +0 -7
  61. package/dist/types/src/packlets/invitations/invitation-extension.d.ts.map +0 -1
@@ -4,6 +4,7 @@
4
4
 
5
5
  import {
6
6
  createAdmissionCredentials,
7
+ createCancelDelegatedSpaceInvitationCredential,
7
8
  createDelegatedSpaceInvitationCredential,
8
9
  getCredentialAssertion,
9
10
  } from '@dxos/credentials';
@@ -87,7 +88,7 @@ export class SpaceInvitationProtocol implements InvitationProtocol {
87
88
 
88
89
  async delegate(invitation: Invitation): Promise<PublicKey> {
89
90
  invariant(this._spaceKey);
90
- const space = await this._spaceManager.spaces.get(this._spaceKey);
91
+ const space = this._spaceManager.spaces.get(this._spaceKey);
91
92
  invariant(space);
92
93
  if (invitation.authMethod === Invitation.AuthMethod.KNOWN_PUBLIC_KEY) {
93
94
  invariant(invitation.guestKeypair?.publicKey);
@@ -118,6 +119,23 @@ export class SpaceInvitationProtocol implements InvitationProtocol {
118
119
  return credential.credential.credential.id!;
119
120
  }
120
121
 
122
+ async cancelDelegation(invitation: Invitation): Promise<void> {
123
+ invariant(this._spaceKey);
124
+ invariant(invitation.type === Invitation.Type.DELEGATED && invitation.delegationCredentialId);
125
+ const space = this._spaceManager.spaces.get(this._spaceKey);
126
+ invariant(space);
127
+
128
+ log('cancelling delegated space invitation', { host: this._signingContext.deviceKey, id: invitation.invitationId });
129
+ const credential = await createCancelDelegatedSpaceInvitationCredential(
130
+ this._signingContext.credentialSigner,
131
+ space.key,
132
+ invitation.delegationCredentialId,
133
+ );
134
+
135
+ invariant(credential.credential);
136
+ await writeMessages(space.inner.controlPipeline.writer, [credential]);
137
+ }
138
+
121
139
  checkInvitation(invitation: Partial<Invitation>) {
122
140
  if (invitation.spaceKey && this._spaceManager.spaces.has(invitation.spaceKey)) {
123
141
  return new AlreadyJoinedError('Already joined space.');
@@ -0,0 +1,27 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import { type Mutex, type MutexGuard } from '@dxos/async';
6
+ import { cancelWithContext, type Context, ContextDisposedError } from '@dxos/context';
7
+ import { Invitation } from '@dxos/protocols/proto/dxos/client/services';
8
+
9
+ export const stateToString = (state: Invitation.State): string => {
10
+ return Object.entries(Invitation.State).find(([key, val]) => val === state)?.[0] ?? 'unknown';
11
+ };
12
+
13
+ export const tryAcquireBeforeContextDisposed = async (ctx: Context, mutex: Mutex): Promise<MutexGuard> => {
14
+ let guard: MutexGuard | undefined;
15
+ return cancelWithContext(
16
+ ctx,
17
+ (async () => {
18
+ guard = await mutex.acquire();
19
+ if (ctx.disposed) {
20
+ guard.release();
21
+ guard = undefined;
22
+ throw new ContextDisposedError();
23
+ }
24
+ return guard;
25
+ })(),
26
+ );
27
+ };
@@ -21,6 +21,7 @@ import { Invitation } from '@dxos/protocols/proto/dxos/client/services';
21
21
  import type { FeedMessage } from '@dxos/protocols/proto/dxos/echo/feed';
22
22
  import { type Credential, type ProfileDocument } from '@dxos/protocols/proto/dxos/halo/credentials';
23
23
  import { type Storage } from '@dxos/random-access-storage';
24
+ import type { TeleportParams } from '@dxos/teleport';
24
25
  import { BlobStore } from '@dxos/teleport-extension-object-sync';
25
26
  import { trace as Trace } from '@dxos/tracing';
26
27
  import { safeInstanceof } from '@dxos/util';
@@ -40,7 +41,8 @@ import {
40
41
  import { InvitationsManager } from '../invitations/invitations-manager';
41
42
  import { DataSpaceManager, type DataSpaceManagerRuntimeParams, type SigningContext } from '../spaces';
42
43
 
43
- export type ServiceContextRuntimeParams = IdentityManagerRuntimeParams & DataSpaceManagerRuntimeParams;
44
+ export type ServiceContextRuntimeParams = IdentityManagerRuntimeParams &
45
+ DataSpaceManagerRuntimeParams & { invitationConnectionDefaultParams?: Partial<TeleportParams> };
44
46
  /**
45
47
  * Shared backend for all client services.
46
48
  */
@@ -81,7 +83,7 @@ export class ServiceContext extends Resource {
81
83
  public readonly level: LevelDB,
82
84
  public readonly networkManager: NetworkManager,
83
85
  public readonly signalManager: SignalManager,
84
- public readonly _runtimeParams?: IdentityManagerRuntimeParams & DataSpaceManagerRuntimeParams,
86
+ public readonly _runtimeParams?: ServiceContextRuntimeParams,
85
87
  ) {
86
88
  super();
87
89
 
@@ -123,7 +125,7 @@ export class ServiceContext extends Resource {
123
125
  storage: this.storage,
124
126
  });
125
127
 
126
- this.invitations = new InvitationsHandler(this.networkManager);
128
+ this.invitations = new InvitationsHandler(this.networkManager, _runtimeParams?.invitationConnectionDefaultParams);
127
129
  this.invitationsManager = new InvitationsManager(
128
130
  this.invitations,
129
131
  (invitation) => this.getInvitationHandler(invitation),
@@ -45,6 +45,7 @@ export type PerformInvitationParams = {
45
45
  guest?: PerformInvitationCallbacks<AuthenticatingInvitation>;
46
46
  };
47
47
  guestDeviceProfile?: DeviceProfileDocument;
48
+ codeInputDelay?: number;
48
49
  };
49
50
 
50
51
  export type Result = { invitation?: Invitation; error?: Error };
@@ -55,7 +56,10 @@ export const performInvitation = ({
55
56
  options,
56
57
  hooks,
57
58
  guestDeviceProfile,
59
+ codeInputDelay,
58
60
  }: PerformInvitationParams): [Promise<Result>, Promise<Result>] => {
61
+ let guestError = false;
62
+ let guestConnected = false;
59
63
  const hostComplete = new Trigger<Result>();
60
64
  const guestComplete = new Trigger<Result>();
61
65
  const authCode = new Trigger<string>();
@@ -65,6 +69,10 @@ export const performInvitation = ({
65
69
  async (hostInvitation: Invitation) => {
66
70
  switch (hostInvitation.state) {
67
71
  case Invitation.State.CONNECTING: {
72
+ if (guestConnected) {
73
+ break;
74
+ }
75
+ guestConnected = true;
68
76
  if (hooks?.host?.onConnecting?.(hostObservable)) {
69
77
  break;
70
78
  }
@@ -89,7 +97,16 @@ export const performInvitation = ({
89
97
  if (hooks?.guest?.onReady?.(guestObservable)) {
90
98
  break;
91
99
  }
92
- await guestObservable.authenticate(await authCode.wait());
100
+ const code = await authCode.wait();
101
+ if (codeInputDelay == null) {
102
+ await guestObservable.authenticate(code);
103
+ } else {
104
+ setTimeout(async () => {
105
+ if (!guestError) {
106
+ await guestObservable.authenticate(code);
107
+ }
108
+ }, codeInputDelay);
109
+ }
93
110
  break;
94
111
  }
95
112
 
@@ -123,6 +140,7 @@ export const performInvitation = ({
123
140
  }
124
141
  },
125
142
  (error: Error) => {
143
+ guestError = true;
126
144
  if (hooks?.guest?.onError?.(guestObservable)) {
127
145
  return;
128
146
  }
@@ -216,8 +234,10 @@ const acceptInvitation = (
216
234
  invitation = sanitizeInvitation(invitation);
217
235
 
218
236
  if (guest instanceof ServiceContext) {
219
- const guestHandler = guest.getInvitationHandler({ kind: invitation.kind });
220
- return guest.invitations.acceptInvitation(guestHandler, invitation, guestDeviceProfile);
237
+ return guest.invitationsManager.acceptInvitation({
238
+ invitation,
239
+ deviceProfile: guestDeviceProfile,
240
+ });
221
241
  }
222
242
 
223
243
  return guest.join(invitation, guestDeviceProfile);
@@ -49,7 +49,9 @@ export const createServiceContext = async ({
49
49
  const level = createTestLevel();
50
50
  await level.open();
51
51
 
52
- return new ServiceContext(storage, level, networkManager, signalManager);
52
+ return new ServiceContext(storage, level, networkManager, signalManager, {
53
+ invitationConnectionDefaultParams: { controlHeartbeatInterval: 200 },
54
+ });
53
55
  };
54
56
 
55
57
  export const createPeers = async (numPeers: number) => {
@@ -6,7 +6,12 @@ import { Event } from '@dxos/async';
6
6
  import { appServiceBundle, type AppServiceBundle, type ShellRuntime, shellServiceBundle } from '@dxos/client-protocol';
7
7
  import { invariant } from '@dxos/invariant';
8
8
  import { type PublicKey } from '@dxos/keys';
9
- import { type AppContextRequest, type LayoutRequest, ShellLayout } from '@dxos/protocols/proto/dxos/iframe';
9
+ import {
10
+ type AppContextRequest,
11
+ type LayoutRequest,
12
+ ShellLayout,
13
+ type InvitationUrlRequest,
14
+ } from '@dxos/protocols/proto/dxos/iframe';
10
15
  import { createProtoRpcPeer, type ProtoRpcPeer, type RpcPort } from '@dxos/rpc';
11
16
 
12
17
  /**
@@ -14,11 +19,19 @@ import { createProtoRpcPeer, type ProtoRpcPeer, type RpcPort } from '@dxos/rpc';
14
19
  */
15
20
  export class ShellRuntimeImpl implements ShellRuntime {
16
21
  readonly layoutUpdate = new Event<LayoutRequest>();
22
+ readonly invitationUrlUpdate = new Event<InvitationUrlRequest>();
23
+
17
24
  private _appRpc?: ProtoRpcPeer<AppServiceBundle>;
18
25
  private _layout = ShellLayout.DEFAULT;
19
- private _invitationCode?: string;
20
26
  private _spaceKey?: PublicKey;
21
27
 
28
+ private _invitationCode?: string;
29
+ private _invitationUrl? = typeof window !== 'undefined' ? window.location.origin : undefined;
30
+
31
+ // TODO(burdon): Change to using underscores (coordinate with @dxos/web-auth).
32
+ private _deviceInvitationParam = 'deviceInvitationCode'; // TODO(burdon): device_invitation_code
33
+ private _spaceInvitationParam = 'spaceInvitationCode'; // TODO(burdon): space_invitation_code
34
+
22
35
  constructor(private readonly _port: RpcPort) {}
23
36
 
24
37
  get layout() {
@@ -33,6 +46,18 @@ export class ShellRuntimeImpl implements ShellRuntime {
33
46
  return this._spaceKey;
34
47
  }
35
48
 
49
+ get invitationUrl() {
50
+ return this._invitationUrl!;
51
+ }
52
+
53
+ get deviceInvitationParam() {
54
+ return this._deviceInvitationParam;
55
+ }
56
+
57
+ get spaceInvitationParam() {
58
+ return this._spaceInvitationParam;
59
+ }
60
+
36
61
  setLayout({ layout, invitationCode, spaceKey }: LayoutRequest) {
37
62
  this._layout = layout;
38
63
  this._invitationCode = invitationCode;
@@ -40,6 +65,13 @@ export class ShellRuntimeImpl implements ShellRuntime {
40
65
  this.layoutUpdate.emit({ layout, invitationCode, spaceKey });
41
66
  }
42
67
 
68
+ setInvitationUrl({ invitationUrl, deviceInvitationParam, spaceInvitationParam }: InvitationUrlRequest) {
69
+ this._invitationUrl = invitationUrl;
70
+ this._deviceInvitationParam = deviceInvitationParam;
71
+ this._spaceInvitationParam = spaceInvitationParam;
72
+ this.invitationUrlUpdate.emit({ invitationUrl, deviceInvitationParam, spaceInvitationParam });
73
+ }
74
+
43
75
  async setAppContext(context: AppContextRequest) {
44
76
  invariant(this._appRpc, 'runtime not open');
45
77
 
@@ -58,6 +90,12 @@ export class ShellRuntimeImpl implements ShellRuntime {
58
90
  this._spaceKey = request.spaceKey;
59
91
  this.layoutUpdate.emit(request);
60
92
  },
93
+ setInvitationUrl: async (request) => {
94
+ this._invitationUrl = request.invitationUrl;
95
+ this._deviceInvitationParam = request.deviceInvitationParam;
96
+ this._spaceInvitationParam = request.spaceInvitationParam;
97
+ this.invitationUrlUpdate.emit(request);
98
+ },
61
99
  },
62
100
  },
63
101
  port: this._port,
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const DXOS_VERSION = "0.5.1-next.260c093";
1
+ export const DXOS_VERSION = "0.5.1-next.26de2f3";