Compare commits

...

58 Commits

Author SHA1 Message Date
e12f01181d Merge branch 'doge_ftl_errors' of https://github.com/garronej/keycloakify into doge_ftl_errors 2021-12-09 02:01:01 +01:00
09763f296d Bump version (changelog ignore) 2021-12-09 02:00:50 +01:00
5c46ecc0ed Update CI workflow (changelog ignore) 2021-12-09 01:57:03 +01:00
4e5038cf69 Merge pull request #64 from Ann2827/pull
fix: errors in common.ftl
2021-12-09 01:49:23 +01:00
cf93b68816 Merge branch 'main' of https://github.com/garronej/keycloakify into main 2021-12-09 01:42:51 +01:00
457421b8d6 Update CI workflow (changelog ignore) 2021-12-09 01:42:43 +01:00
d36ea9539a Update changelog v4.2.13 2021-12-08 14:54:09 +00:00
5a5337dc63 Bump version (changelog ignore) 2021-12-08 15:40:46 +01:00
443081cc28 Fix broken link about how to import fonts #62 2021-12-08 15:40:11 +01:00
ac8503f8c8 Add video to show how to get the template to load faster in developpement (changelog ignore) 2021-12-08 15:32:12 +01:00
1cc1fd0a5a Add a video to show how to test the theme in a local container 2021-12-08 15:28:26 +01:00
34314aa4ca Update changelog v4.2.12 2021-12-08 13:21:26 +00:00
0d8dcf4829 Bump version (changelog ignore) 2021-12-08 14:12:51 +01:00
47c6d0dd62 Update post build instructions 2021-12-08 14:12:35 +01:00
84937e3eec Update notice about long loading time (changelog ignore) 2021-12-08 13:56:06 +01:00
303e270b56 Add instruction for building on windows (changelog ignore) 2021-12-08 13:52:16 +01:00
29fbcdc0a6 fix: errors in common.ftl
ref: https://github.com/InseeFrLab/keycloakify/issues/58
2021-12-08 10:30:36 +03:00
bb1ada6e14 Update changelog v4.2.11 2021-12-07 23:27:11 +00:00
4a422cc796 Bump (changelog ignore) 2021-12-08 00:22:40 +01:00
be0f244c02 Update tss-react (changelog ignore) 2021-12-08 00:22:06 +01:00
78a8dc8458 Merge branch 'main' of https://github.com/garronej/keycloakify into main 2021-12-07 15:20:49 +01:00
38062af889 Add info with pages taking too long to load #58 (changelog ignore) 2021-12-07 15:20:37 +01:00
f2eadf5441 Update changelog v4.2.10 2021-11-12 18:12:04 +00:00
a42931384f Bump version (changelog ignore) 2021-11-12 19:02:49 +01:00
8116ce697b Export an exaustive list of KcLanguageTag 2021-11-12 19:02:25 +01:00
4964b86d67 Update changelog v4.2.9 2021-11-11 19:25:32 +00:00
2b331e7655 Bump version (changelog ignore) 2021-11-11 20:20:47 +01:00
c1468b688e Fix useAdvancedMsg 2021-11-11 20:20:25 +01:00
4f7837c88e Update changelog v4.2.8 2021-11-10 19:25:10 +00:00
fd8e06f1dd Bump version (changelog ignore) 2021-11-10 20:20:48 +01:00
b01a351eaa Update doc about pattern that can be used for user attributes #50 2021-11-10 20:00:53 +01:00
604655c02d Bring back Safari compat 2021-11-10 19:48:18 +01:00
6603ac4389 Update changelog v4.2.7 2021-11-09 00:52:25 +00:00
cca6f952ee Bump version (changelog ignore) 2021-11-09 01:49:15 +01:00
df94a6322d Fix useFormValidationSlice 2021-11-09 01:48:50 +01:00
73e7f64860 Update changelog v4.2.6 2021-11-08 18:38:37 +00:00
e17e1650d5 Bump version (changelog ignore) 2021-11-08 19:33:27 +01:00
3ecb63d500 Fix deepClone so we can overwrite with undefined in when we mock kcContext 2021-11-08 19:33:06 +01:00
41ee7e90ef Update changelog v4.2.5 2021-11-07 19:21:35 +00:00
c70bba727e Bump version (changelog ignore) 2021-11-07 20:17:39 +01:00
747248454d Better debugging experience with user profile 2021-11-07 20:17:14 +01:00
59386241b4 Update changelog v4.2.4 2021-11-01 22:21:39 +00:00
c70b9b0dd1 Bump version (changelog ignore) 2021-11-01 23:15:02 +01:00
2ee00ed919 Better autoComplete typings 2021-11-01 22:28:53 +01:00
cbfc271da5 Update changelog v4.2.3 2021-11-01 21:22:58 +00:00
d45b492837 Bump version (changelog ignore) 2021-11-01 22:16:16 +01:00
ed54c145b7 Make it more easy to understand that error in the log are expected 2021-11-01 22:15:56 +01:00
64ed9a6044 Update changelog v4.2.2 2021-10-27 09:01:53 +00:00
75267abd91 Merge branch 'main' of https://github.com/garronej/keycloakify into main 2021-10-27 10:58:43 +02:00
ba9a3992b7 Bump version (changelog ignore) 2021-10-27 10:58:33 +02:00
a74c32ed6d Update changelog v4.2.1 2021-10-27 10:58:33 +02:00
c5f9812acc Replace 'path' by 'browserify-path' #47 2021-10-27 10:58:10 +02:00
bb0d6853e5 Update changelog v4.2.1 2021-10-26 16:13:04 +00:00
8c9fe168d8 Bump version (changelog ignore) 2021-10-26 18:10:04 +02:00
6c874c01b7 useFormValidationSlice: update when params have changed 2021-10-26 18:09:37 +02:00
5bc84b621c Add notice about the fact that keycloakify have to be updated (changelog ignore) 2021-10-26 17:21:01 +02:00
dd421eedf5 Merge branch 'main' of https://github.com/garronej/keycloakify into main 2021-10-26 16:16:15 +02:00
570d8a73cc Explains that the password can't be validated 2021-10-26 16:16:10 +02:00
22 changed files with 421 additions and 373 deletions

View File

