Compare commits
85 Commits
Author | SHA1 | Date | |
---|---|---|---|
af5ff1ecfb | |||
c9b53b0d3a | |||
d05a62e1ea | |||
a83eec31d8 | |||
729503fe31 | |||
7137ff4257 | |||
6db11a7433 | |||
8666aa62dd | |||
eedcd7a2a6 | |||
e3e8fb663a | |||
6e663210ee | |||
42cd0fe2f0 | |||
daac05c1ad | |||
1d63c393a3 | |||
e8a3751b32 | |||
cb8a41d5be | |||
a8d4f7e23c | |||
93bcdac3be | |||
32d0388556 | |||
633a32ffd6 | |||
cb8b165c8e | |||
57134359b9 | |||
377436e46a | |||
51129aaeff | |||
a2bd5050ff | |||
128c416ce7 | |||
7184773521 | |||
1138313028 | |||
46bd319ebe | |||
cfcc48259c | |||
785ce7a8ab | |||
ad5de216b0 | |||
26b80d6af7 | |||
a8623d8066 | |||
86ab9f72a5 | |||
b3892dab8d | |||
57a5d034dd | |||
cee9569581 | |||
159429da6e | |||
a292cb0b4b | |||
d70985d8d2 | |||
484f95f5d2 | |||
6e0553af9b | |||
cb18d3d765 | |||
f316f38ae5 | |||
5f07cb374b | |||
96d31e07c3 | |||
99a5efe36c | |||
5c46ecc0ed | |||
cf93b68816 | |||
457421b8d6 | |||
d36ea9539a | |||
5a5337dc63 | |||
443081cc28 | |||
ac8503f8c8 | |||
1cc1fd0a5a | |||
34314aa4ca | |||
0d8dcf4829 | |||
47c6d0dd62 | |||
84937e3eec | |||
303e270b56 | |||
bb1ada6e14 | |||
4a422cc796 | |||
be0f244c02 | |||
78a8dc8458 | |||
38062af889 | |||
f2eadf5441 | |||
a42931384f | |||
8116ce697b | |||
4964b86d67 | |||
2b331e7655 | |||
c1468b688e | |||
4f7837c88e | |||
fd8e06f1dd | |||
b01a351eaa | |||
604655c02d | |||
6603ac4389 | |||
cca6f952ee | |||
df94a6322d | |||
73e7f64860 | |||
e17e1650d5 | |||
3ecb63d500 | |||
41ee7e90ef | |||
c70bba727e | |||
747248454d |
36
.github/workflows/ci.yaml
vendored
36
.github/workflows/ci.yaml
vendored
@ -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
2
.gitignore
vendored
@ -44,3 +44,5 @@ jspm_packages
|
|||||||
|
|
||||||
/sample_react_project/
|
/sample_react_project/
|
||||||
/.yarn_home/
|
/.yarn_home/
|
||||||
|
|
||||||
|
.idea
|
||||||
|
84
CHANGELOG.md
84
CHANGELOG.md
@ -1,3 +1,87 @@
|
|||||||
|
### **4.2.21** (2021-12-27)
|
||||||
|
|
||||||
|
- update dependencies
|
||||||
|
|
||||||
|
### **4.2.19** (2021-12-21)
|
||||||
|
|
||||||
|
- Merge pull request #70 from VBustamante/patch-1
|
||||||
|
- Added realm name field to KcContext mocks object
|
||||||
|
- Merge pull request #69 from VBustamante/patch-1
|
||||||
|
|
||||||
|
Adding name field to realm in KcContext type
|
||||||
|
- Adding name field to realm in KcContext type
|
||||||
|
|
||||||
|
### **4.2.18** (2021-12-17)
|
||||||
|
|
||||||
|
- Improve css url() import (fix CRA 5)
|
||||||
|
|
||||||
|
### **4.2.17** (2021-12-16)
|
||||||
|
|
||||||
|
- Fix path.join polyfill
|
||||||
|
|
||||||
|
### **4.2.16** (2021-12-16)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### **4.2.15** (2021-12-16)
|
||||||
|
|
||||||
|
- use custom polyfill for path.join (fix webpack 5 build)
|
||||||
|
|
||||||
|
### **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)
|
### **4.2.4** (2021-11-01)
|
||||||
|
|
||||||
- Better autoComplete typings
|
- Better autoComplete typings
|
||||||
|
105
README.md
105
README.md
@ -5,11 +5,21 @@
|
|||||||
<i>🔏 Create Keycloak themes using React 🔏</i>
|
<i>🔏 Create Keycloak themes using React 🔏</i>
|
||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
<img src="https://github.com/garronej/keycloakify/workflows/ci/badge.svg?branch=develop">
|
<a href="https://github.com/garronej/keycloakify/actions">
|
||||||
<img src="https://img.shields.io/bundlephobia/minzip/keycloakify">
|
<img src="https://github.com/garronej/keycloakify/workflows/ci/badge.svg?branch=main">
|
||||||
<img src="https://img.shields.io/npm/dw/keycloakify">
|
</a>
|
||||||
<img src="https://img.shields.io/npm/l/keycloakify">
|
<a href="https://bundlephobia.com/package/keycloakify">
|
||||||
<img src="https://camo.githubusercontent.com/0f9fcc0ac1b8617ad4989364f60f78b2d6b32985ad6a508f215f14d8f897b8d3/68747470733a2f2f62616467656e2e6e65742f62616467652f547970655363726970742f7374726963742532302546302539462539322541412f626c7565">
|
<img src="https://img.shields.io/bundlephobia/minzip/keycloakify">
|
||||||
|
</a>
|
||||||
|
<a href="https://www.npmjs.com/package/keycloakify">
|
||||||
|
<img src="https://img.shields.io/npm/dw/keycloakify">
|
||||||
|
</a>
|
||||||
|
<a href="https://github.com/garronej/keycloakify/blob/main/LICENSE">
|
||||||
|
<img src="https://img.shields.io/npm/l/keycloakify">
|
||||||
|
</a>
|
||||||
|
<a href="https://github.com/InseeFrLab/keycloakify/blob/729503fe31a155a823f46dd66ad4ff34ca274e0a/tsconfig.json#L14">
|
||||||
|
<img src="https://camo.githubusercontent.com/0f9fcc0ac1b8617ad4989364f60f78b2d6b32985ad6a508f215f14d8f897b8d3/68747470733a2f2f62616467656e2e6e65742f62616467652f547970655363726970742f7374726963742532302546302539462539322541412f626c7565">
|
||||||
|
</a>
|
||||||
<a href="https://github.com/thomasdarimont/awesome-keycloak">
|
<a href="https://github.com/thomasdarimont/awesome-keycloak">
|
||||||
<img src="https://awesome.re/mentioned-badge.svg"/>
|
<img src="https://awesome.re/mentioned-badge.svg"/>
|
||||||
</a>
|
</a>
|
||||||
@ -20,11 +30,6 @@
|
|||||||
<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 v4**
|
|
||||||
|
|
||||||
- Out of the box [frontend form validation](#user-profile-and-frontend-form-validation) 🥳
|
|
||||||
- Improvements (and breaking changes in `import { useKcMessage } from "keycloakify"`.
|
|
||||||
|
|
||||||
# Motivations
|
# Motivations
|
||||||
|
|
||||||
Keycloak provides [theme support](https://www.keycloak.org/docs/latest/server_development/#_themes) for web pages. This allows customizing the look and feel of end-user facing pages so they can be integrated with your applications.
|
Keycloak provides [theme support](https://www.keycloak.org/docs/latest/server_development/#_themes) for web pages. This allows customizing the look and feel of end-user facing pages so they can be integrated with your applications.
|
||||||
@ -71,12 +76,12 @@ If you already have a Keycloak custom theme, it can be easily ported to Keycloak
|
|||||||
- [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)
|
- [Changelog highlights](#changelog-highlights)
|
||||||
@ -87,6 +92,8 @@ If you already have a Keycloak custom theme, it can be easily ported to Keycloak
|
|||||||
|
|
||||||
# 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)
|
||||||
@ -96,7 +103,7 @@ Tested with the following Keycloak versions:
|
|||||||
This tool will be maintained to stay compatible with Keycloak v11 and up, however, the default pages you will get
|
This tool will be maintained to stay compatible with Keycloak v11 and up, however, the default pages you will get
|
||||||
(before you customize it) will always be the ones of Keycloak v11.
|
(before you customize it) will always be the ones of Keycloak v11.
|
||||||
|
|
||||||
This tool assumes you are bundling your app with Webpack (tested with 4.44.2) .
|
This tool assumes you are bundling your app with Webpack (tested with the versions that ships with CRA v4.44.2 and v5.0.0) .
|
||||||
It assumes there is a `build/` directory at the root of your react project directory containing a `index.html` file
|
It assumes there is a `build/` directory at the root of your react project directory containing a `index.html` file
|
||||||
and a `build/static/` directory generated by webpack.
|
and a `build/static/` directory generated by webpack.
|
||||||
For more information see [this issue](https://github.com/InseeFrLab/keycloakify/issues/5#issuecomment-832296432)
|
For more information see [this issue](https://github.com/InseeFrLab/keycloakify/issues/5#issuecomment-832296432)
|
||||||
@ -104,9 +111,7 @@ For more information see [this issue](https://github.com/InseeFrLab/keycloakify/
|
|||||||
**All this is defaults with [`create-react-app`](https://create-react-app.dev)** (tested with 4.0.3)
|
**All this is defaults with [`create-react-app`](https://create-react-app.dev)** (tested with 4.0.3)
|
||||||
|
|
||||||
- `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 `start_keycloak_testing_container.sh` (Instructions provided after running `yarn keycloak`).
|
||||||
|
|
||||||
On Windows you'll have to use [WSL](https://docs.microsoft.com/en-us/windows/wsl/install-win10).
|
|
||||||
|
|
||||||
## My framework doesn’t seem to be supported, what can I do?
|
## My framework doesn’t seem to be supported, what can I do?
|
||||||
|
|
||||||
@ -151,27 +156,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"),
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -212,8 +213,8 @@ reactDom.render(
|
|||||||
<img src="https://user-images.githubusercontent.com/6702424/114326299-6892fc00-9b34-11eb-8d75-85696e55458f.png">
|
<img src="https://user-images.githubusercontent.com/6702424/114326299-6892fc00-9b34-11eb-8d75-85696e55458f.png">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
Example of a customization using only CSS: [here](https://github.com/InseeFrLab/onyxia-ui/blob/012639d62327a9a56be80c46e32c32c9497b82db/src/app/components/KcApp.tsx)
|
Example of a customization using only CSS: [here](https://github.com/InseeFrLab/onyxia-web/blob/012639d62327a9a56be80c46e32c32c9497b82db/src/app/components/KcApp.tsx)
|
||||||
(the [index.tsx](https://github.com/InseeFrLab/onyxia-ui/blob/012639d62327a9a56be80c46e32c32c9497b82db/src/app/index.tsx#L89-L94) )
|
(the [index.tsx](https://github.com/InseeFrLab/onyxia-web/blob/012639d62327a9a56be80c46e32c32c9497b82db/src/app/index.tsx#L89-L94) )
|
||||||
and the result you can expect:
|
and the result you can expect:
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
@ -228,7 +229,7 @@ If you want to go beyond only customizing the CSS you can re-implement some of t
|
|||||||
pages or even add new ones.
|
pages or even add new ones.
|
||||||
|
|
||||||
If you want to go this way checkout the demo setup provided [here](https://github.com/garronej/keycloakify-demo-app/tree/look_and_feel).
|
If you want to go this way checkout the demo setup provided [here](https://github.com/garronej/keycloakify-demo-app/tree/look_and_feel).
|
||||||
If you prefer a real life example you can checkout [onyxia-web's source](https://github.com/InseeFrLab/onyxia-web/tree/main/src/app/components/KcApp).
|
If you prefer a real life example you can checkout [onyxia-web's source](https://github.com/InseeFrLab/onyxia-web/tree/main/src/ui/components/KcApp).
|
||||||
The web app is in production [here](https://datalab.sspcloud.fr).
|
The web app is in production [here](https://datalab.sspcloud.fr).
|
||||||
|
|
||||||
Main takeaways are:
|
Main takeaways are:
|
||||||
@ -307,6 +308,8 @@ Checkout a complete setup [here](https://github.com/garronej/keycloakify-demo-ap
|
|||||||
</a>
|
</a>
|
||||||
</p>
|
</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
|
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),
|
[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
|
what information you want to collect on your users in the register page and to validate inputs
|
||||||
@ -355,7 +358,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.
|
||||||
@ -371,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 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)
|
||||||
@ -431,33 +435,29 @@ keycloakInstance.init({
|
|||||||
|
|
||||||
If you really want to go the extra miles and avoid having the white
|
If you really want to go the extra miles and avoid having the white
|
||||||
flash of the blank html before the js bundle have been evaluated
|
flash of the blank html before the js bundle have been evaluated
|
||||||
[here is a snippet](https://github.com/InseeFrLab/onyxia-ui/blob/a77eb502870cfe6878edd0d956c646d28746d053/public/index.html#L5-L54) that you can place in your `public/index.html` if you are using `powerhooks/useGlobalState`.
|
[here is a snippet](https://github.com/InseeFrLab/onyxia-web/blob/e1c1f309aaa3d5f860df39ba0b75cce89c88a9de/public/index.html#L117-L166) that you can place in your `public/index.html` if you are using `powerhooks/useGlobalState`.
|
||||||
|
|
||||||
# Kickstart video
|
# Kickstart video
|
||||||
|
|
||||||
_NOTE: keycloak-react-theming was renamed keycloakify since this video was recorded_
|
_NOTE: keycloak-react-theming was renamed keycloakify since this video was recorded_
|
||||||
[](https://youtu.be/xTz0Rj7i2v8)
|
[](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_please_ignore_errors" 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_please_ignore_errors" 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_please_ignore_errors object=value depth=(dep... [in template "login.ftl" in macro "objectToJson_please_ignore_errors" 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_please_ignore_errors" 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_please_ignore_errors 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 to show up in the log.
|
It's just noise, they can be safely ignored.
|
||||||
Unfortunately, there is nothing I know of that can be done to avoid them or even mute them.
|
You can, however, and are encouraged to, report any that you would spot.
|
||||||
They can be, however, safely ignored.
|
Just open an issue about it and I will release a patched version of Keycloakify in the better delays.
|
||||||
|
|
||||||
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 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`)
|
||||||
|
|
||||||
@ -467,6 +467,9 @@ 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.
|
||||||
|
|
||||||
|
10
package.json
10
package.json
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "keycloakify",
|
"name": "keycloakify",
|
||||||
"version": "4.2.4",
|
"version": "4.2.21",
|
||||||
"description": "Keycloak theme generator for Reacts app",
|
"description": "Keycloak theme generator for Reacts app",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@ -58,7 +58,7 @@
|
|||||||
"@emotion/react": "^11.4.1",
|
"@emotion/react": "^11.4.1",
|
||||||
"powerhooks": "^0.10.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,16 +72,16 @@
|
|||||||
"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.39",
|
||||||
"minimal-polyfills": "^2.2.1",
|
"minimal-polyfills": "^2.2.1",
|
||||||
"path-browserify": "^1.0.1",
|
"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.9.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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"),
|
||||||
);
|
);
|
||||||
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
@ -1,208 +0,0 @@
|
|||||||
<script>const _=
|
|
||||||
<#macro objectToJson_please_ignore_errors 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_please_ignore_errors 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_please_ignore_errors 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_please_ignore_errors object=.data_model depth=0 />))
|
|
||||||
);
|
|
||||||
|
|
||||||
Object.deepAssign(
|
|
||||||
out,
|
|
||||||
nonAutomaticallyConvertible
|
|
||||||
);
|
|
||||||
|
|
||||||
return out;
|
|
||||||
|
|
||||||
})()
|
|
||||||
</script>
|
|
@ -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>
|
@ -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 };
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@ export function replaceImportsInInlineCssCode(params: { cssCode: string; urlPath
|
|||||||
const { cssCode, urlPathname, urlOrigin } = params;
|
const { cssCode, urlPathname, urlOrigin } = params;
|
||||||
|
|
||||||
const fixedCssCode = cssCode.replace(
|
const fixedCssCode = cssCode.replace(
|
||||||
urlPathname === "/" ? /url\(\/([^/][^)]+)\)/g : new RegExp(`url\\(${urlPathname}([^)]+)\\)`, "g"),
|
urlPathname === "/" ? /url\(["']?\/([^/][^)"']+)["']?\)/g : new RegExp(`url\\(["']?${urlPathname}([^)"']+)["']?\\)`, "g"),
|
||||||
(...[, group]) => `url(${urlOrigin === undefined ? "${url.resourcesPath}/build/" + group : params.urlOrigin + urlPathname + group})`,
|
(...[, group]) => `url(${urlOrigin === undefined ? "${url.resourcesPath}/build/" + group : params.urlOrigin + urlPathname + group})`,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -52,7 +52,7 @@ export function replaceImportsInCssCode(params: { cssCode: string }): {
|
|||||||
|
|
||||||
const cssGlobalsToDefine: Record<string, string> = {};
|
const cssGlobalsToDefine: Record<string, string> = {};
|
||||||
|
|
||||||
new Set(cssCode.match(/url\(\/[^/][^)]+\)[^;}]*/g) ?? []).forEach(
|
new Set(cssCode.match(/url\(["']?\/[^/][^)"']+["']?\)[^;}]*/g) ?? []).forEach(
|
||||||
match => (cssGlobalsToDefine["url" + crypto.createHash("sha256").update(match).digest("hex").substring(0, 15)] = match),
|
match => (cssGlobalsToDefine["url" + crypto.createHash("sha256").update(match).digest("hex").substring(0, 15)] = match),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ import type { KcProps } from "./KcProps";
|
|||||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import { useKcMessage } from "../i18n/useKcMessage";
|
import { useKcMessage } from "../i18n/useKcMessage";
|
||||||
import { headInsert } from "../tools/headInsert";
|
import { headInsert } from "../tools/headInsert";
|
||||||
import { join as pathJoin } from "path";
|
import { pathJoin } from "../tools/pathJoin";
|
||||||
import { useCssAndCx } from "tss-react";
|
import { useCssAndCx } from "tss-react";
|
||||||
|
|
||||||
export const LoginOtp = memo(({ kcContext, ...props }: { kcContext: KcContextBase.LoginOtp } & KcProps) => {
|
export const LoginOtp = memo(({ kcContext, ...props }: { kcContext: KcContextBase.LoginOtp } & KcProps) => {
|
||||||
|
@ -9,7 +9,7 @@ import { getBestMatchAmongKcLanguageTag } from "../i18n/KcLanguageTag";
|
|||||||
import { getKcLanguageTagLabel } from "../i18n/KcLanguageTag";
|
import { getKcLanguageTagLabel } from "../i18n/KcLanguageTag";
|
||||||
import { useCallbackFactory } from "powerhooks/useCallbackFactory";
|
import { useCallbackFactory } from "powerhooks/useCallbackFactory";
|
||||||
import { headInsert } from "../tools/headInsert";
|
import { headInsert } from "../tools/headInsert";
|
||||||
import { join as pathJoin } from "path";
|
import { pathJoin } from "../tools/pathJoin";
|
||||||
import { useConstCallback } from "powerhooks/useConstCallback";
|
import { useConstCallback } from "powerhooks/useConstCallback";
|
||||||
import type { KcTemplateProps } from "./KcProps";
|
import type { KcTemplateProps } from "./KcProps";
|
||||||
import { useCssAndCx } from "tss-react";
|
import { useCssAndCx } from "tss-react";
|
||||||
|
@ -34,6 +34,7 @@ export declare namespace KcContextBase {
|
|||||||
loginUrl: string;
|
loginUrl: string;
|
||||||
};
|
};
|
||||||
realm: {
|
realm: {
|
||||||
|
name: string;
|
||||||
displayName?: string;
|
displayName?: string;
|
||||||
displayNameHtml?: string;
|
displayNameHtml?: string;
|
||||||
internationalizationEnabled: boolean;
|
internationalizationEnabled: boolean;
|
||||||
|
@ -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],
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
11
src/lib/getKcContext/getKcContextFromWindow.ts
Normal file
11
src/lib/getKcContext/getKcContextFromWindow.ts
Normal 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];
|
||||||
|
}
|
@ -1,2 +1,3 @@
|
|||||||
export type { KcContextBase, Attribute, Validators } from "./KcContextBase";
|
export type { KcContextBase, Attribute, Validators } from "./KcContextBase";
|
||||||
|
export type { ExtendsKcContextBase } from "./getKcContextFromWindow";
|
||||||
export { getKcContext } from "./getKcContext";
|
export { getKcContext } from "./getKcContext";
|
||||||
|
@ -5,7 +5,7 @@ import { getKcLanguageTagLabel } from "../../i18n/KcLanguageTag";
|
|||||||
//NOTE: Aside because we want to be able to import them from node
|
//NOTE: Aside because we want to be able to import them from node
|
||||||
import { resourcesCommonPath, resourcesPath } from "./urlResourcesPath";
|
import { resourcesCommonPath, resourcesPath } from "./urlResourcesPath";
|
||||||
import { id } from "tsafe/id";
|
import { id } from "tsafe/id";
|
||||||
import { join as pathJoin } from "path";
|
import { pathJoin } from "../../tools/pathJoin";
|
||||||
|
|
||||||
const PUBLIC_URL = process.env["PUBLIC_URL"] ?? "/";
|
const PUBLIC_URL = process.env["PUBLIC_URL"] ?? "/";
|
||||||
|
|
||||||
@ -18,6 +18,7 @@ export const kcContextCommonMock: KcContextBase.Common = {
|
|||||||
"loginUrl": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg",
|
"loginUrl": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg",
|
||||||
},
|
},
|
||||||
"realm": {
|
"realm": {
|
||||||
|
"name": "myrealm",
|
||||||
"displayName": "myrealm",
|
"displayName": "myrealm",
|
||||||
"displayNameHtml": "myrealm",
|
"displayNameHtml": "myrealm",
|
||||||
"internationalizationEnabled": true,
|
"internationalizationEnabled": true,
|
||||||
@ -278,29 +279,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 {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { join as pathJoin } from "path";
|
import { pathJoin } from "../../tools/pathJoin";
|
||||||
|
|
||||||
export const subDirOfPublicDirBasename = "keycloak_static";
|
export const subDirOfPublicDirBasename = "keycloak_static";
|
||||||
export const resourcesPath = pathJoin(subDirOfPublicDirBasename, "/resources");
|
export const resourcesPath = pathJoin(subDirOfPublicDirBasename, "resources");
|
||||||
export const resourcesCommonPath = pathJoin(subDirOfPublicDirBasename, "/resources_common");
|
export const resourcesCommonPath = pathJoin(subDirOfPublicDirBasename, "resources_common");
|
||||||
|
@ -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(),
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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];
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
6
src/lib/tools/pathJoin.ts
Normal file
6
src/lib/tools/pathJoin.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
export function pathJoin(...path: string[]): string {
|
||||||
|
return path
|
||||||
|
.map((part, i) => (i === 0 ? part : part.replace(/^\/+/, "")))
|
||||||
|
.map((part, i) => (i === path.length - 1 ? part : part.replace(/\/+$/, "")))
|
||||||
|
.join("/");
|
||||||
|
}
|
@ -38,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 [
|
||||||
{
|
{
|
||||||
|
@ -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";
|
||||||
|
166
yarn.lock
166
yarn.lock
@ -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"
|
||||||
@ -567,6 +545,15 @@ evt@2.0.0-beta.38:
|
|||||||
run-exclusive "^2.2.14"
|
run-exclusive "^2.2.14"
|
||||||
tsafe "^0.4.1"
|
tsafe "^0.4.1"
|
||||||
|
|
||||||
|
evt@2.0.0-beta.39:
|
||||||
|
version "2.0.0-beta.39"
|
||||||
|
resolved "https://registry.yarnpkg.com/evt/-/evt-2.0.0-beta.39.tgz#3c859a83b35940f7eecfb5f148f03b7cbf3fee51"
|
||||||
|
integrity sha512-XxJkaHrFWBrzjTbnr5LJYXkGkADsAXReZfq2lFu3Kf1iCEw5/5ibrdXu3bQdWW6xkZ8qwAHT3STU9zYcCl09BA==
|
||||||
|
dependencies:
|
||||||
|
minimal-polyfills "^2.2.1"
|
||||||
|
run-exclusive "^2.2.14"
|
||||||
|
tsafe "^0.4.1"
|
||||||
|
|
||||||
execa@^5.1.1:
|
execa@^5.1.1:
|
||||||
version "5.1.1"
|
version "5.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd"
|
resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd"
|
||||||
@ -665,24 +652,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 +662,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,11 +729,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==
|
||||||
|
|
||||||
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"
|
||||||
@ -1018,11 +971,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"
|
||||||
@ -1033,14 +981,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"
|
||||||
@ -1078,16 +1018,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"
|
||||||
@ -1275,11 +1210,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"
|
||||||
@ -1288,7 +1218,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==
|
||||||
@ -1301,16 +1241,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"
|
||||||
@ -1479,20 +1409,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"
|
||||||
@ -1527,14 +1443,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"
|
||||||
@ -1570,6 +1478,11 @@ tsafe@^0.8.1:
|
|||||||
resolved "https://registry.yarnpkg.com/tsafe/-/tsafe-0.8.1.tgz#9af7e1540bc04313a82d60c98056a5017c8b086b"
|
resolved "https://registry.yarnpkg.com/tsafe/-/tsafe-0.8.1.tgz#9af7e1540bc04313a82d60c98056a5017c8b086b"
|
||||||
integrity sha512-EfPjxQHzndQAV/uh0SMGP26Wg3dCuaw8dRv2VPEuGHen5qzg2oqsMvZw2wkQFkiMisZq2fm95m5lheimW2Fpvg==
|
integrity sha512-EfPjxQHzndQAV/uh0SMGP26Wg3dCuaw8dRv2VPEuGHen5qzg2oqsMvZw2wkQFkiMisZq2fm95m5lheimW2Fpvg==
|
||||||
|
|
||||||
|
tsafe@^0.9.0:
|
||||||
|
version "0.9.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/tsafe/-/tsafe-0.9.0.tgz#8394e5fdf81e690c97e2b8be4180a079a4a19bfb"
|
||||||
|
integrity sha512-wmbu8pI/xmW69b13HoS8WbTcSlRTDjIut9ACblBjVZVTk0vsMRXdoh1k1jMu5EzKNohBavKHhqNOOsccSR7XCA==
|
||||||
|
|
||||||
tslib@^1.9.0:
|
tslib@^1.9.0:
|
||||||
version "1.14.1"
|
version "1.14.1"
|
||||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
|
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
|
||||||
@ -1580,13 +1493,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"
|
||||||
@ -1722,13 +1635,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"
|
||||||
|
Reference in New Issue
Block a user