@dxos/plugin-client 0.10.0 → 0.11.0
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.
- package/dist/lib/ClientPlugin.mjs +65 -0
- package/dist/lib/ClientPlugin.mjs.map +1 -0
- package/dist/lib/ClientPlugin.node.mjs +904 -0
- package/dist/lib/ClientPlugin.node.mjs.map +1 -0
- package/dist/lib/ClientPlugin.workerd.mjs +10 -0
- package/dist/lib/ClientPlugin.workerd.mjs.map +1 -0
- package/dist/lib/capabilities/node.mjs +13 -0
- package/dist/lib/capabilities/node.mjs.map +1 -0
- package/dist/lib/capabilities.mjs +23 -0
- package/dist/lib/capabilities.mjs.map +1 -0
- package/dist/lib/chunk-AccountContainer.mjs +190 -0
- package/dist/lib/chunk-AccountContainer.mjs.map +1 -0
- package/dist/lib/chunk-DevicesContainer.mjs +275 -0
- package/dist/lib/chunk-DevicesContainer.mjs.map +1 -0
- package/dist/lib/chunk-InvitationsContainer.mjs +149 -0
- package/dist/lib/chunk-InvitationsContainer.mjs.map +1 -0
- package/dist/lib/chunk-JoinDialog.mjs +35 -0
- package/dist/lib/chunk-JoinDialog.mjs.map +1 -0
- package/dist/lib/chunk-ProfileContainer.mjs +145 -0
- package/dist/lib/chunk-ProfileContainer.mjs.map +1 -0
- package/dist/lib/chunk-RecoveryCodeDialog.mjs +72 -0
- package/dist/lib/chunk-RecoveryCodeDialog.mjs.map +1 -0
- package/dist/lib/chunk-RecoveryCredentialsContainer.mjs +65 -0
- package/dist/lib/chunk-RecoveryCredentialsContainer.mjs.map +1 -0
- package/dist/lib/chunk-ResetDialog.mjs +47 -0
- package/dist/lib/chunk-ResetDialog.mjs.map +1 -0
- package/dist/lib/chunk-UsageContainer.mjs +217 -0
- package/dist/lib/chunk-UsageContainer.mjs.map +1 -0
- package/dist/lib/{neutral/account-cache-YVQKLNV2.mjs → chunk-account-cache.mjs} +10 -12
- package/dist/lib/chunk-account-cache.mjs.map +1 -0
- package/dist/lib/chunk-app-graph-builder.mjs +111 -0
- package/dist/lib/chunk-app-graph-builder.mjs.map +1 -0
- package/dist/lib/chunk-client.mjs +90 -0
- package/dist/lib/chunk-client.mjs.map +1 -0
- package/dist/lib/{neutral/chunk-GSKMAS3S.mjs → chunk-constants.mjs} +5 -8
- package/dist/lib/chunk-constants.mjs.map +1 -0
- package/dist/lib/chunk-create-agent.mjs +23 -0
- package/dist/lib/chunk-create-agent.mjs.map +1 -0
- package/dist/lib/chunk-create-identity.mjs +28 -0
- package/dist/lib/chunk-create-identity.mjs.map +1 -0
- package/dist/lib/chunk-create-passkey.mjs +85 -0
- package/dist/lib/chunk-create-passkey.mjs.map +1 -0
- package/dist/lib/chunk-create-recovery-code.mjs +31 -0
- package/dist/lib/chunk-create-recovery-code.mjs.map +1 -0
- package/dist/lib/chunk-hooks.mjs +29 -0
- package/dist/lib/chunk-hooks.mjs.map +1 -0
- package/dist/lib/chunk-hub-http-client.mjs +14 -0
- package/dist/lib/chunk-hub-http-client.mjs.map +1 -0
- package/dist/lib/chunk-join-identity.mjs +20 -0
- package/dist/lib/chunk-join-identity.mjs.map +1 -0
- package/dist/lib/chunk-layer-specs.mjs +107 -0
- package/dist/lib/chunk-layer-specs.mjs.map +1 -0
- package/dist/lib/{neutral/chunk-6G4KZLBG.mjs → chunk-meta.mjs} +13 -20
- package/dist/lib/chunk-meta.mjs.map +1 -0
- package/dist/lib/chunk-migrations.mjs +29 -0
- package/dist/lib/chunk-migrations.mjs.map +1 -0
- package/dist/lib/chunk-navigation-handler.mjs +71 -0
- package/dist/lib/chunk-navigation-handler.mjs.map +1 -0
- package/dist/lib/chunk-navigation-target-loader.mjs +46 -0
- package/dist/lib/chunk-navigation-target-loader.mjs.map +1 -0
- package/dist/lib/chunk-open-usage.mjs +14 -0
- package/dist/lib/chunk-open-usage.mjs.map +1 -0
- package/dist/lib/chunk-operation-handler.mjs +11 -0
- package/dist/lib/chunk-operation-handler.mjs.map +1 -0
- package/dist/lib/chunk-operations.mjs +212 -0
- package/dist/lib/chunk-operations.mjs.map +1 -0
- package/dist/lib/chunk-progress.mjs +10 -0
- package/dist/lib/chunk-progress.mjs.map +1 -0
- package/dist/lib/chunk-react-context.mjs +30 -0
- package/dist/lib/chunk-react-context.mjs.map +1 -0
- package/dist/lib/chunk-react-surface.mjs +70 -0
- package/dist/lib/chunk-react-surface.mjs.map +1 -0
- package/dist/lib/chunk-recover-identity.mjs +17 -0
- package/dist/lib/chunk-recover-identity.mjs.map +1 -0
- package/dist/lib/chunk-redeem-passkey.mjs +72 -0
- package/dist/lib/chunk-redeem-passkey.mjs.map +1 -0
- package/dist/lib/chunk-redeem-token.mjs +24 -0
- package/dist/lib/chunk-redeem-token.mjs.map +1 -0
- package/dist/lib/chunk-remote-trace-monitor.mjs +37 -0
- package/dist/lib/chunk-remote-trace-monitor.mjs.map +1 -0
- package/dist/lib/chunk-reset-storage.mjs +17 -0
- package/dist/lib/chunk-reset-storage.mjs.map +1 -0
- package/dist/lib/chunk-resolve-navigation-targets.mjs +32 -0
- package/dist/lib/chunk-resolve-navigation-targets.mjs.map +1 -0
- package/dist/lib/chunk-schema-defs.mjs +53 -0
- package/dist/lib/chunk-schema-defs.mjs.map +1 -0
- package/dist/lib/chunk-share-identity.mjs +16 -0
- package/dist/lib/chunk-share-identity.mjs.map +1 -0
- package/dist/lib/chunk-space-replication-progress.mjs +94 -0
- package/dist/lib/chunk-space-replication-progress.mjs.map +1 -0
- package/dist/lib/chunk-trace-progress.mjs +69 -0
- package/dist/lib/chunk-trace-progress.mjs.map +1 -0
- package/dist/lib/chunk-update-profile.mjs +15 -0
- package/dist/lib/chunk-update-profile.mjs.map +1 -0
- package/dist/lib/components.mjs +0 -0
- package/dist/lib/containers.mjs +15 -0
- package/dist/lib/containers.mjs.map +1 -0
- package/dist/lib/index.mjs +27 -0
- package/dist/lib/index.mjs.map +1 -0
- package/dist/lib/meta.mjs +2 -0
- package/dist/lib/operations.mjs +2 -0
- package/dist/lib/plugin.mjs +9 -0
- package/dist/lib/plugin.mjs.map +1 -0
- package/dist/lib/testing.mjs +21 -0
- package/dist/lib/testing.mjs.map +1 -0
- package/dist/lib/translations.mjs +111 -0
- package/dist/lib/translations.mjs.map +1 -0
- package/dist/lib/types.mjs +139 -0
- package/dist/lib/types.mjs.map +1 -0
- package/dist/types/src/ClientPlugin.d.ts.map +1 -1
- package/dist/types/src/capabilities/account-cache.d.ts +1 -1
- package/dist/types/src/capabilities/app-graph-builder.d.ts +1 -1
- package/dist/types/src/capabilities/client.d.ts +1 -1
- package/dist/types/src/capabilities/client.d.ts.map +1 -1
- package/dist/types/src/capabilities/hub-http-client.d.ts +1 -1
- package/dist/types/src/capabilities/index.d.ts +6 -1
- package/dist/types/src/capabilities/index.d.ts.map +1 -1
- package/dist/types/src/capabilities/layer-specs.d.ts +1 -1
- package/dist/types/src/capabilities/migrations.d.ts +1 -1
- package/dist/types/src/capabilities/navigation-handler/navigation-handler.d.ts +3 -1
- package/dist/types/src/capabilities/navigation-handler/navigation-handler.d.ts.map +1 -1
- package/dist/types/src/capabilities/navigation-target-loader.d.ts +11 -0
- package/dist/types/src/capabilities/navigation-target-loader.d.ts.map +1 -0
- package/dist/types/src/capabilities/node.d.ts +2 -1
- package/dist/types/src/capabilities/node.d.ts.map +1 -1
- package/dist/types/src/capabilities/operation-handler.d.ts +1 -1
- package/dist/types/src/capabilities/react-context.d.ts +1 -1
- package/dist/types/src/capabilities/react-context.d.ts.map +1 -1
- package/dist/types/src/capabilities/react-surface.d.ts +1 -1
- package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
- package/dist/types/src/capabilities/remote-trace-monitor.d.ts +15 -0
- package/dist/types/src/capabilities/remote-trace-monitor.d.ts.map +1 -0
- package/dist/types/src/capabilities/schema-defs.d.ts +1 -1
- package/dist/types/src/capabilities/space-replication-progress.d.ts +11 -0
- package/dist/types/src/capabilities/space-replication-progress.d.ts.map +1 -0
- package/dist/types/src/capabilities/trace-progress.d.ts +17 -0
- package/dist/types/src/capabilities/trace-progress.d.ts.map +1 -0
- package/dist/types/src/commands/account/login/login.d.ts +2 -1
- package/dist/types/src/commands/account/login/login.d.ts.map +1 -1
- package/dist/types/src/commands/account/logout/logout.d.ts +1 -1
- package/dist/types/src/commands/account/logout/logout.d.ts.map +1 -1
- package/dist/types/src/commands/halo/index.d.ts.map +1 -1
- package/dist/types/src/commands/halo/share/share.d.ts.map +1 -1
- package/dist/types/src/commands/profile/create.d.ts.map +1 -1
- package/dist/types/src/containers/AccountContainer/AccountContainer.d.ts +4 -1
- package/dist/types/src/containers/AccountContainer/AccountContainer.d.ts.map +1 -1
- package/dist/types/src/containers/AccountContainer/AccountContainer.stories.d.ts +4 -1
- package/dist/types/src/containers/AccountContainer/AccountContainer.stories.d.ts.map +1 -1
- package/dist/types/src/containers/DevicesContainer/DevicesContainer.d.ts +4 -1
- package/dist/types/src/containers/DevicesContainer/DevicesContainer.d.ts.map +1 -1
- package/dist/types/src/containers/DevicesContainer/DevicesContainer.stories.d.ts +4 -1
- package/dist/types/src/containers/DevicesContainer/DevicesContainer.stories.d.ts.map +1 -1
- package/dist/types/src/containers/InvitationsContainer/InvitationsContainer.d.ts +4 -1
- package/dist/types/src/containers/InvitationsContainer/InvitationsContainer.d.ts.map +1 -1
- package/dist/types/src/containers/InvitationsContainer/InvitationsContainer.stories.d.ts +4 -1
- package/dist/types/src/containers/InvitationsContainer/InvitationsContainer.stories.d.ts.map +1 -1
- package/dist/types/src/containers/JoinDialog/JoinDialog.d.ts +4 -1
- package/dist/types/src/containers/JoinDialog/JoinDialog.d.ts.map +1 -1
- package/dist/types/src/containers/JoinDialog/JoinDialog.stories.d.ts +4 -1
- package/dist/types/src/containers/JoinDialog/JoinDialog.stories.d.ts.map +1 -1
- package/dist/types/src/containers/ProfileContainer/ProfileContainer.d.ts +4 -1
- package/dist/types/src/containers/ProfileContainer/ProfileContainer.d.ts.map +1 -1
- package/dist/types/src/containers/ProfileContainer/ProfileContainer.stories.d.ts +4 -1
- package/dist/types/src/containers/ProfileContainer/ProfileContainer.stories.d.ts.map +1 -1
- package/dist/types/src/containers/RecoveryCodeDialog/RecoveryCodeDialog.d.ts +4 -1
- package/dist/types/src/containers/RecoveryCodeDialog/RecoveryCodeDialog.d.ts.map +1 -1
- package/dist/types/src/containers/RecoveryCredentialsContainer/RecoveryCredentialsContainer.d.ts +4 -1
- package/dist/types/src/containers/RecoveryCredentialsContainer/RecoveryCredentialsContainer.d.ts.map +1 -1
- package/dist/types/src/containers/RecoveryCredentialsContainer/RecoveryCredentialsContainer.stories.d.ts +4 -1
- package/dist/types/src/containers/RecoveryCredentialsContainer/RecoveryCredentialsContainer.stories.d.ts.map +1 -1
- package/dist/types/src/containers/ResetDialog/ResetDialog.d.ts +4 -1
- package/dist/types/src/containers/ResetDialog/ResetDialog.d.ts.map +1 -1
- package/dist/types/src/containers/ResetDialog/ResetDialog.stories.d.ts +4 -1
- package/dist/types/src/containers/ResetDialog/ResetDialog.stories.d.ts.map +1 -1
- package/dist/types/src/containers/UsageContainer/UsageContainer.d.ts +4 -5
- package/dist/types/src/containers/UsageContainer/UsageContainer.d.ts.map +1 -1
- package/dist/types/src/containers/UsageContainer/UsageView.d.ts +4 -7
- package/dist/types/src/containers/UsageContainer/UsageView.d.ts.map +1 -1
- package/dist/types/src/containers/UsageContainer/UsageView.stories.d.ts +4 -2
- package/dist/types/src/containers/UsageContainer/UsageView.stories.d.ts.map +1 -1
- package/dist/types/src/halo-services-layer.d.ts +19 -0
- package/dist/types/src/halo-services-layer.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +3 -0
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/operations/definitions.d.ts +9 -1
- package/dist/types/src/operations/definitions.d.ts.map +1 -1
- package/dist/types/src/operations/errors.d.ts +109 -0
- package/dist/types/src/operations/errors.d.ts.map +1 -0
- package/dist/types/src/operations/errors.test.d.ts +2 -0
- package/dist/types/src/operations/errors.test.d.ts.map +1 -0
- package/dist/types/src/operations/index.d.ts +1 -0
- package/dist/types/src/operations/index.d.ts.map +1 -1
- package/dist/types/src/operations/redeem-passkey.d.ts.map +1 -1
- package/dist/types/src/operations/resolve-navigation-targets.d.ts +5 -0
- package/dist/types/src/operations/resolve-navigation-targets.d.ts.map +1 -0
- package/dist/types/src/operations/update-profile.d.ts +5 -0
- package/dist/types/src/operations/update-profile.d.ts.map +1 -0
- package/dist/types/src/progress/index.d.ts +2 -0
- package/dist/types/src/progress/index.d.ts.map +1 -0
- package/dist/types/src/progress/progress-keys.d.ts +6 -0
- package/dist/types/src/progress/progress-keys.d.ts.map +1 -0
- package/dist/types/src/testing/initializeIdentity.d.ts +5 -1
- package/dist/types/src/testing/initializeIdentity.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types/capabilities.d.ts +10 -1
- package/dist/types/src/types/capabilities.d.ts.map +1 -1
- package/dist/types/src/types/schema.d.ts +6 -0
- package/dist/types/src/types/schema.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +60 -55
- package/src/ClientPlugin.ts +25 -2
- package/src/capabilities/app-graph-builder.ts +1 -1
- package/src/capabilities/client.ts +26 -0
- package/src/capabilities/index.ts +10 -0
- package/src/capabilities/layer-specs.ts +64 -2
- package/src/capabilities/migrations.ts +9 -1
- package/src/capabilities/navigation-handler/navigation-handler.ts +20 -3
- package/src/capabilities/navigation-target-loader.ts +70 -0
- package/src/capabilities/react-context.tsx +20 -2
- package/src/capabilities/{react-surface.tsx → react-surface.ts} +14 -12
- package/src/capabilities/remote-trace-monitor.ts +62 -0
- package/src/capabilities/space-replication-progress.ts +143 -0
- package/src/capabilities/trace-progress.ts +84 -0
- package/src/commands/account/login/login.ts +48 -6
- package/src/commands/account/logout/logout.ts +4 -0
- package/src/commands/halo/index.ts +6 -2
- package/src/commands/halo/share/share.ts +16 -5
- package/src/commands/profile/create.ts +23 -40
- package/src/containers/AccountContainer/AccountContainer.tsx +6 -5
- package/src/containers/DevicesContainer/DevicesContainer.tsx +2 -0
- package/src/containers/InvitationsContainer/InvitationsContainer.tsx +2 -0
- package/src/containers/JoinDialog/JoinDialog.tsx +2 -0
- package/src/containers/ProfileContainer/ProfileContainer.tsx +19 -13
- package/src/containers/RecoveryCodeDialog/RecoveryCodeDialog.tsx +2 -0
- package/src/containers/RecoveryCredentialsContainer/RecoveryCredentialsContainer.tsx +7 -9
- package/src/containers/ResetDialog/ResetDialog.tsx +2 -0
- package/src/containers/UsageContainer/UsageContainer.tsx +10 -1
- package/src/containers/UsageContainer/UsageView.stories.tsx +0 -12
- package/src/containers/UsageContainer/UsageView.tsx +9 -10
- package/src/halo-services-layer.ts +29 -0
- package/src/index.ts +9 -0
- package/src/operations/definitions.ts +9 -1
- package/src/operations/errors.test.ts +27 -0
- package/src/operations/errors.ts +47 -0
- package/src/operations/index.ts +3 -0
- package/src/operations/open-usage.ts +3 -3
- package/src/operations/redeem-passkey.ts +85 -55
- package/src/operations/resolve-navigation-targets.ts +71 -0
- package/src/operations/share-identity.ts +3 -3
- package/src/operations/update-profile.ts +20 -0
- package/src/progress/index.ts +5 -0
- package/src/progress/progress-keys.ts +15 -0
- package/src/testing/initializeIdentity.ts +10 -2
- package/src/translations.ts +2 -0
- package/src/types/capabilities.ts +15 -1
- package/src/types/schema.ts +7 -0
- package/dist/lib/neutral/AccountContainer-7SF53UON.mjs +0 -186
- package/dist/lib/neutral/AccountContainer-7SF53UON.mjs.map +0 -7
- package/dist/lib/neutral/ClientPlugin.mjs +0 -72
- package/dist/lib/neutral/ClientPlugin.mjs.map +0 -7
- package/dist/lib/neutral/ClientPlugin.node.mjs +0 -1156
- package/dist/lib/neutral/ClientPlugin.node.mjs.map +0 -7
- package/dist/lib/neutral/ClientPlugin.workerd.mjs +0 -16
- package/dist/lib/neutral/ClientPlugin.workerd.mjs.map +0 -7
- package/dist/lib/neutral/DevicesContainer-PRWIN36N.mjs +0 -271
- package/dist/lib/neutral/DevicesContainer-PRWIN36N.mjs.map +0 -7
- package/dist/lib/neutral/InvitationsContainer-BI5RAWET.mjs +0 -143
- package/dist/lib/neutral/InvitationsContainer-BI5RAWET.mjs.map +0 -7
- package/dist/lib/neutral/JoinDialog-4BWV4CYB.mjs +0 -53
- package/dist/lib/neutral/JoinDialog-4BWV4CYB.mjs.map +0 -7
- package/dist/lib/neutral/ProfileContainer-YEAYOTX5.mjs +0 -169
- package/dist/lib/neutral/ProfileContainer-YEAYOTX5.mjs.map +0 -7
- package/dist/lib/neutral/RecoveryCodeDialog-7EPNKCLB.mjs +0 -59
- package/dist/lib/neutral/RecoveryCodeDialog-7EPNKCLB.mjs.map +0 -7
- package/dist/lib/neutral/RecoveryCredentialsContainer-6BZ2IWX7.mjs +0 -60
- package/dist/lib/neutral/RecoveryCredentialsContainer-6BZ2IWX7.mjs.map +0 -7
- package/dist/lib/neutral/ResetDialog-KUWX45JY.mjs +0 -57
- package/dist/lib/neutral/ResetDialog-KUWX45JY.mjs.map +0 -7
- package/dist/lib/neutral/UsageContainer-2DWCVVE4.mjs +0 -241
- package/dist/lib/neutral/UsageContainer-2DWCVVE4.mjs.map +0 -7
- package/dist/lib/neutral/account-cache-YVQKLNV2.mjs.map +0 -7
- package/dist/lib/neutral/app-graph-builder-RVXTD6OE.mjs +0 -159
- package/dist/lib/neutral/app-graph-builder-RVXTD6OE.mjs.map +0 -7
- package/dist/lib/neutral/capabilities/index.mjs +0 -34
- package/dist/lib/neutral/capabilities/index.mjs.map +0 -7
- package/dist/lib/neutral/capabilities/node.mjs +0 -21
- package/dist/lib/neutral/capabilities/node.mjs.map +0 -7
- package/dist/lib/neutral/chunk-6G4KZLBG.mjs.map +0 -7
- package/dist/lib/neutral/chunk-7DEIKHB7.mjs +0 -199
- package/dist/lib/neutral/chunk-7DEIKHB7.mjs.map +0 -7
- package/dist/lib/neutral/chunk-A7WDDIWY.mjs +0 -162
- package/dist/lib/neutral/chunk-A7WDDIWY.mjs.map +0 -7
- package/dist/lib/neutral/chunk-GSKMAS3S.mjs.map +0 -7
- package/dist/lib/neutral/chunk-J5LGTIGS.mjs +0 -10
- package/dist/lib/neutral/chunk-J5LGTIGS.mjs.map +0 -7
- package/dist/lib/neutral/chunk-JY35S2CN.mjs +0 -27
- package/dist/lib/neutral/chunk-JY35S2CN.mjs.map +0 -7
- package/dist/lib/neutral/chunk-ZFABGG2E.mjs +0 -8
- package/dist/lib/neutral/chunk-ZFABGG2E.mjs.map +0 -7
- package/dist/lib/neutral/client-7I5Y5SOA.mjs +0 -62
- package/dist/lib/neutral/client-7I5Y5SOA.mjs.map +0 -7
- package/dist/lib/neutral/components/index.mjs +0 -1
- package/dist/lib/neutral/components/index.mjs.map +0 -7
- package/dist/lib/neutral/containers/index.mjs +0 -25
- package/dist/lib/neutral/containers/index.mjs.map +0 -7
- package/dist/lib/neutral/create-agent-6UKJMZS5.mjs +0 -26
- package/dist/lib/neutral/create-agent-6UKJMZS5.mjs.map +0 -7
- package/dist/lib/neutral/create-identity-ULCRB3YH.mjs +0 -42
- package/dist/lib/neutral/create-identity-ULCRB3YH.mjs.map +0 -7
- package/dist/lib/neutral/create-passkey-I7VDS3AC.mjs +0 -81
- package/dist/lib/neutral/create-passkey-I7VDS3AC.mjs.map +0 -7
- package/dist/lib/neutral/create-recovery-code-DH2B6EGP.mjs +0 -36
- package/dist/lib/neutral/create-recovery-code-DH2B6EGP.mjs.map +0 -7
- package/dist/lib/neutral/hub-http-client-WOJUCDKV.mjs +0 -19
- package/dist/lib/neutral/hub-http-client-WOJUCDKV.mjs.map +0 -7
- package/dist/lib/neutral/index.mjs +0 -25
- package/dist/lib/neutral/index.mjs.map +0 -7
- package/dist/lib/neutral/join-identity-PJCHDNLH.mjs +0 -27
- package/dist/lib/neutral/join-identity-PJCHDNLH.mjs.map +0 -7
- package/dist/lib/neutral/layer-specs-YI7BVWKZ.mjs +0 -59
- package/dist/lib/neutral/layer-specs-YI7BVWKZ.mjs.map +0 -7
- package/dist/lib/neutral/meta.json +0 -1
- package/dist/lib/neutral/meta.mjs +0 -8
- package/dist/lib/neutral/meta.mjs.map +0 -7
- package/dist/lib/neutral/migrations-7FTW55ER.mjs +0 -23
- package/dist/lib/neutral/migrations-7FTW55ER.mjs.map +0 -7
- package/dist/lib/neutral/navigation-handler-MQ6MZXLX.mjs +0 -46
- package/dist/lib/neutral/navigation-handler-MQ6MZXLX.mjs.map +0 -7
- package/dist/lib/neutral/open-usage-OJVTPGOY.mjs +0 -27
- package/dist/lib/neutral/open-usage-OJVTPGOY.mjs.map +0 -7
- package/dist/lib/neutral/operation-handler-E3HB7E2T.mjs +0 -13
- package/dist/lib/neutral/operation-handler-E3HB7E2T.mjs.map +0 -7
- package/dist/lib/neutral/operations/index.mjs +0 -12
- package/dist/lib/neutral/operations/index.mjs.map +0 -7
- package/dist/lib/neutral/plugin.mjs +0 -17
- package/dist/lib/neutral/plugin.mjs.map +0 -7
- package/dist/lib/neutral/react-context-V74T6JOJ.mjs +0 -23
- package/dist/lib/neutral/react-context-V74T6JOJ.mjs.map +0 -7
- package/dist/lib/neutral/react-surface-HPXRA65U.mjs +0 -75
- package/dist/lib/neutral/react-surface-HPXRA65U.mjs.map +0 -7
- package/dist/lib/neutral/recover-identity-B7YM2BR4.mjs +0 -26
- package/dist/lib/neutral/recover-identity-B7YM2BR4.mjs.map +0 -7
- package/dist/lib/neutral/redeem-passkey-TYP4XLHO.mjs +0 -66
- package/dist/lib/neutral/redeem-passkey-TYP4XLHO.mjs.map +0 -7
- package/dist/lib/neutral/redeem-token-2J33R3PV.mjs +0 -29
- package/dist/lib/neutral/redeem-token-2J33R3PV.mjs.map +0 -7
- package/dist/lib/neutral/reset-storage-K47HZTGP.mjs +0 -26
- package/dist/lib/neutral/reset-storage-K47HZTGP.mjs.map +0 -7
- package/dist/lib/neutral/schema-defs-IHBCKTMM.mjs +0 -51
- package/dist/lib/neutral/schema-defs-IHBCKTMM.mjs.map +0 -7
- package/dist/lib/neutral/share-identity-TOROBLWL.mjs +0 -31
- package/dist/lib/neutral/share-identity-TOROBLWL.mjs.map +0 -7
- package/dist/lib/neutral/testing/index.mjs +0 -25
- package/dist/lib/neutral/testing/index.mjs.map +0 -7
- package/dist/lib/neutral/translations.mjs +0 -124
- package/dist/lib/neutral/translations.mjs.map +0 -7
- package/dist/lib/neutral/types/index.mjs +0 -16
- package/dist/lib/neutral/types/index.mjs.map +0 -7
|
@@ -5,15 +5,17 @@
|
|
|
5
5
|
import * as Schema from 'effect/Schema';
|
|
6
6
|
import React, { type ChangeEvent, useCallback, useMemo, useState } from 'react';
|
|
7
7
|
|
|
8
|
+
import { useOperationInvoker } from '@dxos/app-framework/ui';
|
|
8
9
|
import { debounce } from '@dxos/async';
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
10
|
+
import { type Identity } from '@dxos/halo';
|
|
11
|
+
import { useIdentity } from '@dxos/halo-react';
|
|
11
12
|
import { ButtonGroup, Clipboard, Input, useTranslation } from '@dxos/react-ui';
|
|
12
13
|
import { Form, type FormFieldMap, type FormUpdateMeta } from '@dxos/react-ui-form';
|
|
13
14
|
import { EmojiPickerBlock, HuePicker } from '@dxos/react-ui-pickers';
|
|
14
15
|
import { hexToEmoji, hexToHue } from '@dxos/util';
|
|
15
16
|
|
|
16
17
|
import { meta } from '#meta';
|
|
18
|
+
import { ClientOperation } from '#operations';
|
|
17
19
|
|
|
18
20
|
// TOOD(burdon): Factor out?
|
|
19
21
|
// TODO(wittjosiah): Integrate annotations with translations.
|
|
@@ -27,33 +29,35 @@ const UserProfile = Schema.Struct({
|
|
|
27
29
|
type UserProfile = Schema.Schema.Type<typeof UserProfile>;
|
|
28
30
|
|
|
29
31
|
// TODO(thure): Factor out?
|
|
30
|
-
const getDefaultHueValue = (identity
|
|
31
|
-
const getHueValue = (identity
|
|
32
|
-
const getDefaultEmojiValue = (identity
|
|
33
|
-
const getEmojiValue = (identity
|
|
32
|
+
const getDefaultHueValue = (identity?: Identity.Info): string => hexToHue(identity?.identityKey ?? '0');
|
|
33
|
+
const getHueValue = (identity?: Identity.Info): string => identity?.data?.hue || getDefaultHueValue(identity);
|
|
34
|
+
const getDefaultEmojiValue = (identity?: Identity.Info): string => hexToEmoji(identity?.identityKey ?? '0');
|
|
35
|
+
const getEmojiValue = (identity?: Identity.Info): string => identity?.data?.emoji || getDefaultEmojiValue(identity);
|
|
34
36
|
|
|
35
37
|
export const ProfileContainer = () => {
|
|
36
38
|
const { t } = useTranslation(meta.profile.key);
|
|
37
|
-
const
|
|
39
|
+
const { invokePromise } = useOperationInvoker();
|
|
38
40
|
const identity = useIdentity();
|
|
39
|
-
const [displayName, setDisplayNameDirectly] = useState(identity?.
|
|
41
|
+
const [displayName, setDisplayNameDirectly] = useState(identity?.displayName ?? '');
|
|
40
42
|
const [emoji, setEmojiDirectly] = useState<string>(getEmojiValue(identity));
|
|
41
43
|
const [hue, setHueDirectly] = useState<string>(getHueValue(identity));
|
|
42
44
|
|
|
43
45
|
const updateProfile = useMemo(
|
|
44
46
|
() =>
|
|
45
47
|
debounce(
|
|
46
|
-
|
|
47
|
-
|
|
48
|
+
// Merge onto the current profile data so unrelated metadata is preserved.
|
|
49
|
+
(profile: Partial<UserProfile>, currentData?: Record<string, unknown>) =>
|
|
50
|
+
invokePromise(ClientOperation.UpdateProfile, {
|
|
48
51
|
displayName: profile.displayName,
|
|
49
52
|
data: {
|
|
53
|
+
...currentData,
|
|
50
54
|
emoji: profile.emoji,
|
|
51
55
|
hue: profile.hue,
|
|
52
56
|
},
|
|
53
57
|
}),
|
|
54
58
|
2_000,
|
|
55
59
|
),
|
|
56
|
-
[],
|
|
60
|
+
[invokePromise],
|
|
57
61
|
);
|
|
58
62
|
|
|
59
63
|
const handleChange = useCallback(
|
|
@@ -76,9 +80,9 @@ export const ProfileContainer = () => {
|
|
|
76
80
|
}
|
|
77
81
|
}
|
|
78
82
|
|
|
79
|
-
void updateProfile(profile);
|
|
83
|
+
void updateProfile(profile, identity?.data);
|
|
80
84
|
},
|
|
81
|
-
[identity],
|
|
85
|
+
[identity, updateProfile],
|
|
82
86
|
);
|
|
83
87
|
|
|
84
88
|
const values = useMemo(
|
|
@@ -184,3 +188,5 @@ export const ProfileContainer = () => {
|
|
|
184
188
|
</Clipboard.Provider>
|
|
185
189
|
);
|
|
186
190
|
};
|
|
191
|
+
|
|
192
|
+
ProfileContainer.displayName = 'ProfileContainer';
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import React from 'react';
|
|
6
6
|
|
|
7
7
|
import { useOperationInvoker } from '@dxos/app-framework/ui';
|
|
8
|
-
import { useCredentials } from '@dxos/react
|
|
8
|
+
import { useCredentials } from '@dxos/halo-react';
|
|
9
9
|
import { Icon, IconButton, Message, useTranslation } from '@dxos/react-ui';
|
|
10
10
|
import { Form } from '@dxos/react-ui-form';
|
|
11
11
|
import { Listbox } from '@dxos/react-ui-list';
|
|
@@ -20,7 +20,7 @@ export const RecoveryCredentialsContainer = () => {
|
|
|
20
20
|
const { invokePromise } = useOperationInvoker();
|
|
21
21
|
const credentials = useCredentials();
|
|
22
22
|
const recoveryCredentials = credentials.filter(
|
|
23
|
-
(credential) => credential.
|
|
23
|
+
(credential) => credential.type === 'dxos.halo.credentials.IdentityRecovery',
|
|
24
24
|
);
|
|
25
25
|
|
|
26
26
|
return (
|
|
@@ -49,19 +49,15 @@ export const RecoveryCredentialsContainer = () => {
|
|
|
49
49
|
{recoveryCredentials.length < 1 ? (
|
|
50
50
|
<Message.Root valence='error'>
|
|
51
51
|
<Message.Title icon='ph--shield-warning--duotone'>{t('no-credentials.title')}</Message.Title>
|
|
52
|
-
<Message.
|
|
52
|
+
<Message.Body>{t('no-credentials.message')}</Message.Body>
|
|
53
53
|
</Message.Root>
|
|
54
54
|
) : (
|
|
55
55
|
<Listbox.Root>
|
|
56
56
|
<Listbox.Content classNames='gap-1'>
|
|
57
57
|
{recoveryCredentials.map((credential, index) => (
|
|
58
|
-
<Listbox.Item
|
|
59
|
-
key={credential.id?.toHex() ?? index}
|
|
60
|
-
id={credential.id?.toHex() ?? `${index}`}
|
|
61
|
-
classNames='gap-2'
|
|
62
|
-
>
|
|
58
|
+
<Listbox.Item key={credential.id ?? index} id={credential.id ?? `${index}`} classNames='gap-2'>
|
|
63
59
|
<Icon icon='ph--key--regular' />
|
|
64
|
-
<Listbox.ItemLabel>{credential.issuanceDate
|
|
60
|
+
<Listbox.ItemLabel>{credential.issuanceDate?.toLocaleString()}</Listbox.ItemLabel>
|
|
65
61
|
</Listbox.Item>
|
|
66
62
|
))}
|
|
67
63
|
</Listbox.Content>
|
|
@@ -73,3 +69,5 @@ export const RecoveryCredentialsContainer = () => {
|
|
|
73
69
|
</Form.Root>
|
|
74
70
|
);
|
|
75
71
|
};
|
|
72
|
+
|
|
73
|
+
RecoveryCredentialsContainer.displayName = 'RecoveryCredentialsContainer';
|
|
@@ -13,7 +13,14 @@ import { useHubHttpClient } from '../../hooks';
|
|
|
13
13
|
import { UsageView, type UsageViewState } from './UsageView';
|
|
14
14
|
|
|
15
15
|
/** State + payload kept together so `ready` always carries data (mirrors the discriminated `UsageViewProps`). */
|
|
16
|
-
type UsageFetchState =
|
|
16
|
+
type UsageFetchState =
|
|
17
|
+
| {
|
|
18
|
+
state: Exclude<UsageViewState, 'ready'>;
|
|
19
|
+
}
|
|
20
|
+
| {
|
|
21
|
+
state: 'ready';
|
|
22
|
+
data: GetProfileUsageResponse;
|
|
23
|
+
};
|
|
17
24
|
|
|
18
25
|
/**
|
|
19
26
|
* Fetches rolling-window profile usage from the hub service and renders it.
|
|
@@ -46,3 +53,5 @@ export const UsageContainer = () => {
|
|
|
46
53
|
|
|
47
54
|
return <UsageView {...fetchState} lastUpdated={lastUpdated} onRefresh={handleRefresh} />;
|
|
48
55
|
};
|
|
56
|
+
|
|
57
|
+
UsageContainer.displayName = 'UsageContainer';
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
6
|
-
import { expect, userEvent, within } from 'storybook/test';
|
|
7
6
|
|
|
8
7
|
import { type GetProfileUsageResponse } from '@dxos/protocols';
|
|
9
8
|
import { withLayout, withTheme } from '@dxos/react-ui/testing';
|
|
@@ -114,14 +113,3 @@ export const Empty: Story = {
|
|
|
114
113
|
export const Unavailable: Story = {
|
|
115
114
|
args: { state: 'unavailable' },
|
|
116
115
|
};
|
|
117
|
-
|
|
118
|
-
// Drives the raw-response disclosure: collapsed until toggled, then the JSON viewer appears.
|
|
119
|
-
export const Test: Story = {
|
|
120
|
-
args: Default.args,
|
|
121
|
-
play: async () => {
|
|
122
|
-
const screen = within(document.body);
|
|
123
|
-
await expect(screen.queryByTestId('usage-raw-json')).toBeNull();
|
|
124
|
-
await userEvent.click(await screen.findByRole('button', { name: 'Raw JSON' }, { timeout: 10_000 }));
|
|
125
|
-
await screen.findByTestId('usage-raw-json');
|
|
126
|
-
},
|
|
127
|
-
};
|
|
@@ -6,9 +6,8 @@ import * as Schema from 'effect/Schema';
|
|
|
6
6
|
import React, { useCallback, useMemo, useState } from 'react';
|
|
7
7
|
|
|
8
8
|
import { type GetProfileUsageResponse, type MeteringLimit, type MeteringUsageItem } from '@dxos/protocols';
|
|
9
|
-
import {
|
|
9
|
+
import { Message, Status, useTranslation } from '@dxos/react-ui';
|
|
10
10
|
import { Form, type FormFieldProvider } from '@dxos/react-ui-form';
|
|
11
|
-
import { JsonHighlighter } from '@dxos/react-ui-syntax-highlighter';
|
|
12
11
|
|
|
13
12
|
import { meta } from '#meta';
|
|
14
13
|
|
|
@@ -214,19 +213,17 @@ export const UsageView = ({ state, data, lastUpdated, onRefresh }: UsageViewProp
|
|
|
214
213
|
<Form.Viewport scroll>
|
|
215
214
|
<Form.Content>
|
|
216
215
|
<Form.Section title={t('usage-section.title')} description={t('usage-section.description')}>
|
|
217
|
-
{
|
|
218
|
-
<Status indeterminate aria-label={t('usage-section.title')} />
|
|
219
|
-
) : message ? (
|
|
216
|
+
{message ? (
|
|
220
217
|
<Message.Root valence={message.valence}>
|
|
221
218
|
<Message.Title icon={message.icon}>{t(message.title)}</Message.Title>
|
|
222
|
-
<Message.
|
|
219
|
+
<Message.Body>{t(message.description)}</Message.Body>
|
|
223
220
|
</Message.Root>
|
|
224
221
|
) : (
|
|
225
222
|
<Form.FieldSet fieldProvider={meterFieldProvider} />
|
|
226
223
|
)}
|
|
227
224
|
</Form.Section>
|
|
228
225
|
|
|
229
|
-
{state === 'ready' && data && (
|
|
226
|
+
{/* {state === 'ready' && data && (
|
|
230
227
|
<Form.Section>
|
|
231
228
|
<Form.Row
|
|
232
229
|
label={
|
|
@@ -246,9 +243,9 @@ export const UsageView = ({ state, data, lastUpdated, onRefresh }: UsageViewProp
|
|
|
246
243
|
)}
|
|
247
244
|
</Form.Row>
|
|
248
245
|
</Form.Section>
|
|
249
|
-
)}
|
|
246
|
+
)} */}
|
|
250
247
|
|
|
251
|
-
{state === 'ready' && data && (
|
|
248
|
+
{/* {state === 'ready' && data && (
|
|
252
249
|
<Form.Section>
|
|
253
250
|
<Form.Row label={t('usage-raw-json.label')}>
|
|
254
251
|
<ToggleIconButton
|
|
@@ -262,9 +259,11 @@ export const UsageView = ({ state, data, lastUpdated, onRefresh }: UsageViewProp
|
|
|
262
259
|
</Form.Row>
|
|
263
260
|
{rawExpanded && <JsonHighlighter data={data} testId='usage-raw-json' />}
|
|
264
261
|
</Form.Section>
|
|
265
|
-
)}
|
|
262
|
+
)} */}
|
|
266
263
|
</Form.Content>
|
|
267
264
|
</Form.Viewport>
|
|
268
265
|
</Form.Root>
|
|
269
266
|
);
|
|
270
267
|
};
|
|
268
|
+
|
|
269
|
+
UsageView.displayName = 'UsageView';
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Layer from 'effect/Layer';
|
|
6
|
+
|
|
7
|
+
import { Capability } from '@dxos/app-framework';
|
|
8
|
+
import { Identity, Space } from '@dxos/halo';
|
|
9
|
+
|
|
10
|
+
import { ClientCapabilities } from './types';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Provides the canonical `@dxos/halo` services (`Identity.Service` / `Space.Service`) from the
|
|
14
|
+
* client-backed capabilities contributed by this plugin. Consumers depend on the halo services
|
|
15
|
+
* directly and provide this layer, rather than reaching for the capability instances:
|
|
16
|
+
*
|
|
17
|
+
* ```ts
|
|
18
|
+
* yield* Identity.getSnapshot; // requires Identity.Service
|
|
19
|
+
* // provided by:
|
|
20
|
+
* effect.pipe(Effect.provide(HaloServicesLayer));
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* The layer itself requires `Capability.Service` (the capability manager), which is in scope inside
|
|
24
|
+
* capability modules and operation handlers.
|
|
25
|
+
*/
|
|
26
|
+
export const HaloServicesLayer: Layer.Layer<Identity.Service | Space.Service, never, Capability.Service> = Layer.merge(
|
|
27
|
+
Capability.layerWith(ClientCapabilities.IdentityService, (service) => Layer.succeed(Identity.Service, service)),
|
|
28
|
+
Capability.layerWith(ClientCapabilities.SpaceService, (service) => Layer.succeed(Space.Service, service)),
|
|
29
|
+
);
|
package/src/index.ts
CHANGED
|
@@ -3,5 +3,14 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
export { meta } from './meta';
|
|
6
|
+
export { HaloServicesLayer } from './halo-services-layer';
|
|
7
|
+
export * from './progress';
|
|
6
8
|
export * from './types';
|
|
7
9
|
export { ClientOperation } from './operations';
|
|
10
|
+
export {
|
|
11
|
+
PasskeyDismissedError,
|
|
12
|
+
type PasskeyFailure,
|
|
13
|
+
PasskeyLoginError,
|
|
14
|
+
PasskeyRejectedError,
|
|
15
|
+
classifyPasskeyFailure,
|
|
16
|
+
} from './operations/errors';
|
|
@@ -6,6 +6,7 @@ import * as Schema from 'effect/Schema';
|
|
|
6
6
|
|
|
7
7
|
import { Capability } from '@dxos/app-framework';
|
|
8
8
|
import { Operation } from '@dxos/compute';
|
|
9
|
+
import { Identity } from '@dxos/halo';
|
|
9
10
|
import { DXN, IdentityDid, SpaceId } from '@dxos/keys';
|
|
10
11
|
|
|
11
12
|
import { meta } from '#meta';
|
|
@@ -32,11 +33,18 @@ const ProfileSchema = Schema.Struct({
|
|
|
32
33
|
|
|
33
34
|
export const CreateIdentity = Operation.make({
|
|
34
35
|
meta: { key: makeKey('createIdentity'), name: 'Create Identity', icon: 'ph--user--regular' },
|
|
35
|
-
services: [Capability.Service],
|
|
36
|
+
services: [Capability.Service, Identity.Service],
|
|
36
37
|
input: ProfileSchema,
|
|
37
38
|
output: IdentitySchema,
|
|
38
39
|
});
|
|
39
40
|
|
|
41
|
+
export const UpdateProfile = Operation.make({
|
|
42
|
+
meta: { key: makeKey('updateProfile'), name: 'Update Profile', icon: 'ph--user--regular' },
|
|
43
|
+
services: [Identity.Service],
|
|
44
|
+
input: ProfileSchema,
|
|
45
|
+
output: Schema.Void,
|
|
46
|
+
});
|
|
47
|
+
|
|
40
48
|
export const JoinIdentity = Operation.make({
|
|
41
49
|
meta: { key: makeKey('joinIdentity'), name: 'Join Identity', icon: 'ph--sign-in--regular' },
|
|
42
50
|
services: [Capability.Service],
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { describe, expect, test } from 'vitest';
|
|
6
|
+
|
|
7
|
+
import { PasskeyDismissedError, classifyPasskeyFailure, toPasskeyAssertionError } from './errors';
|
|
8
|
+
|
|
9
|
+
describe('passkey errors', () => {
|
|
10
|
+
// The native (Tauri) bridge rejects with a plain string rather than a DOMException, and
|
|
11
|
+
// ASAuthorization spells it "canceled" while the web spells it "cancelled".
|
|
12
|
+
test.each(['ASAuthorizationError: the operation was canceled', 'Prompt cancelled by user'])(
|
|
13
|
+
'a native dismissal is recognised without a DOMException (%s)',
|
|
14
|
+
(rejection) => {
|
|
15
|
+
expect(PasskeyDismissedError.is(toPasskeyAssertionError(rejection))).to.be.true;
|
|
16
|
+
},
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
// The bug this guards: a failure the classifier doesn't recognise must still reach the user.
|
|
20
|
+
// A `switch` on the error tag without a default would regress to silence.
|
|
21
|
+
test.each([new Error('Recovery key not registered.'), 'plain string', undefined, null])(
|
|
22
|
+
'an unrecognised failure is still reported (%s)',
|
|
23
|
+
(error) => {
|
|
24
|
+
expect(classifyPasskeyFailure(error)).to.eq('failed');
|
|
25
|
+
},
|
|
26
|
+
);
|
|
27
|
+
});
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { BaseError } from '@dxos/errors';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The platform prompt produced no assertion. WebAuthn reports a dismissed prompt and
|
|
9
|
+
* "this device has no passkey for this site" identically, so the two cannot be told apart.
|
|
10
|
+
*/
|
|
11
|
+
export class PasskeyDismissedError extends BaseError.extend('PasskeyDismissedError', 'No passkey was presented') {}
|
|
12
|
+
|
|
13
|
+
/** The assertion was refused: the passkey is not registered as a recovery credential for any identity. */
|
|
14
|
+
export class PasskeyRejectedError extends BaseError.extend('PasskeyRejectedError', 'Passkey was not accepted') {}
|
|
15
|
+
|
|
16
|
+
/** Passkey login failed before an assertion could be checked (service unreachable, unusable authenticator response). */
|
|
17
|
+
export class PasskeyLoginError extends BaseError.extend('PasskeyLoginError', 'Passkey login failed') {}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Classify a rejection from the authenticator. WebAuthn reports a dismissed prompt and
|
|
21
|
+
* "no credential for this site" as the same `NotAllowedError`, so both map to dismissal;
|
|
22
|
+
* the native (Tauri) bridge rejects with a plain string rather than a `DOMException`.
|
|
23
|
+
*/
|
|
24
|
+
export const toPasskeyAssertionError = (error: unknown): PasskeyDismissedError | PasskeyLoginError => {
|
|
25
|
+
const name = error instanceof DOMException ? error.name : undefined;
|
|
26
|
+
if (name === 'NotAllowedError' || name === 'AbortError' || /cancell?ed/i.test(String(error))) {
|
|
27
|
+
return new PasskeyDismissedError({ cause: error });
|
|
28
|
+
}
|
|
29
|
+
return new PasskeyLoginError({ cause: error });
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/** Discriminates a passkey login failure so callers can pick a message without matching on error names. */
|
|
33
|
+
export type PasskeyFailure = 'dismissed' | 'rejected' | 'failed';
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Classify an error returned by the `RedeemPasskey` operation.
|
|
37
|
+
* Anything unrecognized is reported as a generic failure rather than swallowed.
|
|
38
|
+
*/
|
|
39
|
+
export const classifyPasskeyFailure = (error: unknown): PasskeyFailure => {
|
|
40
|
+
if (PasskeyDismissedError.is(error)) {
|
|
41
|
+
return 'dismissed';
|
|
42
|
+
}
|
|
43
|
+
if (PasskeyRejectedError.is(error)) {
|
|
44
|
+
return 'rejected';
|
|
45
|
+
}
|
|
46
|
+
return 'failed';
|
|
47
|
+
};
|
package/src/operations/index.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import { OperationHandlerSet } from '@dxos/compute';
|
|
6
6
|
|
|
7
7
|
export * as ClientOperation from './definitions';
|
|
8
|
+
export * from './errors';
|
|
8
9
|
|
|
9
10
|
export const ClientOperationHandlerSet = OperationHandlerSet.lazy(
|
|
10
11
|
() => import('./create-agent'),
|
|
@@ -17,5 +18,7 @@ export const ClientOperationHandlerSet = OperationHandlerSet.lazy(
|
|
|
17
18
|
() => import('./redeem-passkey'),
|
|
18
19
|
() => import('./redeem-token'),
|
|
19
20
|
() => import('./reset-storage'),
|
|
21
|
+
() => import('./resolve-navigation-targets'),
|
|
20
22
|
() => import('./share-identity'),
|
|
23
|
+
() => import('./update-profile'),
|
|
21
24
|
);
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import * as Effect from 'effect/Effect';
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import { GraphPath, LayoutOperation } from '@dxos/app-toolkit';
|
|
8
8
|
import { Operation } from '@dxos/compute';
|
|
9
9
|
|
|
10
10
|
import { Account } from '../types';
|
|
@@ -13,9 +13,9 @@ import { OpenUsage } from './definitions';
|
|
|
13
13
|
const handler: Operation.WithHandler<typeof OpenUsage> = OpenUsage.pipe(
|
|
14
14
|
Operation.withHandler(
|
|
15
15
|
Effect.fnUntraced(function* () {
|
|
16
|
-
yield* Operation.invoke(LayoutOperation.SwitchWorkspace, { subject:
|
|
16
|
+
yield* Operation.invoke(LayoutOperation.SwitchWorkspace, { subject: GraphPath.getSpacePath(Account.id) });
|
|
17
17
|
yield* Operation.invoke(LayoutOperation.Open, {
|
|
18
|
-
subject: [
|
|
18
|
+
subject: [GraphPath.getSpacePath(Account.id, Account.Usage)],
|
|
19
19
|
});
|
|
20
20
|
}),
|
|
21
21
|
),
|
|
@@ -13,69 +13,99 @@ import { invariant } from '@dxos/invariant';
|
|
|
13
13
|
|
|
14
14
|
import { ClientCapabilities } from '../types';
|
|
15
15
|
import { RedeemPasskey } from './definitions';
|
|
16
|
+
import { PasskeyDismissedError, PasskeyLoginError, PasskeyRejectedError, toPasskeyAssertionError } from './errors';
|
|
17
|
+
|
|
18
|
+
/** Signed challenge presented to EDGE in exchange for admitting this device. */
|
|
19
|
+
type Assertion = {
|
|
20
|
+
lookupKey: PublicKey;
|
|
21
|
+
signature: Buffer;
|
|
22
|
+
clientDataJson: Buffer;
|
|
23
|
+
authenticatorData: Buffer;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const nativeAssertion = (challenge: string): Effect.Effect<Assertion, PasskeyDismissedError | PasskeyLoginError> =>
|
|
27
|
+
Effect.gen(function* () {
|
|
28
|
+
const result = yield* Effect.tryPromise({
|
|
29
|
+
try: () => NativePasskey.loginNativePasskey({ challenge: Uint8Array.from(Buffer.from(challenge, 'base64')) }),
|
|
30
|
+
catch: toPasskeyAssertionError,
|
|
31
|
+
});
|
|
32
|
+
if (!result?.user_handle || !result.signature) {
|
|
33
|
+
return yield* Effect.fail(new PasskeyLoginError({ message: 'Native passkey login returned no assertion.' }));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
lookupKey: PublicKey.from(NativePasskey.decodeUrlSafeBase64(result.user_handle)),
|
|
38
|
+
signature: Buffer.from(NativePasskey.decodeUrlSafeBase64(result.signature)),
|
|
39
|
+
clientDataJson: Buffer.from(NativePasskey.decodeUrlSafeBase64(result.client_data_json)),
|
|
40
|
+
authenticatorData: Buffer.from(NativePasskey.decodeUrlSafeBase64(result.authenticator_data)),
|
|
41
|
+
};
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const webAssertion = (challenge: string): Effect.Effect<Assertion, PasskeyDismissedError | PasskeyLoginError> =>
|
|
45
|
+
Effect.gen(function* () {
|
|
46
|
+
if (typeof PublicKeyCredential === 'undefined') {
|
|
47
|
+
return yield* Effect.fail(new PasskeyLoginError({ message: 'WebAuthn is not available in this browser.' }));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const credential = yield* Effect.tryPromise({
|
|
51
|
+
try: () =>
|
|
52
|
+
navigator.credentials.get({
|
|
53
|
+
publicKey: {
|
|
54
|
+
challenge: Buffer.from(challenge, 'base64'),
|
|
55
|
+
rpId: location.hostname,
|
|
56
|
+
userVerification: 'required',
|
|
57
|
+
},
|
|
58
|
+
}),
|
|
59
|
+
catch: toPasskeyAssertionError,
|
|
60
|
+
});
|
|
61
|
+
// A null credential means the authenticator resolved without presenting one; same signal as a dismissal.
|
|
62
|
+
if (
|
|
63
|
+
!(credential instanceof PublicKeyCredential) ||
|
|
64
|
+
!(credential.response instanceof AuthenticatorAssertionResponse)
|
|
65
|
+
) {
|
|
66
|
+
return yield* Effect.fail(new PasskeyDismissedError({ message: 'No passkey assertion was returned.' }));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const { signature, clientDataJSON, authenticatorData, userHandle } = credential.response;
|
|
70
|
+
// The user handle carries the lookup key; a passkey created without a resident key has none.
|
|
71
|
+
if (!userHandle) {
|
|
72
|
+
return yield* Effect.fail(new PasskeyLoginError({ message: 'Passkey assertion has no user handle.' }));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
lookupKey: PublicKey.from(new Uint8Array(userHandle)),
|
|
77
|
+
signature: Buffer.from(signature),
|
|
78
|
+
clientDataJson: Buffer.from(clientDataJSON),
|
|
79
|
+
authenticatorData: Buffer.from(authenticatorData),
|
|
80
|
+
};
|
|
81
|
+
});
|
|
16
82
|
|
|
17
83
|
const handler: Operation.WithHandler<typeof RedeemPasskey> = RedeemPasskey.pipe(
|
|
18
84
|
Operation.withHandler(
|
|
19
85
|
Effect.fnUntraced(function* () {
|
|
20
86
|
const client = yield* Capability.get(ClientCapabilities.Client);
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
client.services.services.IdentityService!.requestRecoveryChallenge(),
|
|
24
|
-
);
|
|
87
|
+
const identityService = client.services.services.IdentityService;
|
|
88
|
+
invariant(identityService, 'IdentityService not available');
|
|
25
89
|
|
|
26
|
-
const {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
Effect.gen(function* () {
|
|
31
|
-
const result = yield* Effect.promise(() =>
|
|
32
|
-
NativePasskey.loginNativePasskey({ challenge: Uint8Array.from(Buffer.from(challenge, 'base64')) }),
|
|
33
|
-
);
|
|
34
|
-
invariant(result, 'Native passkey login returned no result');
|
|
35
|
-
return {
|
|
36
|
-
lookupKey: PublicKey.from(NativePasskey.decodeUrlSafeBase64(result.user_handle)),
|
|
37
|
-
signature: Buffer.from(NativePasskey.decodeUrlSafeBase64(result.signature)),
|
|
38
|
-
clientDataJson: Buffer.from(NativePasskey.decodeUrlSafeBase64(result.client_data_json)),
|
|
39
|
-
authenticatorData: Buffer.from(NativePasskey.decodeUrlSafeBase64(result.authenticator_data)),
|
|
40
|
-
};
|
|
41
|
-
}),
|
|
42
|
-
),
|
|
43
|
-
Match.orElse(() =>
|
|
44
|
-
Effect.gen(function* () {
|
|
45
|
-
const credential = yield* Effect.promise(() =>
|
|
46
|
-
navigator.credentials.get({
|
|
47
|
-
publicKey: {
|
|
48
|
-
challenge: Buffer.from(challenge, 'base64'),
|
|
49
|
-
rpId: location.hostname,
|
|
50
|
-
userVerification: 'required',
|
|
51
|
-
},
|
|
52
|
-
}),
|
|
53
|
-
);
|
|
54
|
-
return {
|
|
55
|
-
lookupKey: PublicKey.from(new Uint8Array((credential as any).response.userHandle)),
|
|
56
|
-
signature: Buffer.from((credential as any).response.signature),
|
|
57
|
-
clientDataJson: Buffer.from((credential as any).response.clientDataJSON),
|
|
58
|
-
authenticatorData: Buffer.from((credential as any).response.authenticatorData),
|
|
59
|
-
};
|
|
60
|
-
}),
|
|
61
|
-
),
|
|
62
|
-
);
|
|
90
|
+
const { deviceKey, controlFeedKey, challenge } = yield* Effect.tryPromise({
|
|
91
|
+
try: () => identityService.requestRecoveryChallenge(),
|
|
92
|
+
catch: PasskeyLoginError.wrap({ message: 'Failed to request a recovery challenge.' }),
|
|
93
|
+
});
|
|
63
94
|
|
|
64
|
-
yield*
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
external: {
|
|
68
|
-
lookupKey,
|
|
69
|
-
deviceKey,
|
|
70
|
-
controlFeedKey,
|
|
71
|
-
signature,
|
|
72
|
-
clientDataJson,
|
|
73
|
-
authenticatorData,
|
|
74
|
-
},
|
|
75
|
-
},
|
|
76
|
-
{ timeout: RECOVER_IDENTITY_RPC_TIMEOUT },
|
|
77
|
-
),
|
|
95
|
+
const assertion = yield* Match.value(NativePasskey.supportsNativePasskeys()).pipe(
|
|
96
|
+
Match.when(true, () => nativeAssertion(challenge)),
|
|
97
|
+
Match.orElse(() => webAssertion(challenge)),
|
|
78
98
|
);
|
|
99
|
+
|
|
100
|
+
// EDGE refuses the assertion when the passkey isn't registered as a recovery credential.
|
|
101
|
+
yield* Effect.tryPromise({
|
|
102
|
+
try: () =>
|
|
103
|
+
identityService.recoverIdentity(
|
|
104
|
+
{ external: { ...assertion, deviceKey, controlFeedKey } },
|
|
105
|
+
{ timeout: RECOVER_IDENTITY_RPC_TIMEOUT },
|
|
106
|
+
),
|
|
107
|
+
catch: PasskeyRejectedError.wrap(),
|
|
108
|
+
});
|
|
79
109
|
}),
|
|
80
110
|
),
|
|
81
111
|
);
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Effect from 'effect/Effect';
|
|
6
|
+
|
|
7
|
+
import { Capability } from '@dxos/app-framework';
|
|
8
|
+
import { AppCapabilities, AppSpace, NavigationOperation } from '@dxos/app-toolkit';
|
|
9
|
+
import { Operation } from '@dxos/compute';
|
|
10
|
+
import { Database, EID } from '@dxos/echo';
|
|
11
|
+
import { Position } from '@dxos/util';
|
|
12
|
+
|
|
13
|
+
import { ClientCapabilities } from '../types';
|
|
14
|
+
|
|
15
|
+
const handler: Operation.WithHandler<typeof NavigationOperation.ResolveNavigationTargets> =
|
|
16
|
+
NavigationOperation.ResolveNavigationTargets.pipe(
|
|
17
|
+
Operation.withHandler(
|
|
18
|
+
Effect.fn(function* ({ query }) {
|
|
19
|
+
const capabilities = yield* Capability.Service;
|
|
20
|
+
const client = yield* Capability.get(ClientCapabilities.Client).pipe(
|
|
21
|
+
Effect.catchAll(() => Effect.succeed(undefined)),
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
// Resolvers read a space database to derive a navigation path, so this handler lives with the
|
|
25
|
+
// client: it is the only place that can turn a URI's space id into a database to provide. When
|
|
26
|
+
// the query names a space that is not available, resolve nothing — falling back to the active
|
|
27
|
+
// space would resolve the id against the wrong database (a ref can reach across spaces). The
|
|
28
|
+
// active space serves only space-less queries (`dxn:` URIs, local EIDs, no query).
|
|
29
|
+
const eid = query?.uri ? EID.tryParse(query.uri) : undefined;
|
|
30
|
+
const spaceId = eid ? EID.getSpaceId(eid) : undefined;
|
|
31
|
+
const space = client
|
|
32
|
+
? spaceId
|
|
33
|
+
? client.spaces.get(spaceId)
|
|
34
|
+
: AppSpace.getActiveSpace(client, capabilities)
|
|
35
|
+
: undefined;
|
|
36
|
+
|
|
37
|
+
// Resolvers require `Database.Service`, and an unbound service is a defect rather than a failure
|
|
38
|
+
// (so the `catchAll` below would not contain it) — with no space to derive a database from there
|
|
39
|
+
// is nothing to resolve against, so skip them entirely.
|
|
40
|
+
if (!space) {
|
|
41
|
+
return { targets: [] };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const resolvers = capabilities.getAll(AppCapabilities.NavigationTargetResolver);
|
|
45
|
+
const results = yield* Effect.forEach(resolvers, (resolver) =>
|
|
46
|
+
resolver(query).pipe(Effect.catchAll(() => Effect.succeed([]))),
|
|
47
|
+
).pipe(Effect.provide(Database.layer(space.db)));
|
|
48
|
+
|
|
49
|
+
// Best-first, as the operation's output promises. Sort is stable, so resolvers that declare no
|
|
50
|
+
// position keep contribution order; `position` itself is resolver metadata and not returned.
|
|
51
|
+
// Two resolvers can name the same path (the generic type-section lookup and a type's own
|
|
52
|
+
// resolver), so paths are deduped keeping the best-placed occurrence.
|
|
53
|
+
const seen = new Set<string>();
|
|
54
|
+
return {
|
|
55
|
+
targets: results
|
|
56
|
+
.flat()
|
|
57
|
+
.sort(Position.compare)
|
|
58
|
+
.filter(({ path }) => {
|
|
59
|
+
if (seen.has(path)) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
seen.add(path);
|
|
63
|
+
return true;
|
|
64
|
+
})
|
|
65
|
+
.map(({ path, label, type }) => ({ path, label, type })),
|
|
66
|
+
};
|
|
67
|
+
}),
|
|
68
|
+
),
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
export default handler;
|