@oxyhq/services 27.1.3 → 29.0.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/LICENSE +202 -661
- package/NOTICE +16 -0
- package/README.md +67 -2
- package/android/src/main/java/so/oxy/devicesession/OxyDeviceSessionModule.kt +178 -0
- package/android/src/main/java/so/oxy/devicesession/OxyDeviceSessionProvider.kt +99 -0
- package/android/src/main/java/so/oxy/devicesession/OxyDeviceSessionStore.kt +115 -0
- package/expo-module.config.json +1 -0
- package/lib/commonjs/index.js +78 -16
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/ui/boot/runProviderColdBoot.js +7 -0
- package/lib/commonjs/ui/boot/runProviderColdBoot.js.map +1 -1
- package/lib/commonjs/ui/client.js +12 -7
- package/lib/commonjs/ui/client.js.map +1 -1
- package/lib/commonjs/ui/components/OxyAccountDialogScreen.js +10 -27
- package/lib/commonjs/ui/components/OxyAccountDialogScreen.js.map +1 -1
- package/lib/commonjs/ui/components/OxyAuthChooser.js +132 -44
- package/lib/commonjs/ui/components/OxyAuthChooser.js.map +1 -1
- package/lib/commonjs/ui/components/OxyProvider.js +4 -2
- package/lib/commonjs/ui/components/OxyProvider.js.map +1 -1
- package/lib/commonjs/ui/components/SurfaceScreen.js +31 -3
- package/lib/commonjs/ui/components/SurfaceScreen.js.map +1 -1
- package/lib/commonjs/ui/components/authChooser/AccountsMenuView.js +209 -84
- package/lib/commonjs/ui/components/authChooser/AccountsMenuView.js.map +1 -1
- package/lib/commonjs/ui/components/authChooser/SignInEntryView.js +25 -7
- package/lib/commonjs/ui/components/authChooser/SignInEntryView.js.map +1 -1
- package/lib/commonjs/ui/components/authChooser/primitives.js +35 -22
- package/lib/commonjs/ui/components/authChooser/primitives.js.map +1 -1
- package/lib/commonjs/ui/components/authChooser/signInProgress.js +5 -0
- package/lib/commonjs/ui/components/authChooser/signInProgress.js.map +1 -1
- package/lib/commonjs/ui/components/authChooser/types.js +11 -0
- package/lib/commonjs/ui/components/authChooser/types.js.map +1 -1
- package/lib/commonjs/ui/context/OxyContext.js +257 -306
- package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
- package/lib/commonjs/ui/context/hooks/useAuthOperations.js +27 -37
- package/lib/commonjs/ui/context/hooks/useAuthOperations.js.map +1 -1
- package/lib/commonjs/ui/context/oxyContextHelpers.js +0 -29
- package/lib/commonjs/ui/context/oxyContextHelpers.js.map +1 -1
- package/lib/commonjs/ui/context/useOxyAccountGraph.js +6 -17
- package/lib/commonjs/ui/context/useOxyAccountGraph.js.map +1 -1
- package/lib/commonjs/ui/hooks/accountDialogSnapshot.js +42 -0
- package/lib/commonjs/ui/hooks/accountDialogSnapshot.js.map +1 -0
- package/lib/commonjs/ui/hooks/mutations/useAccountMutations.js +0 -19
- package/lib/commonjs/ui/hooks/mutations/useAccountMutations.js.map +1 -1
- package/lib/commonjs/ui/hooks/useDeviceSwitcher.js +57 -0
- package/lib/commonjs/ui/hooks/useDeviceSwitcher.js.map +1 -0
- package/lib/commonjs/ui/hooks/useI18n.js +22 -3
- package/lib/commonjs/ui/hooks/useI18n.js.map +1 -1
- package/lib/commonjs/ui/hooks/useSessionManagement.js +46 -153
- package/lib/commonjs/ui/hooks/useSessionManagement.js.map +1 -1
- package/lib/commonjs/ui/index.js +12 -7
- package/lib/commonjs/ui/index.js.map +1 -1
- package/lib/commonjs/ui/navigation/routes.js +85 -73
- package/lib/commonjs/ui/navigation/routes.js.map +1 -1
- package/lib/commonjs/ui/navigation/surfaces.js +8 -7
- package/lib/commonjs/ui/navigation/surfaces.js.map +1 -1
- package/lib/commonjs/ui/runtime/createOxyRuntime.js +433 -0
- package/lib/commonjs/ui/runtime/createOxyRuntime.js.map +1 -0
- package/lib/commonjs/ui/runtime/index.js +63 -0
- package/lib/commonjs/ui/runtime/index.js.map +1 -0
- package/lib/commonjs/ui/runtime/runtimeContext.js +83 -0
- package/lib/commonjs/ui/runtime/runtimeContext.js.map +1 -0
- package/lib/commonjs/ui/runtime/types.js +6 -0
- package/lib/commonjs/ui/runtime/types.js.map +1 -0
- package/lib/commonjs/ui/runtime/useRuntimeSnapshot.js +57 -0
- package/lib/commonjs/ui/runtime/useRuntimeSnapshot.js.map +1 -0
- package/lib/commonjs/ui/screens/AccountMembersScreen.js +3 -4
- package/lib/commonjs/ui/screens/AccountMembersScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/CreateAccountScreen.js +3 -12
- package/lib/commonjs/ui/screens/CreateAccountScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/trust/TrustCenterScreen.js +6 -5
- package/lib/commonjs/ui/screens/trust/TrustCenterScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/trust/TrustLeaderboardScreen.js +4 -4
- package/lib/commonjs/ui/screens/trust/TrustLeaderboardScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/trust/TrustRewardsScreen.js +1 -1
- package/lib/commonjs/ui/screens/trust/TrustRulesScreen.js +4 -2
- package/lib/commonjs/ui/screens/trust/TrustRulesScreen.js.map +1 -1
- package/lib/commonjs/ui/server.js +1 -2
- package/lib/commonjs/ui/server.js.map +1 -1
- package/lib/commonjs/ui/session/authStore.js +45 -9
- package/lib/commonjs/ui/session/authStore.js.map +1 -1
- package/lib/commonjs/ui/session/sharedDeviceCredentialStore.js +306 -0
- package/lib/commonjs/ui/session/sharedDeviceCredentialStore.js.map +1 -0
- package/lib/commonjs/ui/stores/authStore.js +154 -55
- package/lib/commonjs/ui/stores/authStore.js.map +1 -1
- package/lib/commonjs/ui/stores/followTargetStore.js +27 -0
- package/lib/commonjs/ui/stores/followTargetStore.js.map +1 -1
- package/lib/commonjs/ui/utils/avatarUtils.js +2 -36
- package/lib/commonjs/ui/utils/avatarUtils.js.map +1 -1
- package/lib/module/index.js +20 -9
- package/lib/module/index.js.map +1 -1
- package/lib/module/ui/boot/runProviderColdBoot.js +7 -0
- package/lib/module/ui/boot/runProviderColdBoot.js.map +1 -1
- package/lib/module/ui/client.js +1 -2
- package/lib/module/ui/client.js.map +1 -1
- package/lib/module/ui/components/OxyAccountDialogScreen.js +10 -27
- package/lib/module/ui/components/OxyAccountDialogScreen.js.map +1 -1
- package/lib/module/ui/components/OxyAuthChooser.js +134 -44
- package/lib/module/ui/components/OxyAuthChooser.js.map +1 -1
- package/lib/module/ui/components/OxyProvider.js +5 -3
- package/lib/module/ui/components/OxyProvider.js.map +1 -1
- package/lib/module/ui/components/SurfaceScreen.js +33 -5
- package/lib/module/ui/components/SurfaceScreen.js.map +1 -1
- package/lib/module/ui/components/authChooser/AccountsMenuView.js +210 -85
- package/lib/module/ui/components/authChooser/AccountsMenuView.js.map +1 -1
- package/lib/module/ui/components/authChooser/SignInEntryView.js +25 -7
- package/lib/module/ui/components/authChooser/SignInEntryView.js.map +1 -1
- package/lib/module/ui/components/authChooser/primitives.js +35 -22
- package/lib/module/ui/components/authChooser/primitives.js.map +1 -1
- package/lib/module/ui/components/authChooser/signInProgress.js +5 -0
- package/lib/module/ui/components/authChooser/signInProgress.js.map +1 -1
- package/lib/module/ui/components/authChooser/types.js +11 -0
- package/lib/module/ui/components/authChooser/types.js.map +1 -1
- package/lib/module/ui/context/OxyContext.js +251 -303
- package/lib/module/ui/context/OxyContext.js.map +1 -1
- package/lib/module/ui/context/hooks/useAuthOperations.js +27 -37
- package/lib/module/ui/context/hooks/useAuthOperations.js.map +1 -1
- package/lib/module/ui/context/oxyContextHelpers.js +0 -28
- package/lib/module/ui/context/oxyContextHelpers.js.map +1 -1
- package/lib/module/ui/context/useOxyAccountGraph.js +6 -17
- package/lib/module/ui/context/useOxyAccountGraph.js.map +1 -1
- package/lib/module/ui/hooks/accountDialogSnapshot.js +38 -0
- package/lib/module/ui/hooks/accountDialogSnapshot.js.map +1 -0
- package/lib/module/ui/hooks/mutations/useAccountMutations.js +0 -19
- package/lib/module/ui/hooks/mutations/useAccountMutations.js.map +1 -1
- package/lib/module/ui/hooks/useDeviceSwitcher.js +53 -0
- package/lib/module/ui/hooks/useDeviceSwitcher.js.map +1 -0
- package/lib/module/ui/hooks/useI18n.js +24 -4
- package/lib/module/ui/hooks/useI18n.js.map +1 -1
- package/lib/module/ui/hooks/useSessionManagement.js +47 -155
- package/lib/module/ui/hooks/useSessionManagement.js.map +1 -1
- package/lib/module/ui/index.js +1 -2
- package/lib/module/ui/index.js.map +1 -1
- package/lib/module/ui/navigation/routes.js +84 -72
- package/lib/module/ui/navigation/routes.js.map +1 -1
- package/lib/module/ui/navigation/surfaces.js +6 -6
- package/lib/module/ui/navigation/surfaces.js.map +1 -1
- package/lib/module/ui/runtime/createOxyRuntime.js +430 -0
- package/lib/module/ui/runtime/createOxyRuntime.js.map +1 -0
- package/lib/module/ui/runtime/index.js +15 -0
- package/lib/module/ui/runtime/index.js.map +1 -0
- package/lib/module/ui/runtime/runtimeContext.js +75 -0
- package/lib/module/ui/runtime/runtimeContext.js.map +1 -0
- package/lib/module/ui/runtime/types.js +4 -0
- package/lib/module/ui/runtime/types.js.map +1 -0
- package/lib/module/ui/runtime/useRuntimeSnapshot.js +52 -0
- package/lib/module/ui/runtime/useRuntimeSnapshot.js.map +1 -0
- package/lib/module/ui/screens/AccountMembersScreen.js +4 -5
- package/lib/module/ui/screens/AccountMembersScreen.js.map +1 -1
- package/lib/module/ui/screens/CreateAccountScreen.js +4 -13
- package/lib/module/ui/screens/CreateAccountScreen.js.map +1 -1
- package/lib/module/ui/screens/trust/TrustCenterScreen.js +6 -5
- package/lib/module/ui/screens/trust/TrustCenterScreen.js.map +1 -1
- package/lib/module/ui/screens/trust/TrustLeaderboardScreen.js +5 -5
- package/lib/module/ui/screens/trust/TrustLeaderboardScreen.js.map +1 -1
- package/lib/module/ui/screens/trust/TrustRewardsScreen.js +1 -1
- package/lib/module/ui/screens/trust/TrustRulesScreen.js +4 -2
- package/lib/module/ui/screens/trust/TrustRulesScreen.js.map +1 -1
- package/lib/module/ui/server.js +0 -1
- package/lib/module/ui/server.js.map +1 -1
- package/lib/module/ui/session/authStore.js +46 -11
- package/lib/module/ui/session/authStore.js.map +1 -1
- package/lib/module/ui/session/sharedDeviceCredentialStore.js +302 -0
- package/lib/module/ui/session/sharedDeviceCredentialStore.js.map +1 -0
- package/lib/module/ui/stores/authStore.js +154 -55
- package/lib/module/ui/stores/authStore.js.map +1 -1
- package/lib/module/ui/stores/followTargetStore.js +25 -0
- package/lib/module/ui/stores/followTargetStore.js.map +1 -1
- package/lib/module/ui/utils/avatarUtils.js +2 -35
- package/lib/module/ui/utils/avatarUtils.js.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +6 -4
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/boot/runProviderColdBoot.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/client.d.ts +1 -2
- package/lib/typescript/commonjs/ui/client.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/OxyAccountDialogScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/OxyAuthChooser.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/OxyProvider.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/SurfaceScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/authChooser/AccountsMenuView.d.ts +11 -4
- package/lib/typescript/commonjs/ui/components/authChooser/AccountsMenuView.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/authChooser/SignInEntryView.d.ts +3 -1
- package/lib/typescript/commonjs/ui/components/authChooser/SignInEntryView.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/authChooser/primitives.d.ts +18 -6
- package/lib/typescript/commonjs/ui/components/authChooser/primitives.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/authChooser/signInProgress.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/authChooser/types.d.ts +33 -1
- package/lib/typescript/commonjs/ui/components/authChooser/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/OxyContext.d.ts +35 -4
- package/lib/typescript/commonjs/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/hooks/useAuthOperations.d.ts +8 -12
- package/lib/typescript/commonjs/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/oxyContextHelpers.d.ts +0 -2
- package/lib/typescript/commonjs/ui/context/oxyContextHelpers.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/oxyContextTypes.d.ts +9 -1
- package/lib/typescript/commonjs/ui/context/oxyContextTypes.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/useOxyAccountGraph.d.ts +1 -3
- package/lib/typescript/commonjs/ui/context/useOxyAccountGraph.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/accountDialogSnapshot.d.ts +15 -0
- package/lib/typescript/commonjs/ui/hooks/accountDialogSnapshot.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useDeviceSwitcher.d.ts +49 -0
- package/lib/typescript/commonjs/ui/hooks/useDeviceSwitcher.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/hooks/useI18n.d.ts +13 -0
- package/lib/typescript/commonjs/ui/hooks/useI18n.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useSessionManagement.d.ts +23 -18
- package/lib/typescript/commonjs/ui/hooks/useSessionManagement.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/index.d.ts +1 -2
- package/lib/typescript/commonjs/ui/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/navigation/routes.d.ts +15 -1
- package/lib/typescript/commonjs/ui/navigation/routes.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/navigation/surfaces.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/runtime/createOxyRuntime.d.ts +144 -0
- package/lib/typescript/commonjs/ui/runtime/createOxyRuntime.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/runtime/index.d.ts +16 -0
- package/lib/typescript/commonjs/ui/runtime/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/runtime/runtimeContext.d.ts +43 -0
- package/lib/typescript/commonjs/ui/runtime/runtimeContext.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/runtime/types.d.ts +89 -0
- package/lib/typescript/commonjs/ui/runtime/types.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/runtime/useRuntimeSnapshot.d.ts +23 -0
- package/lib/typescript/commonjs/ui/runtime/useRuntimeSnapshot.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/screens/AccountMembersScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/CreateAccountScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/trust/TrustLeaderboardScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/trust/TrustRulesScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/server.d.ts +0 -1
- package/lib/typescript/commonjs/ui/server.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/session/authStore.d.ts +60 -9
- package/lib/typescript/commonjs/ui/session/authStore.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/session/sharedDeviceCredentialStore.d.ts +32 -0
- package/lib/typescript/commonjs/ui/session/sharedDeviceCredentialStore.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/stores/authStore.d.ts +49 -5
- package/lib/typescript/commonjs/ui/stores/authStore.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/stores/followTargetStore.d.ts +8 -1
- package/lib/typescript/commonjs/ui/stores/followTargetStore.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/types/navigation.d.ts +26 -0
- package/lib/typescript/commonjs/ui/types/navigation.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/utils/avatarUtils.d.ts +2 -10
- package/lib/typescript/commonjs/ui/utils/avatarUtils.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +6 -4
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/ui/boot/runProviderColdBoot.d.ts.map +1 -1
- package/lib/typescript/module/ui/client.d.ts +1 -2
- package/lib/typescript/module/ui/client.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/OxyAccountDialogScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/OxyAuthChooser.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/OxyProvider.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/SurfaceScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/authChooser/AccountsMenuView.d.ts +11 -4
- package/lib/typescript/module/ui/components/authChooser/AccountsMenuView.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/authChooser/SignInEntryView.d.ts +3 -1
- package/lib/typescript/module/ui/components/authChooser/SignInEntryView.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/authChooser/primitives.d.ts +18 -6
- package/lib/typescript/module/ui/components/authChooser/primitives.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/authChooser/signInProgress.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/authChooser/types.d.ts +33 -1
- package/lib/typescript/module/ui/components/authChooser/types.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/OxyContext.d.ts +35 -4
- package/lib/typescript/module/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/hooks/useAuthOperations.d.ts +8 -12
- package/lib/typescript/module/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/oxyContextHelpers.d.ts +0 -2
- package/lib/typescript/module/ui/context/oxyContextHelpers.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/oxyContextTypes.d.ts +9 -1
- package/lib/typescript/module/ui/context/oxyContextTypes.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/useOxyAccountGraph.d.ts +1 -3
- package/lib/typescript/module/ui/context/useOxyAccountGraph.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/accountDialogSnapshot.d.ts +15 -0
- package/lib/typescript/module/ui/hooks/accountDialogSnapshot.d.ts.map +1 -0
- package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useDeviceSwitcher.d.ts +49 -0
- package/lib/typescript/module/ui/hooks/useDeviceSwitcher.d.ts.map +1 -0
- package/lib/typescript/module/ui/hooks/useI18n.d.ts +13 -0
- package/lib/typescript/module/ui/hooks/useI18n.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useSessionManagement.d.ts +23 -18
- package/lib/typescript/module/ui/hooks/useSessionManagement.d.ts.map +1 -1
- package/lib/typescript/module/ui/index.d.ts +1 -2
- package/lib/typescript/module/ui/index.d.ts.map +1 -1
- package/lib/typescript/module/ui/navigation/routes.d.ts +15 -1
- package/lib/typescript/module/ui/navigation/routes.d.ts.map +1 -1
- package/lib/typescript/module/ui/navigation/surfaces.d.ts.map +1 -1
- package/lib/typescript/module/ui/runtime/createOxyRuntime.d.ts +144 -0
- package/lib/typescript/module/ui/runtime/createOxyRuntime.d.ts.map +1 -0
- package/lib/typescript/module/ui/runtime/index.d.ts +16 -0
- package/lib/typescript/module/ui/runtime/index.d.ts.map +1 -0
- package/lib/typescript/module/ui/runtime/runtimeContext.d.ts +43 -0
- package/lib/typescript/module/ui/runtime/runtimeContext.d.ts.map +1 -0
- package/lib/typescript/module/ui/runtime/types.d.ts +89 -0
- package/lib/typescript/module/ui/runtime/types.d.ts.map +1 -0
- package/lib/typescript/module/ui/runtime/useRuntimeSnapshot.d.ts +23 -0
- package/lib/typescript/module/ui/runtime/useRuntimeSnapshot.d.ts.map +1 -0
- package/lib/typescript/module/ui/screens/AccountMembersScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/CreateAccountScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/trust/TrustLeaderboardScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/trust/TrustRulesScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/server.d.ts +0 -1
- package/lib/typescript/module/ui/server.d.ts.map +1 -1
- package/lib/typescript/module/ui/session/authStore.d.ts +60 -9
- package/lib/typescript/module/ui/session/authStore.d.ts.map +1 -1
- package/lib/typescript/module/ui/session/sharedDeviceCredentialStore.d.ts +32 -0
- package/lib/typescript/module/ui/session/sharedDeviceCredentialStore.d.ts.map +1 -0
- package/lib/typescript/module/ui/stores/authStore.d.ts +49 -5
- package/lib/typescript/module/ui/stores/authStore.d.ts.map +1 -1
- package/lib/typescript/module/ui/stores/followTargetStore.d.ts +8 -1
- package/lib/typescript/module/ui/stores/followTargetStore.d.ts.map +1 -1
- package/lib/typescript/module/ui/types/navigation.d.ts +26 -0
- package/lib/typescript/module/ui/types/navigation.d.ts.map +1 -1
- package/lib/typescript/module/ui/utils/avatarUtils.d.ts +2 -10
- package/lib/typescript/module/ui/utils/avatarUtils.d.ts.map +1 -1
- package/package.json +8 -8
- package/plugins/withSharedDeviceSessionProvider.js +102 -0
- package/plugins/withSharedDeviceSessionReader.js +57 -0
- package/src/__tests__/esmSourcePurity.test.ts +61 -0
- package/src/index.ts +36 -9
- package/src/ui/boot/runProviderColdBoot.ts +7 -0
- package/src/ui/client.ts +1 -2
- package/src/ui/components/OxyAccountDialogScreen.tsx +10 -29
- package/src/ui/components/OxyAuthChooser.tsx +149 -50
- package/src/ui/components/OxyProvider.tsx +7 -5
- package/src/ui/components/SurfaceScreen.tsx +28 -4
- package/src/ui/components/__tests__/followTargetButton.test.ts +39 -0
- package/src/ui/components/authChooser/AccountsMenuView.tsx +246 -96
- package/src/ui/components/authChooser/SignInEntryView.tsx +31 -8
- package/src/ui/components/authChooser/primitives.tsx +37 -23
- package/src/ui/components/authChooser/signInProgress.ts +4 -0
- package/src/ui/components/authChooser/types.ts +34 -1
- package/src/ui/context/OxyContext.tsx +269 -324
- package/src/ui/context/hooks/useAuthOperations.ts +32 -35
- package/src/ui/context/oxyContextHelpers.ts +0 -30
- package/src/ui/context/oxyContextTypes.ts +9 -1
- package/src/ui/context/useOxyAccountGraph.ts +5 -19
- package/src/ui/hooks/accountDialogSnapshot.ts +37 -0
- package/src/ui/hooks/mutations/useAccountMutations.ts +0 -19
- package/src/ui/hooks/queries/__tests__/userCache.test.ts +12 -1
- package/src/ui/hooks/useDeviceSwitcher.ts +110 -0
- package/src/ui/hooks/useI18n.ts +23 -3
- package/src/ui/hooks/useSessionManagement.ts +62 -211
- package/src/ui/index.ts +1 -2
- package/src/ui/navigation/routes.ts +85 -75
- package/src/ui/navigation/surfaces.ts +6 -9
- package/src/ui/runtime/createOxyRuntime.ts +623 -0
- package/src/ui/runtime/index.ts +34 -0
- package/src/ui/runtime/runtimeContext.tsx +111 -0
- package/src/ui/runtime/types.ts +110 -0
- package/src/ui/runtime/useRuntimeSnapshot.ts +52 -0
- package/src/ui/screens/AccountMembersScreen.tsx +6 -5
- package/src/ui/screens/CreateAccountScreen.tsx +3 -16
- package/src/ui/screens/trust/TrustCenterScreen.tsx +7 -7
- package/src/ui/screens/trust/TrustLeaderboardScreen.tsx +4 -5
- package/src/ui/screens/trust/TrustRewardsScreen.tsx +1 -1
- package/src/ui/screens/trust/TrustRulesScreen.tsx +3 -2
- package/src/ui/server.ts +0 -1
- package/src/ui/session/__tests__/authStore.test.ts +90 -0
- package/src/ui/session/__tests__/sharedDeviceCredentialStore.test.ts +289 -0
- package/src/ui/session/authStore.ts +78 -9
- package/src/ui/session/sharedDeviceCredentialStore.ts +318 -0
- package/src/ui/stores/authStore.ts +171 -43
- package/src/ui/stores/followTargetStore.ts +24 -1
- package/src/ui/types/navigation.ts +26 -0
- package/src/ui/utils/avatarUtils.ts +2 -37
- package/lib/commonjs/ui/hooks/useSwitchableAccounts.js +0 -64
- package/lib/commonjs/ui/hooks/useSwitchableAccounts.js.map +0 -1
- package/lib/commonjs/ui/screens/trust/trustTier.js +0 -23
- package/lib/commonjs/ui/screens/trust/trustTier.js.map +0 -1
- package/lib/commonjs/ui/stores/accountStore.js +0 -221
- package/lib/commonjs/ui/stores/accountStore.js.map +0 -1
- package/lib/module/ui/hooks/useSwitchableAccounts.js +0 -60
- package/lib/module/ui/hooks/useSwitchableAccounts.js.map +0 -1
- package/lib/module/ui/screens/trust/trustTier.js +0 -19
- package/lib/module/ui/screens/trust/trustTier.js.map +0 -1
- package/lib/module/ui/stores/accountStore.js +0 -213
- package/lib/module/ui/stores/accountStore.js.map +0 -1
- package/lib/typescript/commonjs/ui/hooks/useSwitchableAccounts.d.ts +0 -23
- package/lib/typescript/commonjs/ui/hooks/useSwitchableAccounts.d.ts.map +0 -1
- package/lib/typescript/commonjs/ui/screens/trust/trustTier.d.ts +0 -9
- package/lib/typescript/commonjs/ui/screens/trust/trustTier.d.ts.map +0 -1
- package/lib/typescript/commonjs/ui/stores/accountStore.d.ts +0 -27
- package/lib/typescript/commonjs/ui/stores/accountStore.d.ts.map +0 -1
- package/lib/typescript/module/ui/hooks/useSwitchableAccounts.d.ts +0 -23
- package/lib/typescript/module/ui/hooks/useSwitchableAccounts.d.ts.map +0 -1
- package/lib/typescript/module/ui/screens/trust/trustTier.d.ts +0 -9
- package/lib/typescript/module/ui/screens/trust/trustTier.d.ts.map +0 -1
- package/lib/typescript/module/ui/stores/accountStore.d.ts +0 -27
- package/lib/typescript/module/ui/stores/accountStore.d.ts.map +0 -1
- package/src/ui/hooks/useSwitchableAccounts.ts +0 -75
- package/src/ui/screens/trust/trustTier.ts +0 -20
- package/src/ui/stores/accountStore.ts +0 -264
|
@@ -0,0 +1,623 @@
|
|
|
1
|
+
import type { DeviceDirectory, DeviceSessionState } from '@oxyhq/contracts';
|
|
2
|
+
import type { ClientSession, DeviceContext, User } from '@oxyhq/core';
|
|
3
|
+
import {
|
|
4
|
+
accountIdsOf,
|
|
5
|
+
activeSessionIdOf,
|
|
6
|
+
activeUserOf,
|
|
7
|
+
deviceStateToClientSessions,
|
|
8
|
+
mergeSessions as mergeSessionLists,
|
|
9
|
+
normalizeAndSortSessions,
|
|
10
|
+
normalizeUserIdentity,
|
|
11
|
+
resolveActiveContext,
|
|
12
|
+
sessionsArraysEqual,
|
|
13
|
+
} from '@oxyhq/core';
|
|
14
|
+
import type { IdentitySessionBinding } from '../session/identityBinding';
|
|
15
|
+
import type { OxyRuntimeSnapshot, OxyRuntimeStatus, OxyTokenStatus } from './types';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* A change of SUBJECT — the account the session acts as.
|
|
19
|
+
*
|
|
20
|
+
* `previous === null` is an initial sign-in (nothing account-scoped is cached
|
|
21
|
+
* yet); `next === null` is a sign-out; both non-null and different is a switch,
|
|
22
|
+
* the only case that must invalidate a warm cache.
|
|
23
|
+
*/
|
|
24
|
+
export interface SubjectTransition {
|
|
25
|
+
previous: string | null;
|
|
26
|
+
next: string | null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The three seams the runtime reads, declared STRUCTURALLY rather than as the
|
|
31
|
+
* concrete `OxyServices` / `SessionClient` / `SessionClientHost` types.
|
|
32
|
+
*
|
|
33
|
+
* The real classes satisfy them, so nothing changes at the call site — but a
|
|
34
|
+
* test can hand the runtime three plain objects instead of casting a stub
|
|
35
|
+
* through `unknown`, which is how a stub silently drifts from the interface it
|
|
36
|
+
* pretends to implement.
|
|
37
|
+
*/
|
|
38
|
+
export interface RuntimeClient {
|
|
39
|
+
getAccessToken(): string | null;
|
|
40
|
+
getUsersByIds(ids: string[]): Promise<User[]>;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface RuntimeSessionClient {
|
|
44
|
+
getState(): DeviceSessionState | null;
|
|
45
|
+
getDirectory(): DeviceDirectory | null;
|
|
46
|
+
subscribe(listener: (state: DeviceSessionState | null) => void): () => void;
|
|
47
|
+
refreshDirectory(): Promise<void>;
|
|
48
|
+
activateContext(contextId: string): Promise<void>;
|
|
49
|
+
signOutContext(contextId: string): Promise<void>;
|
|
50
|
+
signOutPrincipal(principalId: string): Promise<void>;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface RuntimeSessionHost {
|
|
54
|
+
setCurrentAccountId(accountId: string | null): void;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface OxyRuntimeConfig {
|
|
58
|
+
oxyServices: RuntimeClient;
|
|
59
|
+
sessionClient: RuntimeSessionClient;
|
|
60
|
+
sessionClientHost: RuntimeSessionHost;
|
|
61
|
+
/** `null` for every account-mode runtime; the pin binding in identity mode. */
|
|
62
|
+
identity: IdentitySessionBinding | null;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Reset account-scoped caches. Called BETWEEN the bearer commit and the
|
|
66
|
+
* snapshot publish, which is the whole point of routing every fact change
|
|
67
|
+
* through one transition (ADR 0002's ordering invariant): a consumer woken by
|
|
68
|
+
* a switch must never read a cache entry belonging to the previous subject.
|
|
69
|
+
*/
|
|
70
|
+
onSubjectChange?: (transition: SubjectTransition) => void;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* The device reports zero accounts. Local UI teardown only — the durable
|
|
74
|
+
* device credential is the caller's decision, gated on a `request`-origin
|
|
75
|
+
* verdict.
|
|
76
|
+
*/
|
|
77
|
+
onDeviceEmpty: () => Promise<void>;
|
|
78
|
+
|
|
79
|
+
/** Persist the projected session-id list (display/bookkeeping storage). */
|
|
80
|
+
onSessionsProjected?: (sessionIds: string[]) => void;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Identity mode only: the pinned account is unknown or has left this device's
|
|
84
|
+
* session set, so the session must be re-derived from the local identity key.
|
|
85
|
+
* Projecting anything here would mean projecting somebody else's account.
|
|
86
|
+
*/
|
|
87
|
+
onIdentityUnbound?: (binding: IdentitySessionBinding, revision: number) => void;
|
|
88
|
+
|
|
89
|
+
logger: (message: string, error?: unknown) => void;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* The headless session runtime (ADR 0004).
|
|
94
|
+
*
|
|
95
|
+
* It owns the session facts and the ORDER in which they become visible. React
|
|
96
|
+
* is an adapter: the provider hands components this object and they subscribe
|
|
97
|
+
* with selectors, so an unrelated change (a locale, a dialog opening) cannot
|
|
98
|
+
* rebuild an auth consumer's view.
|
|
99
|
+
*/
|
|
100
|
+
export interface OxyRuntime {
|
|
101
|
+
getSnapshot(): OxyRuntimeSnapshot;
|
|
102
|
+
subscribe(listener: () => void): () => void;
|
|
103
|
+
|
|
104
|
+
/** Begin projecting `SessionClient` transitions. Returns the disposer. */
|
|
105
|
+
start(): () => void;
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Project the client's current device state onto the snapshot. Idempotent and
|
|
109
|
+
* revision-guarded: a projection whose profile fetch was overtaken by a
|
|
110
|
+
* fresher state discards itself rather than publishing a stale subject.
|
|
111
|
+
*/
|
|
112
|
+
reconcileFromClient(): Promise<void>;
|
|
113
|
+
|
|
114
|
+
/** Read the device directory and publish it (opts this runtime into ADR 0002). */
|
|
115
|
+
refreshDirectory(): Promise<void>;
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Make one `principal acting as account` context active (ADR 0002).
|
|
119
|
+
*
|
|
120
|
+
* `contextId`, never an account id: on a device holding two people the same
|
|
121
|
+
* organization is reachable through both, and an account id cannot name which
|
|
122
|
+
* route to take.
|
|
123
|
+
*/
|
|
124
|
+
activateContext(contextId: string): Promise<void>;
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Remove ONE `principal → account` pair. The same account reached through a
|
|
128
|
+
* second person is a different session with a different audit actor, and it
|
|
129
|
+
* stays.
|
|
130
|
+
*
|
|
131
|
+
* Context ids are NOT stable across a removal: the server offers the pair
|
|
132
|
+
* again under a NEW id on the next directory read, so nothing may cache one
|
|
133
|
+
* across this call.
|
|
134
|
+
*/
|
|
135
|
+
signOutContext(contextId: string): Promise<void>;
|
|
136
|
+
|
|
137
|
+
/** Remove ONE PERSON and every context they reach, and nobody else's. */
|
|
138
|
+
signOutPrincipal(principalId: string): Promise<void>;
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Run several fact changes as ONE transition.
|
|
142
|
+
*
|
|
143
|
+
* A session commit sets the optimistic session mirror, the active session id
|
|
144
|
+
* and the account. Left as three transitions those are three publishes, and
|
|
145
|
+
* — worse — the account is compared against a different baseline each time,
|
|
146
|
+
* so an account-scoped cache reset could run against a half-applied session.
|
|
147
|
+
* Batched, subscribers are woken once, after all of it, with the reset having
|
|
148
|
+
* run exactly once in between.
|
|
149
|
+
*/
|
|
150
|
+
batch(mutate: () => void): void;
|
|
151
|
+
|
|
152
|
+
setAccount(user: User): void;
|
|
153
|
+
setLoading(loading: boolean): void;
|
|
154
|
+
setError(error: string | null): void;
|
|
155
|
+
setStorageReady(ready: boolean): void;
|
|
156
|
+
setTokenReady(ready: boolean): void;
|
|
157
|
+
setHasAccessToken(has: boolean): void;
|
|
158
|
+
setSwitching(switching: boolean): void;
|
|
159
|
+
markAuthResolved(): void;
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Merge an optimistic local session mirror so the UI moves before the server
|
|
163
|
+
* round-trip. The authoritative projection overwrites it a moment later; both
|
|
164
|
+
* write the SAME field, so there is still exactly one owner.
|
|
165
|
+
*/
|
|
166
|
+
mergeSessions(incoming: ClientSession[], options?: { merge?: boolean; preserveSessionIds?: string[] }): void;
|
|
167
|
+
setActiveSessionId(sessionId: string | null): void;
|
|
168
|
+
|
|
169
|
+
/** Local teardown: drop the session projection without touching the server. */
|
|
170
|
+
clearSession(): void;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
interface RuntimeFacts {
|
|
174
|
+
deviceState: DeviceSessionState | null;
|
|
175
|
+
directory: DeviceDirectory | null;
|
|
176
|
+
profiles: Map<string, User>;
|
|
177
|
+
sessions: ClientSession[];
|
|
178
|
+
activeSessionId: string | null;
|
|
179
|
+
account: User | null;
|
|
180
|
+
pinnedAccountId: string | null;
|
|
181
|
+
isLoading: boolean;
|
|
182
|
+
error: string | null;
|
|
183
|
+
storageReady: boolean;
|
|
184
|
+
tokenReady: boolean;
|
|
185
|
+
hasAccessToken: boolean;
|
|
186
|
+
authResolved: boolean;
|
|
187
|
+
switching: boolean;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const EMPTY_SESSIONS: ClientSession[] = [];
|
|
191
|
+
|
|
192
|
+
function deriveStatus(facts: RuntimeFacts): OxyRuntimeStatus {
|
|
193
|
+
if (!facts.authResolved) {
|
|
194
|
+
return 'booting';
|
|
195
|
+
}
|
|
196
|
+
if (facts.account !== null) {
|
|
197
|
+
return 'authenticated';
|
|
198
|
+
}
|
|
199
|
+
return facts.error !== null ? 'error' : 'signed_out';
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function deriveTokenStatus(facts: RuntimeFacts): OxyTokenStatus {
|
|
203
|
+
if (facts.hasAccessToken && facts.tokenReady) {
|
|
204
|
+
return 'ready';
|
|
205
|
+
}
|
|
206
|
+
if (facts.account !== null || facts.hasAccessToken) {
|
|
207
|
+
return 'refreshing';
|
|
208
|
+
}
|
|
209
|
+
return 'missing';
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* The actor behind the active context, resolved to a full `User`.
|
|
214
|
+
*
|
|
215
|
+
* Falls back to the subject: on a device with no directory, and on any
|
|
216
|
+
* non-delegated context, the two ARE the same person, and saying so is more
|
|
217
|
+
* useful than a `null` every caller would have to special-case.
|
|
218
|
+
*/
|
|
219
|
+
function derivePrincipal(
|
|
220
|
+
activeContext: DeviceContext | null,
|
|
221
|
+
profiles: Map<string, User>,
|
|
222
|
+
account: User | null,
|
|
223
|
+
): User | null {
|
|
224
|
+
if (activeContext === null) {
|
|
225
|
+
return account;
|
|
226
|
+
}
|
|
227
|
+
return profiles.get(activeContext.actor.userId) ?? account;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export function createOxyRuntime(config: OxyRuntimeConfig): OxyRuntime {
|
|
231
|
+
const {
|
|
232
|
+
oxyServices,
|
|
233
|
+
sessionClient,
|
|
234
|
+
sessionClientHost,
|
|
235
|
+
identity,
|
|
236
|
+
onSubjectChange,
|
|
237
|
+
onDeviceEmpty,
|
|
238
|
+
onSessionsProjected,
|
|
239
|
+
onIdentityUnbound,
|
|
240
|
+
logger,
|
|
241
|
+
} = config;
|
|
242
|
+
|
|
243
|
+
const facts: RuntimeFacts = {
|
|
244
|
+
deviceState: null,
|
|
245
|
+
directory: null,
|
|
246
|
+
profiles: new Map(),
|
|
247
|
+
sessions: EMPTY_SESSIONS,
|
|
248
|
+
activeSessionId: null,
|
|
249
|
+
account: null,
|
|
250
|
+
pinnedAccountId: null,
|
|
251
|
+
isLoading: false,
|
|
252
|
+
error: null,
|
|
253
|
+
storageReady: false,
|
|
254
|
+
tokenReady: true,
|
|
255
|
+
hasAccessToken: Boolean(oxyServices.getAccessToken()),
|
|
256
|
+
authResolved: false,
|
|
257
|
+
switching: false,
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
const listeners = new Set<() => void>();
|
|
261
|
+
let snapshot = buildSnapshot();
|
|
262
|
+
/**
|
|
263
|
+
* Depth of the current transition. Nested mutations publish ONCE, at the
|
|
264
|
+
* outermost boundary — a projection that sets sessions, the active id and the
|
|
265
|
+
* account must not wake React three times with two of them half-applied.
|
|
266
|
+
*/
|
|
267
|
+
let depth = 0;
|
|
268
|
+
let dirty = false;
|
|
269
|
+
/** The subject as it stood when the OUTERMOST transition opened. */
|
|
270
|
+
let subjectAtEntry: string | null = null;
|
|
271
|
+
|
|
272
|
+
function buildSnapshot(): OxyRuntimeSnapshot {
|
|
273
|
+
const activeContext = resolveActiveContext(facts.directory);
|
|
274
|
+
return Object.freeze({
|
|
275
|
+
status: deriveStatus(facts),
|
|
276
|
+
device: facts.directory,
|
|
277
|
+
deviceState: facts.deviceState,
|
|
278
|
+
activeContext,
|
|
279
|
+
principal: derivePrincipal(activeContext, facts.profiles, facts.account),
|
|
280
|
+
account: facts.account,
|
|
281
|
+
tokenStatus: deriveTokenStatus(facts),
|
|
282
|
+
authResolved: facts.authResolved,
|
|
283
|
+
switching: facts.switching,
|
|
284
|
+
error: facts.error === null ? null : { message: facts.error, code: 'oxy_runtime_error' },
|
|
285
|
+
sessions: facts.sessions,
|
|
286
|
+
activeSessionId: facts.activeSessionId,
|
|
287
|
+
isLoading: facts.isLoading,
|
|
288
|
+
storageReady: facts.storageReady,
|
|
289
|
+
hasAccessToken: facts.hasAccessToken,
|
|
290
|
+
tokenReady: facts.tokenReady,
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function notify(): void {
|
|
295
|
+
for (const listener of [...listeners]) {
|
|
296
|
+
try {
|
|
297
|
+
listener();
|
|
298
|
+
} catch (error) {
|
|
299
|
+
logger('An OxyRuntime subscriber threw', error);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* The ONE ordered transition. Every fact change goes through here, so the
|
|
306
|
+
* sequence ADR 0002 mandates holds for a local switch, a socket push, a cold
|
|
307
|
+
* boot and a cross-tab wake alike:
|
|
308
|
+
*
|
|
309
|
+
* the bearer is already committed (SessionClient awaits its mint before it
|
|
310
|
+
* calls us) → reset account-scoped caches → publish the snapshot → notify.
|
|
311
|
+
*
|
|
312
|
+
* Publishing before the reset would let a woken component read a cache entry
|
|
313
|
+
* belonging to the previous subject while holding the new subject's bearer,
|
|
314
|
+
* which is the account-switch race in its quietest form: no error, just the
|
|
315
|
+
* wrong person's data.
|
|
316
|
+
*/
|
|
317
|
+
function transition(mutate: () => void): void {
|
|
318
|
+
if (depth === 0) {
|
|
319
|
+
subjectAtEntry = facts.account?.id ?? null;
|
|
320
|
+
}
|
|
321
|
+
depth += 1;
|
|
322
|
+
try {
|
|
323
|
+
mutate();
|
|
324
|
+
} finally {
|
|
325
|
+
depth -= 1;
|
|
326
|
+
}
|
|
327
|
+
if (depth > 0) {
|
|
328
|
+
// A nested transition publishes nothing and fires no reset: the subject
|
|
329
|
+
// is compared once, at the outermost boundary, so a projection that
|
|
330
|
+
// touches the account twice cannot reset the caches twice.
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
const nextSubject = facts.account?.id ?? null;
|
|
334
|
+
if (nextSubject !== subjectAtEntry) {
|
|
335
|
+
dirty = true;
|
|
336
|
+
onSubjectChange?.({ previous: subjectAtEntry, next: nextSubject });
|
|
337
|
+
}
|
|
338
|
+
if (!dirty) {
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
dirty = false;
|
|
342
|
+
snapshot = buildSnapshot();
|
|
343
|
+
notify();
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/** Mark the snapshot stale from inside a transition. */
|
|
347
|
+
function touch(): void {
|
|
348
|
+
dirty = true;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
function setSessions(next: ClientSession[]): void {
|
|
352
|
+
if (sessionsArraysEqual(facts.sessions, next)) {
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
facts.sessions = next;
|
|
356
|
+
touch();
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function setActiveSessionIdFact(sessionId: string | null): void {
|
|
360
|
+
if (facts.activeSessionId === sessionId) {
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
facts.activeSessionId = sessionId;
|
|
364
|
+
touch();
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* The one place a `User` becomes THE subject — and the one place identity is
|
|
369
|
+
* normalised, so a profile arriving as `_id` from one lane and `id` from
|
|
370
|
+
* another can never produce two accounts that are the same person.
|
|
371
|
+
*/
|
|
372
|
+
function setAccountFact(user: User | null): void {
|
|
373
|
+
if (user === null) {
|
|
374
|
+
if (facts.account === null) {
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
facts.account = null;
|
|
378
|
+
touch();
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
if (facts.account === user) {
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
facts.account = normalizeUserIdentity(user);
|
|
385
|
+
touch();
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// ── Projection ───────────────────────────────────────────────────────────
|
|
389
|
+
|
|
390
|
+
async function reconcileFromClient(): Promise<void> {
|
|
391
|
+
const state = sessionClient.getState();
|
|
392
|
+
if (state === null) {
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
if (state.accounts.length === 0) {
|
|
396
|
+
sessionClientHost.setCurrentAccountId(null);
|
|
397
|
+
await onDeviceEmpty();
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
// Capture the revision this projection is for, then bail after the async
|
|
401
|
+
// profile fetch if a fresher state has landed — last-writer-wins, so two
|
|
402
|
+
// overlapping projections can never publish in the wrong order.
|
|
403
|
+
const capturedRevision = state.revision;
|
|
404
|
+
// Resolve the pin BEFORE the fetch (memoised: one storage + keychain read
|
|
405
|
+
// per boot) so the first pass over a freshly applied state is already bound.
|
|
406
|
+
// Deferring it would let a sibling app's switch render once as this
|
|
407
|
+
// client's user before the pin corrected it.
|
|
408
|
+
const pinnedAccountId = identity ? await identity.ensurePinnedAccountId() : null;
|
|
409
|
+
const ids = accountIdsOf(state);
|
|
410
|
+
let users: User[] = [];
|
|
411
|
+
try {
|
|
412
|
+
users = ids.length > 0 ? await oxyServices.getUsersByIds(ids) : [];
|
|
413
|
+
} catch (fetchError) {
|
|
414
|
+
logger('Failed to resolve account profiles during the device projection', fetchError);
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
const latest = sessionClient.getState();
|
|
418
|
+
if (!latest || latest.revision !== capturedRevision) {
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
if (identity && (pinnedAccountId === null || activeSessionIdOf(latest, pinnedAccountId) === null)) {
|
|
422
|
+
// Either no verified pin, or the pinned account left this device's session
|
|
423
|
+
// set. Leave the current projection untouched and re-derive from the key.
|
|
424
|
+
onIdentityUnbound?.(identity, latest.revision);
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
const directory = sessionClient.getDirectory();
|
|
428
|
+
transition(() => {
|
|
429
|
+
facts.pinnedAccountId = pinnedAccountId;
|
|
430
|
+
if (facts.deviceState !== latest) {
|
|
431
|
+
facts.deviceState = latest;
|
|
432
|
+
touch();
|
|
433
|
+
}
|
|
434
|
+
if (facts.directory !== directory) {
|
|
435
|
+
facts.directory = directory;
|
|
436
|
+
touch();
|
|
437
|
+
}
|
|
438
|
+
for (const resolved of users) {
|
|
439
|
+
facts.profiles.set(resolved.id, resolved);
|
|
440
|
+
}
|
|
441
|
+
const projected = normalizeAndSortSessions(
|
|
442
|
+
deviceStateToClientSessions(latest, facts.profiles, pinnedAccountId),
|
|
443
|
+
facts.activeSessionId,
|
|
444
|
+
false,
|
|
445
|
+
);
|
|
446
|
+
setSessions(projected);
|
|
447
|
+
onSessionsProjected?.(projected.map((session) => session.sessionId));
|
|
448
|
+
setActiveSessionIdFact(activeSessionIdOf(latest, pinnedAccountId));
|
|
449
|
+
const activeUser = activeUserOf(latest, facts.profiles, pinnedAccountId);
|
|
450
|
+
if (activeUser) {
|
|
451
|
+
setAccountFact(activeUser);
|
|
452
|
+
}
|
|
453
|
+
});
|
|
454
|
+
// The host's current account feeds the socket handshake and the bearer's
|
|
455
|
+
// token-account comparisons, so it tracks the PIN — never the account
|
|
456
|
+
// another app switched the device to.
|
|
457
|
+
sessionClientHost.setCurrentAccountId(pinnedAccountId ?? latest.activeAccountId);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
return {
|
|
461
|
+
getSnapshot: () => snapshot,
|
|
462
|
+
|
|
463
|
+
subscribe(listener) {
|
|
464
|
+
listeners.add(listener);
|
|
465
|
+
return () => {
|
|
466
|
+
listeners.delete(listener);
|
|
467
|
+
};
|
|
468
|
+
},
|
|
469
|
+
|
|
470
|
+
start() {
|
|
471
|
+
return sessionClient.subscribe(() => {
|
|
472
|
+
void reconcileFromClient().catch((error) => {
|
|
473
|
+
logger('Failed to project a device-state transition', error);
|
|
474
|
+
});
|
|
475
|
+
});
|
|
476
|
+
},
|
|
477
|
+
|
|
478
|
+
reconcileFromClient,
|
|
479
|
+
|
|
480
|
+
batch(mutate) {
|
|
481
|
+
transition(mutate);
|
|
482
|
+
},
|
|
483
|
+
|
|
484
|
+
async activateContext(contextId) {
|
|
485
|
+
// `switching` is published BEFORE the request so a switcher can disable
|
|
486
|
+
// its rows for the whole round trip, and cleared in `finally` so a
|
|
487
|
+
// refused activation does not strand it on.
|
|
488
|
+
this.setSwitching(true);
|
|
489
|
+
try {
|
|
490
|
+
await sessionClient.activateContext(contextId);
|
|
491
|
+
await reconcileFromClient();
|
|
492
|
+
} finally {
|
|
493
|
+
this.setSwitching(false);
|
|
494
|
+
}
|
|
495
|
+
},
|
|
496
|
+
|
|
497
|
+
async signOutContext(contextId) {
|
|
498
|
+
await sessionClient.signOutContext(contextId);
|
|
499
|
+
await reconcileFromClient();
|
|
500
|
+
},
|
|
501
|
+
|
|
502
|
+
async signOutPrincipal(principalId) {
|
|
503
|
+
await sessionClient.signOutPrincipal(principalId);
|
|
504
|
+
await reconcileFromClient();
|
|
505
|
+
},
|
|
506
|
+
|
|
507
|
+
async refreshDirectory() {
|
|
508
|
+
await sessionClient.refreshDirectory();
|
|
509
|
+
const directory = sessionClient.getDirectory();
|
|
510
|
+
transition(() => {
|
|
511
|
+
if (facts.directory === directory) {
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
facts.directory = directory;
|
|
515
|
+
touch();
|
|
516
|
+
});
|
|
517
|
+
},
|
|
518
|
+
|
|
519
|
+
setAccount(user) {
|
|
520
|
+
transition(() => {
|
|
521
|
+
setAccountFact(user);
|
|
522
|
+
if (facts.isLoading) {
|
|
523
|
+
facts.isLoading = false;
|
|
524
|
+
touch();
|
|
525
|
+
}
|
|
526
|
+
if (facts.error !== null) {
|
|
527
|
+
facts.error = null;
|
|
528
|
+
touch();
|
|
529
|
+
}
|
|
530
|
+
facts.profiles.set(user.id, user);
|
|
531
|
+
});
|
|
532
|
+
},
|
|
533
|
+
|
|
534
|
+
setLoading(loading) {
|
|
535
|
+
transition(() => {
|
|
536
|
+
if (facts.isLoading === loading) return;
|
|
537
|
+
facts.isLoading = loading;
|
|
538
|
+
touch();
|
|
539
|
+
});
|
|
540
|
+
},
|
|
541
|
+
|
|
542
|
+
setError(error) {
|
|
543
|
+
transition(() => {
|
|
544
|
+
if (facts.error === error) return;
|
|
545
|
+
facts.error = error;
|
|
546
|
+
touch();
|
|
547
|
+
});
|
|
548
|
+
},
|
|
549
|
+
|
|
550
|
+
setStorageReady(ready) {
|
|
551
|
+
transition(() => {
|
|
552
|
+
if (facts.storageReady === ready) return;
|
|
553
|
+
facts.storageReady = ready;
|
|
554
|
+
touch();
|
|
555
|
+
});
|
|
556
|
+
},
|
|
557
|
+
|
|
558
|
+
setTokenReady(ready) {
|
|
559
|
+
transition(() => {
|
|
560
|
+
if (facts.tokenReady === ready) return;
|
|
561
|
+
facts.tokenReady = ready;
|
|
562
|
+
touch();
|
|
563
|
+
});
|
|
564
|
+
},
|
|
565
|
+
|
|
566
|
+
setHasAccessToken(has) {
|
|
567
|
+
transition(() => {
|
|
568
|
+
if (facts.hasAccessToken === has) return;
|
|
569
|
+
facts.hasAccessToken = has;
|
|
570
|
+
touch();
|
|
571
|
+
});
|
|
572
|
+
},
|
|
573
|
+
|
|
574
|
+
setSwitching(switching) {
|
|
575
|
+
transition(() => {
|
|
576
|
+
if (facts.switching === switching) return;
|
|
577
|
+
facts.switching = switching;
|
|
578
|
+
touch();
|
|
579
|
+
});
|
|
580
|
+
},
|
|
581
|
+
|
|
582
|
+
markAuthResolved() {
|
|
583
|
+
transition(() => {
|
|
584
|
+
if (facts.authResolved) return;
|
|
585
|
+
facts.authResolved = true;
|
|
586
|
+
facts.tokenReady = true;
|
|
587
|
+
touch();
|
|
588
|
+
});
|
|
589
|
+
},
|
|
590
|
+
|
|
591
|
+
mergeSessions(incoming, options = {}) {
|
|
592
|
+
transition(() => {
|
|
593
|
+
const processed = options.merge
|
|
594
|
+
? mergeSessionLists(facts.sessions, incoming, facts.activeSessionId, false)
|
|
595
|
+
: normalizeAndSortSessions(incoming, facts.activeSessionId, false);
|
|
596
|
+
onSessionsProjected?.([
|
|
597
|
+
...processed.map((session) => session.sessionId),
|
|
598
|
+
...(options.preserveSessionIds ?? []),
|
|
599
|
+
]);
|
|
600
|
+
setSessions(processed);
|
|
601
|
+
});
|
|
602
|
+
},
|
|
603
|
+
|
|
604
|
+
setActiveSessionId(sessionId) {
|
|
605
|
+
transition(() => {
|
|
606
|
+
setActiveSessionIdFact(sessionId);
|
|
607
|
+
});
|
|
608
|
+
},
|
|
609
|
+
|
|
610
|
+
clearSession() {
|
|
611
|
+
transition(() => {
|
|
612
|
+
setSessions(EMPTY_SESSIONS);
|
|
613
|
+
setActiveSessionIdFact(null);
|
|
614
|
+
setAccountFact(null);
|
|
615
|
+
facts.deviceState = null;
|
|
616
|
+
facts.directory = null;
|
|
617
|
+
facts.profiles = new Map();
|
|
618
|
+
facts.pinnedAccountId = null;
|
|
619
|
+
touch();
|
|
620
|
+
});
|
|
621
|
+
},
|
|
622
|
+
};
|
|
623
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The headless session runtime (ADR 0004).
|
|
3
|
+
*
|
|
4
|
+
* `createOxyRuntime` owns the session facts and the order in which they become
|
|
5
|
+
* visible; `useRuntimeSnapshot` / `useRuntimeSelector` are the React adapter
|
|
6
|
+
* over it. Nothing here imports React except the adapter, and nothing in the
|
|
7
|
+
* runtime reads React state — which is what lets the provider hold ONE stable
|
|
8
|
+
* reference and stop rebuilding a 58-field context value on every keystroke.
|
|
9
|
+
*/
|
|
10
|
+
export { createOxyRuntime } from './createOxyRuntime';
|
|
11
|
+
export type {
|
|
12
|
+
OxyRuntime,
|
|
13
|
+
OxyRuntimeConfig,
|
|
14
|
+
RuntimeClient,
|
|
15
|
+
RuntimeSessionClient,
|
|
16
|
+
RuntimeSessionHost,
|
|
17
|
+
SubjectTransition,
|
|
18
|
+
} from './createOxyRuntime';
|
|
19
|
+
export { useRuntimeSnapshot, useRuntimeSelector } from './useRuntimeSnapshot';
|
|
20
|
+
export {
|
|
21
|
+
OxyRuntimeHandleProvider,
|
|
22
|
+
OxyRuntimeMissingError,
|
|
23
|
+
useActiveAccount,
|
|
24
|
+
useDeviceDirectory,
|
|
25
|
+
useOxyRuntime,
|
|
26
|
+
useOxySnapshot,
|
|
27
|
+
} from './runtimeContext';
|
|
28
|
+
export type { ActiveAccount } from './runtimeContext';
|
|
29
|
+
export type {
|
|
30
|
+
OxyRuntimeError,
|
|
31
|
+
OxyRuntimeSnapshot,
|
|
32
|
+
OxyRuntimeStatus,
|
|
33
|
+
OxyTokenStatus,
|
|
34
|
+
} from './types';
|