Compare commits
84 Commits
Author | SHA1 | Date | |
---|---|---|---|
341d985610 | |||
76b9a78182 | |||
021c0a9429 | |||
7b3462d158 | |||
c180dee414 | |||
769da5c5ca | |||
1a4dd79240 | |||
5cf290b033 | |||
aec3da25b3 | |||
66d7cb563d | |||
551e9c041e | |||
fffb6d5b5e | |||
ac0bfeb360 | |||
7c30059ca3 | |||
fdb9ae6c40 | |||
3c82ffc0ab | |||
5dd3103aba | |||
84fc81f531 | |||
a20cbc62a5 | |||
e6a93e2838 | |||
3cff54561f | |||
e50a6a7876 | |||
b887ec839b | |||
465daa19a0 | |||
6c2b761d95 | |||
0e8f95ce19 | |||
6a17f343c6 | |||
1a45fb0039 | |||
75032898d6 | |||
88a4c97428 | |||
82e7a7edae | |||
eac28f97b8 | |||
e160882db9 | |||
2bc07e77fd | |||
c9b2db625c | |||
e3b41c9bd1 | |||
4aaee35d9c | |||
beedbc695a | |||
7123edc986 | |||
3008a754ce | |||
70e3fb8de6 | |||
9cf75da732 | |||
2cd266caff | |||
28036f1da5 | |||
0dacf2fe30 | |||
32f5ef5e5c | |||
98d91bbde7 | |||
7a92a75d83 | |||
f5556a02fc | |||
1050f4d928 | |||
9276b08f4b | |||
a501af669c | |||
85343fcefe | |||
b11dfde6e6 | |||
38d2108f02 | |||
2b67544517 | |||
0d443ca88e | |||
71f7a5819d | |||
5f4abee615 | |||
f5ee949006 | |||
7e85085558 | |||
55a0b27f16 | |||
eb0e814f94 | |||
b7fe20c5a5 | |||
2b23d03ca5 | |||
7075be20c8 | |||
3ce8b06246 | |||
ee5c29f30f | |||
242dad3ea0 | |||
d8701925df | |||
e2d669ce31 | |||
af93664c71 | |||
daa3efa534 | |||
2c7c8397f0 | |||
821ba2cbe2 | |||
a17ddb02fa | |||
b89557e8d8 | |||
cad1f8b957 | |||
f82cc788bf | |||
06f9cd3e68 | |||
5113a838e7 | |||
645a84c82a | |||
925fc43d0f | |||
8e33d24c63 |
4
.gitattributes
vendored
4
.gitattributes
vendored
@ -1,3 +1,3 @@
|
|||||||
src/lib/i18n/generated_messages/* linguist-documentation
|
src/lib/i18n/generated_kcMessages/* linguist-documentation
|
||||||
|
src/bin/build-keycloak-theme/index.ts -linguist-detectable
|
||||||
src/bin/install-builtin-keycloak-themes.ts -linguist-detectable
|
src/bin/install-builtin-keycloak-themes.ts -linguist-detectable
|
||||||
src/bin/build-keycloak-theme/index.ts -linguist-detectable
|
|
21
.github/workflows/ci.yaml
vendored
21
.github/workflows/ci.yaml
vendored
@ -13,7 +13,7 @@ jobs:
|
|||||||
runs-on: macos-10.15
|
runs-on: macos-10.15
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
node: [ '14', '13', '12' ]
|
node: [ '15', '14', '13' ]
|
||||||
name: Test with Node v${{ matrix.node }}
|
name: Test with Node v${{ matrix.node }}
|
||||||
steps:
|
steps:
|
||||||
- name: Tell if project is using npm or yarn
|
- name: Tell if project is using npm or yarn
|
||||||
@ -25,14 +25,13 @@ jobs:
|
|||||||
- uses: actions/setup-node@v2.1.3
|
- uses: actions/setup-node@v2.1.3
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node }}
|
node-version: ${{ matrix.node }}
|
||||||
|
- uses: bahmutov/npm-install@v1
|
||||||
- if: steps.step1.outputs.npm_or_yarn == 'yarn'
|
- if: steps.step1.outputs.npm_or_yarn == 'yarn'
|
||||||
run: |
|
run: |
|
||||||
yarn install --frozen-lockfile
|
|
||||||
yarn build
|
yarn build
|
||||||
yarn test
|
yarn test
|
||||||
- if: steps.step1.outputs.npm_or_yarn == 'npm'
|
- if: steps.step1.outputs.npm_or_yarn == 'npm'
|
||||||
run: |
|
run: |
|
||||||
npm ci
|
|
||||||
npm run build
|
npm run build
|
||||||
npm test
|
npm test
|
||||||
check_if_version_upgraded:
|
check_if_version_upgraded:
|
||||||
@ -55,11 +54,10 @@ jobs:
|
|||||||
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.2
|
- uses: garronej/github_actions_toolkit@v2.4
|
||||||
with:
|
with:
|
||||||
action_name: update_changelog
|
action_name: update_changelog
|
||||||
branch: ${{ github.ref }}
|
branch: ${{ github.ref }}
|
||||||
commit_author_email: ts_ci@github.com
|
|
||||||
|
|
||||||
create_github_release:
|
create_github_release:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@ -104,16 +102,13 @@ jobs:
|
|||||||
- uses: actions/setup-node@v2.1.3
|
- uses: actions/setup-node@v2.1.3
|
||||||
with:
|
with:
|
||||||
node-version: '15'
|
node-version: '15'
|
||||||
|
registry-url: https://registry.npmjs.org/
|
||||||
|
- uses: bahmutov/npm-install@v1
|
||||||
- run: |
|
- run: |
|
||||||
PACKAGE_MANAGER=npm
|
PACKAGE_MANAGER=npm
|
||||||
if [ -f "./yarn.lock" ]; then
|
if [ -f "./yarn.lock" ]; then
|
||||||
PACKAGE_MANAGER=yarn
|
PACKAGE_MANAGER=yarn
|
||||||
fi
|
fi
|
||||||
if [ "$PACKAGE_MANAGER" = "yarn" ]; then
|
|
||||||
yarn install --frozen-lockfile
|
|
||||||
else
|
|
||||||
npm ci
|
|
||||||
fi
|
|
||||||
$PACKAGE_MANAGER run build
|
$PACKAGE_MANAGER run build
|
||||||
- run: npx -y -p denoify@0.6.5 denoify_enable_short_npm_import_path
|
- run: npx -y -p denoify@0.6.5 denoify_enable_short_npm_import_path
|
||||||
env:
|
env:
|
||||||
@ -124,13 +119,11 @@ jobs:
|
|||||||
echo "This version is already published"
|
echo "This version is already published"
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
if [ "$NPM_TOKEN" = "" ]; then
|
if [ "$NODE_AUTH_TOKEN" = "" ]; then
|
||||||
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
|
||||||
echo '//registry.npmjs.org/:_authToken=${NPM_TOKEN}' > .npmrc
|
|
||||||
npm publish
|
npm publish
|
||||||
rm .npmrc
|
|
||||||
env:
|
env:
|
||||||
NPM_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 }}
|
101
CHANGELOG.md
101
CHANGELOG.md
@ -1,3 +1,104 @@
|
|||||||
|
### **2.0.8** (2021-07-12)
|
||||||
|
|
||||||
|
- Fix previous release
|
||||||
|
- #20: Add def for clientId and name on kcContext.client
|
||||||
|
|
||||||
|
### **2.0.6** (2021-07-08)
|
||||||
|
|
||||||
|
- Merge pull request #18 from asashay/add-custom-props-to-theme-properties
|
||||||
|
|
||||||
|
Add possibility to add custom properties to theme.properties file
|
||||||
|
- add possibility to add custom properties to theme.properties file
|
||||||
|
|
||||||
|
### **2.0.5** (2021-07-05)
|
||||||
|
|
||||||
|
- Fix broken url for big stylesheet #16
|
||||||
|
|
||||||
|
### **2.0.4** (2021-07-03)
|
||||||
|
|
||||||
|
- Fix: #7
|
||||||
|
|
||||||
|
### **2.0.3** (2021-06-30)
|
||||||
|
|
||||||
|
- Escape double quote in ftl to js conversion #15
|
||||||
|
- Update readme
|
||||||
|
|
||||||
|
### **2.0.2** (2021-06-28)
|
||||||
|
|
||||||
|
- Updagte README for implementing non incuded pages
|
||||||
|
|
||||||
|
### **2.0.1** (2021-06-28)
|
||||||
|
|
||||||
|
- Update documentation for v2
|
||||||
|
|
||||||
|
# **2.0.0** (2021-06-28)
|
||||||
|
|
||||||
|
- Fix last bugs before relasing v2
|
||||||
|
- Implement a mechanism to overload kcContext
|
||||||
|
- Give the option in template to pull the default assets or not
|
||||||
|
- Enable possiblity to support custom pages (without forking keycloakify)
|
||||||
|
- Implement a getter for kcContext
|
||||||
|
- Update README.md
|
||||||
|
|
||||||
|
# **2.0.0** (2021-06-28)
|
||||||
|
|
||||||
|
- Fix last bugs before relasing v2
|
||||||
|
- Implement a mechanism to overload kcContext
|
||||||
|
- Give the option in template to pull the default assets or not
|
||||||
|
- Enable possiblity to support custom pages (without forking keycloakify)
|
||||||
|
- Implement a getter for kcContext
|
||||||
|
- Update README.md
|
||||||
|
|
||||||
|
### **1.2.1** (2021-06-22)
|
||||||
|
|
||||||
|
- Remove unessesary log
|
||||||
|
|
||||||
|
## **1.2.0** (2021-06-22)
|
||||||
|
|
||||||
|
- Generate kcContext automatically :rocket:
|
||||||
|
|
||||||
|
### **1.1.6** (2021-06-21)
|
||||||
|
|
||||||
|
- Fix: Alert messages sometimes includes HTML that is not rendered
|
||||||
|
- Update dist
|
||||||
|
|
||||||
|
### **1.1.5** (2021-06-15)
|
||||||
|
|
||||||
|
- #11: Provide socials in the register
|
||||||
|
|
||||||
|
### **1.1.4** (2021-06-15)
|
||||||
|
|
||||||
|
- Merge pull request #12 from InseeFrLab/email-typo
|
||||||
|
|
||||||
|
Fix typo on email
|
||||||
|
- Fix typo on email
|
||||||
|
|
||||||
|
### **1.1.3** (2021-06-14)
|
||||||
|
|
||||||
|
- Add missing key in Login for providers
|
||||||
|
|
||||||
|
### **1.1.2** (2021-06-14)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### **1.1.1** (2021-06-14)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## **1.1.0** (2021-06-14)
|
||||||
|
|
||||||
|
- Add login-idp-link-confirm.ftl
|
||||||
|
- Fix login-update-profile.ftl
|
||||||
|
- Add login-update-profile.ftl page
|
||||||
|
- Fix default background bug
|
||||||
|
- Remove unused 'markdown' dependency
|
||||||
|
- Fix warning related to powerhooks_useGlobalState_kcLanguageTag
|
||||||
|
- Update README.md
|
||||||
|
|
||||||
|
### **1.0.4** (2021-05-28)
|
||||||
|
|
||||||
|
- Instructions for custom themes with custom components
|
||||||
|
|
||||||
### **1.0.3** (2021-05-23)
|
### **1.0.3** (2021-05-23)
|
||||||
|
|
||||||
- Instuction about how to integrate with non CRA projects
|
- Instuction about how to integrate with non CRA projects
|
||||||
|
57
README.md
57
README.md
@ -9,6 +9,7 @@
|
|||||||
<img src="https://img.shields.io/bundlephobia/minzip/keycloakify">
|
<img src="https://img.shields.io/bundlephobia/minzip/keycloakify">
|
||||||
<img src="https://img.shields.io/npm/dw/keycloakify">
|
<img src="https://img.shields.io/npm/dw/keycloakify">
|
||||||
<img src="https://img.shields.io/npm/l/keycloakify">
|
<img src="https://img.shields.io/npm/l/keycloakify">
|
||||||
|
<img src="https://camo.githubusercontent.com/0f9fcc0ac1b8617ad4989364f60f78b2d6b32985ad6a508f215f14d8f897b8d3/68747470733a2f2f62616467656e2e6e65742f62616467652f547970655363726970742f7374726963742532302546302539462539322541412f626c7565">
|
||||||
<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>
|
||||||
@ -19,6 +20,10 @@
|
|||||||
<img src="https://user-images.githubusercontent.com/6702424/110260457-a1c3d380-7fac-11eb-853a-80459b65626b.png">
|
<img src="https://user-images.githubusercontent.com/6702424/110260457-a1c3d380-7fac-11eb-853a-80459b65626b.png">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
**NEW in v2**
|
||||||
|
- It's now possible to implement custom `.ftl` pages.
|
||||||
|
- Support for Keycloak plugins that introduce non standard ftl values.
|
||||||
|
(Like for example [this plugin](https://github.com/micedre/keycloak-mail-whitelisting) that define `authorizedMailDomains` in `register.ftl`).
|
||||||
# Motivations
|
# Motivations
|
||||||
|
|
||||||
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.
|
||||||
@ -46,8 +51,6 @@ Here is `keycloakify` for you 🍸
|
|||||||
|
|
||||||
**TL;DR**: [Here](https://github.com/garronej/keycloakify-demo-app) is a Hello World React project with Keycloakify set up.
|
**TL;DR**: [Here](https://github.com/garronej/keycloakify-demo-app) is a Hello World React project with Keycloakify set up.
|
||||||
|
|
||||||
*NOTE: No autocomplete here just because it was an incognito window.*
|
|
||||||
|
|
||||||
If you already have a Keycloak custom theme, it can be easily ported to Keycloakify.
|
If you already have a Keycloak custom theme, it can be easily ported to Keycloakify.
|
||||||
|
|
||||||
---
|
---
|
||||||
@ -59,7 +62,7 @@ If you already have a Keycloak custom theme, it can be easily ported to Keycloak
|
|||||||
- [How to use](#how-to-use)
|
- [How to use](#how-to-use)
|
||||||
- [Setting up the build tool](#setting-up-the-build-tool)
|
- [Setting up the build tool](#setting-up-the-build-tool)
|
||||||
- [Changing just the look of the default Keycloak theme](#changing-just-the-look-of-the-default-keycloak-theme)
|
- [Changing just the look of the default Keycloak theme](#changing-just-the-look-of-the-default-keycloak-theme)
|
||||||
- [Changing the look **and** feel](#changing-the-look-and-feel)
|
- [Advanced pages configuration](#advanced-pages-configuration)
|
||||||
- [Hot reload](#hot-reload)
|
- [Hot reload](#hot-reload)
|
||||||
- [Enable loading in a blink of an eye of login pages ⚡ (--external-assets)](#enable-loading-in-a-blink-of-an-eye-of-login-pages----external-assets)
|
- [Enable loading in a blink of an eye of login pages ⚡ (--external-assets)](#enable-loading-in-a-blink-of-an-eye-of-login-pages----external-assets)
|
||||||
- [Support for Terms and conditions](#support-for-terms-and-conditions)
|
- [Support for Terms and conditions](#support-for-terms-and-conditions)
|
||||||
@ -79,6 +82,7 @@ If you already have a Keycloak custom theme, it can be easily ported to Keycloak
|
|||||||
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)
|
||||||
- [12.0.4](https://hub.docker.com/layers/jboss/keycloak/12.0.4/images/sha256-67e0c88e69bd0c7aef972c40bdeb558a974013a28b3668ca790ed63a04d70584?context=explore)
|
- [12.0.4](https://hub.docker.com/layers/jboss/keycloak/12.0.4/images/sha256-67e0c88e69bd0c7aef972c40bdeb558a974013a28b3668ca790ed63a04d70584?context=explore)
|
||||||
|
- Tests ongoing with [14.0.0](https://hub.docker.com/layers/jboss/keycloak/14.0.0/images/sha256-ca713e87ad163da71ab329010de2464a41ff030a25ae0aef15c1c290252f3d7f?context=explore)
|
||||||
|
|
||||||
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.
|
||||||
@ -130,10 +134,12 @@ import { App } from "./<wherever>/App";
|
|||||||
import {
|
import {
|
||||||
KcApp,
|
KcApp,
|
||||||
defaultKcProps,
|
defaultKcProps,
|
||||||
kcContext
|
getKcContext
|
||||||
} from "keycloakify";
|
} from "keycloakify";
|
||||||
import { css } from "tss-react";
|
import { css } from "tss-react";
|
||||||
|
|
||||||
|
const { kcContext } = getKcContext();
|
||||||
|
|
||||||
const myClassName = css({ "color": "red" });
|
const myClassName = css({ "color": "red" });
|
||||||
|
|
||||||
reactDom.render(
|
reactDom.render(
|
||||||
@ -157,10 +163,12 @@ import { App } from "./<wherever>/App";
|
|||||||
import {
|
import {
|
||||||
KcApp,
|
KcApp,
|
||||||
defaultKcProps,
|
defaultKcProps,
|
||||||
kcContext
|
getKcContext
|
||||||
} from "keycloakify";
|
} from "keycloakify";
|
||||||
import { css } from "tss-react";
|
import { css } from "tss-react";
|
||||||
|
|
||||||
|
const { kcContext } = getKcContext();
|
||||||
|
|
||||||
const myClassName = css({ "color": "red" });
|
const myClassName = css({ "color": "red" });
|
||||||
|
|
||||||
reactDom.render(
|
reactDom.render(
|
||||||
@ -195,15 +203,25 @@ and the result you can expect:
|
|||||||
<img src="https://github.com/InseeFrLab/keycloakify/releases/download/v0.3.8/keycloakify_after.gif">
|
<img src="https://github.com/InseeFrLab/keycloakify/releases/download/v0.3.8/keycloakify_after.gif">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
### Changing the look **and** feel
|
### Advanced pages configuration
|
||||||
|
|
||||||
If you want to really re-implement the pages, the best approach is to
|
If you want to go beyond only customizing the CSS you can re-implement some of the
|
||||||
create your own version of the [`<KcApp />`](https://github.com/garronej/keycloakify/blob/develop/src/lib/components/KcApp.tsx).
|
pages or event add new ones.
|
||||||
Copy/past some of [the components](https://github.com/garronej/keycloakify/tree/develop/src/lib/components) provided by this module and start hacking around.
|
|
||||||
|
|
||||||
You can find an example of such customization [here](https://github.com/InseeFrLab/onyxia-ui/tree/master/src/app/components/KcApp).
|
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).
|
||||||
|
The web app is in production [here](https://datalab.sspcloud.fr).
|
||||||
|
|
||||||
And you can test the result in production by trying the login register page of [Onyxia](https://datalab.sspcloud.fr)
|
Main takeaways are:
|
||||||
|
- You must declare your custom pages in the package.json. [example](https://github.com/garronej/keycloakify-demo-app/blob/4eb2a9f63e9823e653b2d439495bda55e5ecc134/package.json#L17-L22)
|
||||||
|
- (TS only) You must declare theses page in the type argument of the getter
|
||||||
|
function for the `kcContext` in order to have the correct typings. [example](https://github.com/garronej/keycloakify-demo-app/blob/4eb2a9f63e9823e653b2d439495bda55e5ecc134/src/KcApp/kcContext.ts#L16-L21)
|
||||||
|
- (TS only) If you use Keycloak plugins that defines non standard `.ftl` values
|
||||||
|
(Like for example [this plugin](https://github.com/micedre/keycloak-mail-whitelisting)
|
||||||
|
that define `authorizedMailDomains` in `register.ftl`) you should
|
||||||
|
declare theses value to get the type. [example](https://github.com/garronej/keycloakify-demo-app/blob/4eb2a9f63e9823e653b2d439495bda55e5ecc134/src/KcApp/kcContext.ts#L6-L13)
|
||||||
|
- You should provide sample data for all the non standard value if you want to be able
|
||||||
|
to debug the page outside of keycloak. [example](https://github.com/garronej/keycloakify-demo-app/blob/4eb2a9f63e9823e653b2d439495bda55e5ecc134/src/KcApp/kcContext.ts#L28-L43)
|
||||||
|
|
||||||
WARNING: If you chose to go this way use:
|
WARNING: If you chose to go this way use:
|
||||||
```json
|
```json
|
||||||
@ -211,21 +229,24 @@ WARNING: If you chose to go this way use:
|
|||||||
"keycloakify": "~X.Y.Z"
|
"keycloakify": "~X.Y.Z"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
in your `package.json` instead of `^X.Y.Z`. A minor release might break your app.
|
in your `package.json` instead of `^X.Y.Z`. A minor update of Keycloakify might break your app.
|
||||||
|
|
||||||
### Hot reload
|
### Hot reload
|
||||||
|
|
||||||
Rebuild the theme each time you make a change to see the result is not practical.
|
Rebuild the theme each time you make a change to see the result is not practical.
|
||||||
If you want to test your login screens outside of Keycloak, in [storybook](https://storybook.js.org/)
|
If you want to test your login screens outside of Keycloak you can mock a given `kcContext`:
|
||||||
for example you can use `kcContextMocks`.
|
|
||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
import {
|
import {
|
||||||
KcApp,
|
KcApp,
|
||||||
defaultKcProps,
|
defaultKcProps,
|
||||||
kcContextMocks
|
getKcContext
|
||||||
} from "keycloakify";
|
} from "keycloakify";
|
||||||
|
|
||||||
|
const { kcContext } = getKcContext({
|
||||||
|
"mockPageId": "login.ftl"
|
||||||
|
});
|
||||||
|
|
||||||
reactDom.render(
|
reactDom.render(
|
||||||
<KcApp
|
<KcApp
|
||||||
kcContext={kcContextMocks.kcLoginContext}
|
kcContext={kcContextMocks.kcLoginContext}
|
||||||
@ -267,12 +288,10 @@ Then to load your own therms of services using [like this](https://github.com/ga
|
|||||||
|
|
||||||
# Some pages still have the default theme. Why?
|
# Some pages still have the default theme. Why?
|
||||||
|
|
||||||
This project only support the most common user facing pages of Keycloak login.
|
This project only support out of the box the most common user facing pages of Keycloak login.
|
||||||
[Here](https://user-images.githubusercontent.com/6702424/116787906-227fe700-aaa7-11eb-92ee-22e7673717c2.png) is the complete list of pages (you get them after running `yarn test`)
|
[Here](https://user-images.githubusercontent.com/6702424/116787906-227fe700-aaa7-11eb-92ee-22e7673717c2.png) is the complete list of pages (you get them after running `yarn test`)
|
||||||
and [here](https://github.com/InseeFrLab/keycloakify/tree/main/src/lib/components) are the pages currently implemented by this module.
|
and [here](https://github.com/InseeFrLab/keycloakify/tree/main/src/lib/components) are the pages currently implemented by this module.
|
||||||
If you need to customize pages that are not supported yet you can submit an issue about it and wait for me get it implemented.
|
If you need to customize pages that are not supported yet or if you need to implement some non standard `.ftl` pages please refer to [Advanced pages configuration](#advanced-pages-configuration).
|
||||||
If you can't wait, PR are welcome! [Here](https://github.com/InseeFrLab/keycloakify/commit/0163459ad6b1ad0afcc34fae5f3cc28dbcf8b4a7) is the commit that adds support
|
|
||||||
for the `login-otp.ftl` page. You can use it as a model for implementing other pages.
|
|
||||||
|
|
||||||
# GitHub Actions
|
# GitHub Actions
|
||||||
|
|
||||||
|
15
package.json
15
package.json
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "keycloakify",
|
"name": "keycloakify",
|
||||||
"version": "1.0.3",
|
"version": "2.0.8",
|
||||||
"description": "Keycloak theme generator for Reacts app",
|
"description": "Keycloak theme generator for Reacts app",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@ -12,7 +12,7 @@
|
|||||||
"clean": "rimraf dist/",
|
"clean": "rimraf dist/",
|
||||||
"build": "yarn clean && tsc && yarn grant-exec-perms && yarn copy-files",
|
"build": "yarn clean && tsc && yarn grant-exec-perms && yarn copy-files",
|
||||||
"grant-exec-perms": "node dist/bin/tools/grant-exec-perms.js",
|
"grant-exec-perms": "node dist/bin/tools/grant-exec-perms.js",
|
||||||
"test": "node dist/test",
|
"test": "node dist/test/bin && node dist/test/lib",
|
||||||
"copy-files": "copyfiles -u 1 src/**/*.ftl src/**/*.xml src/**/*.js dist/",
|
"copy-files": "copyfiles -u 1 src/**/*.ftl src/**/*.xml src/**/*.js dist/",
|
||||||
"generate-messages": "node dist/bin/generate-i18n-messages.js"
|
"generate-messages": "node dist/bin/generate-i18n-messages.js"
|
||||||
},
|
},
|
||||||
@ -46,17 +46,18 @@
|
|||||||
"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",
|
||||||
"typescript": "^4.2.3"
|
"typescript": "^4.2.3",
|
||||||
|
"ts-toolbelt": "^9.6.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cheerio": "^1.0.0-rc.5",
|
"cheerio": "^1.0.0-rc.5",
|
||||||
"evt": "2.0.0-beta.15",
|
"evt": "2.0.0-beta.20",
|
||||||
"markdown": "^0.5.0",
|
|
||||||
"minimal-polyfills": "^2.1.6",
|
"minimal-polyfills": "^2.1.6",
|
||||||
"path": "^0.12.7",
|
"path": "^0.12.7",
|
||||||
"powerhooks": "^0.0.36",
|
"powerhooks": "^0.1.6",
|
||||||
"react-markdown": "^5.0.3",
|
"react-markdown": "^5.0.3",
|
||||||
"scripting-tools": "^0.19.13",
|
"scripting-tools": "^0.19.13",
|
||||||
"tss-react": "^0.0.12"
|
"tss-react": "^0.0.12",
|
||||||
|
"tsafe": "^0.4.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
116
src/bin/build-keycloak-theme/build-keycloak-theme.ts
Normal file
116
src/bin/build-keycloak-theme/build-keycloak-theme.ts
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
import { generateKeycloakThemeResources } from "./generateKeycloakThemeResources";
|
||||||
|
import { generateJavaStackFiles } from "./generateJavaStackFiles";
|
||||||
|
import type { ParsedPackageJson } from "./generateJavaStackFiles";
|
||||||
|
import { join as pathJoin, relative as pathRelative, basename as pathBasename } from "path";
|
||||||
|
import * as child_process from "child_process";
|
||||||
|
import { generateDebugFiles, containerLaunchScriptBasename } from "./generateDebugFiles";
|
||||||
|
import { URL } from "url";
|
||||||
|
|
||||||
|
const reactProjectDirPath = process.cwd();
|
||||||
|
|
||||||
|
const doUseExternalAssets = process.argv[2]?.toLowerCase() === "--external-assets";
|
||||||
|
|
||||||
|
const parsedPackageJson: ParsedPackageJson = require(pathJoin(reactProjectDirPath, "package.json"));
|
||||||
|
|
||||||
|
export const keycloakThemeBuildingDirPath = pathJoin(reactProjectDirPath, "build_keycloak");
|
||||||
|
|
||||||
|
export function main() {
|
||||||
|
|
||||||
|
console.log("🔏 Building the keycloak theme...⌚");
|
||||||
|
|
||||||
|
const extraPagesId: string[] = (parsedPackageJson as any)["keycloakify"]?.["extraPages"] ?? [];
|
||||||
|
const extraThemeProperties: string[] = (parsedPackageJson as any)["keycloakify"]?.["extraThemeProperties"] ?? [];
|
||||||
|
|
||||||
|
generateKeycloakThemeResources({
|
||||||
|
keycloakThemeBuildingDirPath,
|
||||||
|
"reactAppBuildDirPath": pathJoin(reactProjectDirPath, "build"),
|
||||||
|
"themeName": parsedPackageJson.name,
|
||||||
|
...(() => {
|
||||||
|
|
||||||
|
const url = (() => {
|
||||||
|
|
||||||
|
const { homepage } = parsedPackageJson;
|
||||||
|
|
||||||
|
return homepage === undefined ?
|
||||||
|
undefined :
|
||||||
|
new URL(homepage);
|
||||||
|
|
||||||
|
})();
|
||||||
|
|
||||||
|
return {
|
||||||
|
"urlPathname":
|
||||||
|
url === undefined ?
|
||||||
|
"/" :
|
||||||
|
url.pathname.replace(/([^/])$/, "$1/"),
|
||||||
|
"urlOrigin": !doUseExternalAssets ? undefined : (() => {
|
||||||
|
|
||||||
|
if (url === undefined) {
|
||||||
|
console.error("ERROR: You must specify 'homepage' in your package.json");
|
||||||
|
process.exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return url.origin;
|
||||||
|
|
||||||
|
})()
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
})(),
|
||||||
|
extraPagesId,
|
||||||
|
extraThemeProperties
|
||||||
|
});
|
||||||
|
|
||||||
|
const { jarFilePath } = generateJavaStackFiles({
|
||||||
|
parsedPackageJson,
|
||||||
|
keycloakThemeBuildingDirPath
|
||||||
|
});
|
||||||
|
|
||||||
|
child_process.execSync(
|
||||||
|
"mvn package",
|
||||||
|
{ "cwd": keycloakThemeBuildingDirPath }
|
||||||
|
);
|
||||||
|
|
||||||
|
generateDebugFiles({
|
||||||
|
keycloakThemeBuildingDirPath,
|
||||||
|
"packageJsonName": parsedPackageJson.name
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log([
|
||||||
|
'',
|
||||||
|
`✅ Your keycloak theme has been generated and bundled into ./${pathRelative(reactProjectDirPath, jarFilePath)} 🚀`,
|
||||||
|
`It is to be placed in "/opt/jboss/keycloak/standalone/deployments" in the container running a jboss/keycloak Docker image. (Tested with 11.0.3)`,
|
||||||
|
'',
|
||||||
|
'Using Helm (https://github.com/codecentric/helm-charts), edit to reflect:',
|
||||||
|
'',
|
||||||
|
'value.yaml: ',
|
||||||
|
' extraInitContainers: |',
|
||||||
|
' - name: realm-ext-provider',
|
||||||
|
' image: curlimages/curl',
|
||||||
|
' imagePullPolicy: IfNotPresent',
|
||||||
|
' command:',
|
||||||
|
' - sh',
|
||||||
|
' args:',
|
||||||
|
' - -c',
|
||||||
|
` - curl -L -f -S -o /extensions/${pathBasename(jarFilePath)} https://AN.URL.FOR/${pathBasename(jarFilePath)}`,
|
||||||
|
' volumeMounts:',
|
||||||
|
' - name: extensions',
|
||||||
|
' mountPath: /extensions',
|
||||||
|
' ',
|
||||||
|
' extraVolumeMounts: |',
|
||||||
|
' - name: extensions',
|
||||||
|
' mountPath: /opt/jboss/keycloak/standalone/deployments',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
'To test your theme locally, with hot reloading, you can spin up a Keycloak container image with the theme loaded by running:',
|
||||||
|
'',
|
||||||
|
`👉 $ ./${pathRelative(reactProjectDirPath, pathJoin(keycloakThemeBuildingDirPath, containerLaunchScriptBasename))} 👈`,
|
||||||
|
'',
|
||||||
|
'To enable the theme within keycloak log into the admin console ( 👉 http://localhost:8080 username: admin, password: admin 👈), create a realm (called "myrealm" for example),',
|
||||||
|
`go to your realm settings, click on the theme tab then select ${parsedPackageJson.name}.`,
|
||||||
|
`More details: https://www.keycloak.org/getting-started/getting-started-docker`,
|
||||||
|
'',
|
||||||
|
'Once your container is up and configured 👉 http://localhost:8080/auth/realms/myrealm/account 👈',
|
||||||
|
'',
|
||||||
|
].join("\n"));
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,74 @@
|
|||||||
|
|
||||||
|
import * as fs from "fs";
|
||||||
|
import { join as pathJoin, dirname as pathDirname, basename as pathBasename } from "path";
|
||||||
|
|
||||||
|
export const containerLaunchScriptBasename = "start_keycloak_testing_container.sh";
|
||||||
|
|
||||||
|
/** Files for being able to run a hot reload keycloak container */
|
||||||
|
export function generateDebugFiles(
|
||||||
|
params: {
|
||||||
|
packageJsonName: string;
|
||||||
|
keycloakThemeBuildingDirPath: string;
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
|
||||||
|
const { packageJsonName, keycloakThemeBuildingDirPath } = params;
|
||||||
|
|
||||||
|
fs.writeFileSync(
|
||||||
|
pathJoin(keycloakThemeBuildingDirPath, "Dockerfile"),
|
||||||
|
Buffer.from(
|
||||||
|
[
|
||||||
|
"FROM jboss/keycloak:11.0.3",
|
||||||
|
"",
|
||||||
|
"USER root",
|
||||||
|
"",
|
||||||
|
"WORKDIR /",
|
||||||
|
"",
|
||||||
|
"ADD configuration /opt/jboss/keycloak/standalone/configuration/",
|
||||||
|
"",
|
||||||
|
'ENTRYPOINT [ "/opt/jboss/tools/docker-entrypoint.sh" ]',
|
||||||
|
].join("\n"),
|
||||||
|
"utf8"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const dockerImage = `${packageJsonName}/keycloak-hot-reload`;
|
||||||
|
const containerName = "keycloak-testing-container";
|
||||||
|
|
||||||
|
fs.writeFileSync(
|
||||||
|
pathJoin(keycloakThemeBuildingDirPath, containerLaunchScriptBasename),
|
||||||
|
Buffer.from(
|
||||||
|
[
|
||||||
|
"#!/bin/bash",
|
||||||
|
"",
|
||||||
|
`cd ${keycloakThemeBuildingDirPath}`,
|
||||||
|
"",
|
||||||
|
`docker rm ${containerName} || true`,
|
||||||
|
"",
|
||||||
|
`docker build . -t ${dockerImage}`,
|
||||||
|
"",
|
||||||
|
"docker run \\",
|
||||||
|
" -p 8080:8080 \\",
|
||||||
|
` --name ${containerName} \\`,
|
||||||
|
" -e KEYCLOAK_USER=admin \\",
|
||||||
|
" -e KEYCLOAK_PASSWORD=admin \\",
|
||||||
|
` -v ${pathJoin(keycloakThemeBuildingDirPath, "src", "main", "resources", "theme", packageJsonName)
|
||||||
|
}:/opt/jboss/keycloak/themes/${packageJsonName}:rw \\`,
|
||||||
|
` -it ${dockerImage}:latest`,
|
||||||
|
""
|
||||||
|
].join("\n"),
|
||||||
|
"utf8"
|
||||||
|
),
|
||||||
|
{ "mode": 0o755 }
|
||||||
|
);
|
||||||
|
|
||||||
|
const standaloneHaFilePath = pathJoin(keycloakThemeBuildingDirPath, "configuration", "standalone-ha.xml");
|
||||||
|
|
||||||
|
try { fs.mkdirSync(pathDirname(standaloneHaFilePath)); } catch { }
|
||||||
|
|
||||||
|
fs.writeFileSync(
|
||||||
|
standaloneHaFilePath,
|
||||||
|
fs.readFileSync(pathJoin(__dirname, pathBasename(standaloneHaFilePath)))
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
@ -1,74 +1 @@
|
|||||||
|
export * from "./generateDebugFiles";
|
||||||
import * as fs from "fs";
|
|
||||||
import { join as pathJoin, dirname as pathDirname, basename as pathBasename } from "path";
|
|
||||||
|
|
||||||
export const containerLaunchScriptBasename = "start_keycloak_testing_container.sh";
|
|
||||||
|
|
||||||
/** Files for being able to run a hot reload keycloak container */
|
|
||||||
export function generateDebugFiles(
|
|
||||||
params: {
|
|
||||||
packageJsonName: string;
|
|
||||||
keycloakThemeBuildingDirPath: string;
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
|
|
||||||
const { packageJsonName, keycloakThemeBuildingDirPath } = params;
|
|
||||||
|
|
||||||
fs.writeFileSync(
|
|
||||||
pathJoin(keycloakThemeBuildingDirPath, "Dockerfile"),
|
|
||||||
Buffer.from(
|
|
||||||
[
|
|
||||||
"FROM jboss/keycloak:11.0.3",
|
|
||||||
"",
|
|
||||||
"USER root",
|
|
||||||
"",
|
|
||||||
"WORKDIR /",
|
|
||||||
"",
|
|
||||||
"ADD configuration /opt/jboss/keycloak/standalone/configuration/",
|
|
||||||
"",
|
|
||||||
'ENTRYPOINT [ "/opt/jboss/tools/docker-entrypoint.sh" ]',
|
|
||||||
].join("\n"),
|
|
||||||
"utf8"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
const dockerImage = `${packageJsonName}/keycloak-hot-reload`;
|
|
||||||
const containerName = "keycloak-testing-container";
|
|
||||||
|
|
||||||
fs.writeFileSync(
|
|
||||||
pathJoin(keycloakThemeBuildingDirPath, containerLaunchScriptBasename),
|
|
||||||
Buffer.from(
|
|
||||||
[
|
|
||||||
"#!/bin/bash",
|
|
||||||
"",
|
|
||||||
`cd ${keycloakThemeBuildingDirPath}`,
|
|
||||||
"",
|
|
||||||
`docker rm ${containerName} || true`,
|
|
||||||
"",
|
|
||||||
`docker build . -t ${dockerImage}`,
|
|
||||||
"",
|
|
||||||
"docker run \\",
|
|
||||||
" -p 8080:8080 \\",
|
|
||||||
` --name ${containerName} \\`,
|
|
||||||
" -e KEYCLOAK_USER=admin \\",
|
|
||||||
" -e KEYCLOAK_PASSWORD=admin \\",
|
|
||||||
` -v ${pathJoin(keycloakThemeBuildingDirPath, "src", "main", "resources", "theme", packageJsonName)
|
|
||||||
}:/opt/jboss/keycloak/themes/${packageJsonName}:rw \\`,
|
|
||||||
` -it ${dockerImage}:latest`,
|
|
||||||
""
|
|
||||||
].join("\n"),
|
|
||||||
"utf8"
|
|
||||||
),
|
|
||||||
{ "mode": 0o755 }
|
|
||||||
);
|
|
||||||
|
|
||||||
const standaloneHaFilePath = pathJoin(keycloakThemeBuildingDirPath, "configuration", "standalone-ha.xml");
|
|
||||||
|
|
||||||
try { fs.mkdirSync(pathDirname(standaloneHaFilePath)); } catch { }
|
|
||||||
|
|
||||||
fs.writeFileSync(
|
|
||||||
standaloneHaFilePath,
|
|
||||||
fs.readFileSync(pathJoin(__dirname, pathBasename(standaloneHaFilePath)))
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
|
@ -1,26 +0,0 @@
|
|||||||
|
|
||||||
var es = /&(?:amp|#38|lt|#60|gt|#62|apos|#39|quot|#34);/g;
|
|
||||||
|
|
||||||
var unes = {
|
|
||||||
'&': '&',
|
|
||||||
'&': '&',
|
|
||||||
'<': '<',
|
|
||||||
'<': '<',
|
|
||||||
'>': '>',
|
|
||||||
'>': '>',
|
|
||||||
''': "'",
|
|
||||||
''': "'",
|
|
||||||
'"': '"',
|
|
||||||
'"': '"'
|
|
||||||
};
|
|
||||||
var cape = function (m) { return unes[m]; };
|
|
||||||
|
|
||||||
Object.defineProperty(
|
|
||||||
String,
|
|
||||||
"htmlUnescape",
|
|
||||||
{
|
|
||||||
"value": function (un) {
|
|
||||||
return String.prototype.replace.call(un, es, cape);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
@ -1,263 +1,192 @@
|
|||||||
<script>const _=
|
<script>const _=
|
||||||
{
|
<#macro objectToJson object depth>
|
||||||
"url": {
|
<@compress>
|
||||||
"loginAction": (function (){
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
return "${url.loginAction?no_esc}";
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
})(),
|
|
||||||
"resourcesPath": (function (){
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
return "${url.resourcesPath?no_esc}";
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
})(),
|
|
||||||
"resourcesCommonPath": (function (){
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
return "${url.resourcesCommonPath?no_esc}";
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
})(),
|
|
||||||
"loginRestartFlowUrl": (function (){
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
return "${url.loginRestartFlowUrl?no_esc}";
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
})(),
|
|
||||||
"loginUrl": (function (){
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
return "${url.loginUrl?no_esc}";
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
})()
|
|
||||||
},
|
|
||||||
"realm": {
|
|
||||||
"displayName": (function (){
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
return "${realm.displayName!''}" || undefined;
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
})(),
|
|
||||||
"displayNameHtml": (function (){
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
return "${realm.displayNameHtml!''}" || undefined;
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
})(),
|
|
||||||
"internationalizationEnabled": (function (){
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
return ${realm.internationalizationEnabled?c};
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
})(),
|
|
||||||
"registrationEmailAsUsername": (function (){
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
return ${realm.registrationEmailAsUsername?c};
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
})()
|
|
||||||
},
|
|
||||||
"locale": (function (){
|
|
||||||
|
|
||||||
|
<#local isHash = false>
|
||||||
<#attempt>
|
<#attempt>
|
||||||
<#if realm.internationalizationEnabled>
|
<#local isHash = object?is_hash || object?is_hash_ex>
|
||||||
|
|
||||||
return {
|
|
||||||
"supported": (function(){
|
|
||||||
|
|
||||||
var out= [];
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
<#list locale.supported as lng>
|
|
||||||
out.push({
|
|
||||||
"url": (function (){
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
return "${lng.url?no_esc}";
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
})(),
|
|
||||||
"label": (function (){
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
return "${lng.label}";
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
})(),
|
|
||||||
"languageTag": (function (){
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
return "${lng.languageTag}";
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
})()
|
|
||||||
});
|
|
||||||
</#list>
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
return out;
|
|
||||||
|
|
||||||
})(),
|
|
||||||
"current": (function (){
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
return "${locale.current}";
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
})()
|
|
||||||
};
|
|
||||||
|
|
||||||
</#if>
|
|
||||||
<#recover>
|
<#recover>
|
||||||
|
/* can't evaluate if object is hash */
|
||||||
|
undefined
|
||||||
|
<#return>
|
||||||
</#attempt>
|
</#attempt>
|
||||||
|
<#if isHash>
|
||||||
|
|
||||||
})(),
|
<#local keys = "">
|
||||||
"auth": (function (){
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
<#if auth?has_content>
|
|
||||||
|
|
||||||
var out= {
|
|
||||||
"showUsername": (function (){
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
return ${auth.showUsername()?c};
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
})(),
|
|
||||||
"showResetCredentials": (function (){
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
return ${auth.showResetCredentials()?c};
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
})(),
|
|
||||||
"showTryAnotherWayLink": (function(){
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
return ${auth.showTryAnotherWayLink()?c};
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
})()
|
|
||||||
};
|
|
||||||
|
|
||||||
<#attempt>
|
<#attempt>
|
||||||
<#if auth.showUsername() && !auth.showResetCredentials()>
|
<#local keys = object?keys>
|
||||||
Object.assign(
|
|
||||||
out,
|
|
||||||
{
|
|
||||||
"attemptedUsername": (function (){
|
|
||||||
<#attempt>
|
|
||||||
return "${auth.attemptedUsername}";
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
})()
|
|
||||||
}
|
|
||||||
);
|
|
||||||
</#if>
|
|
||||||
<#recover>
|
<#recover>
|
||||||
|
/* can't list keys of object */
|
||||||
|
undefined
|
||||||
|
<#return>
|
||||||
</#attempt>
|
</#attempt>
|
||||||
|
|
||||||
return out;
|
{${'\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 4>
|
||||||
|
/* Avoid calling recustively too many times depth: ${depth}, key: ${key} */
|
||||||
|
<#continue>
|
||||||
|
</#if>
|
||||||
|
|
||||||
|
"${key}": <@objectToJson object=value depth=depth+1/>,
|
||||||
|
|
||||||
|
</#list>
|
||||||
|
|
||||||
|
}${'\n'}
|
||||||
|
|
||||||
|
<#return>
|
||||||
|
|
||||||
</#if>
|
</#if>
|
||||||
|
|
||||||
|
|
||||||
|
<#local isMethod = "">
|
||||||
|
<#attempt>
|
||||||
|
<#local isMethod = object?is_method>
|
||||||
<#recover>
|
<#recover>
|
||||||
|
/* can't test if object is a method */
|
||||||
|
undefined
|
||||||
|
<#return>
|
||||||
</#attempt>
|
</#attempt>
|
||||||
|
|
||||||
})(),
|
<#if isMethod>
|
||||||
"scripts": (function(){
|
undefined
|
||||||
|
<#return>
|
||||||
|
</#if>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<#local isBoolean = "">
|
||||||
|
<#attempt>
|
||||||
|
<#local isBoolean = object?is_boolean>
|
||||||
|
<#recover>
|
||||||
|
/* can't test if object is a boolean */
|
||||||
|
undefined
|
||||||
|
<#return>
|
||||||
|
</#attempt>
|
||||||
|
|
||||||
|
<#if isBoolean>
|
||||||
|
${object?c}
|
||||||
|
<#return>
|
||||||
|
</#if>
|
||||||
|
|
||||||
|
|
||||||
|
<#local isEnumerable = "">
|
||||||
|
<#attempt>
|
||||||
|
<#local isEnumerable = object?is_enumerable>
|
||||||
|
<#recover>
|
||||||
|
/* can't test if object is enumerable */
|
||||||
|
undefined
|
||||||
|
<#return>
|
||||||
|
</#attempt>
|
||||||
|
|
||||||
|
<#if isEnumerable>
|
||||||
|
|
||||||
|
[${'\n'}
|
||||||
|
|
||||||
|
<#list object as item>
|
||||||
|
|
||||||
|
<@objectToJson object=item depth=depth+1/>,
|
||||||
|
|
||||||
|
</#list>
|
||||||
|
|
||||||
|
]${'\n'}
|
||||||
|
|
||||||
|
<#return>
|
||||||
|
</#if>
|
||||||
|
|
||||||
var out = [];
|
|
||||||
|
|
||||||
<#attempt>
|
<#attempt>
|
||||||
<#if scripts??>
|
"${object?replace('"', '\\"')?no_esc}"
|
||||||
<#attempt>
|
<#recover>
|
||||||
<#list scripts as script>
|
/* couldn't convert into string non hash, non method, non boolean, non enumerable object */
|
||||||
out.push((function (){
|
undefined;
|
||||||
|
<#return>
|
||||||
|
</#attempt>
|
||||||
|
|
||||||
|
|
||||||
|
</@compress>
|
||||||
|
</#macro>
|
||||||
|
|
||||||
|
(()=>{
|
||||||
|
|
||||||
|
//Removing all the undefined
|
||||||
|
const obj = JSON.parse(JSON.stringify(<@objectToJson object=.data_model depth=0 />));
|
||||||
|
|
||||||
|
//Freemarker values that can't be automatically converted into a JavaScript object.
|
||||||
|
Object.deepAssign(
|
||||||
|
obj,
|
||||||
|
{
|
||||||
|
"messagesPerField": {
|
||||||
|
"printIfExists": function (key, x) {
|
||||||
|
switch(key){
|
||||||
|
case "userLabel": return (function (){
|
||||||
<#attempt>
|
<#attempt>
|
||||||
return "${script}";
|
return "${messagesPerField.printIfExists('userLabel','1')}" ? x : undefined;
|
||||||
<#recover>
|
<#recover>
|
||||||
</#attempt>
|
</#attempt>
|
||||||
|
})();
|
||||||
|
case "username": return (function (){
|
||||||
|
<#attempt>
|
||||||
|
return "${messagesPerField.printIfExists('username','1')}" ? x : undefined;
|
||||||
|
<#recover>
|
||||||
|
</#attempt>
|
||||||
|
})();
|
||||||
|
case "email": return (function (){
|
||||||
|
<#attempt>
|
||||||
|
return "${messagesPerField.printIfExists('email','1')}" ? x : undefined;
|
||||||
|
<#recover>
|
||||||
|
</#attempt>
|
||||||
|
})();
|
||||||
|
case "firstName": return (function (){
|
||||||
|
<#attempt>
|
||||||
|
return "${messagesPerField.printIfExists('firstName','1')}" ? x : undefined;
|
||||||
|
<#recover>
|
||||||
|
</#attempt>
|
||||||
|
})();
|
||||||
|
case "lastName": return (function (){
|
||||||
|
<#attempt>
|
||||||
|
return "${messagesPerField.printIfExists('lastName','1')}" ? x : undefined;
|
||||||
|
<#recover>
|
||||||
|
</#attempt>
|
||||||
|
})();
|
||||||
|
case "password": return (function (){
|
||||||
|
<#attempt>
|
||||||
|
return "${messagesPerField.printIfExists('password','1')}" ? x : undefined;
|
||||||
|
<#recover>
|
||||||
|
</#attempt>
|
||||||
|
})();
|
||||||
|
case "password-confirm": return (function (){
|
||||||
|
<#attempt>
|
||||||
|
return "${messagesPerField.printIfExists('password-confirm','1')}" ? x : undefined;
|
||||||
|
<#recover>
|
||||||
|
</#attempt>
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"msg": function(){ throw new Error("use import { useKcMessage } from 'keycloakify'"); },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
})());
|
return obj;
|
||||||
</#list>
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
</#if>
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
return out;
|
})()
|
||||||
|
|
||||||
})(),
|
|
||||||
"message": (function (){
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
<#if message?has_content>
|
|
||||||
|
|
||||||
return {
|
|
||||||
"type": (function (){
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
return "${message.type}";
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
})(),
|
|
||||||
"summary": (function (){
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
return String.htmlUnescape("${message.summary}");
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
})()
|
|
||||||
};
|
|
||||||
|
|
||||||
</#if>
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
})(),
|
|
||||||
"isAppInitiatedAction": (function (){
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
<#if isAppInitiatedAction??>
|
|
||||||
return true;
|
|
||||||
</#if>
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
return false;
|
|
||||||
|
|
||||||
})()
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
@ -1,23 +0,0 @@
|
|||||||
<script>const _=
|
|
||||||
{
|
|
||||||
"client": (function (){
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
<#if client??>
|
|
||||||
return {
|
|
||||||
"baseUrl": (function (){
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
return "${(client.baseUrl!'')?no_esc}" || undefined;
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
})()
|
|
||||||
};
|
|
||||||
</#if>
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
})()
|
|
||||||
}
|
|
||||||
</script>
|
|
170
src/bin/build-keycloak-theme/generateFtl/generateFtl.ts
Normal file
170
src/bin/build-keycloak-theme/generateFtl/generateFtl.ts
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
|
||||||
|
|
||||||
|
import cheerio from "cheerio";
|
||||||
|
import {
|
||||||
|
replaceImportsFromStaticInJsCode,
|
||||||
|
replaceImportsInInlineCssCode,
|
||||||
|
generateCssCodeToDefineGlobals
|
||||||
|
} from "../replaceImportFromStatic";
|
||||||
|
import fs from "fs";
|
||||||
|
import { join as pathJoin } from "path";
|
||||||
|
import { objectKeys } from "tsafe/objectKeys";
|
||||||
|
import { ftlValuesGlobalName } from "../ftlValuesGlobalName";
|
||||||
|
|
||||||
|
export const pageIds = [
|
||||||
|
"login.ftl", "register.ftl", "info.ftl",
|
||||||
|
"error.ftl", "login-reset-password.ftl",
|
||||||
|
"login-verify-email.ftl", "terms.ftl",
|
||||||
|
"login-otp.ftl", "login-update-profile.ftl",
|
||||||
|
"login-idp-link-confirm.ftl"
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
export type PageId = typeof pageIds[number];
|
||||||
|
|
||||||
|
function loadAdjacentFile(fileBasename: string) {
|
||||||
|
return fs.readFileSync(pathJoin(__dirname, fileBasename))
|
||||||
|
.toString("utf8");
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export function generateFtlFilesCodeFactory(
|
||||||
|
params: {
|
||||||
|
cssGlobalsToDefine: Record<string, string>;
|
||||||
|
indexHtmlCode: string;
|
||||||
|
urlPathname: string;
|
||||||
|
urlOrigin: undefined | string;
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
|
||||||
|
const { cssGlobalsToDefine, indexHtmlCode, urlPathname, urlOrigin } = params;
|
||||||
|
|
||||||
|
const $ = cheerio.load(indexHtmlCode);
|
||||||
|
|
||||||
|
$("script:not([src])").each((...[, element]) => {
|
||||||
|
|
||||||
|
const { fixedJsCode } = replaceImportsFromStaticInJsCode({
|
||||||
|
"jsCode": $(element).html()!,
|
||||||
|
urlOrigin
|
||||||
|
});
|
||||||
|
|
||||||
|
$(element).text(fixedJsCode);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
$("style").each((...[, element]) => {
|
||||||
|
|
||||||
|
const { fixedCssCode } = replaceImportsInInlineCssCode({
|
||||||
|
"cssCode": $(element).html()!,
|
||||||
|
"urlPathname": params.urlPathname,
|
||||||
|
urlOrigin
|
||||||
|
});
|
||||||
|
|
||||||
|
$(element).text(fixedCssCode);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
([
|
||||||
|
["link", "href"],
|
||||||
|
["script", "src"],
|
||||||
|
] as const).forEach(([selector, attrName]) =>
|
||||||
|
$(selector).each((...[, element]) => {
|
||||||
|
|
||||||
|
const href = $(element).attr(attrName);
|
||||||
|
|
||||||
|
if (href === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$(element).attr(
|
||||||
|
attrName,
|
||||||
|
urlOrigin !== undefined ?
|
||||||
|
href.replace(/^\//, `${urlOrigin}/`) :
|
||||||
|
href.replace(
|
||||||
|
new RegExp(`^${urlPathname.replace(/\//g, "\\/")}`),
|
||||||
|
"${url.resourcesPath}/build/"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
//FTL is no valid html, we can't insert with cheerio, we put placeholder for injecting later.
|
||||||
|
const ftlPlaceholders = {
|
||||||
|
'{ "x": "vIdLqMeOed9sdLdIdOxdK0d" }': loadAdjacentFile("common.ftl")
|
||||||
|
.match(/^<script>const _=((?:.|\n)+)<\/script>[\n]?$/)![1],
|
||||||
|
'<!-- xIdLqMeOedErIdLsPdNdI9dSlxI -->':
|
||||||
|
[
|
||||||
|
'<#if scripts??>',
|
||||||
|
' <#list scripts as script>',
|
||||||
|
' <script src="${script}" type="text/javascript"></script>',
|
||||||
|
' </#list>',
|
||||||
|
'</#if>'
|
||||||
|
].join("\n")
|
||||||
|
};
|
||||||
|
|
||||||
|
const pageSpecificCodePlaceholder = "<!-- dIddLqMeOedErIdLsPdNdI9dSl42sw -->";
|
||||||
|
|
||||||
|
$("head").prepend(
|
||||||
|
[
|
||||||
|
...(Object.keys(cssGlobalsToDefine).length === 0 ? [] : [
|
||||||
|
'',
|
||||||
|
'<style>',
|
||||||
|
generateCssCodeToDefineGlobals({
|
||||||
|
cssGlobalsToDefine,
|
||||||
|
urlPathname
|
||||||
|
}).cssCodeToPrependInHead,
|
||||||
|
'</style>',
|
||||||
|
''
|
||||||
|
]),
|
||||||
|
"<script>",
|
||||||
|
loadAdjacentFile("Object.deepAssign.js"),
|
||||||
|
"</script>",
|
||||||
|
'<script>',
|
||||||
|
` window.${ftlValuesGlobalName}= Object.assign(`,
|
||||||
|
` {},`,
|
||||||
|
` ${objectKeys(ftlPlaceholders)[0]}`,
|
||||||
|
' );',
|
||||||
|
'</script>',
|
||||||
|
'',
|
||||||
|
pageSpecificCodePlaceholder,
|
||||||
|
'',
|
||||||
|
objectKeys(ftlPlaceholders)[1]
|
||||||
|
].join("\n"),
|
||||||
|
);
|
||||||
|
|
||||||
|
const partiallyFixedIndexHtmlCode = $.html();
|
||||||
|
|
||||||
|
function generateFtlFilesCode(
|
||||||
|
params: {
|
||||||
|
pageId: string;
|
||||||
|
}
|
||||||
|
): { ftlCode: string; } {
|
||||||
|
|
||||||
|
const { pageId } = params;
|
||||||
|
|
||||||
|
const $ = cheerio.load(partiallyFixedIndexHtmlCode);
|
||||||
|
|
||||||
|
let ftlCode = $.html()
|
||||||
|
.replace(
|
||||||
|
pageSpecificCodePlaceholder,
|
||||||
|
[
|
||||||
|
'<script>',
|
||||||
|
` Object.deepAssign(`,
|
||||||
|
` window.${ftlValuesGlobalName},`,
|
||||||
|
` { "pageId": "${pageId}" }`,
|
||||||
|
' );',
|
||||||
|
'</script>'
|
||||||
|
].join("\n")
|
||||||
|
);
|
||||||
|
|
||||||
|
objectKeys(ftlPlaceholders)
|
||||||
|
.forEach(id => ftlCode = ftlCode.replace(id, ftlPlaceholders[id]));
|
||||||
|
|
||||||
|
return { ftlCode };
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return { generateFtlFilesCode };
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -1,192 +1 @@
|
|||||||
|
export * from "./generateFtl";
|
||||||
|
|
||||||
import cheerio from "cheerio";
|
|
||||||
import {
|
|
||||||
replaceImportsFromStaticInJsCode,
|
|
||||||
replaceImportsInInlineCssCode,
|
|
||||||
generateCssCodeToDefineGlobals
|
|
||||||
} from "../replaceImportFromStatic";
|
|
||||||
import fs from "fs";
|
|
||||||
import { join as pathJoin } from "path";
|
|
||||||
import { objectKeys } from "evt/tools/typeSafety/objectKeys";
|
|
||||||
import { ftlValuesGlobalName } from "../ftlValuesGlobalName";
|
|
||||||
|
|
||||||
export const pageIds = [
|
|
||||||
"login.ftl", "register.ftl", "info.ftl",
|
|
||||||
"error.ftl", "login-reset-password.ftl",
|
|
||||||
"login-verify-email.ftl", "terms.ftl",
|
|
||||||
"login-otp.ftl"
|
|
||||||
] as const;
|
|
||||||
|
|
||||||
export type PageId = typeof pageIds[number];
|
|
||||||
|
|
||||||
function loadAdjacentFile(fileBasename: string) {
|
|
||||||
return fs.readFileSync(pathJoin(__dirname, fileBasename))
|
|
||||||
.toString("utf8");
|
|
||||||
};
|
|
||||||
|
|
||||||
function loadFtlFile(ftlFileBasename: PageId | "common.ftl") {
|
|
||||||
try {
|
|
||||||
|
|
||||||
return loadAdjacentFile(ftlFileBasename)
|
|
||||||
.match(/^<script>const _=((?:.|\n)+)<\/script>[\n]?$/)![1];
|
|
||||||
|
|
||||||
} catch {
|
|
||||||
return "{}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export function generateFtlFilesCodeFactory(
|
|
||||||
params: {
|
|
||||||
cssGlobalsToDefine: Record<string, string>;
|
|
||||||
indexHtmlCode: string;
|
|
||||||
urlPathname: string;
|
|
||||||
urlOrigin: undefined | string;
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
|
|
||||||
const { cssGlobalsToDefine, indexHtmlCode, urlPathname, urlOrigin } = params;
|
|
||||||
|
|
||||||
const $ = cheerio.load(indexHtmlCode);
|
|
||||||
|
|
||||||
$("script:not([src])").each((...[, element]) => {
|
|
||||||
|
|
||||||
const { fixedJsCode } = replaceImportsFromStaticInJsCode({
|
|
||||||
"jsCode": $(element).html()!,
|
|
||||||
urlOrigin
|
|
||||||
});
|
|
||||||
|
|
||||||
$(element).text(fixedJsCode);
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
$("style").each((...[, element]) => {
|
|
||||||
|
|
||||||
const { fixedCssCode } = replaceImportsInInlineCssCode({
|
|
||||||
"cssCode": $(element).html()!,
|
|
||||||
"urlPathname": params.urlPathname,
|
|
||||||
urlOrigin
|
|
||||||
});
|
|
||||||
|
|
||||||
$(element).text(fixedCssCode);
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
([
|
|
||||||
["link", "href"],
|
|
||||||
["script", "src"],
|
|
||||||
] as const).forEach(([selector, attrName]) =>
|
|
||||||
$(selector).each((...[, element]) => {
|
|
||||||
|
|
||||||
const href = $(element).attr(attrName);
|
|
||||||
|
|
||||||
if (href === undefined) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$(element).attr(
|
|
||||||
attrName,
|
|
||||||
urlOrigin !== undefined ?
|
|
||||||
href.replace(/^\//, `${urlOrigin}/`) :
|
|
||||||
href.replace(
|
|
||||||
new RegExp(`^${urlPathname.replace(/\//g, "\\/")}`),
|
|
||||||
"${url.resourcesPath}/build/"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
//FTL is no valid html, we can't insert with cheerio, we put placeholder for injecting later.
|
|
||||||
const ftlCommonPlaceholders = {
|
|
||||||
'{ "x": "vIdLqMeOed9sdLdIdOxdK0d" }': loadFtlFile("common.ftl"),
|
|
||||||
'<!-- xIdLqMeOedErIdLsPdNdI9dSlxI -->':
|
|
||||||
[
|
|
||||||
'<#if scripts??>',
|
|
||||||
' <#list scripts as script>',
|
|
||||||
' <script src="${script}" type="text/javascript"></script>',
|
|
||||||
' </#list>',
|
|
||||||
'</#if>'
|
|
||||||
].join("\n")
|
|
||||||
};
|
|
||||||
|
|
||||||
const pageSpecificCodePlaceholder = "<!-- dIddLqMeOedErIdLsPdNdI9dSl42sw -->";
|
|
||||||
|
|
||||||
$("head").prepend(
|
|
||||||
[
|
|
||||||
...(Object.keys(cssGlobalsToDefine).length === 0 ? [] : [
|
|
||||||
'',
|
|
||||||
'<style>',
|
|
||||||
generateCssCodeToDefineGlobals({
|
|
||||||
cssGlobalsToDefine,
|
|
||||||
urlPathname
|
|
||||||
}).cssCodeToPrependInHead,
|
|
||||||
'</style>',
|
|
||||||
''
|
|
||||||
]),
|
|
||||||
...["Object.deepAssign.js", "String.htmlUnescape.js"].map(
|
|
||||||
fileBasename => [
|
|
||||||
"<script>",
|
|
||||||
loadAdjacentFile(fileBasename),
|
|
||||||
"</script>"
|
|
||||||
].join("\n")
|
|
||||||
),
|
|
||||||
'<script>',
|
|
||||||
` window.${ftlValuesGlobalName}= Object.assign(`,
|
|
||||||
` {},`,
|
|
||||||
` ${objectKeys(ftlCommonPlaceholders)[0]}`,
|
|
||||||
' );',
|
|
||||||
'</script>',
|
|
||||||
'',
|
|
||||||
pageSpecificCodePlaceholder,
|
|
||||||
'',
|
|
||||||
objectKeys(ftlCommonPlaceholders)[1]
|
|
||||||
].join("\n"),
|
|
||||||
);
|
|
||||||
|
|
||||||
const partiallyFixedIndexHtmlCode = $.html();
|
|
||||||
|
|
||||||
function generateFtlFilesCode(
|
|
||||||
params: {
|
|
||||||
pageId: PageId;
|
|
||||||
}
|
|
||||||
): { ftlCode: string; } {
|
|
||||||
|
|
||||||
const { pageId } = params;
|
|
||||||
|
|
||||||
const $ = cheerio.load(partiallyFixedIndexHtmlCode);
|
|
||||||
|
|
||||||
const ftlPlaceholders = {
|
|
||||||
'{ "x": "kxOlLqMeOed9sdLdIdOxd444" }': loadFtlFile(pageId),
|
|
||||||
...ftlCommonPlaceholders
|
|
||||||
};
|
|
||||||
|
|
||||||
let ftlCode = $.html()
|
|
||||||
.replace(
|
|
||||||
pageSpecificCodePlaceholder,
|
|
||||||
[
|
|
||||||
'<script>',
|
|
||||||
` Object.deepAssign(`,
|
|
||||||
` window.${ftlValuesGlobalName},`,
|
|
||||||
` { "pageId": "${pageId}" }`,
|
|
||||||
' );',
|
|
||||||
` Object.deepAssign(`,
|
|
||||||
` window.${ftlValuesGlobalName},`,
|
|
||||||
` ${objectKeys(ftlPlaceholders)[0]}`,
|
|
||||||
' );',
|
|
||||||
'</script>'
|
|
||||||
].join("\n")
|
|
||||||
);
|
|
||||||
|
|
||||||
objectKeys(ftlPlaceholders)
|
|
||||||
.forEach(id => ftlCode = ftlCode.replace(id, ftlPlaceholders[id]));
|
|
||||||
|
|
||||||
return { ftlCode };
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return { generateFtlFilesCode };
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
@ -1,82 +0,0 @@
|
|||||||
<script>const _=
|
|
||||||
{
|
|
||||||
"messageHeader": (function (){
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
return "${messageHeader!''}" || undefined;
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
})(),
|
|
||||||
"requiredActions": (function (){
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
<#if requiredActions??>
|
|
||||||
|
|
||||||
var out =[];
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
<#list requiredActions>
|
|
||||||
<#attempt>
|
|
||||||
<#items as reqActionItem>
|
|
||||||
out.push((function (){
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
return "${reqActionItem}";
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
})());
|
|
||||||
</#items>
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
</#list>
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
return out;
|
|
||||||
|
|
||||||
</#if>
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
})(),
|
|
||||||
"skipLink": (function (){
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
<#if skipLink??>
|
|
||||||
return true;
|
|
||||||
</#if>
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
return false;
|
|
||||||
|
|
||||||
})(),
|
|
||||||
"pageRedirectUri": (function (){
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
return "${(pageRedirectUri!'')?no_esc}" || undefined;
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
})(),
|
|
||||||
"actionUri": (function (){
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
return "${(actionUri!'')?no_esc}" || undefined;
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
})(),
|
|
||||||
"client": {
|
|
||||||
"baseUrl": (function(){
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
return "${(client.baseUrl!'')?no_esc}" || undefined;
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
})()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
@ -1,37 +0,0 @@
|
|||||||
<script>const _=
|
|
||||||
{
|
|
||||||
"otpLogin": {
|
|
||||||
"userOtpCredentials": (function(){
|
|
||||||
|
|
||||||
var out = [];
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
<#list otpLogin.userOtpCredentials as otpCredential>
|
|
||||||
out.push({
|
|
||||||
"id": (function (){
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
return "${otpCredential.id}";
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
})(),
|
|
||||||
"userLabel": (function (){
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
return "${otpCredential.userLabel}";
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
})()
|
|
||||||
});
|
|
||||||
</#list>
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
return out;
|
|
||||||
|
|
||||||
})()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
@ -1,14 +0,0 @@
|
|||||||
<script>const _=
|
|
||||||
{
|
|
||||||
"realm": {
|
|
||||||
"loginWithEmailAllowed": (function (){
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
return ${realm.loginWithEmailAllowed?c};
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
})()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
@ -1,160 +0,0 @@
|
|||||||
<script>const _=
|
|
||||||
{
|
|
||||||
"url": {
|
|
||||||
"loginResetCredentialsUrl": (function (){
|
|
||||||
<#attempt>
|
|
||||||
return "${url.loginResetCredentialsUrl?no_esc}";
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
})(),
|
|
||||||
"registrationUrl": (function (){
|
|
||||||
<#attempt>
|
|
||||||
return "${url.registrationUrl?no_esc}";
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
})()
|
|
||||||
},
|
|
||||||
"realm": {
|
|
||||||
"loginWithEmailAllowed": (function(){
|
|
||||||
<#attempt>
|
|
||||||
return ${realm.loginWithEmailAllowed?c};
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
})(),
|
|
||||||
"rememberMe": (function (){
|
|
||||||
<#attempt>
|
|
||||||
return ${realm.rememberMe?c};
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
})(),
|
|
||||||
"password": (function (){
|
|
||||||
<#attempt>
|
|
||||||
return ${realm.password?c};
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
})(),
|
|
||||||
"resetPasswordAllowed": (function (){
|
|
||||||
<#attempt>
|
|
||||||
return ${realm.resetPasswordAllowed?c};
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
})(),
|
|
||||||
"registrationAllowed": (function (){
|
|
||||||
<#attempt>
|
|
||||||
return ${realm.registrationAllowed?c};
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
})()
|
|
||||||
},
|
|
||||||
"auth": (function (){
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
<#if auth?has_content>
|
|
||||||
|
|
||||||
return {
|
|
||||||
"selectedCredential": (function (){
|
|
||||||
<#attempt>
|
|
||||||
return "${auth.selectedCredential!''}" || undefined;
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
})()
|
|
||||||
};
|
|
||||||
|
|
||||||
</#if>
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
})(),
|
|
||||||
"social": {
|
|
||||||
"displayInfo": (function (){
|
|
||||||
<#attempt>
|
|
||||||
return ${social.displayInfo?c};
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
})(),
|
|
||||||
"providers": (()=>{
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
<#if social.providers??>
|
|
||||||
|
|
||||||
var out= [];
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
<#list social.providers as p>
|
|
||||||
out.push({
|
|
||||||
"loginUrl": (function (){
|
|
||||||
<#attempt>
|
|
||||||
return "${p.loginUrl?no_esc}";
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
})(),
|
|
||||||
"alias": (function (){
|
|
||||||
<#attempt>
|
|
||||||
return "${p.alias}";
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
})(),
|
|
||||||
"providerId": (function (){
|
|
||||||
<#attempt>
|
|
||||||
return "${p.providerId}";
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
})(),
|
|
||||||
"displayName": (function (){
|
|
||||||
<#attempt>
|
|
||||||
return "${p.displayName}";
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
})()
|
|
||||||
});
|
|
||||||
</#list>
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
return out;
|
|
||||||
|
|
||||||
</#if>
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
})()
|
|
||||||
},
|
|
||||||
"usernameEditDisabled": (function () {
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
<#if usernameEditDisabled??>
|
|
||||||
return true;
|
|
||||||
</#if>
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
return false;
|
|
||||||
|
|
||||||
})(),
|
|
||||||
"login": {
|
|
||||||
"username": (function (){
|
|
||||||
<#attempt>
|
|
||||||
return "${login.username!''}" || undefined;
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
})(),
|
|
||||||
"rememberMe": (function (){
|
|
||||||
<#attempt>
|
|
||||||
<#if login.rememberMe??>
|
|
||||||
return true;
|
|
||||||
</#if>
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
return false;
|
|
||||||
})()
|
|
||||||
},
|
|
||||||
"registrationDisabled": (function (){
|
|
||||||
<#attempt>
|
|
||||||
<#if registrationDisabled??>
|
|
||||||
return true;
|
|
||||||
</#if>
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
return false;
|
|
||||||
})()
|
|
||||||
}
|
|
||||||
</script>
|
|
@ -1,189 +0,0 @@
|
|||||||
<script>const _=
|
|
||||||
{
|
|
||||||
"url": {
|
|
||||||
"registrationAction": (function (){
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
return "${url.registrationAction?no_esc}";
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
})()
|
|
||||||
},
|
|
||||||
"messagesPerField": {
|
|
||||||
"printIfExists": function (key, x) {
|
|
||||||
switch(key){
|
|
||||||
case "userLabel": return (function (){
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
return "${messagesPerField.printIfExists('userLabel','1')}" ? x : undefined;
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
})();
|
|
||||||
case "username": return (function (){
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
return "${messagesPerField.printIfExists('username','1')}" ? x : undefined;
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
})();
|
|
||||||
case "email": return (function (){
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
return "${messagesPerField.printIfExists('email','1')}" ? x : undefined;
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
})();
|
|
||||||
case "firstName": return (function (){
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
return "${messagesPerField.printIfExists('firstName','1')}" ? x : undefined;
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
})();
|
|
||||||
case "lastName": return (function (){
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
return "${messagesPerField.printIfExists('lastName','1')}" ? x : undefined;
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
})();
|
|
||||||
case "password": return (function (){
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
return "${messagesPerField.printIfExists('password','1')}" ? x : undefined;
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
})();
|
|
||||||
case "password-confirm": return (function (){
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
return "${messagesPerField.printIfExists('password-confirm','1')}" ? x : undefined;
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
})();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"register": {
|
|
||||||
"formData": {
|
|
||||||
"firstName": (function (){
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
return "${register.formData.firstName!''}" || undefined;
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
})(),
|
|
||||||
"displayName": (function (){
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
return "${register.formData.displayName!''}" || undefined;
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
})(),
|
|
||||||
"lastName": (function (){
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
return "${register.formData.lastName!''}" || undefined;
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
})(),
|
|
||||||
"email": (function(){
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
return "${register.formData.email!''}" || undefined;
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
})(),
|
|
||||||
"username": (function (){
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
return "${register.formData.username!''}" || undefined;
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
})()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"passwordRequired": (function (){
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
<#if passwordRequired??>
|
|
||||||
return true;
|
|
||||||
</#if>
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
return false;
|
|
||||||
|
|
||||||
})(),
|
|
||||||
"recaptchaRequired": (function (){
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
<#if passwordRequired??>
|
|
||||||
return true;
|
|
||||||
</#if>
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
return false;
|
|
||||||
|
|
||||||
})(),
|
|
||||||
"recaptchaSiteKey": (function (){
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
return "${recaptchaSiteKey!''}" || undefined;
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
})(),
|
|
||||||
"authorizedMailDomains": (function (){
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
return "${authorizedMailDomains!''}" || undefined;
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
})(),
|
|
||||||
"authorizedMailDomains": (function(){
|
|
||||||
|
|
||||||
var out = undefined;
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
<#if authorizedMailDomains??>
|
|
||||||
|
|
||||||
out = [];
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
<#list authorizedMailDomains as authorizedMailDomain>
|
|
||||||
out.push((function (){
|
|
||||||
|
|
||||||
<#attempt>
|
|
||||||
return "${authorizedMailDomain}";
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
})());
|
|
||||||
</#list>
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
</#if>
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
|
|
||||||
return out;
|
|
||||||
|
|
||||||
})(),
|
|
||||||
}
|
|
||||||
</script>
|
|
@ -10,7 +10,7 @@ import { generateFtlFilesCodeFactory, pageIds } from "./generateFtl";
|
|||||||
import { builtinThemesUrl } from "../install-builtin-keycloak-themes";
|
import { builtinThemesUrl } from "../install-builtin-keycloak-themes";
|
||||||
import { downloadAndUnzip } from "../tools/downloadAndUnzip";
|
import { downloadAndUnzip } from "../tools/downloadAndUnzip";
|
||||||
import * as child_process from "child_process";
|
import * as child_process from "child_process";
|
||||||
import { resourcesCommonPath, resourcesPath, subDirOfPublicDirBasename } from "../../lib/kcContextMocks/urlResourcesPath";
|
import { resourcesCommonPath, resourcesPath, subDirOfPublicDirBasename } from "../../lib/getKcContext/kcContextMocks/urlResourcesPath";
|
||||||
import { isInside } from "../tools/isInside";
|
import { isInside } from "../tools/isInside";
|
||||||
|
|
||||||
|
|
||||||
@ -22,10 +22,15 @@ export function generateKeycloakThemeResources(
|
|||||||
urlPathname: string;
|
urlPathname: string;
|
||||||
//If urlOrigin is not undefined then it means --externals-assets
|
//If urlOrigin is not undefined then it means --externals-assets
|
||||||
urlOrigin: undefined | string;
|
urlOrigin: undefined | string;
|
||||||
|
extraPagesId: string[];
|
||||||
|
extraThemeProperties: string[];
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
|
|
||||||
const { themeName, reactAppBuildDirPath, keycloakThemeBuildingDirPath, urlPathname, urlOrigin } = params;
|
const {
|
||||||
|
themeName, reactAppBuildDirPath, keycloakThemeBuildingDirPath,
|
||||||
|
urlPathname, urlOrigin, extraPagesId, extraThemeProperties
|
||||||
|
} = params;
|
||||||
|
|
||||||
const themeDirPath = pathJoin(keycloakThemeBuildingDirPath, "src", "main", "resources", "theme", themeName, "login");
|
const themeDirPath = pathJoin(keycloakThemeBuildingDirPath, "src", "main", "resources", "theme", themeName, "login");
|
||||||
|
|
||||||
@ -92,7 +97,7 @@ export function generateKeycloakThemeResources(
|
|||||||
urlOrigin
|
urlOrigin
|
||||||
});
|
});
|
||||||
|
|
||||||
pageIds.forEach(pageId => {
|
[...pageIds, ...extraPagesId].forEach(pageId => {
|
||||||
|
|
||||||
const { ftlCode } = generateFtlFilesCode({ pageId });
|
const { ftlCode } = generateFtlFilesCode({ pageId });
|
||||||
|
|
||||||
@ -162,7 +167,10 @@ export function generateKeycloakThemeResources(
|
|||||||
|
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(
|
||||||
pathJoin(themeDirPath, "theme.properties"),
|
pathJoin(themeDirPath, "theme.properties"),
|
||||||
Buffer.from("parent=keycloak", "utf8")
|
Buffer.from(
|
||||||
|
"parent=keycloak".concat("\n\n", extraThemeProperties.join("\n\n")),
|
||||||
|
"utf8"
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,115 +1,10 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
import { generateKeycloakThemeResources } from "./generateKeycloakThemeResources";
|
export * from "./build-keycloak-theme";
|
||||||
import { generateJavaStackFiles } from "./generateJavaStackFiles";
|
import { main } from "./build-keycloak-theme";
|
||||||
import type { ParsedPackageJson } from "./generateJavaStackFiles";
|
|
||||||
import { join as pathJoin, relative as pathRelative, basename as pathBasename } from "path";
|
|
||||||
import * as child_process from "child_process";
|
|
||||||
import { generateDebugFiles, containerLaunchScriptBasename } from "./generateDebugFiles";
|
|
||||||
import { URL } from "url";
|
|
||||||
|
|
||||||
|
|
||||||
const reactProjectDirPath = process.cwd();
|
|
||||||
|
|
||||||
const doUseExternalAssets = process.argv[2]?.toLowerCase() === "--external-assets";
|
|
||||||
|
|
||||||
const parsedPackageJson: ParsedPackageJson = require(pathJoin(reactProjectDirPath, "package.json"));
|
|
||||||
|
|
||||||
export const keycloakThemeBuildingDirPath = pathJoin(reactProjectDirPath, "build_keycloak");
|
|
||||||
|
|
||||||
|
|
||||||
if (require.main === module) {
|
if (require.main === module) {
|
||||||
|
|
||||||
console.log("🔏 Building the keycloak theme...⌚");
|
main();
|
||||||
|
|
||||||
generateKeycloakThemeResources({
|
}
|
||||||
keycloakThemeBuildingDirPath,
|
|
||||||
"reactAppBuildDirPath": pathJoin(reactProjectDirPath, "build"),
|
|
||||||
"themeName": parsedPackageJson.name,
|
|
||||||
...(() => {
|
|
||||||
|
|
||||||
const url = (() => {
|
|
||||||
|
|
||||||
const { homepage } = parsedPackageJson;
|
|
||||||
|
|
||||||
return homepage === undefined ?
|
|
||||||
undefined :
|
|
||||||
new URL(homepage);
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
||||||
return {
|
|
||||||
"urlPathname":
|
|
||||||
url === undefined ?
|
|
||||||
"/" :
|
|
||||||
url.pathname.replace(/([^/])$/, "$1/"),
|
|
||||||
"urlOrigin": !doUseExternalAssets ? undefined : (() => {
|
|
||||||
|
|
||||||
if (url === undefined) {
|
|
||||||
console.error("ERROR: You must specify 'homepage' in your package.json");
|
|
||||||
process.exit(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return url.origin;
|
|
||||||
|
|
||||||
})()
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
})()
|
|
||||||
});
|
|
||||||
|
|
||||||
const { jarFilePath } = generateJavaStackFiles({
|
|
||||||
parsedPackageJson,
|
|
||||||
keycloakThemeBuildingDirPath
|
|
||||||
});
|
|
||||||
|
|
||||||
child_process.execSync(
|
|
||||||
"mvn package",
|
|
||||||
{ "cwd": keycloakThemeBuildingDirPath }
|
|
||||||
);
|
|
||||||
|
|
||||||
generateDebugFiles({
|
|
||||||
keycloakThemeBuildingDirPath,
|
|
||||||
"packageJsonName": parsedPackageJson.name
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log([
|
|
||||||
'',
|
|
||||||
`✅ Your keycloak theme has been generated and bundled into ./${pathRelative(reactProjectDirPath, jarFilePath)} 🚀`,
|
|
||||||
`It is to be placed in "/opt/jboss/keycloak/standalone/deployments" in the container running a jboss/keycloak Docker image. (Tested with 11.0.3)`,
|
|
||||||
'',
|
|
||||||
'Using Helm (https://github.com/codecentric/helm-charts), edit to reflect:',
|
|
||||||
'',
|
|
||||||
'value.yaml: ',
|
|
||||||
' extraInitContainers: |',
|
|
||||||
' - name: realm-ext-provider',
|
|
||||||
' image: curlimages/curl',
|
|
||||||
' imagePullPolicy: IfNotPresent',
|
|
||||||
' command:',
|
|
||||||
' - sh',
|
|
||||||
' args:',
|
|
||||||
' - -c',
|
|
||||||
` - curl -L -f -S -o /extensions/${pathBasename(jarFilePath)} https://AN.URL.FOR/${pathBasename(jarFilePath)}`,
|
|
||||||
' volumeMounts:',
|
|
||||||
' - name: extensions',
|
|
||||||
' mountPath: /extensions',
|
|
||||||
' ',
|
|
||||||
' extraVolumeMounts: |',
|
|
||||||
' - name: extensions',
|
|
||||||
' mountPath: /opt/jboss/keycloak/standalone/deployments',
|
|
||||||
'',
|
|
||||||
'',
|
|
||||||
'To test your theme locally, with hot reloading, you can spin up a Keycloak container image with the theme loaded by running:',
|
|
||||||
'',
|
|
||||||
`👉 $ ./${pathRelative(reactProjectDirPath, pathJoin(keycloakThemeBuildingDirPath, containerLaunchScriptBasename))} 👈`,
|
|
||||||
'',
|
|
||||||
'To enable the theme within keycloak log into the admin console ( 👉 http://localhost:8080 username: admin, password: admin 👈), create a realm (called "myrealm" for example),',
|
|
||||||
`go to your realm settings, click on the theme tab then select ${parsedPackageJson.name}.`,
|
|
||||||
`More details: https://www.keycloak.org/getting-started/getting-started-docker`,
|
|
||||||
'',
|
|
||||||
'Once your container is up and configured 👉 http://localhost:8080/auth/realms/myrealm/account 👈',
|
|
||||||
'',
|
|
||||||
].join("\n"));
|
|
||||||
|
|
||||||
}
|
|
@ -5,19 +5,40 @@ import { ftlValuesGlobalName } from "./ftlValuesGlobalName";
|
|||||||
export function replaceImportsFromStaticInJsCode(
|
export function replaceImportsFromStaticInJsCode(
|
||||||
params: {
|
params: {
|
||||||
jsCode: string;
|
jsCode: string;
|
||||||
urlOrigin: undefined | string;
|
urlOrigin: undefined | string;
|
||||||
}
|
}
|
||||||
): { fixedJsCode: string; } {
|
): { fixedJsCode: string; } {
|
||||||
|
|
||||||
|
/*
|
||||||
|
NOTE:
|
||||||
|
|
||||||
|
When we have urlOrigin defined it means that
|
||||||
|
we are building with --external-assets
|
||||||
|
so we have to make sur that the fixed js code will run
|
||||||
|
inside and outside keycloak.
|
||||||
|
|
||||||
|
When urlOrigin isn't defined we can assume the fixedJsCode
|
||||||
|
will always run in keycloak context.
|
||||||
|
*/
|
||||||
|
|
||||||
const { jsCode, urlOrigin } = params;
|
const { jsCode, urlOrigin } = params;
|
||||||
|
|
||||||
const fixedJsCode = jsCode.replace(
|
const fixedJsCode =
|
||||||
/([a-z]+\.[a-z]+)\+"static\//g,
|
jsCode
|
||||||
(...[, group]) =>
|
.replace(
|
||||||
urlOrigin === undefined ?
|
/([a-z]+\.[a-z]+)\+"static\//g,
|
||||||
`window.${ftlValuesGlobalName}.url.resourcesPath + "/build/static/` :
|
(...[, group]) =>
|
||||||
`("${ftlValuesGlobalName}" in window ? "${urlOrigin}" : "") + ${group} + "static/`
|
urlOrigin === undefined ?
|
||||||
);
|
`window.${ftlValuesGlobalName}.url.resourcesPath + "/build/static/` :
|
||||||
|
`("${ftlValuesGlobalName}" in window ? "${urlOrigin}" : "") + ${group} + "static/`
|
||||||
|
)
|
||||||
|
.replace(
|
||||||
|
/".chunk.css",([a-z])+=([a-z]+\.[a-z]+)\+([a-z]+),/,
|
||||||
|
(...[, group1, group2, group3]) =>
|
||||||
|
urlOrigin === undefined ?
|
||||||
|
`".chunk.css",${group1} = window.${ftlValuesGlobalName}.url.resourcesPath + "/build/" + ${group3},` :
|
||||||
|
`".chunk.css",${group1} = ("${ftlValuesGlobalName}" in window ? "${urlOrigin}" : "") + ${group2} + ${group3},`
|
||||||
|
);
|
||||||
|
|
||||||
return { fixedJsCode };
|
return { fixedJsCode };
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ import { downloadAndUnzip } from "./tools/downloadAndUnzip";
|
|||||||
import { join as pathJoin } from "path";
|
import { join as pathJoin } from "path";
|
||||||
|
|
||||||
export const builtinThemesUrl =
|
export const builtinThemesUrl =
|
||||||
"https://github.com/garronej/keycloakify/releases/download/v0.0.1/keycloak_11.0.3_builtin_themes_with_light_mods.zip";
|
"https://github.com/garronej/keycloakify/releases/download/v0.0.1/keycloak_11.0.3_builtin_themes.zip";
|
||||||
|
|
||||||
if (require.main === module) {
|
if (require.main === module) {
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
import * as path from "path";
|
import * as path from "path";
|
||||||
import { crawl } from "./crawl";
|
import { crawl } from "./crawl";
|
||||||
import { id } from "evt/tools/typeSafety/id";
|
import { id } from "tsafe/id";
|
||||||
|
|
||||||
type TransformSourceCode =
|
type TransformSourceCode =
|
||||||
(params: {
|
(params: {
|
||||||
|
@ -1,22 +1,19 @@
|
|||||||
|
|
||||||
import { memo } from "react";
|
import { memo } from "react";
|
||||||
import { Template } from "./Template";
|
import { Template } from "./Template";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import { assert } from "../tools/assert";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import type { KcContext } from "../KcContext";
|
|
||||||
import { useKcMessage } from "../i18n/useKcMessage";
|
import { useKcMessage } from "../i18n/useKcMessage";
|
||||||
|
|
||||||
export const Error = memo(({ kcContext, ...props }: { kcContext: KcContext.Error; } & KcProps) => {
|
export const Error = memo(({ kcContext, ...props }: { kcContext: KcContextBase.Error; } & KcProps) => {
|
||||||
|
|
||||||
const { msg } = useKcMessage();
|
const { msg } = useKcMessage();
|
||||||
|
|
||||||
assert(kcContext.message !== undefined);
|
|
||||||
|
|
||||||
const { message, client } = kcContext;
|
const { message, client } = kcContext;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Template
|
<Template
|
||||||
{...{ kcContext, ...props }}
|
{...{ kcContext, ...props }}
|
||||||
|
doFetchDefaultThemeResources={true}
|
||||||
displayMessage={false}
|
displayMessage={false}
|
||||||
headerNode={msg("errorTitle")}
|
headerNode={msg("errorTitle")}
|
||||||
formNode={
|
formNode={
|
||||||
|
@ -3,10 +3,10 @@ import { memo } from "react";
|
|||||||
import { Template } from "./Template";
|
import { Template } from "./Template";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import { assert } from "../tools/assert";
|
import { assert } from "../tools/assert";
|
||||||
import type { KcContext } from "../KcContext";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import { useKcMessage } from "../i18n/useKcMessage";
|
import { useKcMessage } from "../i18n/useKcMessage";
|
||||||
|
|
||||||
export const Info = memo(({ kcContext, ...props }: { kcContext: KcContext.Info; } & KcProps) => {
|
export const Info = memo(({ kcContext, ...props }: { kcContext: KcContextBase.Info; } & KcProps) => {
|
||||||
|
|
||||||
const { msg } = useKcMessage();
|
const { msg } = useKcMessage();
|
||||||
|
|
||||||
@ -25,6 +25,7 @@ export const Info = memo(({ kcContext, ...props }: { kcContext: KcContext.Info;
|
|||||||
return (
|
return (
|
||||||
<Template
|
<Template
|
||||||
{...{ kcContext, ...props }}
|
{...{ kcContext, ...props }}
|
||||||
|
doFetchDefaultThemeResources={true}
|
||||||
displayMessage={false}
|
displayMessage={false}
|
||||||
headerNode={
|
headerNode={
|
||||||
messageHeader !== undefined ?
|
messageHeader !== undefined ?
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
|
|
||||||
import { memo } from "react";
|
import { memo } from "react";
|
||||||
import type { KcContext } from "../KcContext";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import { Login } from "./Login";
|
import { Login } from "./Login";
|
||||||
import { Register } from "./Register";
|
import { Register } from "./Register";
|
||||||
@ -9,9 +9,11 @@ import { Error } from "./Error";
|
|||||||
import { LoginResetPassword } from "./LoginResetPassword";
|
import { LoginResetPassword } from "./LoginResetPassword";
|
||||||
import { LoginVerifyEmail } from "./LoginVerifyEmail";
|
import { LoginVerifyEmail } from "./LoginVerifyEmail";
|
||||||
import { Terms } from "./Terms";
|
import { Terms } from "./Terms";
|
||||||
import { LoginOtp } from "./LoginOtp";
|
import { LoginOtp } from "./LoginOtp";
|
||||||
|
import { LoginUpdateProfile } from "./LoginUpdateProfile";
|
||||||
|
import { LoginIdpLinkConfirm } from "./LoginIdpLinkConfirm";
|
||||||
|
|
||||||
export const KcApp = memo(({ kcContext, ...props }: { kcContext: KcContext; } & KcProps ) => {
|
export const KcApp = memo(({ kcContext, ...props }: { kcContext: KcContextBase; } & KcProps) => {
|
||||||
switch (kcContext.pageId) {
|
switch (kcContext.pageId) {
|
||||||
case "login.ftl": return <Login {...{ kcContext, ...props }} />;
|
case "login.ftl": return <Login {...{ kcContext, ...props }} />;
|
||||||
case "register.ftl": return <Register {...{ kcContext, ...props }} />;
|
case "register.ftl": return <Register {...{ kcContext, ...props }} />;
|
||||||
@ -19,7 +21,9 @@ export const KcApp = memo(({ kcContext, ...props }: { kcContext: KcContext; } &
|
|||||||
case "error.ftl": return <Error {...{ kcContext, ...props }} />;
|
case "error.ftl": return <Error {...{ kcContext, ...props }} />;
|
||||||
case "login-reset-password.ftl": return <LoginResetPassword {...{ kcContext, ...props }} />;
|
case "login-reset-password.ftl": return <LoginResetPassword {...{ kcContext, ...props }} />;
|
||||||
case "login-verify-email.ftl": return <LoginVerifyEmail {...{ kcContext, ...props }} />;
|
case "login-verify-email.ftl": return <LoginVerifyEmail {...{ kcContext, ...props }} />;
|
||||||
case "terms.ftl": return <Terms {...{ kcContext, ...props }}/>;
|
case "terms.ftl": return <Terms {...{ kcContext, ...props }} />;
|
||||||
case "login-otp.ftl": return <LoginOtp {...{ kcContext, ...props }}/>;
|
case "login-otp.ftl": return <LoginOtp {...{ kcContext, ...props }} />;
|
||||||
|
case "login-update-profile.ftl": return <LoginUpdateProfile {...{ kcContext, ...props }} />;
|
||||||
|
case "login-idp-link-confirm.ftl": return <LoginIdpLinkConfirm {...{ kcContext, ...props }} />;
|
||||||
}
|
}
|
||||||
});
|
});
|
@ -1,6 +1,6 @@
|
|||||||
|
|
||||||
import { allPropertiesValuesToUndefined } from "../tools/allPropertiesValuesToUndefined";
|
import { allPropertiesValuesToUndefined } from "../tools/allPropertiesValuesToUndefined";
|
||||||
import { doExtends } from "evt/tools/typeSafety/doExtends";
|
import { doExtends } from "tsafe/doExtends";
|
||||||
|
|
||||||
/** Class names can be provided as an array or separated by whitespace */
|
/** Class names can be provided as an array or separated by whitespace */
|
||||||
export type KcPropsGeneric<CssClasses extends string> = { [key in CssClasses]: readonly string[] | string | undefined; };
|
export type KcPropsGeneric<CssClasses extends string> = { [key in CssClasses]: readonly string[] | string | undefined; };
|
||||||
|
@ -2,12 +2,12 @@
|
|||||||
import { useState, memo } from "react";
|
import { useState, memo } from "react";
|
||||||
import { Template } from "./Template";
|
import { Template } from "./Template";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import type { KcContext } from "../KcContext";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import { useKcMessage } from "../i18n/useKcMessage";
|
import { useKcMessage } from "../i18n/useKcMessage";
|
||||||
import { cx } from "tss-react";
|
import { cx } from "tss-react";
|
||||||
import { useConstCallback } from "powerhooks";
|
import { useConstCallback } from "powerhooks";
|
||||||
|
|
||||||
export const Login = memo(({ kcContext, ...props }: { kcContext: KcContext.Login; } & KcProps) => {
|
export const Login = memo(({ kcContext, ...props }: { kcContext: KcContextBase.Login; } & KcProps) => {
|
||||||
|
|
||||||
const { msg, msgStr } = useKcMessage();
|
const { msg, msgStr } = useKcMessage();
|
||||||
|
|
||||||
@ -26,6 +26,7 @@ export const Login = memo(({ kcContext, ...props }: { kcContext: KcContext.Login
|
|||||||
return (
|
return (
|
||||||
<Template
|
<Template
|
||||||
{...{ kcContext, ...props }}
|
{...{ kcContext, ...props }}
|
||||||
|
doFetchDefaultThemeResources={true}
|
||||||
displayInfo={social.displayInfo}
|
displayInfo={social.displayInfo}
|
||||||
displayWide={realm.password && social.providers !== undefined}
|
displayWide={realm.password && social.providers !== undefined}
|
||||||
headerNode={msg("doLogIn")}
|
headerNode={msg("doLogIn")}
|
||||||
@ -120,7 +121,7 @@ export const Login = memo(({ kcContext, ...props }: { kcContext: KcContext.Login
|
|||||||
<ul className={cx(props.kcFormSocialAccountListClass, social.providers.length > 4 && props.kcFormSocialAccountDoubleListClass)}>
|
<ul className={cx(props.kcFormSocialAccountListClass, social.providers.length > 4 && props.kcFormSocialAccountDoubleListClass)}>
|
||||||
{
|
{
|
||||||
social.providers.map(p =>
|
social.providers.map(p =>
|
||||||
<li className={cx(props.kcFormSocialAccountListLinkClass)}>
|
<li key={p.providerId} className={cx(props.kcFormSocialAccountListLinkClass)}>
|
||||||
<a href={p.loginUrl} id={`zocial-${p.alias}`} className={cx("zocial", p.providerId)}>
|
<a href={p.loginUrl} id={`zocial-${p.alias}`} className={cx("zocial", p.providerId)}>
|
||||||
<span>{p.displayName}</span>
|
<span>{p.displayName}</span>
|
||||||
</a>
|
</a>
|
||||||
|
59
src/lib/components/LoginIdpLinkConfirm.tsx
Normal file
59
src/lib/components/LoginIdpLinkConfirm.tsx
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
|
||||||
|
import { memo } from "react";
|
||||||
|
import { Template } from "./Template";
|
||||||
|
import type { KcProps } from "./KcProps";
|
||||||
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
|
import { useKcMessage } from "../i18n/useKcMessage";
|
||||||
|
import { cx } from "tss-react";
|
||||||
|
|
||||||
|
export const LoginIdpLinkConfirm = memo(({ kcContext, ...props }: { kcContext: KcContextBase.LoginIdpLinkConfirm; } & KcProps) => {
|
||||||
|
|
||||||
|
const { msg } = useKcMessage();
|
||||||
|
|
||||||
|
const { url, idpAlias } = kcContext;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Template
|
||||||
|
{...{ kcContext, ...props }}
|
||||||
|
doFetchDefaultThemeResources={true}
|
||||||
|
headerNode={msg("confirmLinkIdpTitle")}
|
||||||
|
formNode={
|
||||||
|
<form id="kc-register-form" action={url.loginAction} method="post">
|
||||||
|
<div className={cx(props.kcFormGroupClass)}>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className={cx(
|
||||||
|
props.kcButtonClass,
|
||||||
|
props.kcButtonDefaultClass,
|
||||||
|
props.kcButtonBlockClass,
|
||||||
|
props.kcButtonLargeClass
|
||||||
|
)}
|
||||||
|
name="submitAction"
|
||||||
|
id="updateProfile"
|
||||||
|
value="updateProfile"
|
||||||
|
>
|
||||||
|
{msg("confirmLinkIdpReviewProfile")}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className={cx(
|
||||||
|
props.kcButtonClass,
|
||||||
|
props.kcButtonDefaultClass,
|
||||||
|
props.kcButtonBlockClass,
|
||||||
|
props.kcButtonLargeClass
|
||||||
|
)}
|
||||||
|
name="submitAction"
|
||||||
|
id="linkAccount"
|
||||||
|
value="linkAccount"
|
||||||
|
>
|
||||||
|
{msg("confirmLinkIdpContinue", idpAlias)}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
@ -3,13 +3,13 @@
|
|||||||
import { useEffect, memo } from "react";
|
import { useEffect, memo } from "react";
|
||||||
import { Template } from "./Template";
|
import { Template } from "./Template";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import type { KcContext } from "../KcContext";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import { useKcMessage } from "../i18n/useKcMessage";
|
import { useKcMessage } from "../i18n/useKcMessage";
|
||||||
import { appendHead } from "../tools/appendHead";
|
import { appendHead } from "../tools/appendHead";
|
||||||
import { join as pathJoin } from "path";
|
import { join as pathJoin } from "path";
|
||||||
import { cx } from "tss-react";
|
import { cx } from "tss-react";
|
||||||
|
|
||||||
export const LoginOtp = memo(({ kcContext, ...props }: { kcContext: KcContext.LoginOtp; } & KcProps) => {
|
export const LoginOtp = memo(({ kcContext, ...props }: { kcContext: KcContextBase.LoginOtp; } & KcProps) => {
|
||||||
|
|
||||||
const { otpLogin, url } = kcContext;
|
const { otpLogin, url } = kcContext;
|
||||||
|
|
||||||
@ -43,6 +43,7 @@ export const LoginOtp = memo(({ kcContext, ...props }: { kcContext: KcContext.Lo
|
|||||||
return (
|
return (
|
||||||
<Template
|
<Template
|
||||||
{...{ kcContext, ...props }}
|
{...{ kcContext, ...props }}
|
||||||
|
doFetchDefaultThemeResources={true}
|
||||||
headerNode={msg("doLogIn")}
|
headerNode={msg("doLogIn")}
|
||||||
formNode={
|
formNode={
|
||||||
|
|
||||||
|
@ -2,11 +2,11 @@
|
|||||||
import { memo } from "react";
|
import { memo } from "react";
|
||||||
import { Template } from "./Template";
|
import { Template } from "./Template";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import type { KcContext } from "../KcContext";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import { useKcMessage } from "../i18n/useKcMessage";
|
import { useKcMessage } from "../i18n/useKcMessage";
|
||||||
import { cx } from "tss-react";
|
import { cx } from "tss-react";
|
||||||
|
|
||||||
export const LoginResetPassword = memo(({ kcContext, ...props }: { kcContext: KcContext.LoginResetPassword; } & KcProps) => {
|
export const LoginResetPassword = memo(({ kcContext, ...props }: { kcContext: KcContextBase.LoginResetPassword; } & KcProps) => {
|
||||||
|
|
||||||
const { msg, msgStr } = useKcMessage();
|
const { msg, msgStr } = useKcMessage();
|
||||||
|
|
||||||
@ -19,6 +19,7 @@ export const LoginResetPassword = memo(({ kcContext, ...props }: { kcContext: Kc
|
|||||||
return (
|
return (
|
||||||
<Template
|
<Template
|
||||||
{...{ kcContext, ...props }}
|
{...{ kcContext, ...props }}
|
||||||
|
doFetchDefaultThemeResources={true}
|
||||||
displayMessage={false}
|
displayMessage={false}
|
||||||
headerNode={msg("emailForgotTitle")}
|
headerNode={msg("emailForgotTitle")}
|
||||||
formNode={
|
formNode={
|
||||||
|
131
src/lib/components/LoginUpdateProfile.tsx
Normal file
131
src/lib/components/LoginUpdateProfile.tsx
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
import { memo } from "react";
|
||||||
|
import { Template } from "./Template";
|
||||||
|
import type { KcProps } from "./KcProps";
|
||||||
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
|
import { useKcMessage } from "../i18n/useKcMessage";
|
||||||
|
import { cx } from "tss-react";
|
||||||
|
|
||||||
|
export const LoginUpdateProfile = memo(({ kcContext, ...props }: { kcContext: KcContextBase.LoginUpdateProfile; } & KcProps) => {
|
||||||
|
|
||||||
|
const { msg, msgStr } = useKcMessage();
|
||||||
|
|
||||||
|
const { url, user, messagesPerField, isAppInitiatedAction } = kcContext;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Template
|
||||||
|
{...{ kcContext, ...props }}
|
||||||
|
doFetchDefaultThemeResources={true}
|
||||||
|
headerNode={msg("loginProfileTitle")}
|
||||||
|
formNode={
|
||||||
|
<form id="kc-update-profile-form" className={cx(props.kcFormClass)} action={url.loginAction} method="post">
|
||||||
|
{user.editUsernameAllowed &&
|
||||||
|
<div className={cx(props.kcFormGroupClass, messagesPerField.printIfExists("username", props.kcFormGroupErrorClass))}>
|
||||||
|
<div className={cx(props.kcLabelWrapperClass)}>
|
||||||
|
<label htmlFor="username" className={cx(props.kcLabelClass)}>
|
||||||
|
{msg("username")}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div className={cx(props.kcInputWrapperClass)}>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="username"
|
||||||
|
name="username"
|
||||||
|
defaultValue={user.username ?? ""}
|
||||||
|
className={cx(props.kcInputClass)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
<div className={cx(props.kcFormGroupClass, messagesPerField.printIfExists("email", props.kcFormGroupErrorClass))}>
|
||||||
|
<div className={cx(props.kcLabelWrapperClass)}>
|
||||||
|
<label htmlFor="email" className={cx(props.kcLabelClass)}>
|
||||||
|
{msg("email")}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div className={cx(props.kcInputWrapperClass)}>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="email"
|
||||||
|
name="email"
|
||||||
|
defaultValue={user.email ?? ""}
|
||||||
|
className={cx(props.kcInputClass)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={cx(props.kcFormGroupClass, messagesPerField.printIfExists("firstName", props.kcFormGroupErrorClass))}>
|
||||||
|
<div className={cx(props.kcLabelWrapperClass)}>
|
||||||
|
<label htmlFor="firstName" className={cx(props.kcLabelClass)}>
|
||||||
|
{msg("firstName")}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div className={cx(props.kcInputWrapperClass)}>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="firstName"
|
||||||
|
name="firstName"
|
||||||
|
defaultValue={user.firstName ?? ""}
|
||||||
|
className={cx(props.kcInputClass)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={cx(props.kcFormGroupClass, messagesPerField.printIfExists("lastName", props.kcFormGroupErrorClass))}>
|
||||||
|
<div className={cx(props.kcLabelWrapperClass)}>
|
||||||
|
<label htmlFor="lastName" className={cx(props.kcLabelClass)}>
|
||||||
|
{msg("lastName")}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div className={cx(props.kcInputWrapperClass)}>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="lastName"
|
||||||
|
name="lastName"
|
||||||
|
defaultValue={user.lastName ?? ""}
|
||||||
|
className={cx(props.kcInputClass)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={cx(props.kcFormGroupClass)}>
|
||||||
|
<div id="kc-form-options" className={cx(props.kcFormOptionsClass)}>
|
||||||
|
<div className={cx(props.kcFormOptionsWrapperClass)} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="kc-form-buttons" className={cx(props.kcFormButtonsClass)}>
|
||||||
|
{
|
||||||
|
isAppInitiatedAction ?
|
||||||
|
<>
|
||||||
|
<input
|
||||||
|
className={cx(props.kcButtonClass, props.kcButtonPrimaryClass, props.kcButtonLargeClass)}
|
||||||
|
type="submit"
|
||||||
|
defaultValue={msgStr("doSubmit")}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
className={cx(props.kcButtonClass, props.kcButtonDefaultClass, props.kcButtonLargeClass)}
|
||||||
|
type="submit"
|
||||||
|
name="cancel-aia"
|
||||||
|
value="true"
|
||||||
|
>
|
||||||
|
{msg("doCancel")}
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
:
|
||||||
|
<input
|
||||||
|
className={cx(props.kcButtonClass, props.kcButtonPrimaryClass, props.kcButtonBlockClass, props.kcButtonLargeClass)}
|
||||||
|
type="submit"
|
||||||
|
defaultValue={msgStr("doSubmit")}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
@ -2,10 +2,10 @@
|
|||||||
import { memo } from "react";
|
import { memo } from "react";
|
||||||
import { Template } from "./Template";
|
import { Template } from "./Template";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import type { KcContext } from "../KcContext";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import { useKcMessage } from "../i18n/useKcMessage";
|
import { useKcMessage } from "../i18n/useKcMessage";
|
||||||
|
|
||||||
export const LoginVerifyEmail = memo(({ kcContext, ...props }: { kcContext: KcContext.LoginVerifyEmail; } & KcProps) => {
|
export const LoginVerifyEmail = memo(({ kcContext, ...props }: { kcContext: KcContextBase.LoginVerifyEmail; } & KcProps) => {
|
||||||
|
|
||||||
const { msg } = useKcMessage();
|
const { msg } = useKcMessage();
|
||||||
|
|
||||||
@ -16,6 +16,7 @@ export const LoginVerifyEmail = memo(({ kcContext, ...props }: { kcContext: KcCo
|
|||||||
return (
|
return (
|
||||||
<Template
|
<Template
|
||||||
{...{ kcContext, ...props }}
|
{...{ kcContext, ...props }}
|
||||||
|
doFetchDefaultThemeResources={true}
|
||||||
displayMessage={false}
|
displayMessage={false}
|
||||||
headerNode={msg("emailVerifyTitle")}
|
headerNode={msg("emailVerifyTitle")}
|
||||||
formNode={
|
formNode={
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { memo } from "react";
|
import { memo } from "react";
|
||||||
import { Template } from "./Template";
|
import { Template } from "./Template";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import type { KcContext } from "../KcContext";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import { useKcMessage } from "../i18n/useKcMessage";
|
import { useKcMessage } from "../i18n/useKcMessage";
|
||||||
import { cx } from "tss-react";
|
import { cx } from "tss-react";
|
||||||
|
|
||||||
export const Register = memo(({ kcContext, ...props }: { kcContext: KcContext.Register; } & KcProps) => {
|
export const Register = memo(({ kcContext, ...props }: { kcContext: KcContextBase.Register; } & KcProps) => {
|
||||||
|
|
||||||
const { msg, msgStr } = useKcMessage();
|
const { msg, msgStr } = useKcMessage();
|
||||||
|
|
||||||
@ -22,11 +22,12 @@ export const Register = memo(({ kcContext, ...props }: { kcContext: KcContext.Re
|
|||||||
return (
|
return (
|
||||||
<Template
|
<Template
|
||||||
{...{ kcContext, ...props }}
|
{...{ kcContext, ...props }}
|
||||||
|
doFetchDefaultThemeResources={true}
|
||||||
headerNode={msg("registerTitle")}
|
headerNode={msg("registerTitle")}
|
||||||
formNode={
|
formNode={
|
||||||
<form id="kc-register-form" className={cx(props.kcFormClass)} action={url.registrationAction} method="post">
|
<form id="kc-register-form" className={cx(props.kcFormClass)} action={url.registrationAction} method="post">
|
||||||
|
|
||||||
<div className={cx(props.kcFormGroupClass, messagesPerField.printIfExists('firstName', props.kcFormGroupErrorClass))}>
|
<div className={cx(props.kcFormGroupClass, messagesPerField.printIfExists("firstName", props.kcFormGroupErrorClass))}>
|
||||||
<div className={cx(props.kcLabelWrapperClass)}>
|
<div className={cx(props.kcLabelWrapperClass)}>
|
||||||
<label htmlFor="firstName" className={cx(props.kcLabelClass)}>{msg("firstName")}</label>
|
<label htmlFor="firstName" className={cx(props.kcLabelClass)}>{msg("firstName")}</label>
|
||||||
</div>
|
</div>
|
||||||
|
@ -3,7 +3,7 @@ import { useReducer, useEffect, memo } from "react";
|
|||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react";
|
||||||
import { useKcMessage } from "../i18n/useKcMessage";
|
import { useKcMessage } from "../i18n/useKcMessage";
|
||||||
import { useKcLanguageTag } from "../i18n/useKcLanguageTag";
|
import { useKcLanguageTag } from "../i18n/useKcLanguageTag";
|
||||||
import type { KcContext } from "../KcContext";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import { assert } from "../tools/assert";
|
import { assert } from "../tools/assert";
|
||||||
import { cx } from "tss-react";
|
import { cx } from "tss-react";
|
||||||
import type { KcLanguageTag } from "../i18n/KcLanguageTag";
|
import type { KcLanguageTag } from "../i18n/KcLanguageTag";
|
||||||
@ -25,7 +25,11 @@ export type TemplateProps = {
|
|||||||
showUsernameNode?: ReactNode;
|
showUsernameNode?: ReactNode;
|
||||||
formNode: ReactNode;
|
formNode: ReactNode;
|
||||||
infoNode?: ReactNode;
|
infoNode?: ReactNode;
|
||||||
} & { kcContext: KcContext; } & KcTemplateProps;
|
/** If you write your own page you probably want
|
||||||
|
* to avoid pulling the default theme assets.
|
||||||
|
*/
|
||||||
|
doFetchDefaultThemeResources: boolean;
|
||||||
|
} & { kcContext: KcContextBase; } & KcTemplateProps;
|
||||||
|
|
||||||
export const Template = memo((props: TemplateProps) => {
|
export const Template = memo((props: TemplateProps) => {
|
||||||
|
|
||||||
@ -39,7 +43,8 @@ export const Template = memo((props: TemplateProps) => {
|
|||||||
showUsernameNode = null,
|
showUsernameNode = null,
|
||||||
formNode,
|
formNode,
|
||||||
infoNode = null,
|
infoNode = null,
|
||||||
kcContext
|
kcContext,
|
||||||
|
doFetchDefaultThemeResources
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
useEffect(() => { console.log("Rendering this page with react using keycloakify") }, []);
|
useEffect(() => { console.log("Rendering this page with react using keycloakify") }, []);
|
||||||
@ -84,6 +89,11 @@ export const Template = memo((props: TemplateProps) => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
||||||
|
if (!doFetchDefaultThemeResources) {
|
||||||
|
setExtraCssLoaded();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let isUnmounted = false;
|
let isUnmounted = false;
|
||||||
const cleanups: (() => void)[] = [];
|
const cleanups: (() => void)[] = [];
|
||||||
|
|
||||||
@ -269,7 +279,10 @@ export const Template = memo((props: TemplateProps) => {
|
|||||||
{message.type === "warning" && <span className={cx(props.kcFeedbackWarningIcon)}></span>}
|
{message.type === "warning" && <span className={cx(props.kcFeedbackWarningIcon)}></span>}
|
||||||
{message.type === "error" && <span className={cx(props.kcFeedbackErrorIcon)}></span>}
|
{message.type === "error" && <span className={cx(props.kcFeedbackErrorIcon)}></span>}
|
||||||
{message.type === "info" && <span className={cx(props.kcFeedbackInfoIcon)}></span>}
|
{message.type === "info" && <span className={cx(props.kcFeedbackInfoIcon)}></span>}
|
||||||
<span className="kc-feedback-text">{message.summary}</span>
|
<span
|
||||||
|
className="kc-feedback-text"
|
||||||
|
dangerouslySetInnerHTML={{ "__html": message.summary }}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
{formNode}
|
{formNode}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { memo } from "react";
|
import { memo } from "react";
|
||||||
import { Template } from "./Template";
|
import { Template } from "./Template";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import type { KcContext } from "../KcContext";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import { useKcMessage } from "../i18n/useKcMessage";
|
import { useKcMessage } from "../i18n/useKcMessage";
|
||||||
import { cx } from "tss-react";
|
import { cx } from "tss-react";
|
||||||
|
|
||||||
export const Terms = memo(({ kcContext, ...props }: { kcContext: KcContext.Terms; } & KcProps) => {
|
export const Terms = memo(({ kcContext, ...props }: { kcContext: KcContextBase.Terms; } & KcProps) => {
|
||||||
|
|
||||||
const { msg, msgStr } = useKcMessage();
|
const { msg, msgStr } = useKcMessage();
|
||||||
|
|
||||||
@ -14,6 +14,7 @@ export const Terms = memo(({ kcContext, ...props }: { kcContext: KcContext.Terms
|
|||||||
return (
|
return (
|
||||||
<Template
|
<Template
|
||||||
{...{ kcContext, ...props }}
|
{...{ kcContext, ...props }}
|
||||||
|
doFetchDefaultThemeResources={true}
|
||||||
displayMessage={false}
|
displayMessage={false}
|
||||||
headerNode={msg("termsTitle")}
|
headerNode={msg("termsTitle")}
|
||||||
formNode={
|
formNode={
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
|
|
||||||
import { ftlValuesGlobalName } from "../bin/build-keycloak-theme/ftlValuesGlobalName";
|
import type { PageId } from "../../bin/build-keycloak-theme/generateFtl";
|
||||||
import type { PageId } from "../bin/build-keycloak-theme/generateFtl";
|
import type { KcLanguageTag } from "../i18n/KcLanguageTag";
|
||||||
import { id } from "evt/tools/typeSafety/id";
|
import { doExtends } from "tsafe/doExtends";
|
||||||
import type { KcLanguageTag } from "./i18n/KcLanguageTag";
|
import type { MessageKey } from "../i18n/useKcMessage";
|
||||||
import { doExtends } from "evt/tools/typeSafety/doExtends";
|
import type { LanguageLabel } from "../i18n/KcLanguageTag";
|
||||||
import type { MessageKey } from "./i18n/useKcMessage";
|
|
||||||
import type { LanguageLabel } from "./i18n/KcLanguageTag";
|
|
||||||
|
|
||||||
type ExtractAfterStartingWith<Prefix extends string, StrEnum> =
|
type ExtractAfterStartingWith<Prefix extends string, StrEnum> =
|
||||||
StrEnum extends `${Prefix}${infer U}` ? U : never;
|
StrEnum extends `${Prefix}${infer U}` ? U : never;
|
||||||
@ -14,12 +12,13 @@ type ExtractAfterStartingWith<Prefix extends string, StrEnum> =
|
|||||||
* Some values might be undefined on some pages.
|
* Some values might be undefined on some pages.
|
||||||
* (ex: url.loginAction is undefined on error.ftl)
|
* (ex: url.loginAction is undefined on error.ftl)
|
||||||
*/
|
*/
|
||||||
export type KcContext =
|
export type KcContextBase =
|
||||||
KcContext.Login | KcContext.Register | KcContext.Info |
|
KcContextBase.Login | KcContextBase.Register | KcContextBase.Info |
|
||||||
KcContext.Error | KcContext.LoginResetPassword | KcContext.LoginVerifyEmail |
|
KcContextBase.Error | KcContextBase.LoginResetPassword | KcContextBase.LoginVerifyEmail |
|
||||||
KcContext.Terms | KcContext.LoginOtp;
|
KcContextBase.Terms | KcContextBase.LoginOtp | KcContextBase.LoginUpdateProfile |
|
||||||
|
KcContextBase.LoginIdpLinkConfirm;
|
||||||
|
|
||||||
export declare namespace KcContext {
|
export declare namespace KcContextBase {
|
||||||
|
|
||||||
export type Common = {
|
export type Common = {
|
||||||
url: {
|
url: {
|
||||||
@ -58,6 +57,10 @@ export declare namespace KcContext {
|
|||||||
type: "success" | "warning" | "error" | "info";
|
type: "success" | "warning" | "error" | "info";
|
||||||
summary: string;
|
summary: string;
|
||||||
};
|
};
|
||||||
|
client: {
|
||||||
|
clientId: string;
|
||||||
|
name?: string;
|
||||||
|
}
|
||||||
isAppInitiatedAction: boolean;
|
isAppInitiatedAction: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -124,11 +127,15 @@ export declare namespace KcContext {
|
|||||||
passwordRequired: boolean;
|
passwordRequired: boolean;
|
||||||
recaptchaRequired: boolean;
|
recaptchaRequired: boolean;
|
||||||
recaptchaSiteKey?: string;
|
recaptchaSiteKey?: string;
|
||||||
/**
|
social: {
|
||||||
* Defined when you use the keycloak-mail-whitelisting keycloak plugin
|
displayInfo: boolean;
|
||||||
* (https://github.com/micedre/keycloak-mail-whitelisting)
|
providers?: {
|
||||||
*/
|
loginUrl: string;
|
||||||
authorizedMailDomains?: string[];
|
alias: string;
|
||||||
|
providerId: string;
|
||||||
|
displayName: string;
|
||||||
|
}[]
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Info = Common & {
|
export type Info = Common & {
|
||||||
@ -147,7 +154,8 @@ export declare namespace KcContext {
|
|||||||
pageId: "error.ftl";
|
pageId: "error.ftl";
|
||||||
client?: {
|
client?: {
|
||||||
baseUrl?: string;
|
baseUrl?: string;
|
||||||
}
|
},
|
||||||
|
message: NonNullable<Common["message"]>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type LoginResetPassword = Common & {
|
export type LoginResetPassword = Common & {
|
||||||
@ -172,11 +180,33 @@ export declare namespace KcContext {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type LoginUpdateProfile = Common & {
|
||||||
|
pageId: "login-update-profile.ftl";
|
||||||
|
user: {
|
||||||
|
editUsernameAllowed: boolean;
|
||||||
|
username?: string;
|
||||||
|
email?: string;
|
||||||
|
firstName?: string;
|
||||||
|
lastName?: string;
|
||||||
|
};
|
||||||
|
messagesPerField: {
|
||||||
|
printIfExists<T>(
|
||||||
|
key: "username" | "email" | "firstName" | "lastName",
|
||||||
|
x: T
|
||||||
|
): T | undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
export type LoginIdpLinkConfirm = Common & {
|
||||||
|
pageId: "login-idp-link-confirm.ftl";
|
||||||
|
idpAlias: string;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
doExtends<KcContext["pageId"], PageId>();
|
doExtends<KcContextBase["pageId"], PageId>();
|
||||||
doExtends<PageId, KcContext["pageId"]>();
|
doExtends<PageId, KcContextBase["pageId"]>();
|
||||||
|
|
||||||
export const kcContext = id<KcContext | undefined>((window as any)[ftlValuesGlobalName]);
|
|
||||||
|
|
||||||
|
|
86
src/lib/getKcContext/getKcContext.ts
Normal file
86
src/lib/getKcContext/getKcContext.ts
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
|
||||||
|
import type { KcContextBase } from "./KcContextBase";
|
||||||
|
import { kcContextMocks, kcContextCommonMock } from "./kcContextMocks";
|
||||||
|
import { ftlValuesGlobalName } from "../../bin/build-keycloak-theme/ftlValuesGlobalName";
|
||||||
|
import type { AndByDiscriminatingKey } from "../tools/AndByDiscriminatingKey";
|
||||||
|
import type { DeepPartial } from "../tools/DeepPartial";
|
||||||
|
import { deepAssign } from "../tools/deepAssign";
|
||||||
|
|
||||||
|
|
||||||
|
export type ExtendsKcContextBase<
|
||||||
|
KcContextExtended extends { pageId: string; }
|
||||||
|
> =
|
||||||
|
[KcContextExtended] extends [never] ?
|
||||||
|
KcContextBase :
|
||||||
|
AndByDiscriminatingKey<
|
||||||
|
"pageId",
|
||||||
|
KcContextExtended & KcContextBase.Common,
|
||||||
|
KcContextBase
|
||||||
|
>;
|
||||||
|
|
||||||
|
export function getKcContext<KcContextExtended extends { pageId: string; } = never>(
|
||||||
|
params?: {
|
||||||
|
mockPageId?: ExtendsKcContextBase<KcContextExtended>["pageId"];
|
||||||
|
mockData?: readonly DeepPartial<ExtendsKcContextBase<KcContextExtended>>[];
|
||||||
|
}
|
||||||
|
): { kcContext: ExtendsKcContextBase<KcContextExtended> | undefined; } {
|
||||||
|
|
||||||
|
const {
|
||||||
|
mockPageId,
|
||||||
|
mockData
|
||||||
|
} = params ?? {};
|
||||||
|
|
||||||
|
if (mockPageId !== undefined) {
|
||||||
|
|
||||||
|
//TODO maybe trow if no mock fo custom page
|
||||||
|
|
||||||
|
const kcContextDefaultMock = kcContextMocks.find(({ pageId }) => pageId === mockPageId);
|
||||||
|
|
||||||
|
const partialKcContextCustomMock = mockData?.find(({ pageId }) => pageId === mockPageId);
|
||||||
|
|
||||||
|
if (
|
||||||
|
kcContextDefaultMock === undefined &&
|
||||||
|
partialKcContextCustomMock === undefined
|
||||||
|
) {
|
||||||
|
|
||||||
|
console.warn([
|
||||||
|
`WARNING: You declared the non build in page ${mockPageId} but you didn't `,
|
||||||
|
`provide mock data needed to debug the page outside of Keycloak as you are trying to do now.`,
|
||||||
|
`Please check the documentation of the getKcContext function`
|
||||||
|
].join("\n"));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const kcContext: any = {};
|
||||||
|
|
||||||
|
deepAssign({
|
||||||
|
"target": kcContext,
|
||||||
|
"source": kcContextDefaultMock !== undefined ?
|
||||||
|
kcContextDefaultMock :
|
||||||
|
{ "pageId": mockPageId, ...kcContextCommonMock, }
|
||||||
|
});
|
||||||
|
|
||||||
|
if (partialKcContextCustomMock !== undefined) {
|
||||||
|
|
||||||
|
deepAssign({
|
||||||
|
"target": kcContext,
|
||||||
|
"source": partialKcContextCustomMock
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return { kcContext };
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
"kcContext":
|
||||||
|
typeof window === "undefined" ?
|
||||||
|
undefined :
|
||||||
|
(window as any)[ftlValuesGlobalName]
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
2
src/lib/getKcContext/index.ts
Normal file
2
src/lib/getKcContext/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export type { KcContextBase } from "./KcContextBase";
|
||||||
|
export { getKcContext } from "./getKcContext";
|
1
src/lib/getKcContext/kcContextMocks/index.ts
Normal file
1
src/lib/getKcContext/kcContextMocks/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from "./kcContextMocks";
|
261
src/lib/getKcContext/kcContextMocks/kcContextMocks.ts
Normal file
261
src/lib/getKcContext/kcContextMocks/kcContextMocks.ts
Normal file
@ -0,0 +1,261 @@
|
|||||||
|
|
||||||
|
import type { KcContextBase } from "../KcContextBase";
|
||||||
|
import { getEvtKcLanguage } from "../../i18n/useKcLanguageTag";
|
||||||
|
import { getKcLanguageTagLabel } from "../../i18n/KcLanguageTag";
|
||||||
|
//NOTE: Aside because we want to be able to import them from node
|
||||||
|
import { resourcesCommonPath, resourcesPath } from "./urlResourcesPath";
|
||||||
|
import { id } from "tsafe/id";
|
||||||
|
import { join as pathJoin } from "path";
|
||||||
|
|
||||||
|
const PUBLIC_URL = process.env["PUBLIC_URL"] ?? "/";
|
||||||
|
|
||||||
|
export const kcContextCommonMock: KcContextBase.Common = {
|
||||||
|
"url": {
|
||||||
|
"loginAction": "#",
|
||||||
|
"resourcesPath": pathJoin(PUBLIC_URL, resourcesPath),
|
||||||
|
"resourcesCommonPath": pathJoin(PUBLIC_URL, resourcesCommonPath),
|
||||||
|
"loginRestartFlowUrl": "/auth/realms/myrealm/login-actions/restart?client_id=account&tab_id=HoAx28ja4xg",
|
||||||
|
"loginUrl": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg",
|
||||||
|
},
|
||||||
|
"realm": {
|
||||||
|
"displayName": "myrealm",
|
||||||
|
"displayNameHtml": "myrealm",
|
||||||
|
"internationalizationEnabled": true,
|
||||||
|
"registrationEmailAsUsername": true,
|
||||||
|
},
|
||||||
|
"locale": {
|
||||||
|
"supported": [
|
||||||
|
{
|
||||||
|
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=de",
|
||||||
|
"languageTag": "de"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=no",
|
||||||
|
"languageTag": "no"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=ru",
|
||||||
|
"languageTag": "ru"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=sv",
|
||||||
|
"languageTag": "sv"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=pt-BR",
|
||||||
|
"languageTag": "pt-BR"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=lt",
|
||||||
|
"languageTag": "lt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=en",
|
||||||
|
"languageTag": "en"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=it",
|
||||||
|
"languageTag": "it"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=fr",
|
||||||
|
"languageTag": "fr"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=zh-CN",
|
||||||
|
"languageTag": "zh-CN"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=es",
|
||||||
|
"languageTag": "es"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=cs",
|
||||||
|
"languageTag": "cs"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=ja",
|
||||||
|
"languageTag": "ja"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=sk",
|
||||||
|
"languageTag": "sk"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=pl",
|
||||||
|
"languageTag": "pl"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=ca",
|
||||||
|
"languageTag": "ca"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=nl",
|
||||||
|
"languageTag": "nl"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=tr",
|
||||||
|
"languageTag": "tr"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
//"current": null as any
|
||||||
|
"current": "English"
|
||||||
|
},
|
||||||
|
"auth": {
|
||||||
|
"showUsername": false,
|
||||||
|
"showResetCredentials": false,
|
||||||
|
"showTryAnotherWayLink": false
|
||||||
|
},
|
||||||
|
"client": {
|
||||||
|
"clientId": "myApp"
|
||||||
|
},
|
||||||
|
"scripts": [],
|
||||||
|
"message": {
|
||||||
|
"type": "success",
|
||||||
|
"summary": "This is a test message"
|
||||||
|
},
|
||||||
|
"isAppInitiatedAction": false,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Object.defineProperty(
|
||||||
|
kcContextCommonMock.locale!,
|
||||||
|
"current",
|
||||||
|
{
|
||||||
|
"get": () => getKcLanguageTagLabel(getEvtKcLanguage().state),
|
||||||
|
"enumerable": true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const loginUrl = {
|
||||||
|
...kcContextCommonMock.url,
|
||||||
|
"loginResetCredentialsUrl": "/auth/realms/myrealm/login-actions/reset-credentials?client_id=account&tab_id=HoAx28ja4xg",
|
||||||
|
"registrationUrl": "/auth/realms/myrealm/login-actions/registration?client_id=account&tab_id=HoAx28ja4xg"
|
||||||
|
};
|
||||||
|
|
||||||
|
export const kcContextMocks: KcContextBase[] = [
|
||||||
|
id<KcContextBase.Login>({
|
||||||
|
...kcContextCommonMock,
|
||||||
|
"pageId": "login.ftl",
|
||||||
|
"url": loginUrl,
|
||||||
|
"realm": {
|
||||||
|
...kcContextCommonMock.realm,
|
||||||
|
"loginWithEmailAllowed": true,
|
||||||
|
"rememberMe": true,
|
||||||
|
"password": true,
|
||||||
|
"resetPasswordAllowed": true,
|
||||||
|
"registrationAllowed": true
|
||||||
|
},
|
||||||
|
"auth": kcContextCommonMock.auth!,
|
||||||
|
"social": {
|
||||||
|
"displayInfo": true
|
||||||
|
},
|
||||||
|
"usernameEditDisabled": false,
|
||||||
|
"login": {
|
||||||
|
"rememberMe": false
|
||||||
|
},
|
||||||
|
"registrationDisabled": false,
|
||||||
|
|
||||||
|
}),
|
||||||
|
id<KcContextBase.Register>({
|
||||||
|
...kcContextCommonMock,
|
||||||
|
"pageId": "register.ftl",
|
||||||
|
"url": {
|
||||||
|
...loginUrl,
|
||||||
|
"registrationAction": "http://localhost:8080/auth/realms/myrealm/login-actions/registration?session_code=gwZdUeO7pbYpFTRxiIxRg_QtzMbtFTKrNu6XW_f8asM&execution=12146ce0-b139-4bbd-b25b-0eccfee6577e&client_id=account&tab_id=uS8lYfebLa0"
|
||||||
|
},
|
||||||
|
"messagesPerField": {
|
||||||
|
"printIfExists": (...[, x]) => x
|
||||||
|
},
|
||||||
|
"scripts": [],
|
||||||
|
"isAppInitiatedAction": false,
|
||||||
|
"register": {
|
||||||
|
"formData": {}
|
||||||
|
},
|
||||||
|
"passwordRequired": true,
|
||||||
|
"recaptchaRequired": false,
|
||||||
|
"social": {
|
||||||
|
"displayInfo": true
|
||||||
|
},
|
||||||
|
|
||||||
|
}),
|
||||||
|
id<KcContextBase.Info>({
|
||||||
|
...kcContextCommonMock,
|
||||||
|
"pageId": "info.ftl",
|
||||||
|
"messageHeader": "<Message header>",
|
||||||
|
"requiredActions": undefined,
|
||||||
|
"skipLink": false,
|
||||||
|
"actionUri": "#",
|
||||||
|
"client": {
|
||||||
|
"clientId": "myApp",
|
||||||
|
"baseUrl": "#"
|
||||||
|
}
|
||||||
|
|
||||||
|
}),
|
||||||
|
id<KcContextBase.Error>({
|
||||||
|
...kcContextCommonMock,
|
||||||
|
"pageId": "error.ftl",
|
||||||
|
"client": {
|
||||||
|
"clientId": "myApp",
|
||||||
|
"baseUrl": "#"
|
||||||
|
},
|
||||||
|
"message": {
|
||||||
|
"type": "error",
|
||||||
|
"summary": "This is the error message"
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
id<KcContextBase.LoginResetPassword>({
|
||||||
|
...kcContextCommonMock,
|
||||||
|
"pageId": "login-reset-password.ftl",
|
||||||
|
"realm": {
|
||||||
|
...kcContextCommonMock.realm,
|
||||||
|
"loginWithEmailAllowed": false
|
||||||
|
}
|
||||||
|
|
||||||
|
}),
|
||||||
|
id<KcContextBase.LoginVerifyEmail>({
|
||||||
|
...kcContextCommonMock,
|
||||||
|
"pageId": "login-verify-email.ftl"
|
||||||
|
}),
|
||||||
|
id<KcContextBase.Terms>({
|
||||||
|
...kcContextCommonMock,
|
||||||
|
"pageId": "terms.ftl"
|
||||||
|
|
||||||
|
}),
|
||||||
|
id<KcContextBase.LoginOtp>({
|
||||||
|
...kcContextCommonMock,
|
||||||
|
"pageId": "login-otp.ftl",
|
||||||
|
"otpLogin": {
|
||||||
|
"userOtpCredentials": [
|
||||||
|
{
|
||||||
|
"id": "id1",
|
||||||
|
"userLabel": "label1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "id2",
|
||||||
|
"userLabel": "label2"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
}),
|
||||||
|
id<KcContextBase.LoginUpdateProfile>({
|
||||||
|
...kcContextCommonMock,
|
||||||
|
"pageId": "login-update-profile.ftl",
|
||||||
|
"user": {
|
||||||
|
"editUsernameAllowed": true,
|
||||||
|
"username": "anUsername",
|
||||||
|
"email": "foo@example.com",
|
||||||
|
"firstName": "aFirstName",
|
||||||
|
"lastName": "aLastName"
|
||||||
|
},
|
||||||
|
"messagesPerField": {
|
||||||
|
"printIfExists": () => undefined
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
id<KcContextBase.LoginIdpLinkConfirm>({
|
||||||
|
...kcContextCommonMock,
|
||||||
|
"pageId": "login-idp-link-confirm.ftl",
|
||||||
|
"idpAlias": "FranceConnect"
|
||||||
|
})
|
||||||
|
];
|
@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
import { objectKeys } from "evt/tools/typeSafety/objectKeys";
|
import { objectKeys } from "tsafe/objectKeys";
|
||||||
import { kcMessages } from "./kcMessages/login";
|
import { kcMessages } from "./kcMessages/login";
|
||||||
|
|
||||||
export type KcLanguageTag = keyof typeof kcMessages;
|
export type KcLanguageTag = keyof typeof kcMessages;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
import { kcMessages } from "../generated_kcMessages/login";
|
import { kcMessages } from "../generated_kcMessages/login";
|
||||||
import { Evt } from "evt";
|
import { Evt } from "evt";
|
||||||
import { objectKeys } from "evt/tools/typeSafety/objectKeys";
|
import { objectKeys } from "tsafe/objectKeys";
|
||||||
|
|
||||||
export const evtTermsUpdated = Evt.asNonPostable(Evt.create<void>());
|
export const evtTermsUpdated = Evt.asNonPostable(Evt.create<void>());
|
||||||
|
|
||||||
|
@ -1,21 +1,40 @@
|
|||||||
|
|
||||||
import { createUseGlobalState } from "powerhooks";
|
import { createUseGlobalState } from "powerhooks";
|
||||||
import { kcContext } from "../KcContext";
|
import { getKcContext } from "../getKcContext";
|
||||||
import { getBestMatchAmongKcLanguageTag } from "./KcLanguageTag";
|
import { getBestMatchAmongKcLanguageTag } from "./KcLanguageTag";
|
||||||
|
import type { StatefulEvt } from "powerhooks";
|
||||||
|
import { KcLanguageTag } from "./KcLanguageTag";
|
||||||
|
|
||||||
|
|
||||||
//export const { useKcLanguageTag, evtKcLanguageTag } = createUseGlobalState(
|
//export const { useKcLanguageTag, evtKcLanguageTag } = createUseGlobalState(
|
||||||
const wrap = createUseGlobalState(
|
const wrap = createUseGlobalState(
|
||||||
"kcLanguageTag",
|
"kcLanguageTag",
|
||||||
() => getBestMatchAmongKcLanguageTag(
|
() => {
|
||||||
kcContext?.locale?.current ??
|
|
||||||
navigator.language
|
|
||||||
),
|
const { kcContext } = getKcContext();
|
||||||
{ "persistance": "cookie" }
|
|
||||||
|
const languageLike =
|
||||||
|
kcContext?.locale?.current ??
|
||||||
|
(
|
||||||
|
typeof navigator === "undefined" ?
|
||||||
|
undefined :
|
||||||
|
navigator.language
|
||||||
|
);
|
||||||
|
|
||||||
|
if (languageLike === undefined) {
|
||||||
|
return "en";
|
||||||
|
}
|
||||||
|
|
||||||
|
return getBestMatchAmongKcLanguageTag(languageLike);
|
||||||
|
|
||||||
|
},
|
||||||
|
{ "persistance": "localStorage" }
|
||||||
);
|
);
|
||||||
|
|
||||||
export const { useKcLanguageTag } = wrap;
|
export const { useKcLanguageTag } = wrap;
|
||||||
|
|
||||||
export function getEvtKcLanguage() {
|
export function getEvtKcLanguage(): StatefulEvt<KcLanguageTag> {
|
||||||
return wrap.evtKcLanguageTag;
|
return wrap.evtKcLanguageTag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,6 +9,13 @@ import ReactMarkdown from "react-markdown";
|
|||||||
|
|
||||||
export type MessageKey = keyof typeof kcMessages["en"];
|
export type MessageKey = keyof typeof kcMessages["en"];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When the language is switched the page is reloaded, this may appear
|
||||||
|
* as a bug as you might notice that the language successfully switch before
|
||||||
|
* reload.
|
||||||
|
* However we need to tell Keycloak that the user have changed the language
|
||||||
|
* during login so we can retrieve the "local" field of the JWT encoded accessToken.
|
||||||
|
*/
|
||||||
export function useKcMessage() {
|
export function useKcMessage() {
|
||||||
|
|
||||||
const { kcLanguageTag } = useKcLanguageTag();
|
const { kcLanguageTag } = useKcLanguageTag();
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
export * from "./KcContext";
|
export * from "./getKcContext";
|
||||||
|
|
||||||
export * from "./i18n/KcLanguageTag";
|
export * from "./i18n/KcLanguageTag";
|
||||||
export * from "./i18n/useKcLanguageTag";
|
export * from "./i18n/useKcLanguageTag";
|
||||||
@ -17,5 +17,3 @@ export * from "./keycloakJsAdapter";
|
|||||||
|
|
||||||
export * from "./tools/assert";
|
export * from "./tools/assert";
|
||||||
|
|
||||||
|
|
||||||
export * as kcContextMocks from "./kcContextMocks";
|
|
@ -1,230 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
import type { KcContext } from "../KcContext";
|
|
||||||
import { getEvtKcLanguage } from "../i18n/useKcLanguageTag";
|
|
||||||
import { getKcLanguageTagLabel } from "../i18n/KcLanguageTag";
|
|
||||||
//NOTE: Aside because we want to be able to import them from node
|
|
||||||
import { resourcesCommonPath, resourcesPath } from "./urlResourcesPath";
|
|
||||||
|
|
||||||
const kcCommonContext: KcContext.Common = {
|
|
||||||
"url": {
|
|
||||||
"loginAction": "#",
|
|
||||||
"resourcesPath": `${process.env["PUBLIC_URL"]}/${resourcesPath}`,
|
|
||||||
"resourcesCommonPath": `${process.env["PUBLIC_URL"]}/${resourcesCommonPath}`,
|
|
||||||
"loginRestartFlowUrl": "/auth/realms/myrealm/login-actions/restart?client_id=account&tab_id=HoAx28ja4xg",
|
|
||||||
"loginUrl": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg",
|
|
||||||
},
|
|
||||||
"realm": {
|
|
||||||
"displayName": "myrealm",
|
|
||||||
"displayNameHtml": "myrealm",
|
|
||||||
"internationalizationEnabled": true,
|
|
||||||
"registrationEmailAsUsername": true,
|
|
||||||
},
|
|
||||||
"locale": {
|
|
||||||
"supported": [
|
|
||||||
{
|
|
||||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=de",
|
|
||||||
"languageTag": "de"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=no",
|
|
||||||
"languageTag": "no"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=ru",
|
|
||||||
"languageTag": "ru"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=sv",
|
|
||||||
"languageTag": "sv"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=pt-BR",
|
|
||||||
"languageTag": "pt-BR"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=lt",
|
|
||||||
"languageTag": "lt"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=en",
|
|
||||||
"languageTag": "en"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=it",
|
|
||||||
"languageTag": "it"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=fr",
|
|
||||||
"languageTag": "fr"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=zh-CN",
|
|
||||||
"languageTag": "zh-CN"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=es",
|
|
||||||
"languageTag": "es"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=cs",
|
|
||||||
"languageTag": "cs"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=ja",
|
|
||||||
"languageTag": "ja"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=sk",
|
|
||||||
"languageTag": "sk"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=pl",
|
|
||||||
"languageTag": "pl"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=ca",
|
|
||||||
"languageTag": "ca"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=nl",
|
|
||||||
"languageTag": "nl"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=tr",
|
|
||||||
"languageTag": "tr"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"current": null as any
|
|
||||||
},
|
|
||||||
"auth": {
|
|
||||||
"showUsername": false,
|
|
||||||
"showResetCredentials": false,
|
|
||||||
"showTryAnotherWayLink": false
|
|
||||||
},
|
|
||||||
"scripts": [],
|
|
||||||
"message": {
|
|
||||||
"type": "success",
|
|
||||||
"summary": "This is a test message"
|
|
||||||
},
|
|
||||||
"isAppInitiatedAction": false,
|
|
||||||
};
|
|
||||||
|
|
||||||
Object.defineProperty(
|
|
||||||
kcCommonContext.locale!,
|
|
||||||
"current",
|
|
||||||
{
|
|
||||||
"get": () => getKcLanguageTagLabel(getEvtKcLanguage().state),
|
|
||||||
"enumerable": true
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
export const kcLoginContext: KcContext.Login = {
|
|
||||||
...kcCommonContext,
|
|
||||||
"pageId": "login.ftl",
|
|
||||||
"url": {
|
|
||||||
...kcCommonContext.url,
|
|
||||||
"loginResetCredentialsUrl": "/auth/realms/myrealm/login-actions/reset-credentials?client_id=account&tab_id=HoAx28ja4xg",
|
|
||||||
"registrationUrl": "/auth/realms/myrealm/login-actions/registration?client_id=account&tab_id=HoAx28ja4xg"
|
|
||||||
},
|
|
||||||
"realm": {
|
|
||||||
...kcCommonContext.realm,
|
|
||||||
"loginWithEmailAllowed": true,
|
|
||||||
"rememberMe": true,
|
|
||||||
"password": true,
|
|
||||||
"resetPasswordAllowed": true,
|
|
||||||
"registrationAllowed": true
|
|
||||||
},
|
|
||||||
"auth": kcCommonContext.auth!,
|
|
||||||
"social": {
|
|
||||||
"displayInfo": true
|
|
||||||
},
|
|
||||||
"usernameEditDisabled": false,
|
|
||||||
"login": {
|
|
||||||
"rememberMe": false
|
|
||||||
},
|
|
||||||
"registrationDisabled": false,
|
|
||||||
};
|
|
||||||
|
|
||||||
export const kcRegisterContext: KcContext.Register = {
|
|
||||||
...kcCommonContext,
|
|
||||||
"url": {
|
|
||||||
...kcLoginContext.url,
|
|
||||||
"registrationAction": "http://localhost:8080/auth/realms/myrealm/login-actions/registration?session_code=gwZdUeO7pbYpFTRxiIxRg_QtzMbtFTKrNu6XW_f8asM&execution=12146ce0-b139-4bbd-b25b-0eccfee6577e&client_id=account&tab_id=uS8lYfebLa0"
|
|
||||||
},
|
|
||||||
"messagesPerField": {
|
|
||||||
"printIfExists": (...[, x]) => x
|
|
||||||
},
|
|
||||||
"scripts": [],
|
|
||||||
"isAppInitiatedAction": false,
|
|
||||||
"pageId": "register.ftl",
|
|
||||||
"register": {
|
|
||||||
"formData": {}
|
|
||||||
},
|
|
||||||
"passwordRequired": true,
|
|
||||||
"recaptchaRequired": false,
|
|
||||||
"authorizedMailDomains": [
|
|
||||||
"example.com",
|
|
||||||
"another-example.com",
|
|
||||||
"*.yet-another-example.com",
|
|
||||||
"*.example.com",
|
|
||||||
"hello-world.com"
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
export const kcInfoContext: KcContext.Info = {
|
|
||||||
...kcCommonContext,
|
|
||||||
"pageId": "info.ftl",
|
|
||||||
"messageHeader": "<Message header>",
|
|
||||||
"requiredActions": undefined,
|
|
||||||
"skipLink": false,
|
|
||||||
"actionUri": "#",
|
|
||||||
"client": {
|
|
||||||
"baseUrl": "#"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const kcErrorContext: KcContext.Error = {
|
|
||||||
...kcCommonContext,
|
|
||||||
"pageId": "error.ftl",
|
|
||||||
"client": {
|
|
||||||
"baseUrl": "#"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const kcLoginResetPasswordContext: KcContext.LoginResetPassword = {
|
|
||||||
...kcCommonContext,
|
|
||||||
"pageId": "login-reset-password.ftl",
|
|
||||||
"realm": {
|
|
||||||
...kcCommonContext.realm,
|
|
||||||
"loginWithEmailAllowed": false
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const kcLoginVerifyEmailContext: KcContext.LoginVerifyEmail = {
|
|
||||||
...kcCommonContext,
|
|
||||||
"pageId": "login-verify-email.ftl"
|
|
||||||
};
|
|
||||||
|
|
||||||
export const kcTermsContext: KcContext.Terms = {
|
|
||||||
...kcCommonContext,
|
|
||||||
"pageId": "terms.ftl"
|
|
||||||
};
|
|
||||||
|
|
||||||
export const kcLoginOtpContext: KcContext.LoginOtp = {
|
|
||||||
...kcCommonContext,
|
|
||||||
"pageId": "login-otp.ftl",
|
|
||||||
"otpLogin": {
|
|
||||||
"userOtpCredentials": [
|
|
||||||
{
|
|
||||||
"id": "id1",
|
|
||||||
"userLabel": "label1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "id2",
|
|
||||||
"userLabel": "label2"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
@ -62,7 +62,7 @@ export function createKeycloakAdapter(
|
|||||||
"success": { "value": () => { } },
|
"success": { "value": () => { } },
|
||||||
"error": { "value": () => { } }
|
"error": { "value": () => { } }
|
||||||
}
|
}
|
||||||
);
|
) as any;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"login": options => {
|
"login": options => {
|
||||||
|
35
src/lib/tools/AndByDiscriminatingKey.ts
Normal file
35
src/lib/tools/AndByDiscriminatingKey.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
|
||||||
|
export type AndByDiscriminatingKey<
|
||||||
|
DiscriminatingKey extends string,
|
||||||
|
U1 extends Record<DiscriminatingKey, string>,
|
||||||
|
U2 extends Record<DiscriminatingKey, string>
|
||||||
|
> =
|
||||||
|
AndByDiscriminatingKey.Tf1<DiscriminatingKey, U1, U1, U2>;
|
||||||
|
|
||||||
|
export declare namespace AndByDiscriminatingKey {
|
||||||
|
|
||||||
|
export type Tf1<
|
||||||
|
DiscriminatingKey extends string,
|
||||||
|
U1,
|
||||||
|
U1Again extends Record<DiscriminatingKey, string>,
|
||||||
|
U2 extends Record<DiscriminatingKey, string>
|
||||||
|
> =
|
||||||
|
U1 extends Pick<U2, DiscriminatingKey> ?
|
||||||
|
Tf2<DiscriminatingKey, U1, U2, U1Again> :
|
||||||
|
U1;
|
||||||
|
|
||||||
|
export type Tf2<
|
||||||
|
DiscriminatingKey extends string,
|
||||||
|
SingletonU1 extends Record<DiscriminatingKey, string>,
|
||||||
|
U2,
|
||||||
|
U1 extends Record<DiscriminatingKey, string>
|
||||||
|
> =
|
||||||
|
U2 extends Pick<SingletonU1, DiscriminatingKey> ?
|
||||||
|
U2 & SingletonU1 :
|
||||||
|
U2 extends Pick<U1, DiscriminatingKey> ?
|
||||||
|
never :
|
||||||
|
U2;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
4
src/lib/tools/DeepPartial.ts
Normal file
4
src/lib/tools/DeepPartial.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
|
||||||
|
export type DeepPartial<T> = {
|
||||||
|
[P in keyof T]?: DeepPartial<T[P]>;
|
||||||
|
};
|
@ -1,2 +1,2 @@
|
|||||||
|
|
||||||
export { assert } from "evt/tools/typeSafety/assert";
|
export { assert } from "tsafe/assert";
|
57
src/lib/tools/deepAssign.ts
Normal file
57
src/lib/tools/deepAssign.ts
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
|
||||||
|
import { assert } from "tsafe/assert";
|
||||||
|
import { is } from "tsafe/is";
|
||||||
|
|
||||||
|
//Warning: Be mindful that because of array this is not idempotent.
|
||||||
|
export function deepAssign(
|
||||||
|
params: {
|
||||||
|
target: Record<string, unknown>;
|
||||||
|
source: Record<string, unknown>;
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
|
||||||
|
const { target, source } = params;
|
||||||
|
|
||||||
|
Object.keys(source).forEach(key => {
|
||||||
|
var dereferencedSource = source[key];
|
||||||
|
|
||||||
|
if (
|
||||||
|
target[key] === undefined ||
|
||||||
|
!(dereferencedSource instanceof Object)
|
||||||
|
) {
|
||||||
|
|
||||||
|
Object.defineProperty(
|
||||||
|
target,
|
||||||
|
key,
|
||||||
|
{
|
||||||
|
"enumerable": true,
|
||||||
|
"value": dereferencedSource
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const dereferencedTarget = target[key];
|
||||||
|
|
||||||
|
if (dereferencedSource instanceof Array) {
|
||||||
|
|
||||||
|
assert(is<unknown[]>(dereferencedTarget));
|
||||||
|
assert(is<unknown[]>(dereferencedSource));
|
||||||
|
|
||||||
|
dereferencedSource.forEach(entry => dereferencedTarget.push(entry));
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(is<Record<string, unknown>>(dereferencedTarget));
|
||||||
|
assert(is<Record<string, unknown>>(dereferencedSource));
|
||||||
|
|
||||||
|
deepAssign({
|
||||||
|
"target": dereferencedTarget,
|
||||||
|
"source": dereferencedSource
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
4
src/lib/tools/deepClone.ts
Normal file
4
src/lib/tools/deepClone.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
|
||||||
|
export function deepClone<T>(arg: T): T {
|
||||||
|
return JSON.parse(JSON.stringify(arg));
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
|
|
||||||
import { join as pathJoin } from "path";
|
import { join as pathJoin } from "path";
|
||||||
import { generateKeycloakThemeResources } from "../bin/build-keycloak-theme/generateKeycloakThemeResources";
|
import { generateKeycloakThemeResources } from "../../bin/build-keycloak-theme/generateKeycloakThemeResources";
|
||||||
import {
|
import {
|
||||||
setupSampleReactProject,
|
setupSampleReactProject,
|
||||||
sampleReactProjectDirPath
|
sampleReactProjectDirPath
|
||||||
@ -13,6 +13,8 @@ generateKeycloakThemeResources({
|
|||||||
"reactAppBuildDirPath": pathJoin(sampleReactProjectDirPath, "build"),
|
"reactAppBuildDirPath": pathJoin(sampleReactProjectDirPath, "build"),
|
||||||
"keycloakThemeBuildingDirPath": pathJoin(sampleReactProjectDirPath, "build_keycloak_theme"),
|
"keycloakThemeBuildingDirPath": pathJoin(sampleReactProjectDirPath, "build_keycloak_theme"),
|
||||||
"urlPathname": "/keycloakify-demo-app/",
|
"urlPathname": "/keycloakify-demo-app/",
|
||||||
"urlOrigin": undefined
|
"urlOrigin": undefined,
|
||||||
|
"extraPagesId": ["my-custom-page.ftl"],
|
||||||
|
"extraThemeProperties": ["env=test"]
|
||||||
});
|
});
|
||||||
|
|
@ -6,7 +6,7 @@ import {
|
|||||||
} from "./setupSampleReactProject";
|
} from "./setupSampleReactProject";
|
||||||
import * as st from "scripting-tools";
|
import * as st from "scripting-tools";
|
||||||
import { join as pathJoin } from "path";
|
import { join as pathJoin } from "path";
|
||||||
import { getProjectRoot } from "../bin/tools/getProjectRoot";
|
import { getProjectRoot } from "../../bin/tools/getProjectRoot";
|
||||||
|
|
||||||
setupSampleReactProject();
|
setupSampleReactProject();
|
||||||
|
|
@ -3,7 +3,7 @@ import {
|
|||||||
replaceImportsFromStaticInJsCode,
|
replaceImportsFromStaticInJsCode,
|
||||||
replaceImportsInCssCode,
|
replaceImportsInCssCode,
|
||||||
generateCssCodeToDefineGlobals
|
generateCssCodeToDefineGlobals
|
||||||
} from "../bin/build-keycloak-theme/replaceImportFromStatic";
|
} from "../../bin/build-keycloak-theme/replaceImportFromStatic";
|
||||||
|
|
||||||
const { fixedJsCode } = replaceImportsFromStaticInJsCode({
|
const { fixedJsCode } = replaceImportsFromStaticInJsCode({
|
||||||
"jsCode": `
|
"jsCode": `
|
@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
import { getProjectRoot } from "../bin/tools/getProjectRoot";
|
import { getProjectRoot } from "../../bin/tools/getProjectRoot";
|
||||||
import { join as pathJoin } from "path";
|
import { join as pathJoin } from "path";
|
||||||
import { downloadAndUnzip } from "../bin/tools/downloadAndUnzip";
|
import { downloadAndUnzip } from "../../bin/tools/downloadAndUnzip";
|
||||||
|
|
||||||
export const sampleReactProjectDirPath = pathJoin(getProjectRoot(), "sample_react_project");
|
export const sampleReactProjectDirPath = pathJoin(getProjectRoot(), "sample_react_project");
|
||||||
|
|
250
src/test/lib/getKcContext.ts
Normal file
250
src/test/lib/getKcContext.ts
Normal file
@ -0,0 +1,250 @@
|
|||||||
|
|
||||||
|
import { getKcContext } from "../../lib/getKcContext";
|
||||||
|
import type { KcContextBase } from "../../lib/getKcContext";
|
||||||
|
import type { ExtendsKcContextBase } from "../../lib/getKcContext/getKcContext";
|
||||||
|
import { same } from "evt/tools/inDepth";
|
||||||
|
import { doExtends } from "tsafe/doExtends";
|
||||||
|
import { assert } from "tsafe/assert";
|
||||||
|
import { kcContextMocks, kcContextCommonMock } from "../../lib/getKcContext/kcContextMocks";
|
||||||
|
import { deepClone } from "../../lib/tools/deepClone";
|
||||||
|
import type { Any } from "ts-toolbelt";
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
const authorizedMailDomains = [
|
||||||
|
"example.com",
|
||||||
|
"another-example.com",
|
||||||
|
"*.yet-another-example.com",
|
||||||
|
"*.example.com",
|
||||||
|
"hello-world.com"
|
||||||
|
];
|
||||||
|
|
||||||
|
const displayName = "this is an overwritten common value";
|
||||||
|
|
||||||
|
const aNonStandardValue1 = "a non standard value 1";
|
||||||
|
const aNonStandardValue2 = "a non standard value 2";
|
||||||
|
|
||||||
|
type KcContextExtended = {
|
||||||
|
pageId: "register.ftl";
|
||||||
|
authorizedMailDomains: string[];
|
||||||
|
} | {
|
||||||
|
pageId: "info.ftl";
|
||||||
|
aNonStandardValue1: string;
|
||||||
|
} | {
|
||||||
|
pageId: "my-extra-page-1.ftl";
|
||||||
|
} | {
|
||||||
|
pageId: "my-extra-page-2.ftl";
|
||||||
|
aNonStandardValue2: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getKcContextProxy = (
|
||||||
|
params: {
|
||||||
|
mockPageId: ExtendsKcContextBase<KcContextExtended>["pageId"];
|
||||||
|
}
|
||||||
|
) => {
|
||||||
|
|
||||||
|
const { mockPageId } = params;
|
||||||
|
|
||||||
|
const { kcContext } = getKcContext<KcContextExtended>({
|
||||||
|
mockPageId,
|
||||||
|
"mockData": [
|
||||||
|
{
|
||||||
|
"pageId": "login.ftl",
|
||||||
|
"realm": { displayName }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pageId": "info.ftl",
|
||||||
|
aNonStandardValue1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pageId": "register.ftl",
|
||||||
|
authorizedMailDomains
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pageId": "my-extra-page-2.ftl",
|
||||||
|
aNonStandardValue2
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
return { kcContext };
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
const pageId = "login.ftl";
|
||||||
|
|
||||||
|
const { kcContext } = getKcContextProxy({ "mockPageId": pageId });
|
||||||
|
|
||||||
|
assert(kcContext?.pageId === pageId);
|
||||||
|
|
||||||
|
doExtends<Any.Equals<typeof kcContext, KcContextBase.Login>, 1>();
|
||||||
|
|
||||||
|
assert(same(
|
||||||
|
//NOTE: deepClone for printIfExists or other functions...
|
||||||
|
deepClone(kcContext),
|
||||||
|
(() => {
|
||||||
|
|
||||||
|
const mock = deepClone(kcContextMocks.find(({ pageId: pageId_i }) => pageId_i === pageId)!);
|
||||||
|
|
||||||
|
mock.realm.displayName = displayName;
|
||||||
|
|
||||||
|
return mock;
|
||||||
|
|
||||||
|
})()
|
||||||
|
));
|
||||||
|
|
||||||
|
console.log(`PASS ${pageId}`);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const pageId = "info.ftl";
|
||||||
|
|
||||||
|
const { kcContext } = getKcContextProxy({ "mockPageId": pageId });
|
||||||
|
|
||||||
|
assert(kcContext?.pageId === pageId);
|
||||||
|
|
||||||
|
//NOTE: I don't understand the need to add: pageId: typeof pageId; ...
|
||||||
|
doExtends<Any.Equals<typeof kcContext, KcContextBase.Info & { pageId: typeof pageId; aNonStandardValue1: string; }>, 1>();
|
||||||
|
|
||||||
|
assert(same(
|
||||||
|
deepClone(kcContext),
|
||||||
|
(() => {
|
||||||
|
|
||||||
|
const mock = deepClone(kcContextMocks.find(({ pageId: pageId_i }) => pageId_i === pageId)!);
|
||||||
|
|
||||||
|
Object.assign(mock, { aNonStandardValue1 });
|
||||||
|
|
||||||
|
return mock;
|
||||||
|
|
||||||
|
})()
|
||||||
|
));
|
||||||
|
|
||||||
|
console.log(`PASS ${pageId}`);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const pageId = "register.ftl";
|
||||||
|
|
||||||
|
const { kcContext } = getKcContextProxy({ "mockPageId": pageId });
|
||||||
|
|
||||||
|
assert(kcContext?.pageId === pageId);
|
||||||
|
|
||||||
|
//NOTE: I don't understand the need to add: pageId: typeof pageId; ...
|
||||||
|
doExtends<Any.Equals<typeof kcContext, KcContextBase.Register & { pageId: typeof pageId; authorizedMailDomains: string[]; }>, 1>();
|
||||||
|
|
||||||
|
assert(same(
|
||||||
|
deepClone(kcContext),
|
||||||
|
(() => {
|
||||||
|
|
||||||
|
const mock = deepClone(kcContextMocks.find(({ pageId: pageId_i }) => pageId_i === pageId)!);
|
||||||
|
|
||||||
|
Object.assign(mock, { authorizedMailDomains });
|
||||||
|
|
||||||
|
return mock;
|
||||||
|
|
||||||
|
})()
|
||||||
|
));
|
||||||
|
|
||||||
|
console.log(`PASS ${pageId}`);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
const pageId = "my-extra-page-2.ftl";
|
||||||
|
|
||||||
|
const { kcContext } = getKcContextProxy({ "mockPageId": pageId });
|
||||||
|
|
||||||
|
assert(kcContext?.pageId === pageId);
|
||||||
|
|
||||||
|
doExtends<Any.Equals<typeof kcContext, KcContextBase.Common & { pageId: typeof pageId; aNonStandardValue2: string; }>, 1>();
|
||||||
|
|
||||||
|
kcContext.aNonStandardValue2;
|
||||||
|
|
||||||
|
assert(same(
|
||||||
|
deepClone(kcContext),
|
||||||
|
(() => {
|
||||||
|
|
||||||
|
const mock = deepClone(kcContextCommonMock);
|
||||||
|
|
||||||
|
Object.assign(mock, { pageId, aNonStandardValue2 });
|
||||||
|
|
||||||
|
return mock;
|
||||||
|
|
||||||
|
})()
|
||||||
|
));
|
||||||
|
|
||||||
|
console.log(`PASS ${pageId}`);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
const pageId = "my-extra-page-1.ftl";
|
||||||
|
|
||||||
|
console.log("We expect a warning here =>");
|
||||||
|
|
||||||
|
const { kcContext } = getKcContextProxy({ "mockPageId": pageId });
|
||||||
|
|
||||||
|
|
||||||
|
assert(kcContext?.pageId === pageId);
|
||||||
|
|
||||||
|
doExtends<Any.Equals<typeof kcContext, KcContextBase.Common & { pageId: typeof pageId; }>, 1>();
|
||||||
|
|
||||||
|
assert(same(
|
||||||
|
deepClone(kcContext),
|
||||||
|
(() => {
|
||||||
|
|
||||||
|
const mock = deepClone(kcContextCommonMock);
|
||||||
|
|
||||||
|
Object.assign(mock, { pageId });
|
||||||
|
|
||||||
|
return mock;
|
||||||
|
|
||||||
|
})()
|
||||||
|
));
|
||||||
|
|
||||||
|
console.log(`PASS ${pageId}`);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
const pageId = "login.ftl";
|
||||||
|
|
||||||
|
const { kcContext } = getKcContext({
|
||||||
|
"mockPageId": pageId
|
||||||
|
});
|
||||||
|
|
||||||
|
doExtends<Any.Equals<typeof kcContext, KcContextBase | undefined>, 1>();
|
||||||
|
|
||||||
|
assert(same(
|
||||||
|
deepClone(kcContext),
|
||||||
|
deepClone(kcContextMocks.find(({ pageId: pageId_i }) => pageId_i === pageId)!)
|
||||||
|
));
|
||||||
|
|
||||||
|
console.log("PASS no extension");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
const { kcContext } = getKcContext();
|
||||||
|
|
||||||
|
doExtends<Any.Equals<typeof kcContext, KcContextBase | undefined>, 1>();
|
||||||
|
|
||||||
|
assert(kcContext === undefined);
|
||||||
|
|
||||||
|
console.log("PASS no extension, no mock");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
2
src/test/lib/index.ts
Normal file
2
src/test/lib/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
import "./getKcContext";
|
91
src/test/lib/tools/AndByDiscriminatingKey.type.ts
Normal file
91
src/test/lib/tools/AndByDiscriminatingKey.type.ts
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
|
||||||
|
import { AndByDiscriminatingKey } from "../../../lib/tools/AndByDiscriminatingKey";
|
||||||
|
import { doExtends } from "tsafe/doExtends";
|
||||||
|
|
||||||
|
type Base =
|
||||||
|
{ pageId: "a"; onlyA: string; } |
|
||||||
|
{ pageId: "b"; onlyB: string; } |
|
||||||
|
{ pageId: "only base"; onlyBase: string; };
|
||||||
|
|
||||||
|
type Extension =
|
||||||
|
{ pageId: "a"; onlyExtA: string; } |
|
||||||
|
{ pageId: "b"; onlyExtB: string; } |
|
||||||
|
{ pageId: "only ext"; onlyExt: string; };
|
||||||
|
|
||||||
|
type Got = AndByDiscriminatingKey<"pageId", Extension, Base>;
|
||||||
|
|
||||||
|
type Expected =
|
||||||
|
{ pageId: "a"; onlyA: string; onlyExtA: string; } |
|
||||||
|
{ pageId: "b"; onlyB: string; onlyExtB: string; } |
|
||||||
|
{ pageId: "only base"; onlyBase: string; } |
|
||||||
|
{ pageId: "only ext"; onlyExt: string; };
|
||||||
|
|
||||||
|
doExtends<Got, Expected>();
|
||||||
|
doExtends<Expected, Got>();
|
||||||
|
|
||||||
|
const x: Got = null as any;
|
||||||
|
|
||||||
|
if (x.pageId === "a") {
|
||||||
|
|
||||||
|
x.onlyA;
|
||||||
|
x.onlyExtA;
|
||||||
|
|
||||||
|
//@ts-expect-error
|
||||||
|
x.onlyB;
|
||||||
|
|
||||||
|
//@ts-expect-error
|
||||||
|
x.onlyBase;
|
||||||
|
|
||||||
|
//@ts-expect-error
|
||||||
|
x.onlyExt;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (x.pageId === "b") {
|
||||||
|
|
||||||
|
x.onlyB;
|
||||||
|
x.onlyExtB;
|
||||||
|
|
||||||
|
//@ts-expect-error
|
||||||
|
x.onlyA;
|
||||||
|
|
||||||
|
//@ts-expect-error
|
||||||
|
x.onlyBase;
|
||||||
|
|
||||||
|
//@ts-expect-error
|
||||||
|
x.onlyExt;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (x.pageId === "only base") {
|
||||||
|
|
||||||
|
x.onlyBase;
|
||||||
|
|
||||||
|
//@ts-expect-error
|
||||||
|
x.onlyA;
|
||||||
|
|
||||||
|
//@ts-expect-error
|
||||||
|
x.onlyB;
|
||||||
|
|
||||||
|
//@ts-expect-error
|
||||||
|
x.onlyExt;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (x.pageId === "only ext") {
|
||||||
|
|
||||||
|
x.onlyExt;
|
||||||
|
|
||||||
|
|
||||||
|
//@ts-expect-error
|
||||||
|
x.onlyA;
|
||||||
|
|
||||||
|
//@ts-expect-error
|
||||||
|
x.onlyB;
|
||||||
|
|
||||||
|
//@ts-expect-error
|
||||||
|
x.onlyBase;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
414
yarn.lock
414
yarn.lock
@ -3,72 +3,71 @@
|
|||||||
|
|
||||||
|
|
||||||
"@babel/code-frame@^7.0.0":
|
"@babel/code-frame@^7.0.0":
|
||||||
version "7.12.13"
|
version "7.14.5"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.13.tgz#dcfc826beef65e75c50e21d3837d7d95798dd658"
|
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.14.5.tgz#23b08d740e83f49c5e59945fbf1b43e80bbf4edb"
|
||||||
integrity sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==
|
integrity sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/highlight" "^7.12.13"
|
"@babel/highlight" "^7.14.5"
|
||||||
|
|
||||||
"@babel/helper-module-imports@^7.7.0":
|
"@babel/helper-module-imports@^7.12.13":
|
||||||
version "7.12.13"
|
version "7.14.5"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.12.13.tgz#ec67e4404f41750463e455cc3203f6a32e93fcb0"
|
resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.14.5.tgz#6d1a44df6a38c957aa7c312da076429f11b422f3"
|
||||||
integrity sha512-NGmfvRp9Rqxy0uHSSVP+SRIW1q31a7Ji10cLBcqSDUngGentY4FRiHOFZFE1CLU5eiL0oE8reH7Tg1y99TDM/g==
|
integrity sha512-SwrNHu5QWS84XlHwGYPDtCxcA0hrSlL2yhWYLgeOc0w7ccOl2qv4s/nARI0aYZW+bSwAL5CukeXA47B/1NKcnQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/types" "^7.12.13"
|
"@babel/types" "^7.14.5"
|
||||||
|
|
||||||
"@babel/helper-plugin-utils@^7.12.13":
|
"@babel/helper-plugin-utils@^7.14.5":
|
||||||
version "7.13.0"
|
version "7.14.5"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.13.0.tgz#806526ce125aed03373bc416a828321e3a6a33af"
|
resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz#5ac822ce97eec46741ab70a517971e443a70c5a9"
|
||||||
integrity sha512-ZPafIPSwzUlAoWT8DKs1W2VyF2gOWthGd5NGFMsBcMMol+ZhK+EQY/e6V96poa6PA/Bh+C9plWN0hXO1uB8AfQ==
|
integrity sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ==
|
||||||
|
|
||||||
"@babel/helper-validator-identifier@^7.12.11":
|
"@babel/helper-validator-identifier@^7.14.5":
|
||||||
version "7.12.11"
|
version "7.14.5"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz#c9a1f021917dcb5ccf0d4e453e399022981fc9ed"
|
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz#d0f0e277c512e0c938277faa85a3968c9a44c0e8"
|
||||||
integrity sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==
|
integrity sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg==
|
||||||
|
|
||||||
"@babel/highlight@^7.12.13":
|
"@babel/highlight@^7.14.5":
|
||||||
version "7.13.10"
|
version "7.14.5"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.13.10.tgz#a8b2a66148f5b27d666b15d81774347a731d52d1"
|
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.5.tgz#6861a52f03966405001f6aa534a01a24d99e8cd9"
|
||||||
integrity sha512-5aPpe5XQPzflQrFwL1/QoeHkP2MsA4JCntcXHRhEsdsfPVkvPi2w7Qix4iV7t5S/oC9OodGrggd8aco1g3SZFg==
|
integrity sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/helper-validator-identifier" "^7.12.11"
|
"@babel/helper-validator-identifier" "^7.14.5"
|
||||||
chalk "^2.0.0"
|
chalk "^2.0.0"
|
||||||
js-tokens "^4.0.0"
|
js-tokens "^4.0.0"
|
||||||
|
|
||||||
"@babel/plugin-syntax-jsx@^7.12.1":
|
"@babel/plugin-syntax-jsx@^7.12.13":
|
||||||
version "7.12.13"
|
version "7.14.5"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.13.tgz#044fb81ebad6698fe62c478875575bcbb9b70f15"
|
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.14.5.tgz#000e2e25d8673cce49300517a3eda44c263e4201"
|
||||||
integrity sha512-d4HM23Q1K7oq/SLNmG6mRt85l2csmQ0cHRaxRXjKW0YFdEXqlZ5kzFQKH5Uc3rDJECgu+yCRgPkG04Mm98R/1g==
|
integrity sha512-ohuFIsOMXJnbOMRfX7/w7LocdR6R7whhuRD4ax8IipLcLPlZGJKkBxgHp++U4N/vKyU16/YDQr2f5seajD3jIw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/helper-plugin-utils" "^7.12.13"
|
"@babel/helper-plugin-utils" "^7.14.5"
|
||||||
|
|
||||||
"@babel/runtime@^7.7.2":
|
"@babel/runtime@^7.13.10", "@babel/runtime@^7.7.2":
|
||||||
version "7.13.10"
|
version "7.14.6"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.13.10.tgz#47d42a57b6095f4468da440388fdbad8bebf0d7d"
|
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.6.tgz#535203bc0892efc7dec60bdc27b2ecf6e409062d"
|
||||||
integrity sha512-4QPkjJq6Ns3V/RgpEahRk+AGfL0eO6RHHtTWoNNr5mO49G6B5+X6d6THgWEAvTrznU5xYpbAlVKRYcsCgh/Akw==
|
integrity sha512-/PCB2uJ7oM44tz8YhC4Z/6PeOKXp4K588f+5M3clr1M4zbqztlo0XEfJ2LEzj/FgwfgGcIdl8n7YYjTCI0BYwg==
|
||||||
dependencies:
|
dependencies:
|
||||||
regenerator-runtime "^0.13.4"
|
regenerator-runtime "^0.13.4"
|
||||||
|
|
||||||
"@babel/types@^7.12.13":
|
"@babel/types@^7.14.5":
|
||||||
version "7.13.0"
|
version "7.14.5"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.13.0.tgz#74424d2816f0171b4100f0ab34e9a374efdf7f80"
|
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.14.5.tgz#3bb997ba829a2104cedb20689c4a5b8121d383ff"
|
||||||
integrity sha512-hE+HE8rnG1Z6Wzo+MhaKE5lM5eMx71T4EHJgku2E3xIfaULhDcxiiRxUYgwX8qwP1BBSlag+TdGOt6JAidIZTA==
|
integrity sha512-M/NzBpEL95I5Hh4dwhin5JlE7EzO5PHMAuzjxss3tiOBD46KfQvVedN/3jEPZvdRvtsK2222XfdHogNIttFgcg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/helper-validator-identifier" "^7.12.11"
|
"@babel/helper-validator-identifier" "^7.14.5"
|
||||||
lodash "^4.17.19"
|
|
||||||
to-fast-properties "^2.0.0"
|
to-fast-properties "^2.0.0"
|
||||||
|
|
||||||
"@emotion/babel-plugin@^11.0.0":
|
"@emotion/babel-plugin@^11.0.0":
|
||||||
version "11.2.0"
|
version "11.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.2.0.tgz#f25c6df8ec045dad5ae6ca63df0791673b98c920"
|
resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.3.0.tgz#3a16850ba04d8d9651f07f3fb674b3436a4fb9d7"
|
||||||
integrity sha512-lsnQBnl3l4wu/FJoyHnYRpHJeIPNkOBMbtDUIXcO8luulwRKZXPvA10zd2eXVN6dABIWNX4E34en/jkejIg/yA==
|
integrity sha512-UZKwBV2rADuhRp+ZOGgNWg2eYgbzKzQXfQPtJbu/PLy8onurxlNCLvxMQEvlr1/GudguPI5IU9qIY1+2z1M5bA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/helper-module-imports" "^7.7.0"
|
"@babel/helper-module-imports" "^7.12.13"
|
||||||
"@babel/plugin-syntax-jsx" "^7.12.1"
|
"@babel/plugin-syntax-jsx" "^7.12.13"
|
||||||
"@babel/runtime" "^7.7.2"
|
"@babel/runtime" "^7.13.10"
|
||||||
"@emotion/hash" "^0.8.0"
|
"@emotion/hash" "^0.8.0"
|
||||||
"@emotion/memoize" "^0.7.5"
|
"@emotion/memoize" "^0.7.5"
|
||||||
"@emotion/serialize" "^1.0.0"
|
"@emotion/serialize" "^1.0.2"
|
||||||
babel-plugin-macros "^2.6.1"
|
babel-plugin-macros "^2.6.1"
|
||||||
convert-source-map "^1.5.0"
|
convert-source-map "^1.5.0"
|
||||||
escape-string-regexp "^4.0.0"
|
escape-string-regexp "^4.0.0"
|
||||||
@ -77,9 +76,9 @@
|
|||||||
stylis "^4.0.3"
|
stylis "^4.0.3"
|
||||||
|
|
||||||
"@emotion/cache@^11.1.3":
|
"@emotion/cache@^11.1.3":
|
||||||
version "11.1.3"
|
version "11.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.1.3.tgz#c7683a9484bcd38d5562f2b9947873cf66829afd"
|
resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.4.0.tgz#293fc9d9a7a38b9aad8e9337e5014366c3b09ac0"
|
||||||
integrity sha512-n4OWinUPJVaP6fXxWZD9OUeQ0lY7DvtmtSuqtRWT0Ofo/sBLCVSgb4/Oa0Q5eFxcwablRKjUXqXtNZVyEwCAuA==
|
integrity sha512-Zx70bjE7LErRO9OaZrhf22Qye1y4F7iDl+ITjet0J+i+B88PrAOBkKvaAWhxsZf72tDLajwCgfCjJ2dvH77C3g==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@emotion/memoize" "^0.7.4"
|
"@emotion/memoize" "^0.7.4"
|
||||||
"@emotion/sheet" "^1.0.0"
|
"@emotion/sheet" "^1.0.0"
|
||||||
@ -108,10 +107,10 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.7.5.tgz#2c40f81449a4e554e9fc6396910ed4843ec2be50"
|
resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.7.5.tgz#2c40f81449a4e554e9fc6396910ed4843ec2be50"
|
||||||
integrity sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ==
|
integrity sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ==
|
||||||
|
|
||||||
"@emotion/serialize@^1.0.0":
|
"@emotion/serialize@^1.0.0", "@emotion/serialize@^1.0.2":
|
||||||
version "1.0.1"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.0.1.tgz#322cdebfdbb5a88946f17006548191859b9b0855"
|
resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.0.2.tgz#77cb21a0571c9f68eb66087754a65fa97bfcd965"
|
||||||
integrity sha512-TXlKs5sgUKhFlszp/rg4lIAZd7UUSmJpwaf9/lAEFcUh2vPi32i7x4wk7O8TN8L8v2Ol8k0CxnhRBY0zQalTxA==
|
integrity sha512-95MgNJ9+/ajxU7QIAruiOAdYNjxZX7G2mhgrtDWswA21VviYIRP1R5QilZ/bDY42xiKsaktP4egJb3QdYQZi1A==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@emotion/hash" "^0.8.0"
|
"@emotion/hash" "^0.8.0"
|
||||||
"@emotion/memoize" "^0.7.4"
|
"@emotion/memoize" "^0.7.4"
|
||||||
@ -140,16 +139,16 @@
|
|||||||
integrity sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==
|
integrity sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==
|
||||||
|
|
||||||
"@types/mdast@^3.0.0", "@types/mdast@^3.0.3":
|
"@types/mdast@^3.0.0", "@types/mdast@^3.0.3":
|
||||||
version "3.0.3"
|
version "3.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-3.0.3.tgz#2d7d671b1cd1ea3deb306ea75036c2a0407d2deb"
|
resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-3.0.4.tgz#8ee6b5200751b6cadb9a043ca39612693ad6cb9e"
|
||||||
integrity sha512-SXPBMnFVQg1s00dlMCc/jCdvPqdE4mXaMMCeRlxLDmTAEoegHT53xKtkDnzDTOcmMHUfcjyf36/YYZ6SxRdnsw==
|
integrity sha512-gIdhbLDFlspL53xzol2hVzrXAbzt71erJHoOwQZWssjaiouOotf03lNtMmFm9VfFkvnLWccSVjUAZGQ5Kqw+jA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/unist" "*"
|
"@types/unist" "*"
|
||||||
|
|
||||||
"@types/node@^10.0.0":
|
"@types/node@^10.0.0":
|
||||||
version "10.17.55"
|
version "10.17.60"
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.55.tgz#a147f282edec679b894d4694edb5abeb595fecbd"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.60.tgz#35f3d6213daed95da7f0f73e75bcc6980e90597b"
|
||||||
integrity sha512-koZJ89uLZufDvToeWO5BrC4CR4OUfHnUz2qoPs/daQH6qq3IN62QFxCTZ+bKaCE0xaoCAJYE4AXre8AbghCrhg==
|
integrity sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==
|
||||||
|
|
||||||
"@types/parse-json@^4.0.0":
|
"@types/parse-json@^4.0.0":
|
||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
@ -157,33 +156,28 @@
|
|||||||
integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==
|
integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==
|
||||||
|
|
||||||
"@types/prop-types@*":
|
"@types/prop-types@*":
|
||||||
version "15.7.3"
|
version "15.7.4"
|
||||||
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7"
|
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.4.tgz#fcf7205c25dff795ee79af1e30da2c9790808f11"
|
||||||
integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==
|
integrity sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==
|
||||||
|
|
||||||
"@types/react@^17.0.0":
|
"@types/react@^17.0.0":
|
||||||
version "17.0.3"
|
version "17.0.14"
|
||||||
resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.3.tgz#ba6e215368501ac3826951eef2904574c262cc79"
|
resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.14.tgz#f0629761ca02945c4e8fea99b8177f4c5c61fb0f"
|
||||||
integrity sha512-wYOUxIgs2HZZ0ACNiIayItyluADNbONl7kt8lkLjVK8IitMH5QMyAh75Fwhmo37r1m7L2JaFj03sIfxBVDvRAg==
|
integrity sha512-0WwKHUbWuQWOce61UexYuWTGuGY/8JvtUe/dtQ6lR4sZ3UiylHotJeWpf3ArP9+DSGUoLY3wbU59VyMrJps5VQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/prop-types" "*"
|
"@types/prop-types" "*"
|
||||||
"@types/scheduler" "*"
|
"@types/scheduler" "*"
|
||||||
csstype "^3.0.2"
|
csstype "^3.0.2"
|
||||||
|
|
||||||
"@types/scheduler@*":
|
"@types/scheduler@*":
|
||||||
version "0.16.1"
|
version "0.16.2"
|
||||||
resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.1.tgz#18845205e86ff0038517aab7a18a62a6b9f71275"
|
resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39"
|
||||||
integrity sha512-EaCxbanVeyxDRTQBkdLb3Bvl/HK7PBK6UJjsSixB0iHKoWxE5uu2Q/DgtpOhPIojN0Zl1whvOd7PoHs2P0s5eA==
|
integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==
|
||||||
|
|
||||||
"@types/unist@*", "@types/unist@^2.0.0", "@types/unist@^2.0.2", "@types/unist@^2.0.3":
|
"@types/unist@*", "@types/unist@^2.0.0", "@types/unist@^2.0.2", "@types/unist@^2.0.3":
|
||||||
version "2.0.3"
|
version "2.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.3.tgz#9c088679876f374eb5983f150d4787aa6fb32d7e"
|
resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.5.tgz#fdd299f23205c3455af88ce618dd65c14cb73e22"
|
||||||
integrity sha512-FvUupuM3rlRsRtCN+fDudtmytGO6iHJuuRKS1Ss0pG5z8oX0diNEw94UEL7hgDbpN94rgaK5R7sWm6RrSkZuAQ==
|
integrity sha512-wnra4Vw9dopnuybR6HBywJ/URYpYrKLoepBTEtgfJup8Ahoi2zJECPP2cwiXp7btTvOT2CULv87aQRA4eZSP6g==
|
||||||
|
|
||||||
abbrev@1:
|
|
||||||
version "1.1.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
|
|
||||||
integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==
|
|
||||||
|
|
||||||
ansi-regex@^5.0.0:
|
ansi-regex@^5.0.0:
|
||||||
version "5.0.0"
|
version "5.0.0"
|
||||||
@ -219,9 +213,9 @@ bail@^1.0.0:
|
|||||||
integrity sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ==
|
integrity sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ==
|
||||||
|
|
||||||
balanced-match@^1.0.0:
|
balanced-match@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
|
||||||
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
|
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
|
||||||
|
|
||||||
boolbase@^1.0.0:
|
boolbase@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
@ -265,29 +259,29 @@ character-reference-invalid@^1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz#083329cda0eae272ab3dbbf37e9a382c13af1560"
|
resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz#083329cda0eae272ab3dbbf37e9a382c13af1560"
|
||||||
integrity sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==
|
integrity sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==
|
||||||
|
|
||||||
cheerio-select-tmp@^0.1.0:
|
cheerio-select@^1.5.0:
|
||||||
version "0.1.1"
|
version "1.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/cheerio-select-tmp/-/cheerio-select-tmp-0.1.1.tgz#55bbef02a4771710195ad736d5e346763ca4e646"
|
resolved "https://registry.yarnpkg.com/cheerio-select/-/cheerio-select-1.5.0.tgz#faf3daeb31b17c5e1a9dabcee288aaf8aafa5823"
|
||||||
integrity sha512-YYs5JvbpU19VYJyj+F7oYrIE2BOll1/hRU7rEy/5+v9BzkSo3bK81iAeeQEMI92vRIxz677m72UmJUiVwwgjfQ==
|
integrity sha512-qocaHPv5ypefh6YNxvnbABM07KMxExbtbfuJoIie3iZXX1ERwYmJcIiRrr9H05ucQP1k28dav8rpdDgjQd8drg==
|
||||||
dependencies:
|
dependencies:
|
||||||
css-select "^3.1.2"
|
css-select "^4.1.3"
|
||||||
css-what "^4.0.0"
|
css-what "^5.0.1"
|
||||||
domelementtype "^2.1.0"
|
domelementtype "^2.2.0"
|
||||||
domhandler "^4.0.0"
|
domhandler "^4.2.0"
|
||||||
domutils "^2.4.4"
|
domutils "^2.7.0"
|
||||||
|
|
||||||
cheerio@^1.0.0-rc.5:
|
cheerio@^1.0.0-rc.5:
|
||||||
version "1.0.0-rc.5"
|
version "1.0.0-rc.10"
|
||||||
resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.5.tgz#88907e1828674e8f9fee375188b27dadd4f0fa2f"
|
resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.10.tgz#2ba3dcdfcc26e7956fc1f440e61d51c643379f3e"
|
||||||
integrity sha512-yoqps/VCaZgN4pfXtenwHROTp8NG6/Hlt4Jpz2FEP0ZJQ+ZUkVDd0hAPDNKhj3nakpfPt/CNs57yEtxD1bXQiw==
|
integrity sha512-g0J0q/O6mW8z5zxQ3A8E8J1hUgp4SMOvEoW/x84OwyHKe/Zccz83PVT4y5Crcr530FV6NgmKI1qvGTKVl9XXVw==
|
||||||
dependencies:
|
dependencies:
|
||||||
cheerio-select-tmp "^0.1.0"
|
cheerio-select "^1.5.0"
|
||||||
dom-serializer "~1.2.0"
|
dom-serializer "^1.3.2"
|
||||||
domhandler "^4.0.0"
|
domhandler "^4.2.0"
|
||||||
entities "~2.1.0"
|
htmlparser2 "^6.1.0"
|
||||||
htmlparser2 "^6.0.0"
|
parse5 "^6.0.1"
|
||||||
parse5 "^6.0.0"
|
parse5-htmlparser2-tree-adapter "^6.0.1"
|
||||||
parse5-htmlparser2-tree-adapter "^6.0.0"
|
tslib "^2.2.0"
|
||||||
|
|
||||||
cliui@^7.0.2:
|
cliui@^7.0.2:
|
||||||
version "7.0.4"
|
version "7.0.4"
|
||||||
@ -328,9 +322,9 @@ concat-map@0.0.1:
|
|||||||
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
|
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
|
||||||
|
|
||||||
convert-source-map@^1.5.0:
|
convert-source-map@^1.5.0:
|
||||||
version "1.7.0"
|
version "1.8.0"
|
||||||
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442"
|
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369"
|
||||||
integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==
|
integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==
|
||||||
dependencies:
|
dependencies:
|
||||||
safe-buffer "~5.1.1"
|
safe-buffer "~5.1.1"
|
||||||
|
|
||||||
@ -363,26 +357,26 @@ cosmiconfig@^6.0.0:
|
|||||||
path-type "^4.0.0"
|
path-type "^4.0.0"
|
||||||
yaml "^1.7.2"
|
yaml "^1.7.2"
|
||||||
|
|
||||||
css-select@^3.1.2:
|
css-select@^4.1.3:
|
||||||
version "3.1.2"
|
version "4.1.3"
|
||||||
resolved "https://registry.yarnpkg.com/css-select/-/css-select-3.1.2.tgz#d52cbdc6fee379fba97fb0d3925abbd18af2d9d8"
|
resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.1.3.tgz#a70440f70317f2669118ad74ff105e65849c7067"
|
||||||
integrity sha512-qmss1EihSuBNWNNhHjxzxSfJoFBM/lERB/Q4EnsJQQC62R2evJDW481091oAdOr9uh46/0n4nrg0It5cAnj1RA==
|
integrity sha512-gT3wBNd9Nj49rAbmtFHj1cljIAOLYSX1nZ8CB7TBO3INYckygm5B7LISU/szY//YmdiSLbJvDLOx9VnMVpMBxA==
|
||||||
dependencies:
|
dependencies:
|
||||||
boolbase "^1.0.0"
|
boolbase "^1.0.0"
|
||||||
css-what "^4.0.0"
|
css-what "^5.0.0"
|
||||||
domhandler "^4.0.0"
|
domhandler "^4.2.0"
|
||||||
domutils "^2.4.3"
|
domutils "^2.6.0"
|
||||||
nth-check "^2.0.0"
|
nth-check "^2.0.0"
|
||||||
|
|
||||||
css-what@^4.0.0:
|
css-what@^5.0.0, css-what@^5.0.1:
|
||||||
version "4.0.0"
|
version "5.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/css-what/-/css-what-4.0.0.tgz#35e73761cab2eeb3d3661126b23d7aa0e8432233"
|
resolved "https://registry.yarnpkg.com/css-what/-/css-what-5.0.1.tgz#3efa820131f4669a8ac2408f9c32e7c7de9f4cad"
|
||||||
integrity sha512-teijzG7kwYfNVsUh2H/YN62xW3KK9YhXEgSlbxMlcyjPNvdKJqFx5lrwlJgoFP1ZHlB89iGDlo/JyshKeRhv5A==
|
integrity sha512-FYDTSHb/7KXsWICVsxdmiExPjCfRC4qRFBdVwv7Ax9hMnvMmEjP9RfxTEZ3qPZGmADDn2vAKSo9UcN1jKVYscg==
|
||||||
|
|
||||||
csstype@^3.0.2:
|
csstype@^3.0.2:
|
||||||
version "3.0.7"
|
version "3.0.8"
|
||||||
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.7.tgz#2a5fb75e1015e84dd15692f71e89a1450290950b"
|
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.8.tgz#d2266a792729fb227cd216fb572f43728e1ad340"
|
||||||
integrity sha512-KxnUB0ZMlnUWCsx2Z8MUsr6qV6ja1w9ArPErJaJaF8a5SOWoHLIszeCTKGRGRgtLgYrs1E8CHkNSP1VZTTPc9g==
|
integrity sha512-jXKhWqXPmlUeoQnF/EhTtTl4C9SnrxSH/jZUih3jmO6lBKr99rP3/+FmrMj4EFpOXzMtXHAZkd3x0E6h6Fgflw==
|
||||||
|
|
||||||
d@1, d@^1.0.1:
|
d@1, d@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
@ -393,27 +387,22 @@ d@1, d@^1.0.1:
|
|||||||
type "^1.0.1"
|
type "^1.0.1"
|
||||||
|
|
||||||
debug@^4.0.0:
|
debug@^4.0.0:
|
||||||
version "4.3.1"
|
version "4.3.2"
|
||||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee"
|
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b"
|
||||||
integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==
|
integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==
|
||||||
dependencies:
|
dependencies:
|
||||||
ms "2.1.2"
|
ms "2.1.2"
|
||||||
|
|
||||||
dom-serializer@^1.0.1, dom-serializer@~1.2.0:
|
dom-serializer@^1.0.1, dom-serializer@^1.3.2:
|
||||||
version "1.2.0"
|
version "1.3.2"
|
||||||
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.2.0.tgz#3433d9136aeb3c627981daa385fc7f32d27c48f1"
|
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.3.2.tgz#6206437d32ceefaec7161803230c7a20bc1b4d91"
|
||||||
integrity sha512-n6kZFH/KlCrqs/1GHMOd5i2fd/beQHuehKdWvNNffbGHTr/almdhuVvTVFb3V7fglz+nC50fFusu3lY33h12pA==
|
integrity sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==
|
||||||
dependencies:
|
dependencies:
|
||||||
domelementtype "^2.0.1"
|
domelementtype "^2.0.1"
|
||||||
domhandler "^4.0.0"
|
domhandler "^4.2.0"
|
||||||
entities "^2.0.0"
|
entities "^2.0.0"
|
||||||
|
|
||||||
domelementtype@^2.0.1, domelementtype@^2.1.0:
|
domelementtype@^2.0.1, domelementtype@^2.2.0:
|
||||||
version "2.1.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.1.0.tgz#a851c080a6d1c3d94344aed151d99f669edf585e"
|
|
||||||
integrity sha512-LsTgx/L5VpD+Q8lmsXSHW2WpA+eBlZ9HPf3erD1IoPF00/3JKHZ3BknUVA2QGDNu69ZNmyFmCWBSO45XjYKC5w==
|
|
||||||
|
|
||||||
domelementtype@^2.2.0:
|
|
||||||
version "2.2.0"
|
version "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==
|
||||||
@ -425,37 +414,21 @@ domhandler@^3.3.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
domelementtype "^2.0.1"
|
domelementtype "^2.0.1"
|
||||||
|
|
||||||
domhandler@^4.0.0:
|
domhandler@^4.0.0, domhandler@^4.2.0:
|
||||||
version "4.0.0"
|
version "4.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.0.0.tgz#01ea7821de996d85f69029e81fa873c21833098e"
|
resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.2.0.tgz#f9768a5f034be60a89a27c2e4d0f74eba0d8b059"
|
||||||
integrity sha512-KPTbnGQ1JeEMQyO1iYXoagsI6so/C96HZiFyByU3T6iAzpXn8EGEvct6unm1ZGoed8ByO2oirxgwxBmqKF9haA==
|
integrity sha512-zk7sgt970kzPks2Bf+dwT/PLzghLnsivb9CcxkvR8Mzr66Olr0Ofd8neSbglHJHaHa2MadfoSdNlKYAaafmWfA==
|
||||||
dependencies:
|
|
||||||
domelementtype "^2.1.0"
|
|
||||||
|
|
||||||
domhandler@^4.1.0:
|
|
||||||
version "4.1.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.1.0.tgz#c1d8d494d5ec6db22de99e46a149c2a4d23ddd43"
|
|
||||||
integrity sha512-/6/kmsGlMY4Tup/nGVutdrK9yQi4YjWVcVeoQmixpzjOUK1U7pQkvAPHBJeUxOgxF0J8f8lwCJSlCfD0V4CMGQ==
|
|
||||||
dependencies:
|
dependencies:
|
||||||
domelementtype "^2.2.0"
|
domelementtype "^2.2.0"
|
||||||
|
|
||||||
domutils@^2.4.2:
|
domutils@^2.4.2, domutils@^2.5.2, domutils@^2.6.0, domutils@^2.7.0:
|
||||||
version "2.5.2"
|
version "2.7.0"
|
||||||
resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.5.2.tgz#37ef8ba087dff1a17175e7092e8a042e4b050e6c"
|
resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.7.0.tgz#8ebaf0c41ebafcf55b0b72ec31c56323712c5442"
|
||||||
integrity sha512-MHTthCb1zj8f1GVfRpeZUbohQf/HdBos0oX5gZcQFepOZPLLRyj6Wn7XS7EMnY7CVpwv8863u2vyE83Hfu28HQ==
|
integrity sha512-8eaHa17IwJUPAiB+SoTYBo5mCdeMgdcAoXJ59m6DT1vw+5iLS3gNoqYaRowaBKtGVrOF1Jz4yDTgYKLK2kvfJg==
|
||||||
dependencies:
|
dependencies:
|
||||||
dom-serializer "^1.0.1"
|
dom-serializer "^1.0.1"
|
||||||
domelementtype "^2.2.0"
|
domelementtype "^2.2.0"
|
||||||
domhandler "^4.1.0"
|
domhandler "^4.2.0"
|
||||||
|
|
||||||
domutils@^2.4.3, domutils@^2.4.4:
|
|
||||||
version "2.5.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.5.0.tgz#42f49cffdabb92ad243278b331fd761c1c2d3039"
|
|
||||||
integrity sha512-Ho16rzNMOFk2fPwChGh3D2D9OEHAfG19HgmRR2l+WLSsIstNsAYBzePH412bL0y5T44ejABIVfTHQ8nqi/tBCg==
|
|
||||||
dependencies:
|
|
||||||
dom-serializer "^1.0.1"
|
|
||||||
domelementtype "^2.0.1"
|
|
||||||
domhandler "^4.0.0"
|
|
||||||
|
|
||||||
emoji-regex@^8.0.0:
|
emoji-regex@^8.0.0:
|
||||||
version "8.0.0"
|
version "8.0.0"
|
||||||
@ -467,11 +440,6 @@ entities@^2.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55"
|
resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55"
|
||||||
integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==
|
integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==
|
||||||
|
|
||||||
entities@~2.1.0:
|
|
||||||
version "2.1.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/entities/-/entities-2.1.0.tgz#992d3129cf7df6870b96c57858c249a120f8b8b5"
|
|
||||||
integrity sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==
|
|
||||||
|
|
||||||
error-ex@^1.3.1:
|
error-ex@^1.3.1:
|
||||||
version "1.3.2"
|
version "1.3.2"
|
||||||
resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
|
resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
|
||||||
@ -546,6 +514,15 @@ evt@2.0.0-beta.15:
|
|||||||
minimal-polyfills "^2.1.5"
|
minimal-polyfills "^2.1.5"
|
||||||
run-exclusive "^2.2.14"
|
run-exclusive "^2.2.14"
|
||||||
|
|
||||||
|
evt@2.0.0-beta.20:
|
||||||
|
version "2.0.0-beta.20"
|
||||||
|
resolved "https://registry.yarnpkg.com/evt/-/evt-2.0.0-beta.20.tgz#a018ff46a7dcb475c488e6508ac3aec7910e76fb"
|
||||||
|
integrity sha512-aIIqrDkQ5Hm4zzW7v9STZbO9RcwAI2vMQUkjQ7Bw4YkWOv2zoIl6ooJZ6942JU7UCYKWSi1u5BBtyoiXUSsnMA==
|
||||||
|
dependencies:
|
||||||
|
minimal-polyfills "^2.2.1"
|
||||||
|
run-exclusive "^2.2.14"
|
||||||
|
tsafe "^0.2.2"
|
||||||
|
|
||||||
ext@^1.1.2:
|
ext@^1.1.2:
|
||||||
version "1.4.0"
|
version "1.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/ext/-/ext-1.4.0.tgz#89ae7a07158f79d35517882904324077e4379244"
|
resolved "https://registry.yarnpkg.com/ext/-/ext-1.4.0.tgz#89ae7a07158f79d35517882904324077e4379244"
|
||||||
@ -579,9 +556,9 @@ get-caller-file@^2.0.5:
|
|||||||
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
|
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
|
||||||
|
|
||||||
glob@^7.0.5, glob@^7.1.3:
|
glob@^7.0.5, glob@^7.1.3:
|
||||||
version "7.1.6"
|
version "7.1.7"
|
||||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
|
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90"
|
||||||
integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
|
integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
fs.realpath "^1.0.0"
|
fs.realpath "^1.0.0"
|
||||||
inflight "^1.0.4"
|
inflight "^1.0.4"
|
||||||
@ -622,14 +599,14 @@ htmlparser2@^5.0:
|
|||||||
domutils "^2.4.2"
|
domutils "^2.4.2"
|
||||||
entities "^2.0.0"
|
entities "^2.0.0"
|
||||||
|
|
||||||
htmlparser2@^6.0.0:
|
htmlparser2@^6.1.0:
|
||||||
version "6.0.1"
|
version "6.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.0.1.tgz#422521231ef6d42e56bd411da8ba40aa36e91446"
|
resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7"
|
||||||
integrity sha512-GDKPd+vk4jvSuvCbyuzx/unmXkk090Azec7LovXP8as1Hn8q9p3hbjmDGbUqqhknw0ajwit6LiiWqfiTUPMK7w==
|
integrity sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==
|
||||||
dependencies:
|
dependencies:
|
||||||
domelementtype "^2.0.1"
|
domelementtype "^2.0.1"
|
||||||
domhandler "^4.0.0"
|
domhandler "^4.0.0"
|
||||||
domutils "^2.4.4"
|
domutils "^2.5.2"
|
||||||
entities "^2.0.0"
|
entities "^2.0.0"
|
||||||
|
|
||||||
import-fresh@^3.1.0:
|
import-fresh@^3.1.0:
|
||||||
@ -682,9 +659,9 @@ is-buffer@^2.0.0:
|
|||||||
integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==
|
integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==
|
||||||
|
|
||||||
is-core-module@^2.2.0:
|
is-core-module@^2.2.0:
|
||||||
version "2.2.0"
|
version "2.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.2.0.tgz#97037ef3d52224d85163f5597b2b63d9afed981a"
|
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.4.0.tgz#8e9fc8e15027b011418026e98f0e6f4d86305cc1"
|
||||||
integrity sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==
|
integrity sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==
|
||||||
dependencies:
|
dependencies:
|
||||||
has "^1.0.3"
|
has "^1.0.3"
|
||||||
|
|
||||||
@ -743,11 +720,6 @@ lodash.camelcase@^4.3.0:
|
|||||||
resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6"
|
resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6"
|
||||||
integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY=
|
integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY=
|
||||||
|
|
||||||
lodash@^4.17.19:
|
|
||||||
version "4.17.21"
|
|
||||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
|
||||||
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
|
||||||
|
|
||||||
loose-envify@^1.1.0, loose-envify@^1.4.0:
|
loose-envify@^1.1.0, loose-envify@^1.4.0:
|
||||||
version "1.4.0"
|
version "1.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
|
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
|
||||||
@ -762,13 +734,6 @@ lru-queue@^0.1.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
es5-ext "~0.10.2"
|
es5-ext "~0.10.2"
|
||||||
|
|
||||||
markdown@^0.5.0:
|
|
||||||
version "0.5.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/markdown/-/markdown-0.5.0.tgz#28205b565a8ae7592de207463d6637dc182722b2"
|
|
||||||
integrity sha1-KCBbVlqK51kt4gdGPWY33BgnIrI=
|
|
||||||
dependencies:
|
|
||||||
nopt "~2.1.1"
|
|
||||||
|
|
||||||
mdast-add-list-metadata@1.0.1:
|
mdast-add-list-metadata@1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/mdast-add-list-metadata/-/mdast-add-list-metadata-1.0.1.tgz#95e73640ce2fc1fa2dcb7ec443d09e2bfe7db4cf"
|
resolved "https://registry.yarnpkg.com/mdast-add-list-metadata/-/mdast-add-list-metadata-1.0.1.tgz#95e73640ce2fc1fa2dcb7ec443d09e2bfe7db4cf"
|
||||||
@ -814,10 +779,10 @@ micromark@~2.11.0:
|
|||||||
debug "^4.0.0"
|
debug "^4.0.0"
|
||||||
parse-entities "^2.0.0"
|
parse-entities "^2.0.0"
|
||||||
|
|
||||||
minimal-polyfills@^2.1.5, minimal-polyfills@^2.1.6:
|
minimal-polyfills@^2.1.5, minimal-polyfills@^2.1.6, minimal-polyfills@^2.2.1:
|
||||||
version "2.1.6"
|
version "2.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/minimal-polyfills/-/minimal-polyfills-2.1.6.tgz#eab50832add31afd40a22b38fb76d1fdcd2a51e4"
|
resolved "https://registry.yarnpkg.com/minimal-polyfills/-/minimal-polyfills-2.2.1.tgz#7249d7ece666d3b4e1ec1c1b8f949eb9d44e2308"
|
||||||
integrity sha512-vqoxj7eMzsqX0M6/dkgoNFPw6Mztgn5qjSl0bWGboQeU7Y4UPLeyoqQw6JI+0qmBcJYdkr3nK7dqY8u/fgRp5g==
|
integrity sha512-WLmHQrsZob4rVYf8yHapZPNJZ3sspGa/sN8abuSD59b0FifDEE7HMfLUi24z7mPZqTpBXy4Svp+iGvAmclCmXg==
|
||||||
|
|
||||||
minimatch@^3.0.3, minimatch@^3.0.4:
|
minimatch@^3.0.3, minimatch@^3.0.4:
|
||||||
version "3.0.4"
|
version "3.0.4"
|
||||||
@ -854,13 +819,6 @@ noms@0.0.0:
|
|||||||
inherits "^2.0.1"
|
inherits "^2.0.1"
|
||||||
readable-stream "~1.0.31"
|
readable-stream "~1.0.31"
|
||||||
|
|
||||||
nopt@~2.1.1:
|
|
||||||
version "2.1.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/nopt/-/nopt-2.1.2.tgz#6cccd977b80132a07731d6e8ce58c2c8303cf9af"
|
|
||||||
integrity sha1-bMzZd7gBMqB3MdbozljCyDA8+a8=
|
|
||||||
dependencies:
|
|
||||||
abbrev "1"
|
|
||||||
|
|
||||||
nth-check@^2.0.0:
|
nth-check@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.0.0.tgz#1bb4f6dac70072fc313e8c9cd1417b5074c0a125"
|
resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.0.0.tgz#1bb4f6dac70072fc313e8c9cd1417b5074c0a125"
|
||||||
@ -909,14 +867,14 @@ parse-json@^5.0.0:
|
|||||||
json-parse-even-better-errors "^2.3.0"
|
json-parse-even-better-errors "^2.3.0"
|
||||||
lines-and-columns "^1.1.6"
|
lines-and-columns "^1.1.6"
|
||||||
|
|
||||||
parse5-htmlparser2-tree-adapter@^6.0.0:
|
parse5-htmlparser2-tree-adapter@^6.0.1:
|
||||||
version "6.0.1"
|
version "6.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz#2cdf9ad823321140370d4dbf5d3e92c7c8ddc6e6"
|
resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz#2cdf9ad823321140370d4dbf5d3e92c7c8ddc6e6"
|
||||||
integrity sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==
|
integrity sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==
|
||||||
dependencies:
|
dependencies:
|
||||||
parse5 "^6.0.1"
|
parse5 "^6.0.1"
|
||||||
|
|
||||||
parse5@^6.0.0, parse5@^6.0.1:
|
parse5@^6.0.1:
|
||||||
version "6.0.1"
|
version "6.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b"
|
resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b"
|
||||||
integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==
|
integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==
|
||||||
@ -927,9 +885,9 @@ path-is-absolute@^1.0.0:
|
|||||||
integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
|
integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
|
||||||
|
|
||||||
path-parse@^1.0.6:
|
path-parse@^1.0.6:
|
||||||
version "1.0.6"
|
version "1.0.7"
|
||||||
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c"
|
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
|
||||||
integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==
|
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
|
||||||
|
|
||||||
path-type@^4.0.0:
|
path-type@^4.0.0:
|
||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
@ -944,14 +902,15 @@ path@^0.12.7:
|
|||||||
process "^0.11.1"
|
process "^0.11.1"
|
||||||
util "^0.10.3"
|
util "^0.10.3"
|
||||||
|
|
||||||
powerhooks@^0.0.36:
|
powerhooks@^0.1.6:
|
||||||
version "0.0.36"
|
version "0.1.6"
|
||||||
resolved "https://registry.yarnpkg.com/powerhooks/-/powerhooks-0.0.36.tgz#d973d339ad8ca7ce52ea9d288ebe8e138df7d769"
|
resolved "https://registry.yarnpkg.com/powerhooks/-/powerhooks-0.1.6.tgz#23dfb6c781bbfdd20ea37ff5a2a0fd5a9799d591"
|
||||||
integrity sha512-0fEGKLfJmuFeEYDGsAlTuvGKKdqOH059xezosmYRoGurfC1bbUlaNJD+TuzOT5cTGURi88DCLcDnhk/2eLyCyA==
|
integrity sha512-S1b5awSAimyt9jXZsm+N8/GFBtzLPzmVIUyVLq/IADDe4aAcxFWBgXSWTOFc31PlWE9uz2K/NovsQNDf4Fr52A==
|
||||||
dependencies:
|
dependencies:
|
||||||
evt "2.0.0-beta.15"
|
evt "2.0.0-beta.15"
|
||||||
memoizee "^0.4.15"
|
memoizee "^0.4.15"
|
||||||
resize-observer-polyfill "^1.5.1"
|
resize-observer-polyfill "^1.5.1"
|
||||||
|
tsafe "^0.1.0"
|
||||||
|
|
||||||
process-nextick-args@~2.0.0:
|
process-nextick-args@~2.0.0:
|
||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
@ -1006,9 +965,9 @@ react-markdown@^5.0.3:
|
|||||||
xtend "^4.0.1"
|
xtend "^4.0.1"
|
||||||
|
|
||||||
react@^17.0.1:
|
react@^17.0.1:
|
||||||
version "17.0.1"
|
version "17.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/react/-/react-17.0.1.tgz#6e0600416bd57574e3f86d92edba3d9008726127"
|
resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037"
|
||||||
integrity sha512-lG9c9UuMHdcAexXtigOZLX8exLWkW0Ku29qPRU8uhF2R9BN96dLCt0psvzPLlHc5OWkgymP3qwTRgbnw5BKx3w==
|
integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==
|
||||||
dependencies:
|
dependencies:
|
||||||
loose-envify "^1.1.0"
|
loose-envify "^1.1.0"
|
||||||
object-assign "^4.1.1"
|
object-assign "^4.1.1"
|
||||||
@ -1134,9 +1093,9 @@ strip-ansi@^6.0.0:
|
|||||||
ansi-regex "^5.0.0"
|
ansi-regex "^5.0.0"
|
||||||
|
|
||||||
stylis@^4.0.3:
|
stylis@^4.0.3:
|
||||||
version "4.0.7"
|
version "4.0.10"
|
||||||
resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.0.7.tgz#412a90c28079417f3d27c028035095e4232d2904"
|
resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.0.10.tgz#446512d1097197ab3f02fb3c258358c3f7a14240"
|
||||||
integrity sha512-OFFeUXFgwnGOKvEXaSv0D0KQ5ADP0n6g3SVONx6I/85JzNZ3u50FRwB3lVIk1QO2HNdI75tbVzc4Z66Gdp9voA==
|
integrity sha512-m3k+dk7QeJw660eIKRRn3xPF6uuvHs/FFzjX3HQ5ove0qYsiygoAhwn5a3IYKaZPo5LrYD0rfVmtv1gNY1uYwg==
|
||||||
|
|
||||||
supports-color@^5.3.0:
|
supports-color@^5.3.0:
|
||||||
version "5.5.0"
|
version "5.5.0"
|
||||||
@ -1171,6 +1130,31 @@ trough@^1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.5.tgz#b8b639cefad7d0bb2abd37d433ff8293efa5f406"
|
resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.5.tgz#b8b639cefad7d0bb2abd37d433ff8293efa5f406"
|
||||||
integrity sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==
|
integrity sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==
|
||||||
|
|
||||||
|
ts-toolbelt@^9.6.0:
|
||||||
|
version "9.6.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/ts-toolbelt/-/ts-toolbelt-9.6.0.tgz#50a25426cfed500d4a09bd1b3afb6f28879edfd5"
|
||||||
|
integrity sha512-nsZd8ZeNUzukXPlJmTBwUAuABDe/9qtVDelJeT/qW0ow3ZS3BsQJtNkan1802aM9Uf68/Y8ljw86Hu0h5IUW3w==
|
||||||
|
|
||||||
|
tsafe@^0.1.0:
|
||||||
|
version "0.1.15"
|
||||||
|
resolved "https://registry.yarnpkg.com/tsafe/-/tsafe-0.1.15.tgz#9e2b6137fb5a49fc7c23cb0bc49ae09bab215f2a"
|
||||||
|
integrity sha512-aAWMOACHXMmwE2zRcQpBv+whxYqB4zvAbs+dzwbnGaGK9NAOQ65m0+WyO+jw/41JzCX7orJU/ieFHTmgehTOKA==
|
||||||
|
|
||||||
|
tsafe@^0.2.2:
|
||||||
|
version "0.2.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/tsafe/-/tsafe-0.2.4.tgz#f2cffb9b33e4d20c6f9800aa1c45bee02d917aa1"
|
||||||
|
integrity sha512-oS/0wJKgEv/iDow/6U51xjAascJVtUQfZQMCDdoOS+VnMlWZFdXGMKuoH47JYs6hEWHpO+Z5pC5oAFFHiPfTLg==
|
||||||
|
|
||||||
|
tsafe@^0.4.1:
|
||||||
|
version "0.4.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/tsafe/-/tsafe-0.4.1.tgz#00af1be2db82abb4be531209b90232d7954e1a03"
|
||||||
|
integrity sha512-+OZ0gdgmwcru+MOSheCx+ymAvQz+1/ui+KFJRuaq0t2m8RNrlf7eSzEieptoPQXPY67Mdkqgkdjknn8azoD5sw==
|
||||||
|
|
||||||
|
tslib@^2.2.0:
|
||||||
|
version "2.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.0.tgz#803b8cdab3e12ba581a4ca41c8839bbb0dacb09e"
|
||||||
|
integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==
|
||||||
|
|
||||||
tss-react@^0.0.12:
|
tss-react@^0.0.12:
|
||||||
version "0.0.12"
|
version "0.0.12"
|
||||||
resolved "https://registry.yarnpkg.com/tss-react/-/tss-react-0.0.12.tgz#6463617ae5e7f670742e48e497d8825d59e2a2e9"
|
resolved "https://registry.yarnpkg.com/tss-react/-/tss-react-0.0.12.tgz#6463617ae5e7f670742e48e497d8825d59e2a2e9"
|
||||||
@ -1189,9 +1173,9 @@ type@^2.0.0:
|
|||||||
integrity sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw==
|
integrity sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw==
|
||||||
|
|
||||||
typescript@^4.2.3:
|
typescript@^4.2.3:
|
||||||
version "4.2.3"
|
version "4.3.5"
|
||||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.3.tgz#39062d8019912d43726298f09493d598048c1ce3"
|
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.5.tgz#4d1c37cc16e893973c45a06886b7113234f119f4"
|
||||||
integrity sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw==
|
integrity sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==
|
||||||
|
|
||||||
unified@^9.0.0:
|
unified@^9.0.0:
|
||||||
version "9.2.1"
|
version "9.2.1"
|
||||||
@ -1294,9 +1278,9 @@ xtend@^4.0.1, xtend@~4.0.1:
|
|||||||
integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
|
integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
|
||||||
|
|
||||||
y18n@^5.0.5:
|
y18n@^5.0.5:
|
||||||
version "5.0.5"
|
version "5.0.8"
|
||||||
resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.5.tgz#8769ec08d03b1ea2df2500acef561743bbb9ab18"
|
resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"
|
||||||
integrity sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg==
|
integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==
|
||||||
|
|
||||||
yaml@^1.7.2:
|
yaml@^1.7.2:
|
||||||
version "1.10.2"
|
version "1.10.2"
|
||||||
@ -1304,9 +1288,9 @@ yaml@^1.7.2:
|
|||||||
integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==
|
integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==
|
||||||
|
|
||||||
yargs-parser@^20.2.2:
|
yargs-parser@^20.2.2:
|
||||||
version "20.2.7"
|
version "20.2.9"
|
||||||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.7.tgz#61df85c113edfb5a7a4e36eb8aa60ef423cbc90a"
|
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee"
|
||||||
integrity sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw==
|
integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==
|
||||||
|
|
||||||
yargs@^16.1.0:
|
yargs@^16.1.0:
|
||||||
version "16.2.0"
|
version "16.2.0"
|
||||||
|
Reference in New Issue
Block a user