Compare commits

...

81 Commits

Author SHA1 Message Date
cfcc48259c Update changelog v4.2.14 2021-12-12 19:49:27 +00:00
785ce7a8ab Bump version (changelog ignore) 2021-12-12 20:46:58 +01:00
ad5de216b0 Merge pull request #65 from InseeFrLab/doge_ftl_errors
Prevent ftl errors in Keycloak log
2021-12-12 20:45:55 +01:00
26b80d6af7 Encourage users to report errors in logs 2021-12-12 20:44:03 +01:00
a8623d8066 Fix ftl error related to url.loginAction in saml-post-form.ftl 2021-12-12 20:17:50 +01:00
86ab9f72a5 Ftl prevent error with updateProfileCtx 2021-12-12 19:35:28 +01:00
b3892dab8d Ftl prevent error with auth.attemptedUsername 2021-12-12 19:19:17 +01:00
57a5d034dd Fix ftl error as comment formatting 2021-12-12 19:06:12 +01:00
cee9569581 Refactor: Create ftl function are_same_path (changelog ignore) 2021-12-12 18:59:39 +01:00
159429da6e Remove extra semicollon in ftl (changelog ignore) 2021-12-12 17:39:39 +01:00
a292cb0b4b Merge remote-tracking branch 'origin/main' into doge_ftl_errors 2021-12-12 14:12:31 +01:00
d70985d8d2 Update README, remove all instruction about errors in logs 2021-12-12 14:10:00 +01:00
484f95f5d2 Bump beta version (changelog ignore) 2021-12-12 12:53:11 +01:00
6e0553af9b Avoid error in Keycloak logs, fix long template loading time 2021-12-12 05:38:21 +01:00
cb18d3d765 Bump version (changelog ignore) 2021-12-12 05:29:59 +01:00
f316f38ae5 Update CI workflow (changelog ignore) 2021-12-12 05:29:59 +01:00
5f07cb374b Update changelog v4.2.13 2021-12-12 05:29:59 +01:00
96d31e07c3 Update about future fixes (changelog ignore) 2021-12-11 20:26:37 +01:00
99a5efe36c Add missing collon in README sample code
Add miss ','
2021-12-09 21:16:05 +01:00
5c46ecc0ed Update CI workflow (changelog ignore) 2021-12-09 01:57:03 +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
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
a95df42843 Update changelog v4.2.0 2021-10-26 14:11:15 +00:00
4ecbb30a1b Bump version (changelog ignore) 2021-10-26 16:08:00 +02:00
96b40b9c49 Export types definitions for Attribue and Validator 2021-10-26 16:07:30 +02:00
c32eebdd46 Merge branch 'main' of https://github.com/garronej/keycloakify into main 2021-10-26 14:59:23 +02:00
5b17287555 Move changelog highlight at the bottom of the REAMDE 2021-10-26 14:59:15 +02:00
fb01257c8b Update changelog v4.1.0 2021-10-26 12:56:11 +00:00
53470f8788 Bump version (changelog ignore) 2021-10-26 14:53:09 +02:00
89b86936f6 Document what's new in v4 2021-10-26 14:50:57 +02:00
25 changed files with 780 additions and 628 deletions

View File

@ -22,7 +22,6 @@ jobs:
PACKAGE_MANAGER=yarn PACKAGE_MANAGER=yarn
fi fi
$PACKAGE_MANAGER run format:check $PACKAGE_MANAGER run format:check
test: test:
runs-on: macos-10.15 runs-on: macos-10.15
needs: test_formatting needs: test_formatting
@ -33,7 +32,7 @@ jobs:
steps: steps:
- name: Tell if project is using npm or yarn - name: Tell if project is using npm or yarn
id: step1 id: step1
uses: garronej/github_actions_toolkit@v2.2 uses: garronej/ts-ci@v1.1.3
with: with:
action_name: tell_if_project_uses_npm_or_yarn action_name: tell_if_project_uses_npm_or_yarn
- uses: actions/checkout@v2.3.4 - uses: actions/checkout@v2.3.4
@ -51,28 +50,35 @@ jobs:
npm test npm test
check_if_version_upgraded: check_if_version_upgraded:
name: Check if version upgrade 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 runs-on: ubuntu-latest
needs: test needs: test
outputs: outputs:
from_version: ${{ steps.step1.outputs.from_version }} from_version: ${{ steps.step1.outputs.from_version }}
to_version: ${{ steps.step1.outputs.to_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: steps:
- uses: garronej/github_actions_toolkit@v2.2 - uses: garronej/ts-ci@v1.1.3
id: step1 id: step1
with: with:
action_name: is_package_json_version_upgraded action_name: is_package_json_version_upgraded
branch: ${{ github.head_ref || github.ref }}
update_changelog: update_changelog:
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: check_if_version_upgraded needs: check_if_version_upgraded
if: needs.check_if_version_upgraded.outputs.is_upgraded_version == 'true' if: needs.check_if_version_upgraded.outputs.is_upgraded_version == 'true'
steps: steps:
- uses: garronej/github_actions_toolkit@v2.4 - uses: garronej/ts-ci@v1.1.3
with: with:
action_name: update_changelog action_name: update_changelog
branch: ${{ github.ref }} branch: ${{ github.head_ref || github.ref }}
create_github_release: create_github_release:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@ -80,9 +86,6 @@ jobs:
- update_changelog - update_changelog
- check_if_version_upgraded - check_if_version_upgraded
steps: steps:
- uses: actions/checkout@v2
with:
ref: ${{ github.ref }}
- name: Build GitHub release body - name: Build GitHub release body
id: step1 id: step1
run: | run: |
@ -98,10 +101,10 @@ jobs:
with: with:
name: Release v${{ needs.check_if_version_upgraded.outputs.to_version }} name: Release v${{ needs.check_if_version_upgraded.outputs.to_version }}
tag_name: 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 }} body: ${{ steps.step1.outputs.body }}
draft: false draft: false
prerelease: false prerelease: ${{ needs.check_if_version_upgraded.outputs.is_release_beta == 'true' }}
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 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" 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 false
fi fi
npm publish EXTRA_ARGS=""
if [ "$IS_BETA" = "true" ]; then
EXTRA_ARGS="--tag beta"
fi
npm publish $EXTRA_ARGS
env: env:
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
VERSION: ${{ needs.check_if_version_upgraded.outputs.to_version }} 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/ /sample_react_project/
/.yarn_home/ /.yarn_home/
.idea

