@dxos/plugin-client 0.10.0 → 0.11.1

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 (358) hide show
  1. package/dist/lib/ClientPlugin.mjs +65 -0
  2. package/dist/lib/ClientPlugin.mjs.map +1 -0
  3. package/dist/lib/ClientPlugin.node.mjs +904 -0
  4. package/dist/lib/ClientPlugin.node.mjs.map +1 -0
  5. package/dist/lib/ClientPlugin.workerd.mjs +10 -0
  6. package/dist/lib/ClientPlugin.workerd.mjs.map +1 -0
  7. package/dist/lib/capabilities/node.mjs +13 -0
  8. package/dist/lib/capabilities/node.mjs.map +1 -0
  9. package/dist/lib/capabilities.mjs +23 -0
  10. package/dist/lib/capabilities.mjs.map +1 -0
  11. package/dist/lib/chunk-AccountContainer.mjs +190 -0
  12. package/dist/lib/chunk-AccountContainer.mjs.map +1 -0
  13. package/dist/lib/chunk-DevicesContainer.mjs +275 -0
  14. package/dist/lib/chunk-DevicesContainer.mjs.map +1 -0
  15. package/dist/lib/chunk-InvitationsContainer.mjs +149 -0
  16. package/dist/lib/chunk-InvitationsContainer.mjs.map +1 -0
  17. package/dist/lib/chunk-JoinDialog.mjs +35 -0
  18. package/dist/lib/chunk-JoinDialog.mjs.map +1 -0
  19. package/dist/lib/chunk-ProfileContainer.mjs +145 -0
  20. package/dist/lib/chunk-ProfileContainer.mjs.map +1 -0
  21. package/dist/lib/chunk-RecoveryCodeDialog.mjs +72 -0
  22. package/dist/lib/chunk-RecoveryCodeDialog.mjs.map +1 -0
  23. package/dist/lib/chunk-RecoveryCredentialsContainer.mjs +65 -0
  24. package/dist/lib/chunk-RecoveryCredentialsContainer.mjs.map +1 -0
  25. package/dist/lib/chunk-ResetDialog.mjs +47 -0
  26. package/dist/lib/chunk-ResetDialog.mjs.map +1 -0
  27. package/dist/lib/chunk-UsageContainer.mjs +217 -0
  28. package/dist/lib/chunk-UsageContainer.mjs.map +1 -0
  29. package/dist/lib/{neutral/account-cache-YVQKLNV2.mjs → chunk-account-cache.mjs} +10 -12
  30. package/dist/lib/chunk-account-cache.mjs.map +1 -0
  31. package/dist/lib/chunk-app-graph-builder.mjs +111 -0
  32. package/dist/lib/chunk-app-graph-builder.mjs.map +1 -0
  33. package/dist/lib/chunk-client.mjs +90 -0
  34. package/dist/lib/chunk-client.mjs.map +1 -0
  35. package/dist/lib/{neutral/chunk-GSKMAS3S.mjs → chunk-constants.mjs} +5 -8
  36. package/dist/lib/chunk-constants.mjs.map +1 -0
  37. package/dist/lib/chunk-create-agent.mjs +23 -0
  38. package/dist/lib/chunk-create-agent.mjs.map +1 -0
  39. package/dist/lib/chunk-create-identity.mjs +28 -0
  40. package/dist/lib/chunk-create-identity.mjs.map +1 -0
  41. package/dist/lib/chunk-create-passkey.mjs +85 -0
  42. package/dist/lib/chunk-create-passkey.mjs.map +1 -0
  43. package/dist/lib/chunk-create-recovery-code.mjs +31 -0
  44. package/dist/lib/chunk-create-recovery-code.mjs.map +1 -0
  45. package/dist/lib/chunk-hooks.mjs +29 -0
  46. package/dist/lib/chunk-hooks.mjs.map +1 -0
  47. package/dist/lib/chunk-hub-http-client.mjs +14 -0
  48. package/dist/lib/chunk-hub-http-client.mjs.map +1 -0
  49. package/dist/lib/chunk-join-identity.mjs +20 -0
  50. package/dist/lib/chunk-join-identity.mjs.map +1 -0
  51. package/dist/lib/chunk-layer-specs.mjs +107 -0
  52. package/dist/lib/chunk-layer-specs.mjs.map +1 -0
  53. package/dist/lib/{neutral/chunk-6G4KZLBG.mjs → chunk-meta.mjs} +13 -20
  54. package/dist/lib/chunk-meta.mjs.map +1 -0
  55. package/dist/lib/chunk-migrations.mjs +29 -0
  56. package/dist/lib/chunk-migrations.mjs.map +1 -0
  57. package/dist/lib/chunk-navigation-handler.mjs +71 -0
  58. package/dist/lib/chunk-navigation-handler.mjs.map +1 -0
  59. package/dist/lib/chunk-navigation-target-loader.mjs +46 -0
  60. package/dist/lib/chunk-navigation-target-loader.mjs.map +1 -0
  61. package/dist/lib/chunk-open-usage.mjs +14 -0
  62. package/dist/lib/chunk-open-usage.mjs.map +1 -0
  63. package/dist/lib/chunk-operation-handler.mjs +11 -0
  64. package/dist/lib/chunk-operation-handler.mjs.map +1 -0
  65. package/dist/lib/chunk-operations.mjs +212 -0
  66. package/dist/lib/chunk-operations.mjs.map +1 -0
  67. package/dist/lib/chunk-progress.mjs +10 -0
  68. package/dist/lib/chunk-progress.mjs.map +1 -0
  69. package/dist/lib/chunk-react-context.mjs +30 -0
  70. package/dist/lib/chunk-react-context.mjs.map +1 -0
  71. package/dist/lib/chunk-react-surface.mjs +70 -0
  72. package/dist/lib/chunk-react-surface.mjs.map +1 -0
  73. package/dist/lib/chunk-recover-identity.mjs +17 -0
  74. package/dist/lib/chunk-recover-identity.mjs.map +1 -0
  75. package/dist/lib/chunk-redeem-passkey.mjs +72 -0
  76. package/dist/lib/chunk-redeem-passkey.mjs.map +1 -0
  77. package/dist/lib/chunk-redeem-token.mjs +24 -0
  78. package/dist/lib/chunk-redeem-token.mjs.map +1 -0
  79. package/dist/lib/chunk-remote-trace-monitor.mjs +37 -0
  80. package/dist/lib/chunk-remote-trace-monitor.mjs.map +1 -0
  81. package/dist/lib/chunk-reset-storage.mjs +17 -0
  82. package/dist/lib/chunk-reset-storage.mjs.map +1 -0
  83. package/dist/lib/chunk-resolve-navigation-targets.mjs +32 -0
  84. package/dist/lib/chunk-resolve-navigation-targets.mjs.map +1 -0
  85. package/dist/lib/chunk-schema-defs.mjs +53 -0
  86. package/dist/lib/chunk-schema-defs.mjs.map +1 -0
  87. package/dist/lib/chunk-share-identity.mjs +16 -0
  88. package/dist/lib/chunk-share-identity.mjs.map +1 -0
  89. package/dist/lib/chunk-space-replication-progress.mjs +94 -0
  90. package/dist/lib/chunk-space-replication-progress.mjs.map +1 -0
  91. package/dist/lib/chunk-trace-progress.mjs +69 -0
  92. package/dist/lib/chunk-trace-progress.mjs.map +1 -0
  93. package/dist/lib/chunk-update-profile.mjs +15 -0
  94. package/dist/lib/chunk-update-profile.mjs.map +1 -0
  95. package/dist/lib/components.mjs +0 -0
  96. package/dist/lib/containers.mjs +15 -0
  97. package/dist/lib/containers.mjs.map +1 -0
  98. package/dist/lib/index.mjs +27 -0
  99. package/dist/lib/index.mjs.map +1 -0
  100. package/dist/lib/meta.mjs +2 -0
  101. package/dist/lib/operations.mjs +2 -0
  102. package/dist/lib/plugin.mjs +9 -0
  103. package/dist/lib/plugin.mjs.map +1 -0
  104. package/dist/lib/testing.mjs +21 -0
  105. package/dist/lib/testing.mjs.map +1 -0
  106. package/dist/lib/translations.mjs +111 -0
  107. package/dist/lib/translations.mjs.map +1 -0
  108. package/dist/lib/types.mjs +139 -0
  109. package/dist/lib/types.mjs.map +1 -0
  110. package/dist/types/src/ClientPlugin.d.ts.map +1 -1
  111. package/dist/types/src/capabilities/account-cache.d.ts +1 -1
  112. package/dist/types/src/capabilities/app-graph-builder.d.ts +1 -1
  113. package/dist/types/src/capabilities/client.d.ts +1 -1
  114. package/dist/types/src/capabilities/client.d.ts.map +1 -1
  115. package/dist/types/src/capabilities/hub-http-client.d.ts +1 -1
  116. package/dist/types/src/capabilities/index.d.ts +6 -1
  117. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  118. package/dist/types/src/capabilities/layer-specs.d.ts +1 -1
  119. package/dist/types/src/capabilities/migrations.d.ts +1 -1
  120. package/dist/types/src/capabilities/navigation-handler/navigation-handler.d.ts +3 -1
  121. package/dist/types/src/capabilities/navigation-handler/navigation-handler.d.ts.map +1 -1
  122. package/dist/types/src/capabilities/navigation-target-loader.d.ts +11 -0
  123. package/dist/types/src/capabilities/navigation-target-loader.d.ts.map +1 -0
  124. package/dist/types/src/capabilities/node.d.ts +2 -1
  125. package/dist/types/src/capabilities/node.d.ts.map +1 -1
  126. package/dist/types/src/capabilities/operation-handler.d.ts +1 -1
  127. package/dist/types/src/capabilities/react-context.d.ts +1 -1
  128. package/dist/types/src/capabilities/react-context.d.ts.map +1 -1
  129. package/dist/types/src/capabilities/react-surface.d.ts +1 -1
  130. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
  131. package/dist/types/src/capabilities/remote-trace-monitor.d.ts +15 -0
  132. package/dist/types/src/capabilities/remote-trace-monitor.d.ts.map +1 -0
  133. package/dist/types/src/capabilities/schema-defs.d.ts +1 -1
  134. package/dist/types/src/capabilities/space-replication-progress.d.ts +11 -0
  135. package/dist/types/src/capabilities/space-replication-progress.d.ts.map +1 -0
  136. package/dist/types/src/capabilities/trace-progress.d.ts +17 -0
  137. package/dist/types/src/capabilities/trace-progress.d.ts.map +1 -0
  138. package/dist/types/src/commands/account/login/login.d.ts +2 -1
  139. package/dist/types/src/commands/account/login/login.d.ts.map +1 -1
  140. package/dist/types/src/commands/account/logout/logout.d.ts +1 -1
  141. package/dist/types/src/commands/account/logout/logout.d.ts.map +1 -1
  142. package/dist/types/src/commands/halo/index.d.ts.map +1 -1
  143. package/dist/types/src/commands/halo/share/share.d.ts.map +1 -1
  144. package/dist/types/src/commands/profile/create.d.ts.map +1 -1
  145. package/dist/types/src/containers/AccountContainer/AccountContainer.d.ts +4 -1
  146. package/dist/types/src/containers/AccountContainer/AccountContainer.d.ts.map +1 -1
  147. package/dist/types/src/containers/AccountContainer/AccountContainer.stories.d.ts +4 -1
  148. package/dist/types/src/containers/AccountContainer/AccountContainer.stories.d.ts.map +1 -1
  149. package/dist/types/src/containers/DevicesContainer/DevicesContainer.d.ts +4 -1
  150. package/dist/types/src/containers/DevicesContainer/DevicesContainer.d.ts.map +1 -1
  151. package/dist/types/src/containers/DevicesContainer/DevicesContainer.stories.d.ts +4 -1
  152. package/dist/types/src/containers/DevicesContainer/DevicesContainer.stories.d.ts.map +1 -1
  153. package/dist/types/src/containers/InvitationsContainer/InvitationsContainer.d.ts +4 -1
  154. package/dist/types/src/containers/InvitationsContainer/InvitationsContainer.d.ts.map +1 -1
  155. package/dist/types/src/containers/InvitationsContainer/InvitationsContainer.stories.d.ts +4 -1
  156. package/dist/types/src/containers/InvitationsContainer/InvitationsContainer.stories.d.ts.map +1 -1
  157. package/dist/types/src/containers/JoinDialog/JoinDialog.d.ts +4 -1
  158. package/dist/types/src/containers/JoinDialog/JoinDialog.d.ts.map +1 -1
  159. package/dist/types/src/containers/JoinDialog/JoinDialog.stories.d.ts +4 -1
  160. package/dist/types/src/containers/JoinDialog/JoinDialog.stories.d.ts.map +1 -1
  161. package/dist/types/src/containers/ProfileContainer/ProfileContainer.d.ts +4 -1
  162. package/dist/types/src/containers/ProfileContainer/ProfileContainer.d.ts.map +1 -1
  163. package/dist/types/src/containers/ProfileContainer/ProfileContainer.stories.d.ts +4 -1
  164. package/dist/types/src/containers/ProfileContainer/ProfileContainer.stories.d.ts.map +1 -1
  165. package/dist/types/src/containers/RecoveryCodeDialog/RecoveryCodeDialog.d.ts +4 -1
  166. package/dist/types/src/containers/RecoveryCodeDialog/RecoveryCodeDialog.d.ts.map +1 -1
  167. package/dist/types/src/containers/RecoveryCredentialsContainer/RecoveryCredentialsContainer.d.ts +4 -1
  168. package/dist/types/src/containers/RecoveryCredentialsContainer/RecoveryCredentialsContainer.d.ts.map +1 -1
  169. package/dist/types/src/containers/RecoveryCredentialsContainer/RecoveryCredentialsContainer.stories.d.ts +4 -1
  170. package/dist/types/src/containers/RecoveryCredentialsContainer/RecoveryCredentialsContainer.stories.d.ts.map +1 -1
  171. package/dist/types/src/containers/ResetDialog/ResetDialog.d.ts +4 -1
  172. package/dist/types/src/containers/ResetDialog/ResetDialog.d.ts.map +1 -1
  173. package/dist/types/src/containers/ResetDialog/ResetDialog.stories.d.ts +4 -1
  174. package/dist/types/src/containers/ResetDialog/ResetDialog.stories.d.ts.map +1 -1
  175. package/dist/types/src/containers/UsageContainer/UsageContainer.d.ts +4 -5
  176. package/dist/types/src/containers/UsageContainer/UsageContainer.d.ts.map +1 -1
  177. package/dist/types/src/containers/UsageContainer/UsageView.d.ts +4 -7
  178. package/dist/types/src/containers/UsageContainer/UsageView.d.ts.map +1 -1
  179. package/dist/types/src/containers/UsageContainer/UsageView.stories.d.ts +4 -2
  180. package/dist/types/src/containers/UsageContainer/UsageView.stories.d.ts.map +1 -1
  181. package/dist/types/src/halo-services-layer.d.ts +19 -0
  182. package/dist/types/src/halo-services-layer.d.ts.map +1 -0
  183. package/dist/types/src/index.d.ts +3 -0
  184. package/dist/types/src/index.d.ts.map +1 -1
  185. package/dist/types/src/operations/definitions.d.ts +9 -1
  186. package/dist/types/src/operations/definitions.d.ts.map +1 -1
  187. package/dist/types/src/operations/errors.d.ts +109 -0
  188. package/dist/types/src/operations/errors.d.ts.map +1 -0
  189. package/dist/types/src/operations/errors.test.d.ts +2 -0
  190. package/dist/types/src/operations/errors.test.d.ts.map +1 -0
  191. package/dist/types/src/operations/index.d.ts +1 -0
  192. package/dist/types/src/operations/index.d.ts.map +1 -1
  193. package/dist/types/src/operations/redeem-passkey.d.ts.map +1 -1
  194. package/dist/types/src/operations/resolve-navigation-targets.d.ts +5 -0
  195. package/dist/types/src/operations/resolve-navigation-targets.d.ts.map +1 -0
  196. package/dist/types/src/operations/update-profile.d.ts +5 -0
  197. package/dist/types/src/operations/update-profile.d.ts.map +1 -0
  198. package/dist/types/src/progress/index.d.ts +2 -0
  199. package/dist/types/src/progress/index.d.ts.map +1 -0
  200. package/dist/types/src/progress/progress-keys.d.ts +6 -0
  201. package/dist/types/src/progress/progress-keys.d.ts.map +1 -0
  202. package/dist/types/src/testing/initializeIdentity.d.ts +5 -1
  203. package/dist/types/src/testing/initializeIdentity.d.ts.map +1 -1
  204. package/dist/types/src/translations.d.ts.map +1 -1
  205. package/dist/types/src/types/capabilities.d.ts +10 -1
  206. package/dist/types/src/types/capabilities.d.ts.map +1 -1
  207. package/dist/types/src/types/schema.d.ts +6 -0
  208. package/dist/types/src/types/schema.d.ts.map +1 -1
  209. package/dist/types/tsconfig.tsbuildinfo +1 -1
  210. package/package.json +60 -55
  211. package/src/ClientPlugin.ts +25 -2
  212. package/src/capabilities/app-graph-builder.ts +1 -1
  213. package/src/capabilities/client.ts +26 -0
  214. package/src/capabilities/index.ts +10 -0
  215. package/src/capabilities/layer-specs.ts +64 -2
  216. package/src/capabilities/migrations.ts +9 -1
  217. package/src/capabilities/navigation-handler/navigation-handler.ts +20 -3
  218. package/src/capabilities/navigation-target-loader.ts +70 -0
  219. package/src/capabilities/react-context.tsx +20 -2
  220. package/src/capabilities/{react-surface.tsx → react-surface.ts} +14 -12
  221. package/src/capabilities/remote-trace-monitor.ts +62 -0
  222. package/src/capabilities/space-replication-progress.ts +143 -0
  223. package/src/capabilities/trace-progress.ts +84 -0
  224. package/src/commands/account/login/login.ts +48 -6
  225. package/src/commands/account/logout/logout.ts +4 -0
  226. package/src/commands/halo/index.ts +6 -2
  227. package/src/commands/halo/share/share.ts +16 -5
  228. package/src/commands/profile/create.ts +23 -40
  229. package/src/containers/AccountContainer/AccountContainer.tsx +6 -5
  230. package/src/containers/DevicesContainer/DevicesContainer.tsx +2 -0
  231. package/src/containers/InvitationsContainer/InvitationsContainer.tsx +2 -0
  232. package/src/containers/JoinDialog/JoinDialog.tsx +2 -0
  233. package/src/containers/ProfileContainer/ProfileContainer.tsx +19 -13
  234. package/src/containers/RecoveryCodeDialog/RecoveryCodeDialog.tsx +2 -0
  235. package/src/containers/RecoveryCredentialsContainer/RecoveryCredentialsContainer.tsx +7 -9
  236. package/src/containers/ResetDialog/ResetDialog.tsx +2 -0
  237. package/src/containers/UsageContainer/UsageContainer.tsx +10 -1
  238. package/src/containers/UsageContainer/UsageView.stories.tsx +0 -12
  239. package/src/containers/UsageContainer/UsageView.tsx +9 -10
  240. package/src/halo-services-layer.ts +29 -0
  241. package/src/index.ts +9 -0
  242. package/src/operations/definitions.ts +9 -1
  243. package/src/operations/errors.test.ts +27 -0
  244. package/src/operations/errors.ts +47 -0
  245. package/src/operations/index.ts +3 -0
  246. package/src/operations/open-usage.ts +3 -3
  247. package/src/operations/redeem-passkey.ts +85 -55
  248. package/src/operations/resolve-navigation-targets.ts +71 -0
  249. package/src/operations/share-identity.ts +3 -3
  250. package/src/operations/update-profile.ts +20 -0
  251. package/src/progress/index.ts +5 -0
  252. package/src/progress/progress-keys.ts +15 -0
  253. package/src/testing/initializeIdentity.ts +10 -2
  254. package/src/translations.ts +2 -0
  255. package/src/types/capabilities.ts +15 -1
  256. package/src/types/schema.ts +7 -0
  257. package/dist/lib/neutral/AccountContainer-7SF53UON.mjs +0 -186
  258. package/dist/lib/neutral/AccountContainer-7SF53UON.mjs.map +0 -7
  259. package/dist/lib/neutral/ClientPlugin.mjs +0 -72
  260. package/dist/lib/neutral/ClientPlugin.mjs.map +0 -7
  261. package/dist/lib/neutral/ClientPlugin.node.mjs +0 -1156
  262. package/dist/lib/neutral/ClientPlugin.node.mjs.map +0 -7
  263. package/dist/lib/neutral/ClientPlugin.workerd.mjs +0 -16
  264. package/dist/lib/neutral/ClientPlugin.workerd.mjs.map +0 -7
  265. package/dist/lib/neutral/DevicesContainer-PRWIN36N.mjs +0 -271
  266. package/dist/lib/neutral/DevicesContainer-PRWIN36N.mjs.map +0 -7
  267. package/dist/lib/neutral/InvitationsContainer-BI5RAWET.mjs +0 -143
  268. package/dist/lib/neutral/InvitationsContainer-BI5RAWET.mjs.map +0 -7
  269. package/dist/lib/neutral/JoinDialog-4BWV4CYB.mjs +0 -53
  270. package/dist/lib/neutral/JoinDialog-4BWV4CYB.mjs.map +0 -7
  271. package/dist/lib/neutral/ProfileContainer-YEAYOTX5.mjs +0 -169
  272. package/dist/lib/neutral/ProfileContainer-YEAYOTX5.mjs.map +0 -7
  273. package/dist/lib/neutral/RecoveryCodeDialog-7EPNKCLB.mjs +0 -59
  274. package/dist/lib/neutral/RecoveryCodeDialog-7EPNKCLB.mjs.map +0 -7
  275. package/dist/lib/neutral/RecoveryCredentialsContainer-6BZ2IWX7.mjs +0 -60
  276. package/dist/lib/neutral/RecoveryCredentialsContainer-6BZ2IWX7.mjs.map +0 -7
  277. package/dist/lib/neutral/ResetDialog-KUWX45JY.mjs +0 -57
  278. package/dist/lib/neutral/ResetDialog-KUWX45JY.mjs.map +0 -7
  279. package/dist/lib/neutral/UsageContainer-2DWCVVE4.mjs +0 -241
  280. package/dist/lib/neutral/UsageContainer-2DWCVVE4.mjs.map +0 -7
  281. package/dist/lib/neutral/account-cache-YVQKLNV2.mjs.map +0 -7
  282. package/dist/lib/neutral/app-graph-builder-RVXTD6OE.mjs +0 -159
  283. package/dist/lib/neutral/app-graph-builder-RVXTD6OE.mjs.map +0 -7
  284. package/dist/lib/neutral/capabilities/index.mjs +0 -34
  285. package/dist/lib/neutral/capabilities/index.mjs.map +0 -7
  286. package/dist/lib/neutral/capabilities/node.mjs +0 -21
  287. package/dist/lib/neutral/capabilities/node.mjs.map +0 -7
  288. package/dist/lib/neutral/chunk-6G4KZLBG.mjs.map +0 -7
  289. package/dist/lib/neutral/chunk-7DEIKHB7.mjs +0 -199
  290. package/dist/lib/neutral/chunk-7DEIKHB7.mjs.map +0 -7
  291. package/dist/lib/neutral/chunk-A7WDDIWY.mjs +0 -162
  292. package/dist/lib/neutral/chunk-A7WDDIWY.mjs.map +0 -7
  293. package/dist/lib/neutral/chunk-GSKMAS3S.mjs.map +0 -7
  294. package/dist/lib/neutral/chunk-J5LGTIGS.mjs +0 -10
  295. package/dist/lib/neutral/chunk-J5LGTIGS.mjs.map +0 -7
  296. package/dist/lib/neutral/chunk-JY35S2CN.mjs +0 -27
  297. package/dist/lib/neutral/chunk-JY35S2CN.mjs.map +0 -7
  298. package/dist/lib/neutral/chunk-ZFABGG2E.mjs +0 -8
  299. package/dist/lib/neutral/chunk-ZFABGG2E.mjs.map +0 -7
  300. package/dist/lib/neutral/client-7I5Y5SOA.mjs +0 -62
  301. package/dist/lib/neutral/client-7I5Y5SOA.mjs.map +0 -7
  302. package/dist/lib/neutral/components/index.mjs +0 -1
  303. package/dist/lib/neutral/components/index.mjs.map +0 -7
  304. package/dist/lib/neutral/containers/index.mjs +0 -25
  305. package/dist/lib/neutral/containers/index.mjs.map +0 -7
  306. package/dist/lib/neutral/create-agent-6UKJMZS5.mjs +0 -26
  307. package/dist/lib/neutral/create-agent-6UKJMZS5.mjs.map +0 -7
  308. package/dist/lib/neutral/create-identity-ULCRB3YH.mjs +0 -42
  309. package/dist/lib/neutral/create-identity-ULCRB3YH.mjs.map +0 -7
  310. package/dist/lib/neutral/create-passkey-I7VDS3AC.mjs +0 -81
  311. package/dist/lib/neutral/create-passkey-I7VDS3AC.mjs.map +0 -7
  312. package/dist/lib/neutral/create-recovery-code-DH2B6EGP.mjs +0 -36
  313. package/dist/lib/neutral/create-recovery-code-DH2B6EGP.mjs.map +0 -7
  314. package/dist/lib/neutral/hub-http-client-WOJUCDKV.mjs +0 -19
  315. package/dist/lib/neutral/hub-http-client-WOJUCDKV.mjs.map +0 -7
  316. package/dist/lib/neutral/index.mjs +0 -25
  317. package/dist/lib/neutral/index.mjs.map +0 -7
  318. package/dist/lib/neutral/join-identity-PJCHDNLH.mjs +0 -27
  319. package/dist/lib/neutral/join-identity-PJCHDNLH.mjs.map +0 -7
  320. package/dist/lib/neutral/layer-specs-YI7BVWKZ.mjs +0 -59
  321. package/dist/lib/neutral/layer-specs-YI7BVWKZ.mjs.map +0 -7
  322. package/dist/lib/neutral/meta.json +0 -1
  323. package/dist/lib/neutral/meta.mjs +0 -8
  324. package/dist/lib/neutral/meta.mjs.map +0 -7
  325. package/dist/lib/neutral/migrations-7FTW55ER.mjs +0 -23
  326. package/dist/lib/neutral/migrations-7FTW55ER.mjs.map +0 -7
  327. package/dist/lib/neutral/navigation-handler-MQ6MZXLX.mjs +0 -46
  328. package/dist/lib/neutral/navigation-handler-MQ6MZXLX.mjs.map +0 -7
  329. package/dist/lib/neutral/open-usage-OJVTPGOY.mjs +0 -27
  330. package/dist/lib/neutral/open-usage-OJVTPGOY.mjs.map +0 -7
  331. package/dist/lib/neutral/operation-handler-E3HB7E2T.mjs +0 -13
  332. package/dist/lib/neutral/operation-handler-E3HB7E2T.mjs.map +0 -7
  333. package/dist/lib/neutral/operations/index.mjs +0 -12
  334. package/dist/lib/neutral/operations/index.mjs.map +0 -7
  335. package/dist/lib/neutral/plugin.mjs +0 -17
  336. package/dist/lib/neutral/plugin.mjs.map +0 -7
  337. package/dist/lib/neutral/react-context-V74T6JOJ.mjs +0 -23
  338. package/dist/lib/neutral/react-context-V74T6JOJ.mjs.map +0 -7
  339. package/dist/lib/neutral/react-surface-HPXRA65U.mjs +0 -75
  340. package/dist/lib/neutral/react-surface-HPXRA65U.mjs.map +0 -7
  341. package/dist/lib/neutral/recover-identity-B7YM2BR4.mjs +0 -26
  342. package/dist/lib/neutral/recover-identity-B7YM2BR4.mjs.map +0 -7
  343. package/dist/lib/neutral/redeem-passkey-TYP4XLHO.mjs +0 -66
  344. package/dist/lib/neutral/redeem-passkey-TYP4XLHO.mjs.map +0 -7
  345. package/dist/lib/neutral/redeem-token-2J33R3PV.mjs +0 -29
  346. package/dist/lib/neutral/redeem-token-2J33R3PV.mjs.map +0 -7
  347. package/dist/lib/neutral/reset-storage-K47HZTGP.mjs +0 -26
  348. package/dist/lib/neutral/reset-storage-K47HZTGP.mjs.map +0 -7
  349. package/dist/lib/neutral/schema-defs-IHBCKTMM.mjs +0 -51
  350. package/dist/lib/neutral/schema-defs-IHBCKTMM.mjs.map +0 -7
  351. package/dist/lib/neutral/share-identity-TOROBLWL.mjs +0 -31
  352. package/dist/lib/neutral/share-identity-TOROBLWL.mjs.map +0 -7
  353. package/dist/lib/neutral/testing/index.mjs +0 -25
  354. package/dist/lib/neutral/testing/index.mjs.map +0 -7
  355. package/dist/lib/neutral/translations.mjs +0 -124
  356. package/dist/lib/neutral/translations.mjs.map +0 -7
  357. package/dist/lib/neutral/types/index.mjs +0 -16
  358. 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 { useClient } from '@dxos/react-client';
