@aws-amplify/ui
Version:
`@aws-amplify/ui` contains low-level logic & styles for stand-alone usage or re-use in framework-specific implementations.
147 lines (144 loc) • 6.17 kB
JavaScript
import { getActorContext, getActorState } from './actor.mjs';
import { NAVIGABLE_ROUTE_EVENT } from './constants.mjs';
import { getRoute } from './getRoute.mjs';
/**
* This file contains helpers that translates xstate internals to more
* understandable authenticator contexts. We provide these contexts with
* `useAuthenticator` hook/composable/service.
*/
/**
* Creates public facing auth helpers that abstracts out xstate implementation
* detail. Each framework implementation can export these helpers so that
* developers can send events without having to learn internals.
*
* ```
* const [state, send] = useActor(...);
* const { submit } = getSendEventAliases(send);
* submit({ username, password})
* ```
*/
const getSendEventAliases = (send) => {
const sendToMachine = (type) => {
// TODO If these were created during the creation of the machine & provider,
// then invalid transitions could be caught via https://xstate.js.org/docs/guides/states.html#state-can-event
return (data) => send({ type, data });
};
return {
initializeMachine: sendToMachine('INIT'),
resendCode: sendToMachine('RESEND'),
selectAuthMethod: sendToMachine('SELECT_METHOD'),
signOut: sendToMachine('SIGN_OUT'),
submitForm: sendToMachine('SUBMIT'),
toShowAuthMethods: sendToMachine('SHOW_AUTH_METHODS'),
updateForm: sendToMachine('CHANGE'),
updateBlur: sendToMachine('BLUR'),
// Actions that don't immediately invoke a service but instead transition to a screen
// are prefixed with `to*`
toFederatedSignIn: sendToMachine('FEDERATED_SIGN_IN'),
toForgotPassword: sendToMachine('FORGOT_PASSWORD'),
toSignIn: sendToMachine('SIGN_IN'),
toSignUp: sendToMachine('SIGN_UP'),
skipVerification: sendToMachine('SKIP'),
};
};
const getNextSendEventAliases = (send) => {
const { toFederatedSignIn, submitForm, resendCode, skipVerification } = getSendEventAliases(send);
return {
handleSubmit: submitForm,
resendConfirmationCode: resendCode,
// manual "route" navigation
setRoute: (route) => send({ type: NAVIGABLE_ROUTE_EVENT[route] }),
skipAttributeVerification: skipVerification,
toFederatedSignIn,
};
};
const getServiceContextFacade = (state) => {
const actorContext = (getActorContext(state) ?? {});
const { allowedMfaTypes, availableAuthMethods, challengeName, codeDeliveryDetails, preferredChallenge, remoteError: error, selectedAuthMethod, validationError: validationErrors, totpSecretCode = null, unverifiedUserAttributes, username, } = actorContext;
const { socialProviders = [], loginMechanisms } = state.context?.config ?? {};
const loginMechanism = loginMechanisms?.[0];
// check for user in actorContext prior to state context. actorContext is more "up to date",
// but is not available on all states
const user = actorContext?.user ?? state.context?.user;
const hasValidationErrors = !!(validationErrors && Object.keys(validationErrors).length > 0);
const actorState = getActorState(state);
const isPending = state.hasTag('pending') || actorState?.hasTag('pending');
const route = getRoute(state, actorState);
// Auth status represents the current state of the auth flow
// The `configuring` state is used to indicate when the xState machine is loading
const authStatus = ((route) => {
switch (route) {
case 'idle':
case 'setup':
return 'configuring';
case 'authenticated':
return 'authenticated';
default:
return 'unauthenticated';
}
})(route);
const facade = {
allowedMfaTypes,
authStatus,
availableAuthMethods,
challengeName,
codeDeliveryDetails,
error,
hasValidationErrors,
isPending,
loginMechanism,
preferredChallenge,
route,
selectedAuthMethod,
socialProviders,
totpSecretCode,
unverifiedUserAttributes,
user,
username,
validationErrors,
// @v6-migration-note
// While most of the properties
// on `AuthenticatorServiceContextFacade` can resolve to `undefined`, updating
// the interface requires material changes in consumers (namely `useAuthenticator`)
// which will have implications on the UI layer as typeguards and non-null checks
// are required to pass type checking. As the `Authenticator` is behaving as expected
// with the `AuthenticatorServiceContextFacade` interface, prefer to cast
};
return facade;
};
const getNextServiceContextFacade = (state) => {
const actorContext = (getActorContext(state) ?? {});
const { allowedMfaTypes, challengeName, codeDeliveryDetails, remoteError: errorMessage, totpSecretCode, unverifiedUserAttributes, username, } = actorContext;
const { socialProviders: federatedProviders, loginMechanisms } = state.context?.config ?? {};
const loginMechanism = loginMechanisms?.[0];
const actorState = getActorState(state);
const isPending = state.hasTag('pending') || actorState?.hasTag('pending');
// @todo-migration remove this cast for Authenticator.Next
const route = getRoute(state, actorState);
return {
allowedMfaTypes,
challengeName,
codeDeliveryDetails,
errorMessage,
federatedProviders,
isPending,
loginMechanism,
route,
totpSecretCode,
unverifiedUserAttributes,
username,
};
};
const getServiceFacade = ({ send, state, }) => {
const sendEventAliases = getSendEventAliases(send);
const serviceContext = getServiceContextFacade(state);
return {
...sendEventAliases,
...serviceContext,
};
};
const getNextServiceFacade = ({ send, state, }) => ({
...getNextSendEventAliases(send),
...getNextServiceContextFacade(state),
});
export { getNextServiceContextFacade, getNextServiceFacade, getSendEventAliases, getServiceContextFacade, getServiceFacade };