View File

@ -1,3 +1,83 @@
### **4.2.14** (2021-12-12)
- Merge pull request #65 from InseeFrLab/doge_ftl_errors
Prevent ftl errors in Keycloak log
- Encourage users to report errors in logs
- Fix ftl error related to url.loginAction in saml-post-form.ftl
- Ftl prevent error with updateProfileCtx
- Ftl prevent error with auth.attemptedUsername
- Fix ftl error as comment formatting
- Merge remote-tracking branch 'origin/main' into doge_ftl_errors
- Update README, remove all instruction about errors in logs
- Avoid error in Keycloak logs, fix long template loading time
- Add missing collon in README sample code
Add miss ','
### **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
## **4.1.0** (2021-10-26)
- Document what's new in v4
# **4.0.0** (2021-10-26) # **4.0.0** (2021-10-26)
- fix RegisterUserProfile password confirmation field - fix RegisterUserProfile password confirmation field

149
README.md
View File

@ -20,30 +20,10 @@
<img src="https://user-images.githubusercontent.com/6702424/110260457-a1c3d380-7fac-11eb-853a-80459b65626b.png"> <img src="https://user-images.githubusercontent.com/6702424/110260457-a1c3d380-7fac-11eb-853a-80459b65626b.png">
</p> </p>
**NEW in v3** **NEW in 4.2.14**
No breaking changes except that `@emotion/react`, [`tss-react`](https://www.npmjs.com/package/tss-react) and [`powerhooks`](https://www.npmjs.com/package/powerhooks) are now `peerDependencies` instead of being just dependencies. - No more error in Keycloak logs 🍾
It's important to avoid problem when using `keycloakify` alongside [`mui`](https://mui.com) and - Templates now load in fraction of a second 🏎
[when passing params from the app to the login page](https://github.com/InseeFrLab/keycloakify#implement-context-persistence-optional).
**NEW in v2.5**
- User Profile ([`register-user-profile.ftl`](https://github.com/InseeFrLab/keycloakify/blob/main/src/lib/components/RegisterUserProfile.tsx))
is now supported! 🎉
It 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
[**on the frontend**, in realtime](https://github.com/InseeFrLab/keycloakify/blob/6dca6a93d8cfe634ee4d8574ad0c091641220092/src/lib/getKcContext/KcContextBase.ts#L225-L261)!
NOTE: User profile is only available in Keycloak 15 and it's a beta feature that
[needs to be enabled when launching keycloak](https://github.com/InseeFrLab/keycloakify/blob/59f106bf9e210b63b190826da2bf5f75fc8b7644/src/bin/build-keycloak-theme/build-keycloak-theme.ts#L116-L117) and [enabled in the console](https://user-images.githubusercontent.com/6702424/136874428-b071d614-c7f7-440d-9b2e-670faadc0871.png).
- Feature [Use advanced message](https://github.com/InseeFrLab/keycloakify/blob/59f106bf9e210b63b190826da2bf5f75fc8b7644/src/lib/i18n/useKcMessage.tsx#L53-L66)
and [`messagesPerFields`](https://github.com/InseeFrLab/keycloakify/blob/59f106bf9e210b63b190826da2bf5f75fc8b7644/src/lib/getKcContext/KcContextBase.ts#L70-L75) (implementation [here](https://github.com/InseeFrLab/keycloakify/blob/59f106bf9e210b63b190826da2bf5f75fc8b7644/src/bin/build-keycloak-theme/generateFtl/common.ftl#L130-L189))
- Test container now uses Keycloak version `15.0.2`.
**NEW in v2**
- It's now possible to implement custom `.ftl` pages.
- Support for Keycloak plugins that introduce non standard ftl values.
(Like for example [this plugin](https://github.com/micedre/keycloak-mail-whitelisting) that define `authorizedMailDomains` in `register.ftl`).
# Motivations # Motivations
@ -85,22 +65,30 @@ If you already have a Keycloak custom theme, it can be easily ported to Keycloak
- [Advanced pages configuration](#advanced-pages-configuration) - [Advanced pages configuration](#advanced-pages-configuration)
- [Hot reload](#hot-reload) - [Hot reload](#hot-reload)
- [Enable loading in a blink of an eye of login pages ⚡ (--external-assets)](#enable-loading-in-a-blink-of-an-eye-of-login-pages----external-assets) - [Enable loading in a blink of an eye of login pages ⚡ (--external-assets)](#enable-loading-in-a-blink-of-an-eye-of-login-pages----external-assets)
- [User profile and frontend form validation](#user-profile-and-frontend-form-validation)
- [Support for Terms and conditions](#support-for-terms-and-conditions) - [Support for Terms and conditions](#support-for-terms-and-conditions)
- [Some pages still have the default theme. Why?](#some-pages-still-have-the-default-theme-why) - [Some pages still have the default theme. Why?](#some-pages-still-have-the-default-theme-why)
- [GitHub Actions](#github-actions) - [GitHub Actions](#github-actions)
- [Limitations](#limitations) - [Limitations](#limitations)
- [`process.env.PUBLIC_URL` not supported.](#processenvpublic_url-not-supported) - [`process.env.PUBLIC_URL` not supported.](#processenvpublic_url-not-supported)
- [`@font-face` importing fonts from the `src/` dir](#font-face-importing-fonts-from-thesrc-dir) - [`@font-face` importing fonts from the `src/` dir](#font-face-importing-fonts-from-the-src-dir)
- [Example of setup that **won't** work](#example-of-setup-that-wont-work) - [Example of setup that **won't** work](#example-of-setup-that-wont-work)
- [Possible workarounds](#possible-workarounds) - [Possible workarounds](#possible-workarounds)
- [Implement context persistence (optional)](#implement-context-persistence-optional) - [Implement context persistence (optional)](#implement-context-persistence-optional)
- [Kickstart video](#kickstart-video) - [Kickstart video](#kickstart-video)
- [About the errors related to `objectToJson` in Keycloak logs.](#about-the-errors-related-to-objecttojson-in-keycloak-logs) - [FTL errors related to `ftl_object_to_js_code_declaring_an_object` in Keycloak logs.](#ftl-errors-related-to-ftl_object_to_js_code_declaring_an_object-in-keycloak-logs)
- [Adding custom message (to `i18n/useKcMessage.tsx`)](#adding-custom-message-to-i18nusekcmessagetsx) - [Adding custom message (to `i18n/useKcMessage.tsx`)](#adding-custom-message-to-i18nusekcmessagetsx)
- [Email domain whitelist](#email-domain-whitelist) - [Email domain whitelist](#email-domain-whitelist)
- [Changelog highlights](#changelog-highlights)
- [v4](#v4)
- [v3](#v3)
- [v2.5](#v25)
- [v2](#v2)
# Requirements # 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: Tested with the following Keycloak versions:
- [11.0.3](https://hub.docker.com/layers/jboss/keycloak/11.0.3/images/sha256-4438f1e51c1369371cb807dffa526e1208086b3ebb9cab009830a178de949782?context=explore) - [11.0.3](https://hub.docker.com/layers/jboss/keycloak/11.0.3/images/sha256-4438f1e51c1369371cb807dffa526e1208086b3ebb9cab009830a178de949782?context=explore)
@ -120,8 +108,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. - `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`. - `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? ## My framework doesnt seem to be supported, what can I do?
Currently Keycloakify is only compatible with `create-react-app` apps. Currently Keycloakify is only compatible with `create-react-app` apps.
@ -165,27 +151,23 @@ your index should look something like:
`src/index.tsx` `src/index.tsx`
```tsx ```tsx
import { App } from "./<wherever>/App"; import { App } from "./<wherever>/App";
import { import { KcApp, defaultKcProps, getKcContext } from "keycloakify";
KcApp, import { css } from "tss-react/@emotion/css";
defaultKcProps,
getKcContext
} from "keycloakify";
import { css } from "tss-react/@emotion/css";
const { kcContext } = getKcContext(); const { kcContext } = getKcContext();
const myClassName = css({ "color": "red" }); const myClassName = css({ "color": "red" });
reactDom.render( reactDom.render(
<KcApp <KcApp
kcContext={kcContext} kcContext={kcContext}
{...{ {...{
...defaultKcProps, ...defaultKcProps,
"kcHeaderWrapperClass": myClassName "kcHeaderWrapperClass": myClassName,
}} }}
/> />,
document.getElementById("root") document.getElementById("root"),
); );
``` ```
@ -313,6 +295,34 @@ performance boost if you jump through those hoops:
Checkout a complete setup [here](https://github.com/garronej/keycloakify-demo-app#about-keycloakify) Checkout a complete setup [here](https://github.com/garronej/keycloakify-demo-app#about-keycloakify)
# User profile and frontend form validation
<p align="center">
<a href="https://github.com/InseeFrLab/keycloakify/releases/download/v0.0.1/keycloakify_fontend_validation.mp4">
<img src="https://user-images.githubusercontent.com/6702424/138880146-6fef3280-c4a5-46d2-bbb3-8b9598c057a5.gif">
</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
[**on the frontend**, in realtime](https://github.com/InseeFrLab/keycloakify/blob/6dca6a93d8cfe634ee4d8574ad0c091641220092/src/lib/getKcContext/KcContextBase.ts#L225-L261)!
NOTE: User profile is only available in Keycloak 15 and it's a beta feature that
[needs to be enabled when launching keycloak](https://github.com/InseeFrLab/keycloakify/blob/59f106bf9e210b63b190826da2bf5f75fc8b7644/src/bin/build-keycloak-theme/build-keycloak-theme.ts#L116-L117)
and [enabled in the console](https://user-images.githubusercontent.com/6702424/136874428-b071d614-c7f7-440d-9b2e-670faadc0871.png).
Keycloakify, in [`register-user-profile.ftl`](https://github.com/InseeFrLab/keycloakify/blob/main/src/lib/components/RegisterUserProfile.tsx),
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 # 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). [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).
@ -343,7 +353,7 @@ the building and publishing of the theme (the .jar file).
You won't be able to [import things from your public directory **in your JavaScript code**](https://create-react-app.dev/docs/using-the-public-folder/#adding-assets-outside-of-the-module-system). You won't be able to [import things from your public directory **in your JavaScript code**](https://create-react-app.dev/docs/using-the-public-folder/#adding-assets-outside-of-the-module-system).
(This isn't recommended anyway). (This isn't recommended anyway).
## `@font-face` importing fonts from the `src/` dir ## `@font-face` importing fonts from the `src/` dir
If you are building the theme with [--external-assets](#enable-loading-in-a-blink-of-a-eye-of-login-pages-) If you are building the theme with [--external-assets](#enable-loading-in-a-blink-of-a-eye-of-login-pages-)
this limitation doesn't apply, you can import fonts however you see fit. this limitation doesn't apply, you can import fonts however you see fit.
@ -359,7 +369,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 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` - 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`. 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). - 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) # Implement context persistence (optional)
@ -426,24 +437,22 @@ flash of the blank html before the js bundle have been evaluated
_NOTE: keycloak-react-theming was renamed keycloakify since this video was recorded_ _NOTE: keycloak-react-theming was renamed keycloakify since this video was recorded_
[![kickstart_video](https://user-images.githubusercontent.com/6702424/108877866-f146ee80-75ff-11eb-8120-003b3c5f6dd8.png)](https://youtu.be/xTz0Rj7i2v8) [![kickstart_video](https://user-images.githubusercontent.com/6702424/108877866-f146ee80-75ff-11eb-8120-003b3c5f6dd8.png)](https://youtu.be/xTz0Rj7i2v8)
# About the errors related to `objectToJson` in Keycloak logs. # FTL errors related to `ftl_object_to_js_code_declaring_an_object` in Keycloak logs.
The logs of your keycloak server will always show this kind of errors every time a client request a page: If you ever encounter one of these errors:
```log ```log
FTL stack trace ("~" means nesting-related): FTL stack trace ("~" means nesting-related):
- Failed at: #local value = object[key] [in template "login.ftl" in macro "objectToJson" at line 70, column 21] - Failed at: #local value = object[key] [in template "login.ftl" in macro "ftl_object_to_js_code_declaring_an_object" at line 70, column 21]
- Reached through: @compress [in template "login.ftl" in macro "objectToJson" at line 36, column 5] - Reached through: @compress [in template "login.ftl" in macro "ftl_object_to_js_code_declaring_an_object" 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: @ftl_object_to_js_code_declaring_an_object object=value depth=(dep... [in template "login.ftl" in macro "ftl_object_to_js_code_declaring_an_object" at line 81, column 27]
- Reached through: @compress [in template "login.ftl" in macro "objectToJson" at line 36, column 5] - Reached through: @compress [in template "login.ftl" in macro "ftl_object_to_js_code_declaring_an_object" at line 36, column 5]
- Reached through: @objectToJson object=(.data_model) de... [in template "login.ftl" at line 163, column 43] - Reached through: @ftl_object_to_js_code_declaring_an_object object=(.data_model) de... [in template "login.ftl" at line 163, column 43]
``` ```
Theses are expected and can be safely ignored. It's just noise, they can be safely ignored.
You can, however, and are encouraged to, report any that you would spot.
To [converts the `.ftl` values into a JavaScript object](https://github.com/InseeFrLab/keycloakify/blob/main/src/bin/build-keycloak-theme/generateFtl/common.ftl) Just open an issue about it and I will release a patched version of Keycloakify in the better delays.
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.
# Adding custom message (to `i18n/useKcMessage.tsx`) # Adding custom message (to `i18n/useKcMessage.tsx`)
@ -453,5 +462,33 @@ This approach is a bit hacky as it doesn't provide type safety but it works.
# Email domain whitelist # 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) 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. and `kcRegisterContext["authorizedMailDomains"]` to validate on.
# Changelog highlights
## v4
- Out of the box [frontend form validation](#user-profile-and-frontend-form-validation) 🥳
- Improvements (and breaking changes in `import { useKcMessage } from "keycloakify"`.
## v3
No breaking changes except that `@emotion/react`, [`tss-react`](https://www.npmjs.com/package/tss-react) and [`powerhooks`](https://www.npmjs.com/package/powerhooks) are now `peerDependencies` instead of being just dependencies.
It's important to avoid problem when using `keycloakify` alongside [`mui`](https://mui.com) and
[when passing params from the app to the login page](https://github.com/InseeFrLab/keycloakify#implement-context-persistence-optional).
## v2.5
- Feature [Use advanced message](https://github.com/InseeFrLab/keycloakify/blob/59f106bf9e210b63b190826da2bf5f75fc8b7644/src/lib/i18n/useKcMessage.tsx#L53-L66)
and [`messagesPerFields`](https://github.com/InseeFrLab/keycloakify/blob/59f106bf9e210b63b190826da2bf5f75fc8b7644/src/lib/getKcContext/KcContextBase.ts#L70-L75) (implementation [here](https://github.com/InseeFrLab/keycloakify/blob/59f106bf9e210b63b190826da2bf5f75fc8b7644/src/bin/build-keycloak-theme/generateFtl/common.ftl#L130-L189))
- Test container now uses Keycloak version `15.0.2`.
## v2
- It's now possible to implement custom `.ftl` pages.
- Support for Keycloak plugins that introduce non standard ftl values.
(Like for example [this plugin](https://github.com/micedre/keycloak-mail-whitelisting) that define `authorizedMailDomains` in `register.ftl`).

View File

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

View File

@ -121,11 +121,14 @@ export function main() {
"", "",
`👉 $ ./${pathRelative(reactProjectDirPath, pathJoin(keycloakThemeBuildingDirPath, containerLaunchScriptBasename))} 👈`, `👉 $ ./${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),', "Once your container is up and running: ",
`go to your realm settings, click on the theme tab then select ${themeName}.`, "- Log into the admin console 👉 http://localhost:8080 username: admin, password: admin 👈",
`More details: https://www.keycloak.org/getting-started/getting-started-docker`, '- 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"), ].join("\n"),
); );

View File

@ -1,28 +0,0 @@
Object.defineProperty(
Object,
"deepAssign",
{
"value": function callee(target, source) {
Object.keys(source).forEach(function (key) {
var value = source[key];
if (target[key] === undefined) {
target[key] = value;
return;
}
if (value instanceof Object) {
if (value instanceof Array) {
value.forEach(function (entry) {
target[key].push(entry);
});
return;
}
callee(target[key], value);
return;
}
target[key] = value;
});
return target;
}
}
);

View File

@ -1,208 +0,0 @@
<script>const _=
<#macro objectToJson object depth>
<@compress>
<#local isHash = false>
<#attempt>
<#local isHash = object?is_hash || object?is_hash_ex>
<#recover>
/* can't evaluate if object is hash */
undefined
<#return>
</#attempt>
<#if isHash>
<#local keys = "">
<#attempt>
<#local keys = object?keys>
<#recover>
/* can't list keys of object */
undefined
<#return>
</#attempt>
{${'\n'}
<#list keys as key>
<#if key == "class">
/* skipping "class" property of object */
<#continue>
</#if>
<#local value = "">
<#attempt>
<#local value = object[key]>
<#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/>,
</#list>
}${'\n'}
<#return>
</#if>
<#local isMethod = "">
<#attempt>
<#local isMethod = object?is_method>
<#recover>
/* can't test if object is a method */
undefined
<#return>
</#attempt>
<#if isMethod>
undefined
<#return>
</#if>
<#local isBoolean = "">
<#attempt>
<#local isBoolean = object?is_boolean>
<#recover>
/* can't test if object is a boolean */
undefined
<#return>
</#attempt>
<#if isBoolean>
${object?c}
<#return>
</#if>
<#local isEnumerable = "">
<#attempt>
<#local isEnumerable = object?is_enumerable>
<#recover>
/* can't test if object is enumerable */
undefined
<#return>
</#attempt>
<#if isEnumerable>
[${'\n'}
<#list object as item>
<@objectToJson object=item depth=depth+1/>,
</#list>
]${'\n'}
<#return>
</#if>
<#attempt>
"${object?replace('"', '\\"')?no_esc}"
<#recover>
/* couldn't convert into string non hash, non method, non boolean, non enumerable object */
undefined;
<#return>
</#attempt>
</@compress>
</#macro>
(()=>{
const nonAutomaticallyConvertible = {
"messagesPerField": {
<#assign fieldNames = ["global", "userLabel", "username", "email", "firstName", "lastName", "password", "password-confirm"]>
<#attempt>
<#list profile.attributes as attribute>
<#assign fieldNames += [attribute.name]>
</#list>
<#recover>
</#attempt>
"printIfExists": function (fieldName, x) {
<#list fieldNames as fieldName>
if(fieldName === "${fieldName}" ){
<#attempt>
return "${messagesPerField.printIfExists(fieldName,'1')}" ? x : undefined;
<#recover>
</#attempt>
}
</#list>
throw new Error("There is no " + fieldName + " field");
},
"existsError": function (fieldName) {
<#list fieldNames as fieldName>
if(fieldName === "${fieldName}" ){
<#attempt>
return <#if messagesPerField.existsError('${fieldName}')>true<#else>false</#if>;
<#recover>
</#attempt>
}
</#list>
throw new Error("There is no " + fieldName + " field");
},
"get": function (fieldName) {
<#list fieldNames as fieldName>
if(fieldName === "${fieldName}" ){
<#attempt>
<#if messagesPerField.existsError('${fieldName}')>
return "${messagesPerField.get('${fieldName}')?no_esc}";
</#if>
<#recover>
</#attempt>
}
</#list>
throw new Error("There is no " + fieldName + " field");
},
"exists": function (fieldName) {
<#list fieldNames as fieldName>
if(fieldName === "${fieldName}" ){
<#attempt>
return <#if messagesPerField.exists('${fieldName}')>true<#else>false</#if>;
<#recover>
</#attempt>
}
</#list>
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'"); }
};
const out = {};
Object.deepAssign(
out,
//Removing all the undefined
JSON.parse(JSON.stringify(<@objectToJson object=.data_model depth=0 />))
);
Object.deepAssign(
out,
nonAutomaticallyConvertible
);
return out;
})()
</script>

View File

@ -0,0 +1,290 @@
<script>const _=
<#assign pageId="PAGE_ID_xIgLsPgGId9D8e">
(()=>{
const out =
${ftl_object_to_js_code_declaring_an_object(.data_model, [])?no_esc};
out["msg"]= function(){ throw new Error("use import { useKcMessage } from 'keycloakify'"); };
out["advancedMsg"]= function(){ throw new Error("use import { useKcMessage } from 'keycloakify'"); };
out["messagesPerField"]= {
<#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"
]>
<#attempt>
<#if profile?? && profile.attributes?? && profile.attributes?is_enumerable>
<#list profile.attributes as attribute>
<#if fieldNames?seq_contains(attribute.name)>
<#continue>
</#if>
<#assign fieldNames += [attribute.name]>
</#list>
</#if>
<#recover>
</#attempt>
"printIfExists": function (fieldName, x) {
<#list fieldNames as fieldName>
if(fieldName === "${fieldName}" ){
<#attempt>
return "${messagesPerField.printIfExists(fieldName,'1')}" ? x : undefined;
<#recover>
</#attempt>
}
</#list>
throw new Error("There is no " + fieldName + " field");
},
"existsError": function (fieldName) {
<#list fieldNames as fieldName>
if(fieldName === "${fieldName}" ){
<#attempt>
return <#if messagesPerField.existsError('${fieldName}')>true<#else>false</#if>;
<#recover>
</#attempt>
}
</#list>
throw new Error("There is no " + fieldName + " field");
},
"get": function (fieldName) {
<#list fieldNames as fieldName>
if(fieldName === "${fieldName}" ){
<#attempt>
<#if messagesPerField.existsError('${fieldName}')>
return "${messagesPerField.get('${fieldName}')?no_esc}";
</#if>
<#recover>
</#attempt>
}
</#list>
throw new Error("There is no " + fieldName + " field");
},
"exists": function (fieldName) {
<#list fieldNames as fieldName>
if(fieldName === "${fieldName}" ){
<#attempt>
return <#if messagesPerField.exists('${fieldName}')>true<#else>false</#if>;
<#recover>
</#attempt>
}
</#list>
throw new Error("There is no " + fieldName + " field");
}
};
out["pageId"] = "${pageId}";
return out;
})()
<#function ftl_object_to_js_code_declaring_an_object object path>
<#local isHash = "">
<#attempt>
<#local isHash = object?is_hash || object?is_hash_ex>
<#recover>
<#return "ABORT: Can't evaluate if " + path?join(".") + " is hash">
</#attempt>
<#if isHash>
<#if path?size gt 10>
<#return "ABORT: Too many recursive calls">
</#if>
<#local keys = "">
<#attempt>
<#local keys = object?keys>
<#recover>
<#return "ABORT: We can't list keys on this object">
</#attempt>
<#local out_seq = []>
<#list keys as key>
<#if ["class","declaredConstructors","superclass","declaringClass" ]?seq_contains(key) >
<#continue>
</#if>
<#if
(
["loginUpdatePasswordUrl", "loginUpdateProfileUrl", "loginUsernameReminderUrl", "loginUpdateTotpUrl"]?seq_contains(key) &&
are_same_path(path, ["url"])
) || (
key == "updateProfileCtx" &&
are_same_path(path, [])
) || (
<#-- https://github.com/InseeFrLab/keycloakify/pull/65#issuecomment-991896344 -->
key == "loginAction" &&
are_same_path(path, ["url"]) &&
pageId == "saml-post-form.ftl"
)
>
<#local out_seq += ["/*If you need '" + key + "' on " + pageId + ", please submit an issue to the Keycloakify repo*/"]>
<#continue>
</#if>
<#if key == "attemptedUsername" && are_same_path(path, ["auth"])>
<#attempt>
<#-- https://github.com/keycloak/keycloak/blob/3a2bf0c04bcde185e497aaa32d0bb7ab7520cf4a/themes/src/main/resources/theme/base/login/template.ftl#L63 -->
<#if !(auth?has_content && auth.showUsername() && !auth.showResetCredentials())>
<#continue>
</#if>
<#recover>
</#attempt>
</#if>
<#attempt>
<#if !object[key]??>
<#continue>
</#if>
<#recover>
<#local out_seq += ["/*Couldn't test if '" + key + "' is available on this object*/"]>
<#continue>
</#attempt>
<#local propertyValue = "">
<#attempt>
<#local propertyValue = object[key]>
<#recover>
<#local out_seq += ["/*Couldn't dereference '" + key + "' on this object*/"]>
<#continue>
</#attempt>
<#local rec_out = ftl_object_to_js_code_declaring_an_object(propertyValue, path + [ key ])>
<#if rec_out?starts_with("ABORT:")>
<#local errorMessage = rec_out?remove_beginning("ABORT:")>
<#if errorMessage != " It's a method" >
<#local out_seq += ["/*" + key + ": " + errorMessage + "*/"]>
</#if>
<#continue>
</#if>
<#local out_seq += ['"' + key + '": ' + rec_out + ","]>
</#list>
<#return (["{"] + out_seq?map(str -> ""?right_pad(4 * (path?size + 1)) + str) + [ ""?right_pad(4 * path?size) + "}"])?join("\n")>
</#if>
<#local isMethod = "">
<#attempt>
<#local isMethod = object?is_method>
<#recover>
<#return "ABORT: Can't test if it'sa method.">
</#attempt>
<#if isMethod>
<#return "ABORT: It's a method">
</#if>
<#local isBoolean = "">
<#attempt>
<#local isBoolean = object?is_boolean>
<#recover>
<#return "ABORT: Can't test if it's a boolean">
</#attempt>
<#if isBoolean>
<#return object?c>
</#if>
<#local isEnumerable = "">
<#attempt>
<#local isEnumerable = object?is_enumerable>
<#recover>
<#return "ABORT: Can't test if it's an enumerable">
</#attempt>
<#if isEnumerable>
<#local out_seq = []>
<#local i = 0>
<#list object as array_item>
<#local rec_out = ftl_object_to_js_code_declaring_an_object(array_item, path + [ i ])>
<#local i = i + 1>
<#if rec_out?starts_with("ABORT:")>
<#local errorMessage = rec_out?remove_beginning("ABORT:")>
<#if errorMessage != " It's a method" >
<#local out_seq += ["/*" + i?string + ": " + errorMessage + "*/"]>
</#if>
<#continue>
</#if>
<#local out_seq += [rec_out + ","]>
</#list>
<#return (["["] + out_seq?map(str -> ""?right_pad(4 * (path?size + 1)) + str) + [ ""?right_pad(4 * path?size) + "]"])?join("\n")>
</#if>
<#attempt>
<#return '"' + object?js_string + '"'>;
<#recover>
</#attempt>
<#return "ABORT: Couldn't convert into string non hash, non method, non boolean, non enumerable object">
</#function>
<#function are_same_path path searchedPath>
<#if path?size != path?size>
<#return false>
</#if>
<#local i=0>
<#list path as property>
<#local searchedProperty=searchedPath[i]>
<#if searchedProperty?is_string && searchedProperty == "*">
<#continue>
</#if>
<#if searchedProperty?is_string && !property?is_string>
<#return false>
</#if>
<#if searchedProperty?is_number && !property?is_number>
<#return false>
</#if>
<#if searchedProperty?string != property?string>
<#return false>
</#if>
<#local i+= 1>
</#list>
<#return true>
</#function>
</script>

View File

@ -21,10 +21,6 @@ export const pageIds = [
export type PageId = typeof pageIds[number]; export type PageId = typeof pageIds[number];
function loadAdjacentFile(fileBasename: string) {
return fs.readFileSync(pathJoin(__dirname, fileBasename)).toString("utf8");
}
export function generateFtlFilesCodeFactory(params: { export function generateFtlFilesCodeFactory(params: {
cssGlobalsToDefine: Record<string, string>; cssGlobalsToDefine: Record<string, string>;
indexHtmlCode: string; indexHtmlCode: string;
@ -77,8 +73,11 @@ export function generateFtlFilesCodeFactory(params: {
); );
//FTL is no valid html, we can't insert with cheerio, we put placeholder for injecting later. //FTL is no valid html, we can't insert with cheerio, we put placeholder for injecting later.
const ftlPlaceholders = { const replaceValueBySearchValue = {
'{ "x": "vIdLqMeOed9sdLdIdOxdK0d" }': loadAdjacentFile("common.ftl").match(/^<script>const _=((?:.|\n)+)<\/script>[\n]?$/)![1], '{ "x": "vIdLqMeOed9sdLdIdOxdK0d" }': fs
.readFileSync(pathJoin(__dirname, "ftl_object_to_js_code_declaring_an_object.ftl"))
.toString("utf8")
.match(/^<script>const _=((?:.|\n)+)<\/script>[\n]?$/)![1],
"<!-- xIdLqMeOedErIdLsPdNdI9dSlxI -->": [ "<!-- xIdLqMeOedErIdLsPdNdI9dSlxI -->": [
"<#if scripts??>", "<#if scripts??>",
" <#list scripts as script>", " <#list scripts as script>",
@ -88,8 +87,6 @@ export function generateFtlFilesCodeFactory(params: {
].join("\n"), ].join("\n"),
}; };
const pageSpecificCodePlaceholder = "<!-- dIddLqMeOedErIdLsPdNdI9dSl42sw -->";
$("head").prepend( $("head").prepend(
[ [
...(Object.keys(cssGlobalsToDefine).length === 0 ...(Object.keys(cssGlobalsToDefine).length === 0
@ -105,18 +102,10 @@ export function generateFtlFilesCodeFactory(params: {
"", "",
]), ]),
"<script>", "<script>",
loadAdjacentFile("Object.deepAssign.js"), ` window.${ftlValuesGlobalName}= ${objectKeys(replaceValueBySearchValue)[0]};`,
"</script>",
"<script>",
` window.${ftlValuesGlobalName}= Object.assign(`,
` {},`,
` ${objectKeys(ftlPlaceholders)[0]}`,
" );",
"</script>", "</script>",
"", "",
pageSpecificCodePlaceholder, objectKeys(replaceValueBySearchValue)[1],
"",
objectKeys(ftlPlaceholders)[1],
].join("\n"), ].join("\n"),
); );
@ -129,19 +118,13 @@ export function generateFtlFilesCodeFactory(params: {
const $ = cheerio.load(partiallyFixedIndexHtmlCode); const $ = cheerio.load(partiallyFixedIndexHtmlCode);
let ftlCode = $.html().replace( let ftlCode = $.html();
pageSpecificCodePlaceholder,
[
"<script>",
` Object.deepAssign(`,
` window.${ftlValuesGlobalName},`,
` { "pageId": "${pageId}" }`,
" );",
"</script>",
].join("\n"),
);
objectKeys(ftlPlaceholders).forEach(id => (ftlCode = ftlCode.replace(id, ftlPlaceholders[id]))); Object.entries({
...replaceValueBySearchValue,
//If updated, don't forget to change in the ftl script as well.
"PAGE_ID_xIgLsPgGId9D8e": pageId,
}).map(([searchValue, replaceValue]) => (ftlCode = ftlCode.replace(searchValue, replaceValue)));
return { ftlCode }; return { ftlCode };
} }

View File

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

View File

@ -217,10 +217,64 @@ export type Attribute = {
groupDisplayHeader?: string; groupDisplayHeader?: string;
groupDisplayDescription?: string; groupDisplayDescription?: string;
readOnly: boolean; readOnly: boolean;
autocomplete?: string;
validators: Validators; validators: Validators;
annotations: Record<string, string>; annotations: Record<string, string>;
groupAnnotations: 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<{ 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 { 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 type { DeepPartial } from "../tools/DeepPartial";
import { deepAssign } from "../tools/deepAssign"; import { deepAssign } from "../tools/deepAssign";
import { id } from "tsafe/id";
export type ExtendsKcContextBase<KcContextExtended extends { pageId: string }> = [KcContextExtended] extends [never] import { exclude } from "tsafe/exclude";
? KcContextBase import { assert } from "tsafe/assert";
: AndByDiscriminatingKey<"pageId", KcContextExtended & KcContextBase.Common, KcContextBase>; import type { ExtendsKcContextBase } from "./getKcContextFromWindow";
import { getKcContextFromWindow } from "./getKcContextFromWindow";
export function getKcContext<KcContextExtended extends { pageId: string } = never>(params?: { export function getKcContext<KcContextExtended extends { pageId: string } = never>(params?: {
mockPageId?: ExtendsKcContextBase<KcContextExtended>["pageId"]; mockPageId?: ExtendsKcContextBase<KcContextExtended>["pageId"];
@ -44,12 +43,55 @@ export function getKcContext<KcContextExtended extends { pageId: string } = neve
"target": kcContext, "target": kcContext,
"source": partialKcContextCustomMock, "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 };
} }
return { return { "kcContext": getKcContextFromWindow<KcContextExtended>() };
"kcContext": typeof window === "undefined" ? undefined : (window as any)[ftlValuesGlobalName],
};
} }

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 } from "./KcContextBase"; export type { KcContextBase, Attribute, Validators } from "./KcContextBase";
export type { ExtendsKcContextBase } from "./getKcContextFromWindow";
export { getKcContext } from "./getKcContext"; export { getKcContext } from "./getKcContext";

View File

@ -278,29 +278,6 @@ export const kcContextMocks: KcContextBase[] = [
"readOnly": false, "readOnly": false,
"name": "lastName", "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 { return {

View File

@ -34,7 +34,7 @@ export function getKcLanguageTagLabel(language: KcLanguageTag): LanguageLabel {
return kcLanguageByTagLabel[language] ?? language; 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 * 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 { export function getBestMatchAmongKcLanguageTag(languageLike: string): KcLanguageTag {
const iso2LanguageLike = languageLike.split("-")[0].toLowerCase(); const iso2LanguageLike = languageLike.split("-")[0].toLowerCase();
const kcLanguageTag = availableLanguages.find( const kcLanguageTag = kcLanguageTags.find(
language => language =>
language.toLowerCase().includes(iso2LanguageLike) || language.toLowerCase().includes(iso2LanguageLike) ||
getKcLanguageTagLabel(language).toLocaleLowerCase() === languageLike.toLocaleLowerCase(), getKcLanguageTagLabel(language).toLocaleLowerCase() === languageLike.toLocaleLowerCase(),

View File

@ -1,5 +1,5 @@
import { createUseGlobalState } from "powerhooks/useGlobalState"; import { createUseGlobalState } from "powerhooks/useGlobalState";
import { getKcContext } from "../getKcContext"; import { getKcContextFromWindow } from "../getKcContext/getKcContextFromWindow";
import { getBestMatchAmongKcLanguageTag } from "./KcLanguageTag"; import { getBestMatchAmongKcLanguageTag } from "./KcLanguageTag";
import type { StatefulEvt } from "powerhooks"; import type { StatefulEvt } from "powerhooks";
import { KcLanguageTag } from "./KcLanguageTag"; import { KcLanguageTag } from "./KcLanguageTag";
@ -8,7 +8,7 @@ import { KcLanguageTag } from "./KcLanguageTag";
const wrap = createUseGlobalState( const wrap = createUseGlobalState(
"kcLanguageTag", "kcLanguageTag",
() => { () => {
const { kcContext } = getKcContext(); const kcContext = getKcContextFromWindow();
const languageLike = kcContext?.locale?.current ?? (typeof navigator === "undefined" ? undefined : navigator.language); 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 = (() => { str = (() => {
const startIndex = str const startIndex = str
.match(/(?<={)[0-9]+(?=})/g) .match(/{[0-9]+}/g)
?.map(g => parseInt(g)) ?.map(g => g.match(/{([0-9]+)}/)![1])
.map(indexStr => parseInt(indexStr))
.sort((a, b) => a - b)[0]; .sort((a, b) => a - b)[0];
if (startIndex === undefined) { if (startIndex === undefined) {
@ -67,16 +68,16 @@ function resolveMsgAdvanced<Key extends string, DoRenderMarkdown extends boolean
const match = key.match(/^\$\{([^{]+)\}$/); const match = key.match(/^\$\{([^{]+)\}$/);
const resolvedKey = match === null ? key : match[1]; const keyUnwrappedFromCurlyBraces = match === null ? key : match[1];
const out = resolveMsg({ const out = resolveMsg({
"key": resolvedKey, "key": keyUnwrappedFromCurlyBraces,
args, args,
kcLanguageTag, kcLanguageTag,
doRenderMarkdown, 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

@ -14,5 +14,6 @@ export * from "./components/Error";
export * from "./components/LoginResetPassword"; export * from "./components/LoginResetPassword";
export * from "./components/LoginVerifyEmail"; export * from "./components/LoginVerifyEmail";
export * from "./keycloakJsAdapter"; export * from "./keycloakJsAdapter";
export * from "./useFormValidationSlice";
export * from "./tools/assert"; export * from "./tools/assert";

View File

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

View File

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

View File

@ -1,6 +1,6 @@
import { getKcContext } from "../../lib/getKcContext"; import { getKcContext } from "../../lib/getKcContext";
import type { KcContextBase } 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 { same } from "evt/tools/inDepth";
import { assert } from "tsafe/assert"; import { assert } from "tsafe/assert";
import type { Equals } from "tsafe"; import type { Equals } from "tsafe";

182
yarn.lock
View File

@ -64,7 +64,7 @@
"@emotion/weak-memoize" "^0.2.5" "@emotion/weak-memoize" "^0.2.5"
hoist-non-react-statics "^3.3.1" hoist-non-react-statics "^3.3.1"
"@emotion/serialize@^1.0.2": "@emotion/serialize@*", "@emotion/serialize@^1.0.2":
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.0.2.tgz#77cb21a0571c9f68eb66087754a65fa97bfcd965" resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.0.2.tgz#77cb21a0571c9f68eb66087754a65fa97bfcd965"
integrity sha512-95MgNJ9+/ajxU7QIAruiOAdYNjxZX7G2mhgrtDWswA21VviYIRP1R5QilZ/bDY42xiKsaktP4egJb3QdYQZi1A== integrity sha512-95MgNJ9+/ajxU7QIAruiOAdYNjxZX7G2mhgrtDWswA21VviYIRP1R5QilZ/bDY42xiKsaktP4egJb3QdYQZi1A==
@ -75,16 +75,6 @@
"@emotion/utils" "^1.0.0" "@emotion/utils" "^1.0.0"
csstype "^3.0.2" 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": "@emotion/sheet@^1.0.0", "@emotion/sheet@^1.0.2":
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.0.2.tgz#1d9ffde531714ba28e62dac6a996a8b1089719d0" 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" resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.7.5.tgz#77211291c1900a700b8a78cfafda3160d76949ed"
integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg== integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==
"@emotion/utils@^1.0.0": "@emotion/utils@*", "@emotion/utils@^1.0.0":
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.0.0.tgz#abe06a83160b10570816c913990245813a2fd6af" resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.0.0.tgz#abe06a83160b10570816c913990245813a2fd6af"
integrity sha512-mQC2b3XLDs6QCW+pDQDiyO/EdGZYOygE8s5N5rrzjSI4M3IejPE/JPndCBwRT9z982aqQNi6beWs1UeayrQxxA== integrity sha512-mQC2b3XLDs6QCW+pDQDiyO/EdGZYOygE8s5N5rrzjSI4M3IejPE/JPndCBwRT9z982aqQNi6beWs1UeayrQxxA==
@ -220,11 +210,6 @@ braces@^3.0.1:
dependencies: dependencies:
fill-range "^7.0.1" 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: callsites@^3.0.0:
version "3.1.0" version "3.1.0"
resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" 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" resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.2.0.tgz#9a0b6c2782ed6a1c7323d42267183df9bd8b1d57"
integrity sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A== 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" version "4.2.2"
resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.2.2.tgz#e825d721d19a86b8c201a35264e226c678ee755f" resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.2.2.tgz#e825d721d19a86b8c201a35264e226c678ee755f"
integrity sha512-PzE9aBMsdZO8TK4BnuJwH0QT41wgMbRzuZrHUcpYncEjmQazq8QEaBWgLG7ZyC/DAZKEgglpIA6j4Qn/HmxS3w== 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" domelementtype "^2.2.0"
domhandler "^4.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: emoji-regex@^8.0.0:
version "8.0.0" version "8.0.0"
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" 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: dependencies:
react-is "^16.7.0" 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: html-to-react@^1.3.4:
version "1.4.7" version "1.4.7"
resolved "https://registry.yarnpkg.com/html-to-react/-/html-to-react-1.4.7.tgz#a58129c1b77c6d4e047a647372bd194e25420b89" 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" lodash.camelcase "^4.3.0"
ramda "^0.27.1" ramda "^0.27.1"
html-tokenize@^2.0.0: htmlparser2@^6.1.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:
version "6.1.0" version "6.1.0"
resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7" resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7"
integrity sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A== 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" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== 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: is-alphabetical@^1.0.0:
version "1.0.4" version "1.0.4"
resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-1.0.4.tgz#9e7d6b94916be22153745d184c298cbf986a686d" 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: dependencies:
brace-expansion "^1.1.7" 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: mkdirp@^1.0.4:
version "1.0.4" version "1.0.4"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" 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" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== 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: next-tick@1, next-tick@^1.1.0:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb"
@ -1083,16 +1009,11 @@ nth-check@^2.0.0:
dependencies: dependencies:
boolbase "^1.0.0" boolbase "^1.0.0"
object-assign@^4.1.0, object-assign@^4.1.1: object-assign@^4.1.1:
version "4.1.1" version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= 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: once@^1.3.0:
version "1.4.0" version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 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" resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b"
integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== 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: path-exists@^4.0.0:
version "4.0.0" version "4.0.0"
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" 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" resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== 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: picomatch@^2.2.3:
version "2.3.0" version "2.3.0"
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" 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" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== 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: prop-types@^15.7.2:
version "15.7.2" version "15.7.2"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" 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" unist-util-visit "^2.0.0"
xtend "^4.0.1" 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: react@^17.0.1:
version "17.0.2" version "17.0.2"
resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037" 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" loose-envify "^1.1.0"
object-assign "^4.1.1" 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" version "2.3.7"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== 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" string_decoder "~1.1.1"
util-deprecate "~1.0.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: regenerator-runtime@^0.13.4:
version "0.13.9" version "0.13.9"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" 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" resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad"
integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== 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: stylis@^4.0.3:
version "4.0.10" version "4.0.10"
resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.0.10.tgz#446512d1097197ab3f02fb3c258358c3f7a14240" 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" readable-stream "~2.3.6"
xtend "~4.0.1" 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: through@^2.3.8:
version "2.3.8" version "2.3.8"
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" 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" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01"
integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==
tss-react@^1.1.0: tss-react@^3.0.0:
version "1.1.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/tss-react/-/tss-react-1.1.0.tgz#bbbf12b3d30eb02e1a39ac6dba4ca05a9c6bb674" resolved "https://registry.yarnpkg.com/tss-react/-/tss-react-3.0.0.tgz#b084e1d10d1ea23c925b6a2141c1e91a0c5e9a2d"
integrity sha512-0UUQaMCbefyXsHesnScLwmoo6lD5sdAVR1h5dgIvCOFTk0i5A5a68K2B9gm89hQFfIKPl7fzolCsJ+G9RE/vpw== integrity sha512-aZ/DZEuUvqki/1TKKBM4OmRx6TI5lcaF4BLMo0D8lyT/5S7zRFaAdVfAlsirHcQNgOAdf5IjLUcEbCYWcY6PJw==
dependencies: dependencies:
"@emotion/server" "^11.4.0" "@emotion/serialize" "*"
html-react-parser "^1.2.7" "@emotion/utils" "*"
type-fest@^0.21.3: type-fest@^0.21.3:
version "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" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= 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: vfile-message@^2.0.0:
version "2.0.4" version "2.0.4"
resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-2.0.4.tgz#5b43b88171d409eae58477d13f23dd41d52c371a" 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" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== 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: y18n@^5.0.5:
version "5.0.8" version "5.0.8"
resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"