@ -22,7 +22,6 @@ jobs:
PACKAGE_MANAGER=yarn
fi
$PACKAGE_MANAGER run format:check
test:
runs-on: macos-10.15
needs: test_formatting
@ -33,7 +32,7 @@ jobs:
steps:
- name: Tell if project is using npm or yarn
id: step1
uses: garronej/github_actions_toolkit@v2.2
uses: garronej/ts-ci@v1.1.3
with:
action_name: tell_if_project_uses_npm_or_yarn
- uses: actions/checkout@v2.3.4
@ -51,28 +50,35 @@ jobs:
npm test
check_if_version_upgraded:
name: Check if version upgrade
if: github.event_name == 'push'
# We run this only if it's a push on the default branch or if it's a PR from a
# branch (meaning not a PR from a fork). It would be more straightforward to test if secrets.NPM_TOKEN is
# defined but GitHub Action don't allow it yet.
if: |
github.event_name == 'push' ||
github.event.pull_request.head.repo.owner.login == github.event.pull_request.base.repo.owner.login
runs-on: ubuntu-latest
needs: test
outputs:
from_version: ${{ steps.step1.outputs.from_version }}
to_version: ${{ steps.step1.outputs.to_version }}
is_upgraded_version: ${{steps.step1.outputs.is_upgraded_version }}
is_upgraded_version: ${{ steps.step1.outputs.is_upgraded_version }}
is_release_beta: ${{steps.step1.outputs.is_release_beta }}
steps:
- uses: garronej/github_actions_toolkit@v2.2
- uses: garronej/ts-ci@v1.1.3
id: step1
with:
action_name: is_package_json_version_upgraded
branch: ${{ github.head_ref || github.ref }}
update_changelog:
runs-on: ubuntu-latest
needs: check_if_version_upgraded
if: needs.check_if_version_upgraded.outputs.is_upgraded_version == 'true'
steps:
- uses: garronej/github_actions_toolkit@v2.4
- uses: garronej/ts-ci@v1.1.3
with:
action_name: update_changelog
branch: ${{ github.ref }}
branch: ${{ github.head_ref || github.ref }}
create_github_release:
runs-on: ubuntu-latest
@ -80,9 +86,6 @@ jobs:
- update_changelog
- check_if_version_upgraded
steps:
- uses: actions/checkout@v2
with:
ref: ${{ github.ref }}
- name: Build GitHub release body
id: step1
run: |
@ -98,10 +101,10 @@ jobs:
with:
name: Release v${{ needs.check_if_version_upgraded.outputs.to_version }}
tag_name: v${{ needs.check_if_version_upgraded.outputs.to_version }}
target_commitish: ${{ github.ref }}
target_commitish: ${{ github.head_ref || github.ref }}
body: ${{ steps.step1.outputs.body }}
draft: false
prerelease: false
prerelease: ${{ needs.check_if_version_upgraded.outputs.is_release_beta == 'true' }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@ -138,7 +141,12 @@ jobs:
echo "Can't publish on NPM, You must first create a secret called NPM_TOKEN that contains your NPM auth token. https://help.github.com/en/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets"
false
fi
npm publish
EXTRA_ARGS=""
if [ "$IS_BETA" = "true" ]; then
EXTRA_ARGS="--tag beta"
fi
npm publish $EXTRA_ARGS
env:
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
VERSION: ${{ needs.check_if_version_upgraded.outputs.to_version }}
IS_BETA: ${{ needs.check_if_version_upgraded.outputs.is_release_beta }}

2
.gitignore vendored
View File

@ -44,3 +44,5 @@ jspm_packages
/sample_react_project/
/.yarn_home/
.idea

View File

@ -1,3 +1,58 @@
### **4.2.13** (2021-12-08)
- Fix broken link about how to import fonts #62
- Add a video to show how to test the theme in a local container
### **4.2.12** (2021-12-08)
- Update post build instructions
### **4.2.11** (2021-12-07)
### **4.2.10** (2021-11-12)
- Export an exaustive list of KcLanguageTag
### **4.2.9** (2021-11-11)
- Fix useAdvancedMsg
### **4.2.8** (2021-11-10)
- Update doc about pattern that can be used for user attributes #50
- Bring back Safari compat
### **4.2.7** (2021-11-09)
- Fix useFormValidationSlice
### **4.2.6** (2021-11-08)
- Fix deepClone so we can overwrite with undefined in when we mock kcContext
### **4.2.5** (2021-11-07)
- Better debugging experience with user profile
### **4.2.4** (2021-11-01)
- Better autoComplete typings
### **4.2.3** (2021-11-01)
- Make it more easy to understand that error in the log are expected
### **4.2.2** (2021-10-27)
- Replace 'path' by 'browserify-path' #47
### **4.2.1** (2021-10-26)
- useFormValidationSlice: update when params have changed
- Explains that the password can't be validated
## **4.2.0** (2021-10-26)
- Export types definitions for Attribue and Validator

View File

@ -77,6 +77,7 @@ If you already have a Keycloak custom theme, it can be easily ported to Keycloak
- [Implement context persistence (optional)](#implement-context-persistence-optional)
- [Kickstart video](#kickstart-video)
- [About the errors related to `objectToJson` in Keycloak logs.](#about-the-errors-related-to-objecttojson-in-keycloak-logs)
- [The pages take too long to load ?](#the-pages-take-too-long-to-load-)
- [Adding custom message (to `i18n/useKcMessage.tsx`)](#adding-custom-message-to-i18nusekcmessagetsx)
- [Email domain whitelist](#email-domain-whitelist)
- [Changelog highlights](#changelog-highlights)
@ -87,6 +88,8 @@ If you already have a Keycloak custom theme, it can be easily ported to Keycloak
# Requirements
On Windows OS you'll have to use [WSL](https://docs.microsoft.com/en-us/windows/wsl/install-win10). More info [here](https://github.com/InseeFrLab/keycloakify/issues/54%23issuecomment-984834217)
Tested with the following Keycloak versions:
- [11.0.3](https://hub.docker.com/layers/jboss/keycloak/11.0.3/images/sha256-4438f1e51c1369371cb807dffa526e1208086b3ebb9cab009830a178de949782?context=explore)
@ -106,8 +109,6 @@ For more information see [this issue](https://github.com/InseeFrLab/keycloakify/
- `mvn` ([Maven](https://maven.apache.org/)), `rm`, `mkdir`, `curl`, `unzip` are assumed to be available.
- `docker` must be up and running when running `yarn keycloak`.
On Windows you'll have to use [WSL](https://docs.microsoft.com/en-us/windows/wsl/install-win10).
## My framework doesnt seem to be supported, what can I do?
Currently Keycloakify is only compatible with `create-react-app` apps.
@ -307,6 +308,8 @@ Checkout a complete setup [here](https://github.com/garronej/keycloakify-demo-ap
</a>
</p>
NOTE: In reality the regexp used in this gif doesn't work server side, the regexp pattern should be `^[^@]@gmail\.com$` 😬.
User Profile is a Keycloak feature that enables to
[define, from the admin console](https://user-images.githubusercontent.com/6702424/136872461-1f5b64ef-d2ef-4c6b-bb8d-07d4729552b3.png),
what information you want to collect on your users in the register page and to validate inputs
@ -322,6 +325,9 @@ provides frontend validation out of the box.
For implementing your own `register-user-profile.ftl` page, you can use [`import { useFormValidationSlice } from "keycloakify";`](https://github.com/InseeFrLab/keycloakify/blob/main/src/lib/useFormValidationSlice.tsx).
Find usage example [`here`](https://github.com/InseeFrLab/keycloakify/blob/d3a07edfcb3739e30032dc96fc2a55944dfc3387/src/lib/components/RegisterUserProfile.tsx#L79-L112).
As for right now [it's not possible to define a pattern for the password](https://keycloak.discourse.group/t/make-password-policies-available-to-freemarker/11632)
from the admin console. You can however pass validators for it to the `useFormValidationSlice` function.
# Support for Terms and conditions
[Many organizations have a requirement that when a new user logs in for the first time, they need to agree to the terms and conditions of the website.](https://www.keycloak.org/docs/4.8/server_admin/#terms-and-conditions).
@ -368,7 +374,8 @@ this limitation doesn't apply, you can import fonts however you see fit.
- If it is possible, use Google Fonts or any other font provider.
- If you want to host your font recommended approach is to move your fonts into the `public`
directory and to place your `@font-face` statements in the `public/index.html`.
Example [here](https://github.com/InseeFrLab/onyxia-ui/blob/0e3a04610cfe872ca71dad59e05ced8f785dee4b/public/index.html#L6-L51).
Example [here](https://github.com/garronej/keycloakify-demo-app/blob/9aa2dbaec28a7786d6b2983c9a59d393dec1b2d6/public/index.html#L27-L73)
(and the font are [here](https://github.com/garronej/keycloakify-demo-app/tree/main/public/fonts/WorkSans)).
- You can also [use non relative url](https://github.com/garronej/keycloakify-demo-app/blob/2de8a9eb6f5de9c94f9cd3991faad0377e63268c/src/fonts.scss#L16) but don't forget [`Access-Control-Allow-Origin`](https://github.com/garronej/keycloakify-demo-app/blob/2de8a9eb6f5de9c94f9cd3991faad0377e63268c/nginx.conf#L17-L19).
# Implement context persistence (optional)
@ -441,18 +448,29 @@ The logs of your keycloak server will always show this kind of errors every time
```log
FTL stack trace ("~" means nesting-related):
- Failed at: #local value = object[key] [in template "login.ftl" in macro "objectToJson" at line 70, column 21]
- Reached through: @compress [in template "login.ftl" in macro "objectToJson" at line 36, column 5]
- Reached through: @objectToJson object=value depth=(dep... [in template "login.ftl" in macro "objectToJson" at line 81, column 27]
- Reached through: @compress [in template "login.ftl" in macro "objectToJson" at line 36, column 5]
- Reached through: @objectToJson object=(.data_model) de... [in template "login.ftl" at line 163, column 43]
- Failed at: #local value = object[key] [in template "login.ftl" in macro "objectToJson_please_ignore_errors" at line 70, column 21]
- Reached through: @compress [in template "login.ftl" in macro "objectToJson_please_ignore_errors" at line 36, column 5]
- Reached through: @objectToJson_please_ignore_errors object=value depth=(dep... [in template "login.ftl" in macro "objectToJson_please_ignore_errors" at line 81, column 27]
- Reached through: @compress [in template "login.ftl" in macro "objectToJson_please_ignore_errors" at line 36, column 5]
- Reached through: @objectToJson_please_ignore_errors object=(.data_model) de... [in template "login.ftl" at line 163, column 43]
```
Theses are expected and can be safely ignored.
Theses are expected to show up in the log.
Unfortunately, there is nothing I know of that can be done to avoid them or even mute them.
They can be, however, safely ignored.
To [converts the `.ftl` values into a JavaScript object](https://github.com/InseeFrLab/keycloakify/blob/main/src/bin/build-keycloak-theme/generateFtl/common.ftl)
without making assumptions on the `.data_model` we have to do things that throws.
It's all-right though because every statement that can fail is inside an `<#attempt><#recorver>` block but it results in errors being printed to the logs.
It's all-right because every statement that can fail is inside an `<#attempt><#recorver>` block but it results in errors being printed to the logs.
# The pages take too long to load ?
The problem of templates taking a long time to load only happens in the test environment, when you have a console logging all the above-mentioned `.ftl` warnings in real time. Logging all those warnings is what takes time. Once in production page load is way faster.
If you run the docker container locally we acknowledge that the loading time is getting out of hand.
We are [in the process](https://github.com/InseeFrLab/keycloakify/pull/63) of resolving this issue.
In the meantime we recommend [to run the docker container as a background task](https://youtu.be/F29Z1GaH-jk).
# Adding custom message (to `i18n/useKcMessage.tsx`)
@ -462,6 +480,9 @@ This approach is a bit hacky as it doesn't provide type safety but it works.
# Email domain whitelist
NOTE: This have been kind of deprecated by [user attribute](#user-profile-and-frontend-form-validation) you could
use a pattern [like this one](https://github.com/InseeFrLab/onyxia-web/blob/f1206e0329b3b8d401ca7bffa95ca9c213cb190a/src/app/components/KcApp/kcContext.ts#L106) to whitelist email domains.
If you want to restrict the emails domain that can register, you can use [this plugin](https://github.com/micedre/keycloak-mail-whitelisting)
and `kcRegisterContext["authorizedMailDomains"]` to validate on.

View File

@ -1,6 +1,6 @@
{
"name": "keycloakify",
"version": "4.2.0",
"version": "4.2.14-beta.0",
"description": "Keycloak theme generator for Reacts app",
"repository": {
"type": "git",
@ -56,9 +56,9 @@
"homepage": "https://github.com/garronej/keycloakify",
"peerDependencies": {
"@emotion/react": "^11.4.1",
"powerhooks": "^0.11.0",
"powerhooks": "^0.10.0",
"react": "^16.8.0 || ^17.0.0",
"tss-react": "^1.1.0"
"tss-react": "^1.1.0 || ^3.0.0"
},
"devDependencies": {
"@emotion/react": "^11.4.1",
@ -72,14 +72,14 @@
"properties-parser": "^0.3.1",
"react": "^17.0.1",
"rimraf": "^3.0.2",
"tss-react": "^1.1.0",
"tss-react": "^3.0.0",
"typescript": "^4.2.3"
},
"dependencies": {
"cheerio": "^1.0.0-rc.5",
"evt": "2.0.0-beta.38",
"minimal-polyfills": "^2.2.1",
"path": "^0.12.7",
"path-browserify": "^1.0.1",
"react-markdown": "^5.0.3",
"scripting-tools": "^0.19.13",
"tsafe": "^0.8.1"

View File

@ -121,11 +121,14 @@ export function main() {
"",
`👉 $ ./${pathRelative(reactProjectDirPath, pathJoin(keycloakThemeBuildingDirPath, containerLaunchScriptBasename))} 👈`,
"",
'To enable the theme within keycloak log into the admin console ( 👉 http://localhost:8080 username: admin, password: admin 👈), create a realm (called "myrealm" for example),',
`go to your realm settings, click on the theme tab then select ${themeName}.`,
`More details: https://www.keycloak.org/getting-started/getting-started-docker`,
"Once your container is up and running: ",
"- Log into the admin console 👉 http://localhost:8080 username: admin, password: admin 👈",
'- Create a realm named "myrealm"',
'- Create a client with id "myclient" and root url: "https://www.keycloak.org/app/"',
`- Select Login Theme: ${themeName} (don't forget to save at the bottom of the page)`,
`- Go to 👉 https://www.keycloak.org/app/ 👈 Click "Save" then "Sign in". You should see your login page`,
"",
"Once your container is up and configured 👉 http://localhost:8080/auth/realms/myrealm/account 👈",
"Video demoing this process: https://youtu.be/N3wlBoH4hKg",
"",
].join("\n"),
);

View File

@ -1,7 +1,6 @@
<script>const _=
<#macro objectToJson object depth>
<#macro objectToJson_please_ignore_errors object depth arr ftl>
<@compress>
<#local isHash = false>
<#attempt>
<#local isHash = object?is_hash || object?is_hash_ex>
@ -11,9 +10,7 @@
<#return>
</#attempt>
<#if isHash>
<#local keys = "">
<#attempt>
<#local keys = object?keys>
<#recover>
@ -21,41 +18,42 @@
undefined
<#return>
</#attempt>
<#if arr?sequence?size gte 1>
<#local keys = arr>
</#if>
{${'\n'}
<#list keys as key>
<#if key == "class">
/* skipping "class" property of object */
<#continue>
</#if>
<#local value = "">
<#attempt>
<#local value = object[key]>
<#local value = object[key]!"error_data_is_undefined">
<#recover>
/* couldn't dereference ${key} of object */
<#continue>
</#attempt>
<#if depth gt 7>
/* Avoid calling recustively too many times depth: ${depth}, key: ${key} */
<#continue>
</#if>
"${key}": <@objectToJson object=value depth=depth+1/>,
<#local arr_keys = []>
<#if depth==0 && key=="url" && ftl=="saml-post-form.ftl" >
<#local arr_keys = ["resourcesPath", "resourcesCommonPath", "loginRestartFlowUrl", "loginUrl", "loginResetCredentialsUrl", "registrationUrl", "registrationAction", "oauth2DeviceVerificationAction", "oauthAction", "loginResetCredentialsUrl"]>
<#elseif depth==0 && key=="url">
<#local arr_keys = ["loginAction", "resourcesPath", "resourcesCommonPath", "loginRestartFlowUrl", "loginUrl", "loginResetCredentialsUrl", "registrationUrl", "registrationAction", "oauth2DeviceVerificationAction", "oauthAction", "loginResetCredentialsUrl"]>
</#if>
<#if depth==0 && key=="auth" && ftl=="login-reset-password.ftl" >
<#local arr_keys = ["showUsername", "showResetCredentials", "showTryAnotherWayLink", "attemptedUsername"]>
<#elseif depth==0 && key=="auth">
<#local arr_keys = ["showUsername", "showResetCredentials", "showTryAnotherWayLink"]>
</#if>
<#if depth==0 && key=="updateProfileCtx" ><#continue></#if>
"${key}": <@objectToJson_please_ignore_errors object=value depth=depth+1 arr=arr_keys ftl=ftl />,
</#list>
}${'\n'}
<#return>
</#if>
<#local isMethod = "">
<#attempt>
<#local isMethod = object?is_method>
@ -64,14 +62,10 @@
undefined
<#return>
</#attempt>
<#if isMethod>
undefined
<#return>
</#if>
<#local isBoolean = "">
<#attempt>
<#local isBoolean = object?is_boolean>
@ -80,13 +74,10 @@
undefined
<#return>
</#attempt>
<#if isBoolean>
${object?c}
<#return>
</#if>
<#local isEnumerable = "">
<#attempt>
<#local isEnumerable = object?is_enumerable>
@ -95,23 +86,14 @@
undefined
<#return>
</#attempt>
<#if isEnumerable>
[${'\n'}
<#list object as item>
<@objectToJson object=item depth=depth+1/>,
<@objectToJson_please_ignore_errors object=item depth=depth+1 arr=[] ftl=ftl/>,
</#list>
]${'\n'}
<#return>
</#if>
<#attempt>
"${object?replace('"', '\\"')?no_esc}"
<#recover>
@ -119,25 +101,21 @@
undefined;
<#return>
</#attempt>
</@compress>
</#macro>
(()=>{
const nonAutomaticallyConvertible = {
"messagesPerField": {
<#assign fieldNames = ["global", "userLabel", "username", "email", "firstName", "lastName", "password", "password-confirm"]>
<#assign fieldNames = ["global", "userLabel", "username", "email", "firstName", "lastName", "password", "password-confirm", "totp", "totpSecret", "SAMLRequest", "SAMLResponse", "relayState", "device_user_code", "code", "password-new", "rememberMe", "login", "authenticationExecution", "cancel-aia", "clientDataJSON", "authenticatorData", "signature", "credentialId", "userHandle", "error", "authn_use_chk", "authenticationExecution", "isSetRetry", "try-again", "attestationObject", "publicKeyCredentialId", "authenticatorLabel"]>
<#assign attributes = (profile.attributes)![]>
<#attempt>
<#list profile.attributes as attribute>
<#assign fieldNames += [attribute.name]>
<#list attributes as attribute>
<#if (attribute.name)??>
<#assign fieldNames += [attribute.name]>
</#if>
</#list>
<#recover>
</#attempt>
"printIfExists": function (fieldName, x) {
<#list fieldNames as fieldName>
if(fieldName === "${fieldName}" ){
@ -185,24 +163,19 @@
throw new Error("There is no " + fieldName + " field");
}
},
"msg": function(){ throw new Error("use import { useKcMessage } from 'keycloakify'"); },
"advancedMsg": function(){ throw new Error("use import { useKcMessage } from 'keycloakify'"); }
"msg": function(){ throw new Error("use import { useKcMessage } from 'keycloakify'"); },
"advancedMsg": function(){ throw new Error("use import { useKcMessage } from 'keycloakify'"); }
};
const out = {};
Object.deepAssign(
out,
//Removing all the undefined
JSON.parse(JSON.stringify(<@objectToJson object=.data_model depth=0 />))
JSON.parse(JSON.stringify(<@objectToJson_please_ignore_errors object=.data_model depth=0 arr=[] ftl="ftl_template_for_replacement" />), (key, value) => value === "error_data_is_undefined" ? undefined : value)
);
Object.deepAssign(
out,
nonAutomaticallyConvertible
);
return out;
})()
</script>

View File

@ -141,7 +141,9 @@ export function generateFtlFilesCodeFactory(params: {
].join("\n"),
);
objectKeys(ftlPlaceholders).forEach(id => (ftlCode = ftlCode.replace(id, ftlPlaceholders[id])));
objectKeys(ftlPlaceholders).forEach(
id => (ftlCode = ftlCode.replace(id, ftlPlaceholders[id]).replace("ftl_template_for_replacement", pageId)),
);
return { ftlCode };
}

View File

@ -149,15 +149,6 @@ const UserProfileFormFields = memo(({ kcContext, onIsFormSubmittableValueChange,
</div>
<div className={cx(props.kcInputWrapperClass)}>
<input
autoComplete={(() => {
switch (attribute.name) {
case "password-confirm":
case "password":
return "new-password";
default:
return undefined;
}
})()}
type={(() => {
switch (attribute.name) {
case "password-confirm":
@ -174,11 +165,7 @@ const UserProfileFormFields = memo(({ kcContext, onIsFormSubmittableValueChange,
className={cx(props.kcInputClass)}
aria-invalid={displayableErrors.length !== 0}
disabled={attribute.readOnly}
{...(attribute.autocomplete === undefined
? {}
: {
"autoComplete": attribute.autocomplete,
})}
autoComplete={attribute.autocomplete}
onBlur={onBlurFactory(attribute.name)}
/>
{displayableErrors.length !== 0 && (

View File

@ -217,10 +217,64 @@ export type Attribute = {
groupDisplayHeader?: string;
groupDisplayDescription?: string;
readOnly: boolean;
autocomplete?: string;
validators: Validators;
annotations: Record<string, string>;
groupAnnotations: Record<string, string>;
autocomplete?:
| "on"
| "off"
| "name"
| "honorific-prefix"
| "given-name"
| "additional-name"
| "family-name"
| "honorific-suffix"
| "nickname"
| "email"
| "username"
| "new-password"
| "current-password"
| "one-time-code"
| "organization-title"
| "organization"
| "street-address"
| "address-line1"
| "address-line2"
| "address-line3"
| "address-level4"
| "address-level3"
| "address-level2"
| "address-level1"
| "country"
| "country-name"
| "postal-code"
| "cc-name"
| "cc-given-name"
| "cc-additional-name"
| "cc-family-name"
| "cc-number"
| "cc-exp"
| "cc-exp-month"
| "cc-exp-year"
| "cc-csc"
| "cc-type"
| "transaction-currency"
| "transaction-amount"
| "language"
| "bday"
| "bday-day"
| "bday-month"
| "bday-year"
| "sex"
| "tel"
| "tel-country-code"
| "tel-national"
| "tel-area-code"
| "tel-local"
| "tel-extension"
| "impp"
| "url"
| "photo";
};
export type Validators = Partial<{

View File

@ -1,13 +1,12 @@
import type { KcContextBase } from "./KcContextBase";
import type { KcContextBase, Attribute } from "./KcContextBase";
import { kcContextMocks, kcContextCommonMock } from "./kcContextMocks";
import { ftlValuesGlobalName } from "../../bin/build-keycloak-theme/ftlValuesGlobalName";
import type { AndByDiscriminatingKey } from "../tools/AndByDiscriminatingKey";
import type { DeepPartial } from "../tools/DeepPartial";
import { deepAssign } from "../tools/deepAssign";
export type ExtendsKcContextBase<KcContextExtended extends { pageId: string }> = [KcContextExtended] extends [never]
? KcContextBase
: AndByDiscriminatingKey<"pageId", KcContextExtended & KcContextBase.Common, KcContextBase>;
import { id } from "tsafe/id";
import { exclude } from "tsafe/exclude";
import { assert } from "tsafe/assert";
import type { ExtendsKcContextBase } from "./getKcContextFromWindow";
import { getKcContextFromWindow } from "./getKcContextFromWindow";
export function getKcContext<KcContextExtended extends { pageId: string } = never>(params?: {
mockPageId?: ExtendsKcContextBase<KcContextExtended>["pageId"];
@ -44,12 +43,55 @@ export function getKcContext<KcContextExtended extends { pageId: string } = neve
"target": kcContext,
"source": partialKcContextCustomMock,
});
if (partialKcContextCustomMock.pageId === "register-user-profile.ftl") {
assert(kcContextDefaultMock?.pageId === "register-user-profile.ftl");
const { attributes } = kcContextDefaultMock.profile;
id<KcContextBase.RegisterUserProfile>(kcContext).profile.attributes = [];
id<KcContextBase.RegisterUserProfile>(kcContext).profile.attributesByName = {};
const partialAttributes = [
...((partialKcContextCustomMock as DeepPartial<KcContextBase.RegisterUserProfile>).profile?.attributes ?? []),
].filter(exclude(undefined));
attributes.forEach(attribute => {
const partialAttribute = partialAttributes.find(({ name }) => name === attribute.name);
const augmentedAttribute: Attribute = {} as any;
deepAssign({
"target": augmentedAttribute,
"source": attribute,
});
if (partialAttribute !== undefined) {
partialAttributes.splice(partialAttributes.indexOf(partialAttribute), 1);
deepAssign({
"target": augmentedAttribute,
"source": partialAttribute,
});
}
id<KcContextBase.RegisterUserProfile>(kcContext).profile.attributes.push(augmentedAttribute);
id<KcContextBase.RegisterUserProfile>(kcContext).profile.attributesByName[augmentedAttribute.name] = augmentedAttribute;
});
partialAttributes.forEach(partialAttribute => {
const { name } = partialAttribute;
assert(name !== undefined, "If you define a mock attribute it must have at least a name");
id<KcContextBase.RegisterUserProfile>(kcContext).profile.attributes.push(partialAttribute as any);
id<KcContextBase.RegisterUserProfile>(kcContext).profile.attributesByName[name] = partialAttribute as any;
});
}
}
return { kcContext };
}
return {
"kcContext": typeof window === "undefined" ? undefined : (window as any)[ftlValuesGlobalName],
};
return { "kcContext": getKcContextFromWindow<KcContextExtended>() };
}

View File

@ -0,0 +1,11 @@
import type { KcContextBase } from "./KcContextBase";
import type { AndByDiscriminatingKey } from "../tools/AndByDiscriminatingKey";
import { ftlValuesGlobalName } from "../../bin/build-keycloak-theme/ftlValuesGlobalName";
export type ExtendsKcContextBase<KcContextExtended extends { pageId: string }> = [KcContextExtended] extends [never]
? KcContextBase
: AndByDiscriminatingKey<"pageId", KcContextExtended & KcContextBase.Common, KcContextBase>;
export function getKcContextFromWindow<KcContextExtended extends { pageId: string } = never>(): ExtendsKcContextBase<KcContextExtended> | undefined {
return typeof window === "undefined" ? undefined : (window as any)[ftlValuesGlobalName];
}

View File

@ -1,2 +1,3 @@
export type { KcContextBase, Attribute, Validators } from "./KcContextBase";
export type { ExtendsKcContextBase } from "./getKcContextFromWindow";
export { getKcContext } from "./getKcContext";

View File

@ -278,29 +278,6 @@ export const kcContextMocks: KcContextBase[] = [
"readOnly": false,
"name": "lastName",
},
{
"validators": {
"length": {
"ignore.empty.value": true,
"min": "3",
"max": "9",
},
"up-immutable-attribute": {},
"up-attribute-required-by-metadata-value": {},
"email": {
"ignore.empty.value": true,
},
},
"displayName": "${foo}",
"annotations": {
"this_is_second_key": "this_is_second_value",
"this_is_first_key": "this_is_first_value",
},
"required": true,
"groupAnnotations": {},
"readOnly": false,
"name": "foo",
},
];
return {

View File

@ -34,7 +34,7 @@ export function getKcLanguageTagLabel(language: KcLanguageTag): LanguageLabel {
return kcLanguageByTagLabel[language] ?? language;
}
const availableLanguages = objectKeys(kcMessages);
export const kcLanguageTags = objectKeys(kcMessages);
/**
* Pass in "fr-FR" or "français" for example, it will return the AvailableLanguage
@ -45,7 +45,7 @@ const availableLanguages = objectKeys(kcMessages);
export function getBestMatchAmongKcLanguageTag(languageLike: string): KcLanguageTag {
const iso2LanguageLike = languageLike.split("-")[0].toLowerCase();
const kcLanguageTag = availableLanguages.find(
const kcLanguageTag = kcLanguageTags.find(
language =>
language.toLowerCase().includes(iso2LanguageLike) ||
getKcLanguageTagLabel(language).toLocaleLowerCase() === languageLike.toLocaleLowerCase(),

View File

@ -1,5 +1,5 @@
import { createUseGlobalState } from "powerhooks/useGlobalState";
import { getKcContext } from "../getKcContext";
import { getKcContextFromWindow } from "../getKcContext/getKcContextFromWindow";
import { getBestMatchAmongKcLanguageTag } from "./KcLanguageTag";
import type { StatefulEvt } from "powerhooks";
import { KcLanguageTag } from "./KcLanguageTag";
@ -8,7 +8,7 @@ import { KcLanguageTag } from "./KcLanguageTag";
const wrap = createUseGlobalState(
"kcLanguageTag",
() => {
const { kcContext } = getKcContext();
const kcContext = getKcContextFromWindow();
const languageLike = kcContext?.locale?.current ?? (typeof navigator === "undefined" ? undefined : navigator.language);

View File

@ -27,8 +27,9 @@ function resolveMsg<Key extends string, DoRenderMarkdown extends boolean>(props:
str = (() => {
const startIndex = str
.match(/(?<={)[0-9]+(?=})/g)
?.map(g => parseInt(g))
.match(/{[0-9]+}/g)
?.map(g => g.match(/{([0-9]+)}/)![1])
.map(indexStr => parseInt(indexStr))
.sort((a, b) => a - b)[0];
if (startIndex === undefined) {
@ -67,16 +68,16 @@ function resolveMsgAdvanced<Key extends string, DoRenderMarkdown extends boolean
const match = key.match(/^\$\{([^{]+)\}$/);
const resolvedKey = match === null ? key : match[1];
const keyUnwrappedFromCurlyBraces = match === null ? key : match[1];
const out = resolveMsg({
"key": resolvedKey,
"key": keyUnwrappedFromCurlyBraces,
args,
kcLanguageTag,
doRenderMarkdown,
});
return (out !== undefined ? out : match === null ? doRenderMarkdown ? <span>{key}</span> : key : undefined) as any;
return (out !== undefined ? out : doRenderMarkdown ? <span>{keyUnwrappedFromCurlyBraces}</span> : keyUnwrappedFromCurlyBraces) as any;
}
/**

View File

@ -1,9 +1,12 @@
import { assert } from "tsafe/assert";
import { is } from "tsafe/is";
import { deepClone } from "./deepClone";
//Warning: Be mindful that because of array this is not idempotent.
export function deepAssign(params: { target: Record<string, unknown>; source: Record<string, unknown> }) {
const { target, source } = params;
const { target } = params;
const source = deepClone(params.source);
Object.keys(source).forEach(key => {
var dereferencedSource = source[key];

View File

@ -1,3 +1,17 @@
export function deepClone<T>(arg: T): T {
return JSON.parse(JSON.stringify(arg));
import "minimal-polyfills/Object.fromEntries";
export function deepClone<T>(o: T): T {
if (!(o instanceof Object)) {
return o;
}
if (typeof o === "function") {
return o;
}
if (o instanceof Array) {
return o.map(deepClone) as any;
}
return Object.fromEntries(Object.entries(o).map(([key, value]) => [key, deepClone(value)])) as any;
}

View File

@ -5,7 +5,6 @@ import { useKcMessage } from "./i18n/useKcMessage";
import { useConstCallback } from "powerhooks/useConstCallback";
import { id } from "tsafe/id";
import type { MessageKey } from "./i18n/useKcMessage";
import { useConst } from "powerhooks/useConst";
import { emailRegexp } from "./tools/emailRegExp";
export type KcContextLike = {
@ -39,8 +38,24 @@ export function useGetErrors(params: {
const { value: defaultValue, validators } = attributes.find(attribute => attribute.name === name)!;
if (defaultValue === value && messagesPerField.existsError(value)) {
const errorMessageStr = messagesPerField.get(value);
block: {
if (defaultValue !== value) {
break block;
}
let doesErrorExist: boolean;
try {
doesErrorExist = messagesPerField.existsError(name);
} catch {
break block;
}
if (!doesErrorExist) {
break block;
}
const errorMessageStr = messagesPerField.get(name);
return [
{
@ -278,6 +293,7 @@ export function useFormValidationSlice(params: {
passwordRequired: boolean;
realm: { registrationEmailAsUsername: boolean };
};
/** NOTE: Try to avoid passing a new ref every render for better performances. */
passwordValidators?: Validators;
}) {
const {
@ -290,49 +306,53 @@ export function useFormValidationSlice(params: {
},
} = params;
const attributesWithPassword = useConst(() =>
!kcContext.passwordRequired
? kcContext.profile.attributes
: (() => {
const name = kcContext.realm.registrationEmailAsUsername ? "email" : "username";
const attributesWithPassword = useMemo(
() =>
!kcContext.passwordRequired
? kcContext.profile.attributes
: (() => {
const name = kcContext.realm.registrationEmailAsUsername ? "email" : "username";
return kcContext.profile.attributes.reduce<Attribute[]>(
(prev, curr) => [
...prev,
...(curr.name !== name
? [curr]
: [
curr,
id<Attribute>({
"name": "password",
"displayName": id<`\${${MessageKey}}`>("${password}"),
"required": true,
"readOnly": false,
"validators": passwordValidators,
"annotations": {},
"groupAnnotations": {},
}),
id<Attribute>({
"name": "password-confirm",
"displayName": id<`\${${MessageKey}}`>("${passwordConfirm}"),
"required": true,
"readOnly": false,
"validators": {
"_compareToOther": {
"name": "password",
"ignore.empty.value": true,
"shouldBe": "equal",
"error-message": id<`\${${MessageKey}}`>("${invalidPasswordConfirmMessage}"),
return kcContext.profile.attributes.reduce<Attribute[]>(
(prev, curr) => [
...prev,
...(curr.name !== name
? [curr]
: [
curr,
id<Attribute>({
"name": "password",
"displayName": id<`\${${MessageKey}}`>("${password}"),
"required": true,
"readOnly": false,
"validators": passwordValidators,
"annotations": {},
"groupAnnotations": {},
"autocomplete": "new-password",
}),
id<Attribute>({
"name": "password-confirm",
"displayName": id<`\${${MessageKey}}`>("${passwordConfirm}"),
"required": true,
"readOnly": false,
"validators": {
"_compareToOther": {
"name": "password",
"ignore.empty.value": true,
"shouldBe": "equal",
"error-message": id<`\${${MessageKey}}`>("${invalidPasswordConfirmMessage}"),
},
},
},
"annotations": {},
"groupAnnotations": {},
}),
]),
],
[],
);
})(),
"annotations": {},
"groupAnnotations": {},
"autocomplete": "new-password",
}),
]),
],
[],
);
})(),
[kcContext, passwordValidators],
);
const { getErrors } = useGetErrors({
@ -344,27 +364,29 @@ export function useFormValidationSlice(params: {
},
});
const initialInternalState = useConst(() =>
Object.fromEntries(
attributesWithPassword
.map(attribute => ({
attribute,
"errors": getErrors({
"name": attribute.name,
"fieldValueByAttributeName": Object.fromEntries(
attributesWithPassword.map(({ name, value }) => [name, { "value": value ?? "" }]),
),
}),
}))
.map(({ attribute, errors }) => [
attribute.name,
{
"value": attribute.value ?? "",
errors,
"doDisplayPotentialErrorMessages": errors.length !== 0,
},
]),
),
const initialInternalState = useMemo(
() =>
Object.fromEntries(
attributesWithPassword
.map(attribute => ({
attribute,
"errors": getErrors({
"name": attribute.name,
"fieldValueByAttributeName": Object.fromEntries(
attributesWithPassword.map(({ name, value }) => [name, { "value": value ?? "" }]),
),
}),
}))
.map(({ attribute, errors }) => [
attribute.name,
{
"value": attribute.value ?? "",
errors,
"doDisplayPotentialErrorMessages": errors.length !== 0,
},
]),
),
[attributesWithPassword],
);
type InternalState = typeof initialInternalState;
@ -421,7 +443,7 @@ export function useFormValidationSlice(params: {
errors.length === 0 && (value !== "" || !attributesWithPassword.find(attribute => attribute.name === name)!.required),
),
}),
[formValidationInternalState],
[formValidationInternalState, attributesWithPassword],
);
return { formValidationState, formValidationReducer, attributesWithPassword };

View File

@ -1,6 +1,6 @@
import { getKcContext } from "../../lib/getKcContext";
import type { KcContextBase } from "../../lib/getKcContext";
import type { ExtendsKcContextBase } from "../../lib/getKcContext/getKcContext";
import type { ExtendsKcContextBase } from "../../lib/getKcContext";
import { same } from "evt/tools/inDepth";
import { assert } from "tsafe/assert";
import type { Equals } from "tsafe";

182
yarn.lock
View File

@ -64,7 +64,7 @@
"@emotion/weak-memoize" "^0.2.5"
hoist-non-react-statics "^3.3.1"
"@emotion/serialize@^1.0.2":
"@emotion/serialize@*", "@emotion/serialize@^1.0.2":
version "1.0.2"
resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.0.2.tgz#77cb21a0571c9f68eb66087754a65fa97bfcd965"
integrity sha512-95MgNJ9+/ajxU7QIAruiOAdYNjxZX7G2mhgrtDWswA21VviYIRP1R5QilZ/bDY42xiKsaktP4egJb3QdYQZi1A==
@ -75,16 +75,6 @@
"@emotion/utils" "^1.0.0"
csstype "^3.0.2"
"@emotion/server@^11.4.0":
version "11.4.0"
resolved "https://registry.yarnpkg.com/@emotion/server/-/server-11.4.0.tgz#3ae1d74cb31c7d013c3c76e88c0c4439076e9f66"
integrity sha512-IHovdWA3V0DokzxLtUNDx4+hQI82zUXqQFcVz/om2t44O0YSc+NHB+qifnyAOoQwt3SXcBTgaSntobwUI9gnfA==
dependencies:
"@emotion/utils" "^1.0.0"
html-tokenize "^2.0.0"
multipipe "^1.0.2"
through "^2.3.8"
"@emotion/sheet@^1.0.0", "@emotion/sheet@^1.0.2":
version "1.0.2"
resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.0.2.tgz#1d9ffde531714ba28e62dac6a996a8b1089719d0"
@ -95,7 +85,7 @@
resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.7.5.tgz#77211291c1900a700b8a78cfafda3160d76949ed"
integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==
"@emotion/utils@^1.0.0":
"@emotion/utils@*", "@emotion/utils@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.0.0.tgz#abe06a83160b10570816c913990245813a2fd6af"
integrity sha512-mQC2b3XLDs6QCW+pDQDiyO/EdGZYOygE8s5N5rrzjSI4M3IejPE/JPndCBwRT9z982aqQNi6beWs1UeayrQxxA==
@ -220,11 +210,6 @@ braces@^3.0.1:
dependencies:
fill-range "^7.0.1"
buffer-from@~0.1.1:
version "0.1.2"
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-0.1.2.tgz#15f4b9bcef012044df31142c14333caf6e0260d0"
integrity sha512-RiWIenusJsmI2KcvqQABB83tLxCByE3upSP8QU3rJDMVFGPWLvPQJt/O1Su9moRWeH7d+Q2HYb68f6+v+tw2vg==
callsites@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
@ -452,7 +437,7 @@ domelementtype@^2.0.1, domelementtype@^2.2.0:
resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.2.0.tgz#9a0b6c2782ed6a1c7323d42267183df9bd8b1d57"
integrity sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==
domhandler@4.2.2, domhandler@^4.0, domhandler@^4.0.0, domhandler@^4.2.0, domhandler@^4.2.2:
domhandler@^4.0, domhandler@^4.0.0, domhandler@^4.2.0, domhandler@^4.2.2:
version "4.2.2"
resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.2.2.tgz#e825d721d19a86b8c201a35264e226c678ee755f"
integrity sha512-PzE9aBMsdZO8TK4BnuJwH0QT41wgMbRzuZrHUcpYncEjmQazq8QEaBWgLG7ZyC/DAZKEgglpIA6j4Qn/HmxS3w==
@ -468,13 +453,6 @@ domutils@^2.5.2, domutils@^2.6.0, domutils@^2.7.0, domutils@^2.8.0:
domelementtype "^2.2.0"
domhandler "^4.2.0"
duplexer2@^0.1.2:
version "0.1.4"
resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1"
integrity sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=
dependencies:
readable-stream "^2.0.2"
emoji-regex@^8.0.0:
version "8.0.0"
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
@ -665,24 +643,6 @@ hoist-non-react-statics@^3.3.1:
dependencies:
react-is "^16.7.0"
html-dom-parser@1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/html-dom-parser/-/html-dom-parser-1.0.2.tgz#bb5ff844f214657d899aa4fb7b0a9e7d15607e96"
integrity sha512-Jq4oVkVSn+10ut3fyc2P/Fs1jqTo0l45cP6Q8d2ef/9jfkYwulO0QXmyLI0VUiZrXF4czpGgMEJRa52CQ6Fk8Q==
dependencies:
domhandler "4.2.2"
htmlparser2 "6.1.0"
html-react-parser@^1.2.7:
version "1.4.0"
resolved "https://registry.yarnpkg.com/html-react-parser/-/html-react-parser-1.4.0.tgz#bf264f38b9fdf4d94e2120f6a39586c15cb81bd0"
integrity sha512-v8Kxy+7L90ZFSM690oJWBNRzZWZOQquYPpQt6kDQPzQyZptXgOJ69kHSi7xdqNdm1mOfsDPwF4K9Bo/dS5gRTQ==
dependencies:
domhandler "4.2.2"
html-dom-parser "1.0.2"
react-property "2.0.0"
style-to-js "1.1.0"
html-to-react@^1.3.4:
version "1.4.7"
resolved "https://registry.yarnpkg.com/html-to-react/-/html-to-react-1.4.7.tgz#a58129c1b77c6d4e047a647372bd194e25420b89"
@ -693,18 +653,7 @@ html-to-react@^1.3.4:
lodash.camelcase "^4.3.0"
ramda "^0.27.1"
html-tokenize@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/html-tokenize/-/html-tokenize-2.0.1.tgz#c3b2ea6e2837d4f8c06693393e9d2a12c960be5f"
integrity sha512-QY6S+hZ0f5m1WT8WffYN+Hg+xm/w5I8XeUcAq/ZYP5wVC8xbKi4Whhru3FtrAebD5EhBW8rmFzkDI6eCAuFe2w==
dependencies:
buffer-from "~0.1.1"
inherits "~2.0.1"
minimist "~1.2.5"
readable-stream "~1.0.27-1"
through2 "~0.4.1"
htmlparser2@6.1.0, htmlparser2@^6.1.0:
htmlparser2@^6.1.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7"
integrity sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==
@ -771,16 +720,6 @@ inherits@2, inherits@^2.0.1, inherits@~2.0.1, inherits@~2.0.3:
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
inherits@2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
inline-style-parser@0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/inline-style-parser/-/inline-style-parser-0.1.1.tgz#ec8a3b429274e9c0a1f1c4ffa9453a7fef72cea1"
integrity sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==
is-alphabetical@^1.0.0:
version "1.0.4"
resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-1.0.4.tgz#9e7d6b94916be22153745d184c298cbf986a686d"
@ -1023,11 +962,6 @@ minimatch@^3.0.3, minimatch@^3.0.4:
dependencies:
brace-expansion "^1.1.7"
minimist@~1.2.5:
version "1.2.5"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
mkdirp@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
@ -1038,14 +972,6 @@ ms@2.1.2:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
multipipe@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/multipipe/-/multipipe-1.0.2.tgz#cc13efd833c9cda99f224f868461b8e1a3fd939d"
integrity sha1-zBPv2DPJzamfIk+GhGG44aP9k50=
dependencies:
duplexer2 "^0.1.2"
object-assign "^4.1.0"
next-tick@1, next-tick@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb"
@ -1083,16 +1009,11 @@ nth-check@^2.0.0:
dependencies:
boolbase "^1.0.0"
object-assign@^4.1.0, object-assign@^4.1.1:
object-assign@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
object-keys@~0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-0.4.0.tgz#28a6aae7428dd2c3a92f3d95f21335dd204e0336"
integrity sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=
once@^1.3.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
@ -1174,6 +1095,11 @@ parse5@^6.0.1:
resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b"
integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==
path-browserify@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd"
integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==
path-exists@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
@ -1194,14 +1120,6 @@ path-type@^4.0.0:
resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
path@^0.12.7:
version "0.12.7"
resolved "https://registry.yarnpkg.com/path/-/path-0.12.7.tgz#d4dc2a506c4ce2197eb481ebfcd5b36c0140b10f"
integrity sha1-1NwqUGxM4hl+tIHr/NWzbAFAsQ8=
dependencies:
process "^0.11.1"
util "^0.10.3"
picomatch@^2.2.3:
version "2.3.0"
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972"
@ -1241,11 +1159,6 @@ process-nextick-args@~2.0.0:
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
process@^0.11.1:
version "0.11.10"
resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI=
prop-types@^15.7.2:
version "15.7.2"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
@ -1288,11 +1201,6 @@ react-markdown@^5.0.3:
unist-util-visit "^2.0.0"
xtend "^4.0.1"
react-property@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/react-property/-/react-property-2.0.0.tgz#2156ba9d85fa4741faf1918b38efc1eae3c6a136"
integrity sha512-kzmNjIgU32mO4mmH5+iUyrqlpFQhF8K2k7eZ4fdLSOPFrD1XgEuSBv9LDEgxRXTMBqMd8ppT0x6TIzqE5pdGdw==
react@^17.0.1:
version "17.0.2"
resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037"
@ -1301,7 +1209,17 @@ react@^17.0.1:
loose-envify "^1.1.0"
object-assign "^4.1.1"
readable-stream@^2.0.2, readable-stream@~2.3.6:
readable-stream@~1.0.31:
version "1.0.34"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c"
integrity sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=
dependencies:
core-util-is "~1.0.0"
inherits "~2.0.1"
isarray "0.0.1"
string_decoder "~0.10.x"
readable-stream@~2.3.6:
version "2.3.7"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
@ -1314,16 +1232,6 @@ readable-stream@^2.0.2, readable-stream@~2.3.6:
string_decoder "~1.1.1"
util-deprecate "~1.0.1"
readable-stream@~1.0.17, readable-stream@~1.0.27-1, readable-stream@~1.0.31:
version "1.0.34"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c"
integrity sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=
dependencies:
core-util-is "~1.0.0"
inherits "~2.0.1"
isarray "0.0.1"
string_decoder "~0.10.x"
regenerator-runtime@^0.13.4:
version "0.13.9"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52"
@ -1492,20 +1400,6 @@ strip-final-newline@^2.0.0:
resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad"
integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==
style-to-js@1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/style-to-js/-/style-to-js-1.1.0.tgz#631cbb20fce204019b3aa1fcb5b69d951ceac4ac"
integrity sha512-1OqefPDxGrlMwcbfpsTVRyzwdhr4W0uxYQzeA2F1CBc8WG04udg2+ybRnvh3XYL4TdHQrCahLtax2jc8xaE6rA==
dependencies:
style-to-object "0.3.0"
style-to-object@0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/style-to-object/-/style-to-object-0.3.0.tgz#b1b790d205991cc783801967214979ee19a76e46"
integrity sha512-CzFnRRXhzWIdItT3OmF8SQfWyahHhjq3HwcMNCNLn+N7klOOqPjMeG/4JSu77D7ypZdGvSzvkrbyeTMizz2VrA==
dependencies:
inline-style-parser "0.1.1"
stylis@^4.0.3:
version "4.0.10"
resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.0.10.tgz#446512d1097197ab3f02fb3c258358c3f7a14240"
@ -1540,14 +1434,6 @@ through2@^2.0.1:
readable-stream "~2.3.6"
xtend "~4.0.1"
through2@~0.4.1:
version "0.4.2"
resolved "https://registry.yarnpkg.com/through2/-/through2-0.4.2.tgz#dbf5866031151ec8352bb6c4db64a2292a840b9b"
integrity sha1-2/WGYDEVHsg1K7bE22SiKSqEC5s=
dependencies:
readable-stream "~1.0.17"
xtend "~2.1.1"
through@^2.3.8:
version "2.3.8"
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
@ -1593,13 +1479,13 @@ tslib@^2.2.0:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01"
integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==
tss-react@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/tss-react/-/tss-react-1.1.0.tgz#bbbf12b3d30eb02e1a39ac6dba4ca05a9c6bb674"
integrity sha512-0UUQaMCbefyXsHesnScLwmoo6lD5sdAVR1h5dgIvCOFTk0i5A5a68K2B9gm89hQFfIKPl7fzolCsJ+G9RE/vpw==
tss-react@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/tss-react/-/tss-react-3.0.0.tgz#b084e1d10d1ea23c925b6a2141c1e91a0c5e9a2d"
integrity sha512-aZ/DZEuUvqki/1TKKBM4OmRx6TI5lcaF4BLMo0D8lyT/5S7zRFaAdVfAlsirHcQNgOAdf5IjLUcEbCYWcY6PJw==
dependencies:
"@emotion/server" "^11.4.0"
html-react-parser "^1.2.7"
"@emotion/serialize" "*"
"@emotion/utils" "*"
type-fest@^0.21.3:
version "0.21.3"
@ -1677,13 +1563,6 @@ util-deprecate@~1.0.1:
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
util@^0.10.3:
version "0.10.4"
resolved "https://registry.yarnpkg.com/util/-/util-0.10.4.tgz#3aa0125bfe668a4672de58857d3ace27ecb76901"
integrity sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==
dependencies:
inherits "2.0.3"
vfile-message@^2.0.0:
version "2.0.4"
resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-2.0.4.tgz#5b43b88171d409eae58477d13f23dd41d52c371a"
@ -1742,13 +1621,6 @@ xtend@^4.0.1, xtend@~4.0.1:
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
xtend@~2.1.1:
version "2.1.2"
resolved "https://registry.yarnpkg.com/xtend/-/xtend-2.1.2.tgz#6efecc2a4dad8e6962c4901b337ce7ba87b5d28b"
integrity sha1-bv7MKk2tjmlixJAbM3znuoe10os=
dependencies:
object-keys "~0.4.0"
y18n@^5.0.5:
version "5.0.8"
resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"