10
- import { type Identity, useIdentity } from '@dxos/react-client/halo';
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: Identity | null) => hexToHue(identity?.identityKey.toHex() ?? '0');
31
- const getHueValue = (identity: Identity | null) => identity?.profile?.data?.hue || getDefaultHueValue(identity);
32
- const getDefaultEmojiValue = (identity: Identity | null) => hexToEmoji(identity?.identityKey.toHex() ?? '0');
33
- const getEmojiValue = (identity: Identity | null) => identity?.profile?.data?.emoji || getDefaultEmojiValue(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 client = useClient();
39
+ const { invokePromise } = useOperationInvoker();
38
40
  const identity = useIdentity();
39
- const [displayName, setDisplayNameDirectly] = useState(identity?.profile?.displayName ?? '');
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
- (profile: Partial<UserProfile>) =>
47
- client.halo.updateProfile({
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';
@@ -69,3 +69,5 @@ const Code = ({ code }: { code: string }) => {
69
69
  </div>
70
70
  );
71
71
  };
72
+
73
+ RecoveryCodeDialog.displayName = 'RecoveryCodeDialog';
@@ -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-client/halo';
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.subject.assertion['@type'] === 'dxos.halo.credentials.IdentityRecovery',
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.Content>{t('no-credentials.message')}</Message.Content>
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.toLocaleString()}</Listbox.ItemLabel>
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';
@@ -60,3 +60,5 @@ export const ResetDialog = ({ mode, onReset, onBeforeReset, capabilityManager }:
60
60
  </Dialog.Content>
61
61
  );
62
62
  };
