@easyyo/ory.elements-react
Version:
Ory Elements React - a collection of React components for authentication UIs.
1 lines • 680 kB
Source Map (JSON)
{"version":3,"sources":["../../../src/theme/default/assets/ory-badge-horizontal.svg","../../../src/theme/default/assets/ory-badge-vertical.svg","../../../src/theme/default/components/card/badge.tsx","../../../src/theme/default/components/card/content.tsx","../../../src/theme/default/components/card/footer.tsx","../../../src/components/card/two-step/state-select-method.tsx","../../../src/context/component.tsx","../../../src/context/flow-context.tsx","../../../src/context/form-state.ts","../../../src/components/card/two-step/utils.ts","../../../src/theme/default/utils/form.ts","../../../src/util/ui/index.ts","../../../src/context/config.tsx","../../../src/client/config.ts","../../../src/util/client.ts","../../../src/context/intl-context.tsx","../../../src/components/form/form.tsx","../../../src/components/form/useOryFormSubmit.ts","../../../src/util/onSubmitLogin.ts","../../../src/util/sdk-helpers/error.ts","../../../src/util/sdk-helpers/utils.ts","../../../src/util/onSubmitRecovery.ts","../../../src/util/onSubmitRegistration.ts","../../../src/util/onSubmitSettings.ts","../../../src/util/onSubmitVerification.ts","../../../src/components/form/form-helpers.ts","../../../src/components/form/messages.tsx","../../../src/components/form/social.tsx","../../../src/components/form/form-provider.tsx","../../../src/components/form/form-resolver.ts","../../../src/components/form/settings-section.tsx","../../../src/components/generic/divider.tsx","../../../src/components/generic/page-header.tsx","../../../src/components/settings/settings-card.tsx","../../../src/util/showToast.tsx","../../../src/components/form/nodes/input.tsx","../../../src/components/form/nodes/node.tsx","../../../src/components/settings/oidc-settings.tsx","../../../src/components/settings/passkey-settings.tsx","../../../src/components/settings/recovery-codes-settings.tsx","../../../src/components/settings/totp-settings.tsx","../../../src/components/settings/webauthn-settings.tsx","../../../src/util/i18n/index.ts","../../../src/locales/en.json","../../../src/locales/de.json","../../../src/locales/es.json","../../../src/locales/fr.json","../../../src/locales/nl.json","../../../src/locales/pl.json","../../../src/locales/pt.json","../../../src/locales/sv.json","../../../src/locales/no.json","../../../src/locales/index.ts","../../../src/context/provider.tsx","../../../src/components/card/header.tsx","../../../src/components/card/card.tsx","../../../src/components/card/footer.tsx","../../../src/components/card/content.tsx","../../../src/components/card/card-two-step.tsx","../../../src/components/card/two-step/state-method-active.tsx","../../../src/components/card/two-step/state-provide-identifier.tsx","../../../src/components/card/card-consent.tsx","../../../src/components/card/two-step/list-methods.tsx","../../../src/util/nodes.ts","../../../src/theme/default/utils/logout.ts","../../../src/theme/default/utils/url.ts","../../../src/theme/default/components/card/header.tsx","../../../src/theme/default/utils/constructCardHeader.ts","../../../src/theme/default/components/card/current-identifier-button.tsx","../../../src/theme/default/utils/attributes.ts","../../../src/util/omitAttributes.ts","../../../src/theme/default/assets/icons/arrow-left.svg","../../../src/theme/default/components/card/logo.tsx","../../../src/theme/default/components/card/index.tsx","../../../src/theme/default/utils/cn.ts","../../../src/theme/default/components/form/index.tsx","../../../src/theme/default/components/form/sso.tsx","../../../src/theme/default/provider-logos/apple.svg","../../../src/theme/default/provider-logos/auth0.svg","../../../src/theme/default/provider-logos/discord.svg","../../../src/theme/default/provider-logos/facebook.svg","../../../src/theme/default/provider-logos/github.svg","../../../src/theme/default/provider-logos/gitlab.svg","../../../src/theme/default/provider-logos/google.svg","../../../src/theme/default/provider-logos/linkedin.svg","../../../src/theme/default/provider-logos/microsoft.svg","../../../src/theme/default/provider-logos/slack.svg","../../../src/theme/default/provider-logos/spotify.svg","../../../src/theme/default/provider-logos/yandex.svg","../../../src/theme/default/provider-logos/x.svg","../../../src/theme/default/provider-logos/index.ts","../../../src/theme/default/components/form/spinner.tsx","../../../src/theme/default/components/card/auth-method-list-item.tsx","../../../src/theme/default/assets/icons/alert-triangle.svg","../../../src/theme/default/assets/icons/code-asterix.svg","../../../src/theme/default/assets/icons/code.svg","../../../src/theme/default/assets/icons/passkey.svg","../../../src/theme/default/assets/icons/password.svg","../../../src/theme/default/assets/icons/totp.svg","../../../src/theme/default/assets/icons/webauthn.svg","../../../src/theme/default/components/card/list-item.tsx","../../../src/theme/default/components/form/button.tsx","../../../src/theme/default/components/form/checkbox.tsx","../../../src/theme/default/components/ui/checkbox-label.tsx","../../../src/theme/default/components/form/group-container.tsx","../../../src/util/childCounter.ts","../../../src/theme/default/components/form/horizontal-divider.tsx","../../../src/theme/default/components/form/image.tsx","../../../src/theme/default/components/form/input.tsx","../../../src/theme/default/assets/icons/eye-off.svg","../../../src/theme/default/assets/icons/eye.svg","../../../src/theme/default/components/form/label.tsx","../../../src/theme/default/components/form/link-button.tsx","../../../src/theme/default/components/form/pin-code-input.tsx","../../../src/theme/default/components/form/shadcn/otp-input.tsx","../../../src/theme/default/components/form/section.tsx","../../../src/theme/default/components/form/text.tsx","../../../src/theme/default/components/generic/page-header.tsx","../../../src/theme/default/components/ui/user-menu.tsx","../../../src/theme/default/assets/icons/logout.svg","../../../src/theme/default/assets/icons/settings.svg","../../../src/theme/default/utils/user.ts","../../../src/theme/default/components/ui/dropdown-menu.tsx","../../../src/theme/default/components/ui/user-avater.tsx","../../../src/theme/default/assets/icons/user.svg","../../../src/theme/default/components/settings/settings-oidc.tsx","../../../src/theme/default/assets/icons/trash.svg","../../../src/theme/default/components/settings/settings-passkey.tsx","../../../src/theme/default/assets/icons/download.svg","../../../src/theme/default/assets/icons/refresh.svg","../../../src/theme/default/components/settings/settings-recovery-codes.tsx","../../../src/theme/default/components/settings/settings-totp.tsx","../../../src/theme/default/assets/icons/qrcode.svg","../../../src/theme/default/components/settings/settings-webauthn.tsx","../../../src/theme/default/assets/icons/key.svg","../../../src/theme/default/components/card/auth-method-list-container.tsx","../../../src/theme/default/components/form/captcha.tsx","../../../src/theme/default/assets/icons/personal.svg","../../../src/theme/default/assets/icons/message.svg","../../../src/theme/default/assets/icons/phone.svg","../../../src/theme/default/components/form/consent-scope-checkbox.tsx","../../../src/theme/default/components/generic/toast.tsx","../../../src/theme/default/components/default-components.tsx","../../../src/theme/default/flows/error.tsx","../../../src/theme/default/flows/login.tsx","../../../src/theme/default/flows/recovery.tsx","../../../src/theme/default/flows/registration.tsx","../../../src/theme/default/flows/settings.tsx","../../../src/theme/default/flows/verification.tsx","../../../src/theme/default/flows/consent.tsx","../../../src/theme/default/utils/oauth2.ts"],"sourcesContent":["import * as React from \"react\";\nconst SvgOryBadgeHorizontal = props => <svg xmlns=\"http://www.w3.org/2000/svg\" width={props?.width ? props.width : props?.size ?? 20} height={props?.height ? props.height : props?.size ?? 20} fill=\"none\" {...props}><path fill=\"currentColor\" d=\"M18.007 8h-1.71l2.007-3.996L16.296 0h1.711l1.145 2.301L20.327 0H22z\" /><path fill=\"currentColor\" fillRule=\"evenodd\" d=\"M12.902 4.86a2.47 2.47 0 0 0 1.796-2.365v-.038C14.687 1.097 13.592 0 12.245 0H9.037v1.44l1.208 1.985H9.04V8h1.51V4.949h.633L13.04 8h1.775zm-.876-1.431h.223c.52 0 .943-.427.943-.953a.95.95 0 0 0-.943-.952h-1.39zM4 0a4 4 0 1 0-.001 7.999A4 4 0 0 0 4 0M1.524 4a2.476 2.476 0 1 0 4.952 0 2.476 2.476 0 0 0-4.952 0\" clipRule=\"evenodd\" /></svg>;\nexport default SvgOryBadgeHorizontal;","import * as React from \"react\";\nconst SvgOryBadgeVertical = props => <svg xmlns=\"http://www.w3.org/2000/svg\" width={props?.width ? props.width : props?.size ?? 20} height={props?.height ? props.height : props?.size ?? 20} fill=\"none\" {...props}><path fill=\"currentColor\" d=\"M8 3.993v1.71L4.004 3.697 0 5.704V3.993l2.301-1.145L0 1.673V0z\" /><path fill=\"currentColor\" fillRule=\"evenodd\" d=\"M4.86 9.099a2.47 2.47 0 0 0-2.365-1.797h-.038C1.097 7.313 0 8.408 0 9.755v3.208h1.44l1.985-1.208v1.204H8v-1.51H4.949v-.633L8 8.96V7.185zm-1.431.875v-.223a.95.95 0 0 0-.953-.943.95.95 0 0 0-.952.943v1.39zM0 18a4 4 0 1 0 8 0 4 4 0 0 0-8 0m4 2.476a2.476 2.476 0 1 0 0-4.952 2.476 2.476 0 0 0 0 4.952\" clipRule=\"evenodd\" /></svg>;\nexport default SvgOryBadgeVertical;","// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport OryLogoHorizontal from \"../../assets/ory-badge-horizontal.svg\"\nimport OryLogoVertical from \"../../assets/ory-badge-vertical.svg\"\n\nexport function Badge() {\n return (\n <div className=\"absolute border border-ory-border-default bg-ory-background-default p-2 font-bold text-ory-foreground-default max-sm:bottom-0 max-sm:left-8 max-sm:translate-y-full max-sm:rounded-b-branding max-sm:py-[7px] sm:top-8 sm:right-0 sm:translate-x-full sm:rounded-r-branding sm:pl-[7px]\">\n <OryLogoHorizontal width={22} height={8} className=\"sm:hidden\" />\n <OryLogoVertical width={8} height={22} className=\"max-sm:hidden\" />\n </div>\n )\n}\n","// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { OryCardContentProps } from \"@ory/elements-react\"\n\n/**\n * Simply renders the children passed to it.\n *\n * @param props - pass children to render instead of the default Ory Card components\n * @returns\n * @group Components\n * @category Default Components\n */\nexport function DefaultCardContent({ children }: OryCardContentProps) {\n return children\n}\n","// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { FlowType, LoginFlow, UiNodeInputAttributes } from \"@ory/client-fetch\"\nimport {\n ConsentFlow,\n FormState,\n useComponents,\n useOryConfiguration,\n useOryFlow,\n} from \"@ory/elements-react\"\nimport { useIntl } from \"react-intl\"\nimport { toAuthMethodPickerOptions } from \"../../../../components/card/two-step/state-select-method\"\nimport { findScreenSelectionButton } from \"../../../../util/nodes\"\nimport {\n findNode,\n nodesToAuthMethodGroups,\n useNodeGroupsWithVisibleNodes,\n} from \"../../../../util/ui\"\nimport { useClientLogout } from \"../../utils/logout\"\nimport { initFlowUrl, restartFlowUrl } from \"../../utils/url\"\n\n/**\n * DefaultCardFooter renders the default footer for the card component based on the current flow type.\n *\n * @returns The default card footer component that renders the appropriate footer based on the current flow type.\n * @group Components\n * @category Default Components\n */\nexport function DefaultCardFooter() {\n const oryFlow = useOryFlow()\n switch (oryFlow.flowType) {\n case FlowType.Login:\n return <LoginCardFooter flow={oryFlow.flow} />\n case FlowType.Registration:\n return <RegistrationCardFooter />\n case FlowType.Recovery:\n return <RecoveryCardFooter />\n case FlowType.Verification:\n return <VerificationCardFooter />\n case FlowType.OAuth2Consent:\n return <ConsentCardFooter flow={oryFlow.flow} />\n default:\n return null\n }\n}\n\nfunction shouldShowLogoutButton(\n flow: LoginFlow,\n formState: FormState,\n authMethods: string[],\n) {\n // Always for refresh flows, as we know there is a session\n if (flow.refresh) {\n return true\n }\n\n // In aal2 flows we sometimes show the logout button\n if (flow.requested_aal === \"aal2\") {\n // Always on the \"method selector\" screen\n if (formState.current === \"select_method\") {\n return true\n }\n // On the \"method active\" screen, if it's a code method\n // If the method is any other than code, we want to show a \"Choose another method\" button\n // This is handled below.\n // TODO: refactor this, to not have this logic in two places\n if (formState.current === \"method_active\" && flow.active === \"code\") {\n return true\n }\n // If there are no other methods, we want to show the logout button\n // This is the case when the user only has one method (e.g. code or totp), set up\n // and the user is on the \"method active\" screen\n // In that case there is no \"select_method\" state, so going back to that screen wouldn't work\n if (formState.current === \"method_active\" && authMethods.length === 1) {\n return true\n }\n }\n return false\n}\n\ntype LoginCardFooterProps = {\n flow: LoginFlow\n}\n\nfunction LoginCardFooter({ flow }: LoginCardFooterProps) {\n const { dispatchFormState, formState } = useOryFlow()\n const config = useOryConfiguration()\n const intl = useIntl()\n\n const authMethods = nodesToAuthMethodGroups(flow.ui.nodes)\n\n let returnTo = config.project.default_redirect_url\n if (flow.return_to) {\n returnTo = flow.return_to\n }\n if (!returnTo) {\n returnTo = restartFlowUrl(\n flow,\n `${config.sdk.url}/self-service/${FlowType.Login}/browser`,\n )\n }\n\n if (shouldShowLogoutButton(flow, formState, authMethods)) {\n return <LogoutButton returnTo={returnTo} />\n }\n\n return (\n <>\n {formState.current === \"provide_identifier\" &&\n config.project.registration_enabled && (\n <span className=\"leading-normal font-normal text-interface-foreground-default-primary antialiased\">\n {intl.formatMessage({\n id: \"login.registration-label\",\n defaultMessage: \"No account?\",\n })}{\" \"}\n <a\n className=\"text-button-link-brand-brand underline transition-colors hover:text-button-link-brand-brand-hover\"\n href={initFlowUrl(config.sdk.url, \"registration\", flow)}\n data-testid={\"ory/screen/login/action/register\"}\n >\n {intl.formatMessage({\n id: \"login.registration-button\",\n defaultMessage: \"Sign up\",\n })}\n </a>\n </span>\n )}\n {authMethods.length > 1 && formState.current === \"method_active\" && (\n <span className=\"leading-normal font-normal text-interface-foreground-default-primary antialiased\">\n <button\n className=\"text-button-link-brand-brand underline transition-colors hover:text-button-link-brand-brand-hover\"\n onClick={() => {\n dispatchFormState({\n type: \"action_clear_active_method\",\n })\n }}\n data-testid={\"ory/screen/login/mfa/action/selectMethod\"}\n >\n {intl.formatMessage({\n id: \"login.2fa.method.go-back\",\n })}\n </button>\n </span>\n )}\n {authMethods.length === 1 &&\n authMethods[0] === \"code\" &&\n formState.current === \"method_active\" && (\n <span className=\"leading-normal font-normal text-interface-foreground-default-primary antialiased\">\n <a\n className=\"text-button-link-brand-brand underline transition-colors hover:text-button-link-brand-brand-hover\"\n href={returnTo}\n data-testid={\"ory/screen/login/action/cancel\"}\n >\n {intl.formatMessage({\n id: \"login.2fa.go-back.link\",\n })}\n </a>\n </span>\n )}\n </>\n )\n}\n\ntype LogoutButtonProps = {\n returnTo?: string\n}\n\nfunction LogoutButton({ returnTo }: LogoutButtonProps) {\n const config = useOryConfiguration()\n const intl = useIntl()\n const { logoutFlow: logout, didLoad: didLoadLogout } = useClientLogout(config)\n\n return (\n <span className=\"leading-normal font-normal text-interface-foreground-default-primary antialiased\">\n {intl.formatMessage({\n id: \"login.2fa.go-back\",\n })}{\" \"}\n <a\n className=\"text-button-link-brand-brand underline transition-colors hover:text-button-link-brand-brand-hover\"\n href={logout ? logout?.logout_url : returnTo}\n data-testid={\n // Only add the test-id when the logout link has loaded.\n didLoadLogout ? \"ory/screen/login/action/logout\" : undefined\n }\n >\n {intl.formatMessage({\n id:\n !didLoadLogout || logout\n ? \"login.logout-button\"\n : \"login.2fa.go-back.link\",\n })}\n </a>\n </span>\n )\n}\n\nfunction RegistrationCardFooter() {\n const intl = useIntl()\n const { flow, formState, dispatchFormState } = useOryFlow()\n const config = useOryConfiguration()\n const visibleGroups = useNodeGroupsWithVisibleNodes(flow.ui.nodes)\n const authMethodBlocks = toAuthMethodPickerOptions(visibleGroups)\n\n const screenSelectionNode = findScreenSelectionButton(flow.ui.nodes)\n switch (formState.current) {\n case \"method_active\":\n if (!screenSelectionNode || Object.entries(authMethodBlocks).length < 2) {\n return null\n }\n\n return (\n <span className=\"leading-normal font-normal text-interface-foreground-default-primary antialiased\">\n <button\n className=\"text-button-link-brand-brand underline transition-colors hover:text-button-link-brand-brand-hover\"\n onClick={() => {\n dispatchFormState({\n type: \"action_clear_active_method\",\n })\n }}\n data-testid={\"ory/screen/registration/action/selectMethod\"}\n type=\"button\"\n >\n {intl.formatMessage({\n id: \"card.footer.select-another-method\",\n defaultMessage: \"Select another method\",\n })}\n </button>\n </span>\n )\n case \"select_method\":\n default:\n return (\n <span className=\"leading-normal font-normal text-interface-foreground-default-primary antialiased\">\n {intl.formatMessage({\n id: \"registration.login-label\",\n defaultMessage: \"Already have an account?\",\n })}{\" \"}\n <a\n className=\"text-button-link-brand-brand underline transition-colors hover:text-button-link-brand-brand-hover\"\n href={initFlowUrl(config.sdk.url, \"login\", flow)}\n data-testid={\"ory/screen/registration/action/login\"}\n >\n {intl.formatMessage({\n id: \"registration.login-button\",\n defaultMessage: \"Sign in\",\n })}\n </a>\n </span>\n )\n }\n}\n\nfunction RecoveryCardFooter() {\n return null\n}\n\nfunction VerificationCardFooter() {\n return null\n}\n\n/**\n * Props for the ConsentCardFooter component.\n *\n * @hidden\n * @inline\n */\ntype ConsentCardFooterProps = {\n /** The consent flow to render the footer for. */\n flow: ConsentFlow\n}\n\nfunction ConsentCardFooter({ flow }: ConsentCardFooterProps) {\n const { Node } = useComponents()\n\n const rememberNode = findNode(flow.ui.nodes, {\n group: \"oauth2_consent\",\n node_type: \"input\",\n name: \"remember\",\n })\n\n return (\n <div className=\"flex flex-col gap-8\">\n <div>\n <p className=\"leading-normal font-medium text-interface-foreground-default-secondary\">\n Make sure you trust {flow.consent_request.client?.client_name}\n </p>\n <p className=\"leading-normal text-interface-foreground-default-secondary\">\n You may be sharing sensitive information with this site or\n application.\n </p>\n </div>\n {rememberNode && (\n <Node.Checkbox\n attributes={rememberNode.attributes}\n node={rememberNode}\n />\n )}\n <div className=\"grid grid-cols-1 gap-2 md:grid-cols-2\">\n {flow.ui.nodes\n .filter(\n (n) =>\n n.attributes.node_type === \"input\" &&\n n.attributes.type === \"submit\",\n )\n .map((n) => {\n const attributes = n.attributes as UiNodeInputAttributes\n return (\n <Node.Button\n key={attributes.value}\n node={n}\n attributes={attributes}\n />\n )\n })}\n </div>\n <p className=\"text-sm\">\n <span className=\"text-interface-foreground-default-tertiary\">\n Authorizing will redirect to{\" \"}\n {flow.consent_request.client?.client_name}\n </span>\n </p>\n </div>\n )\n}\n","// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport {\n isUiNodeScriptAttributes,\n UiNode,\n UiNodeGroupEnum,\n UiText,\n} from \"@ory/client-fetch\"\nimport { OryCard, OryCardContent, OryCardFooter } from \"./../\"\nimport { useComponents, useNodeSorter, useOryFlow } from \"../../../context\"\nimport {\n findNode,\n GroupedNodes,\n hasSingleSignOnNodes,\n useFunctionalNodes,\n useNodeGroupsWithVisibleNodes,\n} from \"../../../util/ui\"\nimport { OryForm } from \"../../form/form\"\nimport { OryCardValidationMessages } from \"../../form/messages\"\nimport { Node } from \"../../form/nodes/node\"\nimport { OryFormSsoForm } from \"../../form/social\"\nimport { handleAfterFormSubmit } from \"./utils\"\nimport { OryCardHeader } from \"../header\"\nimport { useIntl } from \"react-intl\"\nimport { AuthMethodList, AuthMethodOptions } from \"./list-methods\"\n\n/**\n * Converts the visible groups of nodes into a format suitable for the\n * AuthMethodOptions\n *\n * @param visibleGroups - The visible groups of nodes\n */\nexport function toAuthMethodPickerOptions(\n visibleGroups: GroupedNodes,\n): AuthMethodOptions {\n return Object.fromEntries(\n Object.values(UiNodeGroupEnum)\n .filter((group) => visibleGroups[group]?.length)\n .filter(\n (group) =>\n !(\n [\n UiNodeGroupEnum.Oidc,\n UiNodeGroupEnum.Saml,\n UiNodeGroupEnum.Default,\n UiNodeGroupEnum.IdentifierFirst,\n UiNodeGroupEnum.Profile,\n UiNodeGroupEnum.Captcha,\n ] as UiNodeGroupEnum[]\n ).includes(group),\n )\n .map((g) => [g, {}]),\n )\n}\n\nexport function SelectMethodForm() {\n const { Form, Card } = useComponents()\n const { flow, flowType, dispatchFormState } = useOryFlow()\n const { ui } = flow\n\n const nodeSorter = useNodeSorter()\n const sortNodes = (a: UiNode, b: UiNode) => nodeSorter(a, b, { flowType })\n\n const visibleGroups = useNodeGroupsWithVisibleNodes(ui.nodes)\n const authMethodBlocks = toAuthMethodPickerOptions(visibleGroups)\n const authMethodAdditionalNodes = useFunctionalNodes(ui.nodes)\n // TODO(jonas): rework this (again). The above doesn't work to include the credential nodes and the Captcha nodes behave slightly differently.\n // This is a workaround to include the credential nodes in the auth method blocks.\n const hiddenNodes = ui.nodes.filter(\n (n) =>\n n.group !== UiNodeGroupEnum.Captcha &&\n ((n.attributes.node_type === \"input\" && n.attributes.type === \"hidden\") ||\n isUiNodeScriptAttributes(n.attributes)),\n )\n\n // Special case to show the address on code method selector\n if (UiNodeGroupEnum.Code in authMethodBlocks) {\n let identifier = findNode(ui.nodes, {\n group: \"identifier_first\",\n node_type: \"input\",\n name: \"identifier\",\n })?.attributes?.value\n identifier ||= findNode(ui.nodes, {\n group: \"code\",\n node_type: \"input\",\n name: \"address\",\n })?.attributes?.value\n if (identifier) {\n authMethodBlocks[UiNodeGroupEnum.Code] = {\n title: {\n id: \"identities.messages.1010023\",\n values: { address: identifier },\n },\n }\n }\n }\n\n return (\n <OryCard>\n <OryCardHeader />\n <OryCardContent>\n <OryCardValidationMessages />\n <OryFormSsoForm />\n {Object.entries(authMethodBlocks).length > 0 ? (\n <OryForm onAfterSubmit={handleAfterFormSubmit(dispatchFormState)}>\n <Form.Group>\n <Card.Divider />\n <AuthMethodList\n options={authMethodBlocks}\n setSelectedGroup={(group) =>\n dispatchFormState({\n type: \"action_select_method\",\n method: group,\n })\n }\n />\n {authMethodAdditionalNodes.sort(sortNodes).map((node, k) => (\n <Node node={node} key={k} />\n ))}\n </Form.Group>\n {hiddenNodes.map((node, k) => (\n <Node node={node} key={k} />\n ))}\n </OryForm>\n ) : (\n !hasSingleSignOnNodes(ui.nodes) && <NoMethodsMessage />\n )}\n </OryCardContent>\n <OryCardFooter />\n </OryCard>\n )\n}\n\nfunction NoMethodsMessage() {\n const intl = useIntl()\n const { Message } = useComponents()\n\n // This is defined in Ory Kratos as well.\n const noMethods: UiText = {\n id: 5000002,\n text: intl.formatMessage({\n id: `identities.messages.5000002`,\n defaultMessage:\n \"No authentication methods are available for this request. Please contact the site or app owner.\",\n }),\n type: \"error\",\n }\n\n return (\n <div data-testid={`ory/form/methods/local`}>\n <Message.Root>\n <Message.Content key={noMethods.id} message={noMethods} />\n </Message.Root>\n </div>\n )\n}\n","// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport {\n isUiNodeInputAttributes,\n UiNode,\n UiNodeGroupEnum,\n} from \"@ory/client-fetch\"\nimport { PropsWithChildren, createContext, useContext } from \"react\"\nimport { OryFlowComponents } from \"../components\"\n\ntype ComponentContextValue = {\n components: OryFlowComponents\n nodeSorter: (a: UiNode, b: UiNode, ctx: { flowType: string }) => number\n groupSorter: (a: UiNodeGroupEnum, b: UiNodeGroupEnum) => number\n}\n\nconst ComponentContext = createContext<ComponentContextValue>({\n components: null!, // fine because we throw an error if it's not provided\n nodeSorter: () => 0,\n groupSorter: () => 0,\n})\n\n/**\n * The `useComponents` hook provides access to the Ory Flow components provided in the `OryComponentProvider`.\n *\n * You can use this hook to access the components defined in the `components` prop of the `OryComponentProvider`.\n *\n * @returns the current component context value.\n * @group Hooks\n */\nexport function useComponents() {\n const ctx = useContext(ComponentContext)\n if (!ctx) {\n throw new Error(\"useComponents must be used within a ComponentProvider\")\n }\n return ctx.components\n}\n\n/**\n * The `useNodeSorter` hook provides a way to access the node sorting function\n *\n * The node sorting function is used to determine the order of nodes in a flow based on their attributes and context.\n *\n * To customize the sorting behavior, you can provide a custom `nodeSorter` function to the `OryComponentProvider`.\n *\n * @returns a function that sorts nodes based on the provided context.\n * @group Hooks\n */\nexport function useNodeSorter() {\n const ctx = useContext(ComponentContext)\n if (!ctx) {\n throw new Error(\"useNodeSorter must be used within a ComponentProvider\")\n }\n return ctx.nodeSorter\n}\n\nexport function useGroupSorter() {\n const ctx = useContext(ComponentContext)\n if (!ctx) {\n throw new Error(\"useGroupSorter must be used within a ComponentProvider\")\n }\n return ctx.groupSorter\n}\n\nconst defaultNodeOrder = [\n \"oidc\",\n \"saml\",\n \"identifier_first\",\n \"default\",\n \"profile\",\n \"password\",\n // CAPTCHA is below password because otherwise the password input field\n // would be above the captcha. Somehow, we sort the password sign up button somewhere else to be always at the bottom.\n \"captcha\",\n \"passkey\",\n \"code\",\n \"webauthn\",\n]\n\nexport function defaultNodeSorter(\n a: UiNode,\n b: UiNode,\n // ctx: { flowType: string },\n): number {\n // First handle the special case: captcha vs submit button\n const aIsCaptcha = a.group === \"captcha\"\n const bIsCaptcha = b.group === \"captcha\"\n const aIsSubmit =\n isUiNodeInputAttributes(a.attributes) && a.attributes.type === \"submit\"\n const bIsSubmit =\n isUiNodeInputAttributes(b.attributes) && b.attributes.type === \"submit\"\n\n // If comparing captcha and submit, always put captcha first\n if (aIsCaptcha && bIsSubmit) {\n return -1 // a (captcha) comes before b (submit)\n }\n if (bIsCaptcha && aIsSubmit) {\n return 1 // b (captcha) comes before a (submit)\n }\n\n // For all other cases, use the standard group ordering\n const aGroupWeight = defaultNodeOrder.indexOf(a.group) ?? 999\n const bGroupWeight = defaultNodeOrder.indexOf(b.group) ?? 999\n\n return aGroupWeight - bGroupWeight\n}\n\nconst defaultGroupOrder: UiNodeGroupEnum[] = [\n UiNodeGroupEnum.Default,\n UiNodeGroupEnum.Profile,\n UiNodeGroupEnum.Password,\n UiNodeGroupEnum.Oidc,\n UiNodeGroupEnum.Code,\n UiNodeGroupEnum.LookupSecret,\n UiNodeGroupEnum.Passkey,\n UiNodeGroupEnum.Webauthn,\n UiNodeGroupEnum.Totp,\n]\n\nfunction defaultGroupSorter(a: UiNodeGroupEnum, b: UiNodeGroupEnum): number {\n const aGroupWeight = defaultGroupOrder.indexOf(a) ?? 999\n const bGroupWeight = defaultGroupOrder.indexOf(b) ?? 999\n\n return aGroupWeight - bGroupWeight\n}\n\ntype ComponentProviderProps = {\n components: OryFlowComponents\n nodeSorter?: (a: UiNode, b: UiNode, ctx: { flowType: string }) => number\n groupSorter?: (a: UiNodeGroupEnum, b: UiNodeGroupEnum) => number\n}\n\nexport function OryComponentProvider({\n children,\n components,\n nodeSorter = defaultNodeSorter,\n groupSorter = defaultGroupSorter,\n}: PropsWithChildren<ComponentProviderProps>) {\n return (\n <ComponentContext.Provider\n value={{\n components,\n nodeSorter,\n groupSorter,\n }}\n >\n {children}\n </ComponentContext.Provider>\n )\n}\n","// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport {\n Dispatch,\n PropsWithChildren,\n createContext,\n useContext,\n useState,\n} from \"react\"\nimport { OryFlowContainer } from \"../util/flowContainer\"\nimport { FormState, FormStateAction, useFormStateReducer } from \"./form-state\"\n\n/**\n * Returns an object that contains the current flow and the flow type, as well as the configuration.\n *\n * @returns The current flow container\n * @group Hooks\n */\nexport function useOryFlow() {\n const ctx = useContext(OryFlowContext)\n if (!ctx) {\n throw new Error(\"useOryFlow must be used within a OryFlowProvider\")\n }\n\n return ctx\n}\n\n/**\n * Function to set the flow container.\n * @interface\n */\nexport type FlowContainerSetter = Dispatch<OryFlowContainer>\n\n/**\n * The return value of the OryFlowContext.\n */\nexport type FlowContextValue = OryFlowContainer & {\n /**\n * Function to set the flow container.\n */\n setFlowContainer: FlowContainerSetter\n\n /**\n * The current form state.\n * @see FormState\n */\n formState: FormState\n\n /**\n * Dispatch function to update the form state.\n */\n dispatchFormState: Dispatch<FormStateAction>\n}\n\n// This is fine, because we don't export the context itself and guard from it being null in useOryFlow\nconst OryFlowContext = createContext<FlowContextValue>(null!)\n\n/**\n * Props type for the OryFlowProvider component.\n *\n * @hidden\n * @inline\n */\nexport type OryFlowProviderProps = PropsWithChildren<OryFlowContainer>\n\n/**\n *\n * @param props - The properties for the OryFlowProvider component.\n * @returns\n */\nexport function OryFlowProvider({\n children,\n ...container\n}: OryFlowProviderProps) {\n const [flowContainer, setFlowContainer] = useState(container)\n const [formState, dispatchFormState] = useFormStateReducer(container)\n\n return (\n <OryFlowContext.Provider\n value={\n {\n ...flowContainer,\n setFlowContainer: (flowContainer) => {\n setFlowContainer(flowContainer)\n dispatchFormState({\n type: \"action_flow_update\",\n flow: flowContainer,\n })\n },\n formState,\n dispatchFormState,\n } as FlowContextValue\n }\n >\n {children}\n </OryFlowContext.Provider>\n )\n}\n","// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { FlowType, UiNode, UiNodeGroupEnum } from \"@ory/client-fetch\"\nimport { useReducer, useState } from \"react\"\nimport { isChoosingMethod } from \"../components/card/two-step/utils\"\nimport { OryFlowContainer } from \"../util\"\nimport { nodesToAuthMethodGroups } from \"../util/ui\"\n\n/**\n * Represents the state of the form when selecting an authentication method.\n * This type is used when the user is in the process of selecting an authentication method\n * (e.g., password, passkey, etc.) during the login or registration flow.\n * @inline\n * @hidden\n */\nexport type FormStateSelectMethod = { current: \"select_method\" }\n/**\n * Represents the state of the form when providing an identifier.\n * This type is used when the user is required to provide an identifier (e.g., email or username)\n * before proceeding with the authentication flow.\n * @inline\n * @hidden\n */\nexport type FormStateProvideIdentifier = { current: \"provide_identifier\" }\n/**\n * Represents the state of the form when an authentication method is active.\n * This type is used when the user is interacting with a specific authentication method\n * (e.g., entering a password or entering a code received via email).\n *\n * The `method` field indicates which authentication method is currently active.\n * @inline\n * @hidden\n */\nexport type FormStateMethodActive = {\n current: \"method_active\"\n method: UiNodeGroupEnum\n}\n\n/**\n * Represents the state of the form based on the flow type and active method.\n * This type is used to determine which part of the form should be displayed.\n *\n * It can be one of the following:\n * - `select_method`: The user is selecting an authentication method.\n * - `provide_identifier`: The user is providing an identifier (e.g., email or username).\n * - `method_active`: An authentication method is active, and the user is interacting with it.\n * - `success_screen`: The flow has successfully completed (only used in the verification flow).\n * - `settings`: The user is in the settings flow.\n */\nexport type FormState =\n | FormStateSelectMethod\n | FormStateProvideIdentifier\n | FormStateMethodActive\n | { current: \"success_screen\" }\n | { current: \"settings\" }\n\n/**\n * Represents the actions that can be dispatched to update the form state.\n * These actions are used to change the current state of the form based on user interactions or flow updates.\n */\nexport type FormStateAction =\n | {\n /**\n * Action to update the flow state.\n * This action is dispatched when the flow is updated, and it will parse the new flow\n * to determine the current form state.\n */\n type: \"action_flow_update\"\n /**\n * The updated flow container that contains the new flow data.\n */\n flow: OryFlowContainer\n }\n | {\n /**\n * Action to select an authentication method.\n * This action is dispatched when the user selects an authentication method\n * (e.g., password, passkey, etc.) from the available options.\n */\n type: \"action_select_method\"\n /**\n * The authentication method that the user has selected.\n */\n method: UiNodeGroupEnum\n }\n | {\n /**\n * Action to clear the active authentication method.\n * This action is dispatched when the user wants to clear the currently active method\n * and return to the method selection state.\n */\n type: \"action_clear_active_method\"\n }\n\nfunction findMethodWithMessage(nodes?: UiNode[]) {\n return nodes\n ?.filter((n) => ![\"default\", \"identifier_first\"].includes(n.group))\n ?.find((node) => node.messages?.length > 0)\n}\n\nfunction parseStateFromFlow(flow: OryFlowContainer): FormState {\n switch (flow.flowType) {\n case FlowType.Registration:\n case FlowType.Login: {\n const methodWithMessage = findMethodWithMessage(flow.flow.ui.nodes)\n if (flow.flow.active == \"link_recovery\") {\n return { current: \"method_active\", method: \"link\" }\n } else if (flow.flow.active == \"code_recovery\") {\n return { current: \"method_active\", method: \"code\" }\n } else if (methodWithMessage) {\n return { current: \"method_active\", method: methodWithMessage.group }\n } else if (flow.flow.ui.messages?.some((m) => m.id === 1010016)) {\n // Account linking edge case\n return { current: \"select_method\" }\n } else if (\n flow.flow.active &&\n ![\"default\", \"identifier_first\"].includes(flow.flow.active)\n ) {\n return { current: \"method_active\", method: flow.flow.active }\n } else if (isChoosingMethod(flow)) {\n // Login has a special case where we only have one method. Here, we\n // do not want to display the chooser.\n const authMethods = nodesToAuthMethodGroups(flow.flow.ui.nodes)\n if (\n authMethods.length === 1 &&\n ![\"code\", \"passkey\"].includes(authMethods[0])\n ) {\n // TODO: https://github.com/ory/kratos/issues/4271 - once this is fixed in Kratos, we can remove the check for \"code\"\n return { current: \"method_active\", method: authMethods[0] }\n }\n return { current: \"select_method\" }\n }\n return { current: \"provide_identifier\" }\n }\n case FlowType.Recovery:\n case FlowType.Verification:\n // The API does not provide types for the active field of the recovery flow\n // TODO: Add types for the recovery flow in Kratos\n if (flow.flow.active === \"code\" || flow.flow.active === \"link\") {\n if (flow.flow.state === \"choose_method\") {\n return { current: \"provide_identifier\" }\n }\n return { current: \"method_active\", method: flow.flow.active }\n }\n break\n case FlowType.Settings:\n return { current: \"settings\" }\n case FlowType.OAuth2Consent:\n return { current: \"method_active\", method: \"oauth2_consent\" }\n }\n console.warn(\n `[Ory/Elements React] Encountered an unknown form state on ${flow.flowType} flow with ID ${flow.flow.id}`,\n )\n throw new Error(\"Unknown form state\")\n}\n\n/**\n * The `useFormStateReducer` hook manages the state of the form based on the flow data.\n *\n * It uses a reducer to handle actions that update the form state, such as selecting an authentication method or updating the flow.\n *\n * @see FormState\n * @see FormStateAction\n * @param flow - The flow container that contains the flow data.\n * @returns a tuple containing the current form state and a dispatch function to update the state.\n */\nexport function useFormStateReducer(flow: OryFlowContainer) {\n const action = parseStateFromFlow(flow)\n const [selectedMethod, setSelectedMethod] = useState<\n UiNodeGroupEnum | undefined\n >()\n\n const formStateReducer = (\n state: FormState,\n action: FormStateAction,\n ): FormState => {\n switch (action.type) {\n case \"action_flow_update\": {\n if (selectedMethod) {\n return { current: \"method_active\", method: selectedMethod }\n }\n return parseStateFromFlow(action.flow)\n }\n case \"action_select_method\": {\n setSelectedMethod(action.method)\n return { current: \"method_active\", method: action.method }\n }\n case \"action_clear_active_method\": {\n return {\n current: \"select_method\",\n }\n }\n }\n return state\n }\n\n return useReducer(formStateReducer, action)\n}\n","// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport { FlowType, UiNode, UiNodeGroupEnum } from \"@ory/client-fetch\"\nimport {\n LoginFlowContainer,\n RegistrationFlowContainer,\n} from \"../../../util/flowContainer\"\nimport { isGroupImmediateSubmit } from \"../../../theme/default/utils/form\"\nimport { GroupedNodes, isUiNodeGroupEnum } from \"../../../util/ui\"\nimport { Dispatch } from \"react\"\nimport { FormStateAction } from \"@ory/elements-react\"\n\nfunction isScreenSelectionNode(node: UiNode) {\n if (\n \"name\" in node.attributes &&\n node.attributes.name === \"screen\" &&\n \"value\" in node.attributes &&\n node.attributes.value === \"previous\"\n ) {\n return true\n }\n if (\n node.group === UiNodeGroupEnum.IdentifierFirst &&\n \"name\" in node.attributes &&\n node.attributes.name === \"identifier\" &&\n node.attributes.type === \"hidden\"\n ) {\n return true\n }\n return false\n}\n\nexport function isChoosingMethod(\n flow: LoginFlowContainer | RegistrationFlowContainer,\n): boolean {\n if (flow.flowType === FlowType.Login) {\n if (flow.flow.requested_aal === \"aal2\") {\n return true\n }\n if (\n flow.flow.refresh &&\n // TODO: Once https://github.com/ory/kratos/issues/4194 is fixed, this can be removed\n // Without this, we show the method chooser, and an email input, which looks weird\n !flow.flow.ui.nodes.some((n) => n.group === \"code\")\n ) {\n return true\n }\n }\n return flow.flow.ui.nodes.some(isScreenSelectionNode)\n}\n\nexport function getFinalNodes(\n uniqueGroups: GroupedNodes,\n selectedGroup: UiNodeGroupEnum | undefined,\n): UiNode[] {\n const selectedNodes: UiNode[] = selectedGroup\n ? (uniqueGroups[selectedGroup] ?? [])\n : []\n\n return [\n ...(uniqueGroups?.identifier_first ?? []),\n ...(uniqueGroups?.default ?? []),\n ...(uniqueGroups?.captcha ?? []),\n ]\n .flat()\n .filter(\n (node) => \"type\" in node.attributes && node.attributes.type === \"hidden\",\n )\n .concat(selectedNodes)\n}\n\nexport const handleAfterFormSubmit =\n (dispatchFormState: Dispatch<FormStateAction>) => (method: unknown) => {\n if (typeof method !== \"string\" || !isUiNodeGroupEnum(method)) {\n return\n }\n\n if (isGroupImmediateSubmit(method)) {\n dispatchFormState({\n type: \"action_select_method\",\n method: method,\n })\n }\n }\n","// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nexport function isGroupImmediateSubmit(group: string) {\n // TODO: Other methods might also benefit from this.\n return group === \"code\"\n}\n","// Copyright © 2024 Ory Corp\n// SPDX-License-Identifier: Apache-2.0\n\nimport {\n isUiNodeInputAttributes,\n isUiNodeScriptAttributes,\n UiNode,\n} from \"@ory/client-fetch\"\n\nimport type {\n UiNodeAttributes,\n UiNodeInputAttributesOnclickTriggerEnum,\n UiNodeInputAttributesOnloadTriggerEnum,\n} from \"@ory/client-fetch\"\nimport { UiNodeGroupEnum } from \"@ory/client-fetch\"\nimport { useMemo } from \"react\"\nimport { useGroupSorter } from \"../../context/component\"\n\nexport function triggerToWindowCall(\n trigger:\n | UiNodeInputAttributesOnclickTriggerEnum\n | UiNodeInputAttributesOnloadTriggerEnum\n | undefined,\n) {\n if (!trigger) {\n return\n }\n\n const fn = triggerToFunction(trigger)\n if (fn) {\n fn()\n return\n }\n\n // Retry every 100ms for 10 seconds\n let i = 0\n const ms = 100\n const interval = setInterval(() => {\n i++\n if (i > 100) {\n clearInterval(interval)\n throw new Error(\n \"Unable to load Ory's WebAuthn script. Is it being blocked or otherwise failing to load? If you are running an old version of Ory Elements, please upgrade. For more information, please check your browser's developer console.\",\n )\n }\n\n const fn = triggerToFunction(trigger)\n if (fn) {\n clearInterval(interval)\n return fn()\n }\n }, ms)\n return\n}\n\nexport function triggerToFunction(\n trigger:\n | UiNodeInputAttributesOnclickTriggerEnum\n | UiNodeInputAttributesOnloadTriggerEnum,\n) {\n if (typeof window === \"undefined\") {\n console.debug(\n \"The Ory SDK is missing a required function: window is undefined.\",\n )\n return undefined\n }\n\n const typedWindow = window as { [key: string]: any } // eslint-disable-line @typescript-eslint/no-explicit-any\n if (!(trigger in typedWindow) || !typedWindow[trigger]) {\n console.debug(`The Ory SDK is missing a required function: ${trigger}.`)\n return undefined\n }\n const triggerFn = typedWindow[trigger]\n if (typeof triggerFn !== \"function\") {\n console.debug(\n `The Ory SDK is missing a required function: ${trigger}. It is not a function.`,\n )\n return undefined\n }\n return triggerFn as () => void\n}\n\ntype Entries<T> = {\n [K in keyof T]: [K, T[K]]\n}[keyof T][]\n\n/**\n * Returns a list of auth methods from a list of nodes. For example,\n * if Password and Passkey are present, it will return [password, passkey].\n *\n * Please note that OIDC is not considered an auth method because it is\n * usually shown as a separate auth method\n *\n * This method the default, identifier_first, and profile groups.\n *\n * @param nodes - The nodes to extract the auth methods from\n * @param excludeAuthMethods - A list of auth methods to exclude\n */\nexport function nodesToAuthMethodGroups(\n nodes: Array<UiNode>,\n excludeAuthMethods = [],\n): UiNodeGroupEnum[] {\n const groups: Partial<Record<UiNodeGroupEnum, UiNode[]>> = {}\n\n for (const node of nodes) {\n if (node.type === \"script\") {\n // We always render all scripts, because the scripts for passkeys are part of the webauthn group,\n // which leads to this hook returning a webauthn group on passkey flows (which it should not - webauthn is the \"legacy\" passkey implementation).\n continue\n }\n const groupNodes = groups[node.group] ?? []\n groupNodes.push(node)\n groups[node.group] = groupNodes\n }\n\n return Object.values(UiNodeGroupEnum)\n .filter((group) => groups[group]?.length)\n .filter(\n (group) =>\n !(\n [\n UiNodeGroupEnum.Default,\n UiNodeGroupEnum.IdentifierFirst,\n UiNodeGroupEnum.Profile,\n UiNodeGroupEnum.Captcha,\n ...excludeAuthMethods,\n ] as UiNodeGroupEnum[]\n ).includes(group),\n )\n}\n\n/**\n * Groups nodes by their group and returns an object with the groups and entries.\n *\n * @deprecated use useNodeGroupsWithVisibleNodes instead\n * @param nodes - The nodes to group\n * @param opts - The options to use\n */\nexport function useNodesGroups(\n nodes: UiNode[],\n { omit }: { omit?: Array<\"script\" | \"input_hidden\"> } = {},\n) {\n const groupSorter = useGroupSorter()\n\n const groups = useMemo(() => {\n const groups: Partial<Record<UiNodeGroupEnum, UiNode[]>> = {}\n const groupRetained: Partial<Record<UiNodeGroupEnum, number>> = {}\n\n for (const node of nodes) {\n const groupNodes = groups[node.group] ?? []\n groupNodes.push(node)\n groups[node.group] = groupNodes\n\n if (\n omit?.includes(\"script\") &&\n isUiNodeScriptAttributes(node.attributes)\n ) {\n continue\n }\n\n if (\n omit?.includes(\"input_hidden\") &&\n isUiNodeInputAttributes(node.attributes) &&\n node.attributes.type === \"hidden\"\n ) {\n continue\n }\n\n groupRetained[node.group] = (groupRetained[node.group] ?? 0) + 1\n }\n\n const finalGroups: Partial<Record<UiNodeGroupEnum, UiNode[]>> = {}\n for (const [group, count] of Object.entries(groupRetained)) {\n if (count > 0) {\n finalGroups[group as UiNodeGroupEnum] = groups[group as UiNodeGroupEnum]\n }\n }\n\n return finalGroups\n }, [nodes, omit])\n\n const entries = useMemo(\n () =>\n (\n Object.entries(groups) as Entries<Record<UiNodeGroupEnum, UiNode[]>>\n ).sort(([a], [b]) => groupSorter(a, b)),\n [groups, groupSorter],\n )\n\n return {\n groups,\n entries,\n }\n}\n\n/**\n * Find a node\n * @param nodes - The list of nodes to search\n * @param opt - The matching options\n * @returns The first matching node\n */\nexport const findNode = <T extends UiNodeAttributes[\"node_type\"]>(\n nodes: UiNode[],\n opt: {\n node_type: T\n group: UiNodeGroupEnum | RegExp\n name?: string | RegExp\n type?: string | RegExp\n },\n) =>\n nodes.find((n) => {\n return (\n n.attributes.node_type === opt.node_type &&\n (opt.group instanceof RegExp\n ? n.group.match(opt.group)\n : n.group === opt.group) &&\n (opt.name && n.attributes.node_type === \"input\"\n ? opt.name instanc