63
+
64
+ ResetDialog.displayName = 'ResetDialog';
@@ -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 = { state: Exclude<UsageViewState, 'ready'> } | { state: 'ready'; data: GetProfileUsageResponse };
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 { IconButton, Message, Status, ToggleIconButton, useTranslation } from '@dxos/react-ui';
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
- {state === 'loading' ? (
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.Content>{t(message.description)}</Message.Content>
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
+ };
@@ -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 { LayoutOperation, Paths } from '@dxos/app-toolkit';
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: Paths.getSpacePath(Account.id) });
16
+ yield* Operation.invoke(LayoutOperation.SwitchWorkspace, { subject: GraphPath.getSpacePath(Account.id) });
17
17
  yield* Operation.invoke(LayoutOperation.Open, {
18
- subject: [Paths.getSpacePath(Account.id, Account.Usage)],
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
- invariant(client.services.services.IdentityService, 'IdentityService not available');
22
- const { deviceKey, controlFeedKey, challenge } = yield* Effect.promise(() =>
23
- client.services.services.IdentityService!.requestRecoveryChallenge(),
24
- );
87
+ const identityService = client.services.services.IdentityService;
88
+ invariant(identityService, 'IdentityService not available');
25
89
 
26
- const { lookupKey, signature, clientDataJson, authenticatorData } = yield* Match.value(
27
- NativePasskey.supportsNativePasskeys(),
28
- ).pipe(
29
- Match.when(true, () =>
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* Effect.promise(() =>
65
- client.services.services.IdentityService!.recoverIdentity(
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;