Compare commits
40 Commits
Author | SHA1 | Date | |
---|---|---|---|
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 |
56
CHANGELOG.md
56
CHANGELOG.md
@ -1,3 +1,59 @@
|
|||||||
|
### **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)
|
### **1.1.4** (2021-06-15)
|
||||||
|
|
||||||
- Merge pull request #12 from InseeFrLab/email-typo
|
- Merge pull request #12 from InseeFrLab/email-typo
|
||||||
|
57
README.md
57
README.md
@ -20,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.
|
||||||
@ -58,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)
|
||||||
@ -78,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.
|
||||||
@ -129,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(
|
||||||
@ -156,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(
|
||||||
@ -194,20 +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)
|
||||||
Note that you don’t have to re write **all** components, only the ones that you most need customized.
|
- (TS only) You must declare theses page in the type argument of the getter
|
||||||
Look at [here for example](https://github.com/InseeFrLab/onyxia-ui/blob/3bf18aa82b198fc6ba7998c30abf0a9ae54a58b1/src/app/components/KcApp/KcApp.tsx#L112-L120).
|
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)
|
||||||
We want to have our very own login and register page, so we wrote customs [Login.tsx](https://github.com/InseeFrLab/onyxia-ui/blob/master/src/app/components/KcApp/Login.tsx) and [Register.txs](https://github.com/InseeFrLab/onyxia-ui/blob/master/src/app/components/KcApp/Register.tsx), we import them [here](https://github.com/InseeFrLab/onyxia-ui/blob/3bf18aa82b198fc6ba7998c30abf0a9ae54a58b1/src/app/components/KcApp/KcApp.tsx#L9-L10) and use them [here](https://github.com/InseeFrLab/onyxia-ui/blob/3bf18aa82b198fc6ba7998c30abf0a9ae54a58b1/src/app/components/KcApp/KcApp.tsx#L113-L114).
|
- (TS only) If you use Keycloak plugins that defines non standard `.ftl` values
|
||||||
We don't want to bother, however, customizing `login-reset-password.ftl`. We are fine using the component from [the default theme](https://github.com/InseeFrLab/onyxia-ui/blob/3bf18aa82b198fc6ba7998c30abf0a9ae54a58b1/src/app/components/KcApp/KcApp.tsx#L13) with just some [CSS customization](https://github.com/InseeFrLab/onyxia-ui/blob/3bf18aa82b198fc6ba7998c30abf0a9ae54a58b1/src/app/components/KcApp/KcApp.tsx#L103-L110).
|
(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
|
||||||
@ -220,16 +234,19 @@ in your `package.json` instead of `^X.Y.Z`. A minor update of Keycloakify might
|
|||||||
### 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}
|
||||||
@ -271,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
|
||||||
|
|
||||||
|
14
package.json
14
package.json
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "keycloakify",
|
"name": "keycloakify",
|
||||||
"version": "1.1.4",
|
"version": "2.0.5",
|
||||||
"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,16 +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",
|
||||||
"minimal-polyfills": "^2.1.6",
|
"minimal-polyfills": "^2.1.6",
|
||||||
"path": "^0.12.7",
|
"path": "^0.12.7",
|
||||||
"powerhooks": "^0.1.0",
|
"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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@ import * as child_process from "child_process";
|
|||||||
import { generateDebugFiles, containerLaunchScriptBasename } from "./generateDebugFiles";
|
import { generateDebugFiles, containerLaunchScriptBasename } from "./generateDebugFiles";
|
||||||
import { URL } from "url";
|
import { URL } from "url";
|
||||||
|
|
||||||
|
|
||||||
const reactProjectDirPath = process.cwd();
|
const reactProjectDirPath = process.cwd();
|
||||||
|
|
||||||
const doUseExternalAssets = process.argv[2]?.toLowerCase() === "--external-assets";
|
const doUseExternalAssets = process.argv[2]?.toLowerCase() === "--external-assets";
|
||||||
@ -19,6 +18,8 @@ export function main() {
|
|||||||
|
|
||||||
console.log("🔏 Building the keycloak theme...⌚");
|
console.log("🔏 Building the keycloak theme...⌚");
|
||||||
|
|
||||||
|
const extraPagesId: string[] = (parsedPackageJson as any)["keycloakify"]?.["extraPages"] ?? [];
|
||||||
|
|
||||||
generateKeycloakThemeResources({
|
generateKeycloakThemeResources({
|
||||||
keycloakThemeBuildingDirPath,
|
keycloakThemeBuildingDirPath,
|
||||||
"reactAppBuildDirPath": pathJoin(reactProjectDirPath, "build"),
|
"reactAppBuildDirPath": pathJoin(reactProjectDirPath, "build"),
|
||||||
@ -53,7 +54,8 @@ export function main() {
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
})()
|
})(),
|
||||||
|
extraPagesId
|
||||||
});
|
});
|
||||||
|
|
||||||
const { jarFilePath } = generateJavaStackFiles({
|
const { jarFilePath } = generateJavaStackFiles({
|
||||||
|
@ -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>
|
|
@ -8,14 +8,14 @@ import {
|
|||||||
} from "../replaceImportFromStatic";
|
} from "../replaceImportFromStatic";
|
||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
import { join as pathJoin } from "path";
|
import { join as pathJoin } from "path";
|
||||||
import { objectKeys } from "evt/tools/typeSafety/objectKeys";
|
import { objectKeys } from "tsafe/objectKeys";
|
||||||
import { ftlValuesGlobalName } from "../ftlValuesGlobalName";
|
import { ftlValuesGlobalName } from "../ftlValuesGlobalName";
|
||||||
|
|
||||||
export const pageIds = [
|
export const pageIds = [
|
||||||
"login.ftl", "register.ftl", "info.ftl",
|
"login.ftl", "register.ftl", "info.ftl",
|
||||||
"error.ftl", "login-reset-password.ftl",
|
"error.ftl", "login-reset-password.ftl",
|
||||||
"login-verify-email.ftl", "terms.ftl",
|
"login-verify-email.ftl", "terms.ftl",
|
||||||
"login-otp.ftl", "login-update-profile.ftl",
|
"login-otp.ftl", "login-update-profile.ftl",
|
||||||
"login-idp-link-confirm.ftl"
|
"login-idp-link-confirm.ftl"
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
@ -26,17 +26,6 @@ function loadAdjacentFile(fileBasename: string) {
|
|||||||
.toString("utf8");
|
.toString("utf8");
|
||||||
};
|
};
|
||||||
|
|
||||||
function loadFtlFile(ftlFileBasename: PageId | "common.ftl") {
|
|
||||||
try {
|
|
||||||
|
|
||||||
return loadAdjacentFile(ftlFileBasename)
|
|
||||||
.match(/^<script>const _=((?:.|\n)+)<\/script>[\n]?$/)![1];
|
|
||||||
|
|
||||||
} catch {
|
|
||||||
return "{}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export function generateFtlFilesCodeFactory(
|
export function generateFtlFilesCodeFactory(
|
||||||
params: {
|
params: {
|
||||||
@ -100,8 +89,9 @@ export function generateFtlFilesCodeFactory(
|
|||||||
);
|
);
|
||||||
|
|
||||||
//FTL is no valid html, we can't insert with cheerio, we put placeholder for injecting later.
|
//FTL is no valid html, we can't insert with cheerio, we put placeholder for injecting later.
|
||||||
const ftlCommonPlaceholders = {
|
const ftlPlaceholders = {
|
||||||
'{ "x": "vIdLqMeOed9sdLdIdOxdK0d" }': loadFtlFile("common.ftl"),
|
'{ "x": "vIdLqMeOed9sdLdIdOxdK0d" }': loadAdjacentFile("common.ftl")
|
||||||
|
.match(/^<script>const _=((?:.|\n)+)<\/script>[\n]?$/)![1],
|
||||||
'<!-- xIdLqMeOedErIdLsPdNdI9dSlxI -->':
|
'<!-- xIdLqMeOedErIdLsPdNdI9dSlxI -->':
|
||||||
[
|
[
|
||||||
'<#if scripts??>',
|
'<#if scripts??>',
|
||||||
@ -126,23 +116,19 @@ export function generateFtlFilesCodeFactory(
|
|||||||
'</style>',
|
'</style>',
|
||||||
''
|
''
|
||||||
]),
|
]),
|
||||||
...["Object.deepAssign.js", "String.htmlUnescape.js"].map(
|
"<script>",
|
||||||
fileBasename => [
|
loadAdjacentFile("Object.deepAssign.js"),
|
||||||
"<script>",
|
"</script>",
|
||||||
loadAdjacentFile(fileBasename),
|
|
||||||
"</script>"
|
|
||||||
].join("\n")
|
|
||||||
),
|
|
||||||
'<script>',
|
'<script>',
|
||||||
` window.${ftlValuesGlobalName}= Object.assign(`,
|
` window.${ftlValuesGlobalName}= Object.assign(`,
|
||||||
` {},`,
|
` {},`,
|
||||||
` ${objectKeys(ftlCommonPlaceholders)[0]}`,
|
` ${objectKeys(ftlPlaceholders)[0]}`,
|
||||||
' );',
|
' );',
|
||||||
'</script>',
|
'</script>',
|
||||||
'',
|
'',
|
||||||
pageSpecificCodePlaceholder,
|
pageSpecificCodePlaceholder,
|
||||||
'',
|
'',
|
||||||
objectKeys(ftlCommonPlaceholders)[1]
|
objectKeys(ftlPlaceholders)[1]
|
||||||
].join("\n"),
|
].join("\n"),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -150,7 +136,7 @@ export function generateFtlFilesCodeFactory(
|
|||||||
|
|
||||||
function generateFtlFilesCode(
|
function generateFtlFilesCode(
|
||||||
params: {
|
params: {
|
||||||
pageId: PageId;
|
pageId: string;
|
||||||
}
|
}
|
||||||
): { ftlCode: string; } {
|
): { ftlCode: string; } {
|
||||||
|
|
||||||
@ -158,11 +144,6 @@ export function generateFtlFilesCodeFactory(
|
|||||||
|
|
||||||
const $ = cheerio.load(partiallyFixedIndexHtmlCode);
|
const $ = cheerio.load(partiallyFixedIndexHtmlCode);
|
||||||
|
|
||||||
const ftlPlaceholders = {
|
|
||||||
'{ "x": "kxOlLqMeOed9sdLdIdOxd444" }': loadFtlFile(pageId),
|
|
||||||
...ftlCommonPlaceholders
|
|
||||||
};
|
|
||||||
|
|
||||||
let ftlCode = $.html()
|
let ftlCode = $.html()
|
||||||
.replace(
|
.replace(
|
||||||
pageSpecificCodePlaceholder,
|
pageSpecificCodePlaceholder,
|
||||||
@ -172,10 +153,6 @@ export function generateFtlFilesCodeFactory(
|
|||||||
` window.${ftlValuesGlobalName},`,
|
` window.${ftlValuesGlobalName},`,
|
||||||
` { "pageId": "${pageId}" }`,
|
` { "pageId": "${pageId}" }`,
|
||||||
' );',
|
' );',
|
||||||
` Object.deepAssign(`,
|
|
||||||
` window.${ftlValuesGlobalName},`,
|
|
||||||
` ${objectKeys(ftlPlaceholders)[0]}`,
|
|
||||||
' );',
|
|
||||||
'</script>'
|
'</script>'
|
||||||
].join("\n")
|
].join("\n")
|
||||||
);
|
);
|
||||||
|
@ -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,11 +0,0 @@
|
|||||||
<script>const _=
|
|
||||||
{
|
|
||||||
"idpAlias": (function (){
|
|
||||||
<#attempt>
|
|
||||||
return "${idpAlias}";
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
return "";
|
|
||||||
})()
|
|
||||||
}
|
|
||||||
</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,67 +0,0 @@
|
|||||||
<script>const _=
|
|
||||||
{
|
|
||||||
"user": {
|
|
||||||
"editUsernameAllowed": (function (){
|
|
||||||
<#attempt>
|
|
||||||
return ${user.editUsernameAllowed?c};
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
})(),
|
|
||||||
"username": (function (){
|
|
||||||
<#attempt>
|
|
||||||
return "${user.username!''}" || undefined;
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
})(),
|
|
||||||
"email": (function (){
|
|
||||||
<#attempt>
|
|
||||||
return "${user.email!''}" || undefined;
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
})(),
|
|
||||||
"firstName": (function (){
|
|
||||||
<#attempt>
|
|
||||||
return "${user.firstName!''}" || undefined;
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
})(),
|
|
||||||
"lastName": (function (){
|
|
||||||
<#attempt>
|
|
||||||
return "${user.lastName!''}" || undefined;
|
|
||||||
<#recover>
|
|
||||||
</#attempt>
|
|
||||||
})()
|
|
||||||
},
|
|
||||||
"messagesPerField": {
|
|
||||||
"printIfExists": function (key, x) {
|
|
||||||
switch(key){
|
|
||||||
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>
|
|
||||||
})();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
}
|
|
||||||
</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,14 @@ 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[];
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
|
|
||||||
const { themeName, reactAppBuildDirPath, keycloakThemeBuildingDirPath, urlPathname, urlOrigin } = params;
|
const {
|
||||||
|
themeName, reactAppBuildDirPath, keycloakThemeBuildingDirPath,
|
||||||
|
urlPathname, urlOrigin, extraPagesId
|
||||||
|
} = params;
|
||||||
|
|
||||||
const themeDirPath = pathJoin(keycloakThemeBuildingDirPath, "src", "main", "resources", "theme", themeName, "login");
|
const themeDirPath = pathJoin(keycloakThemeBuildingDirPath, "src", "main", "resources", "theme", themeName, "login");
|
||||||
|
|
||||||
@ -92,7 +96,7 @@ export function generateKeycloakThemeResources(
|
|||||||
urlOrigin
|
urlOrigin
|
||||||
});
|
});
|
||||||
|
|
||||||
pageIds.forEach(pageId => {
|
[...pageIds, ...extraPagesId].forEach(pageId => {
|
||||||
|
|
||||||
const { ftlCode } = generateFtlFilesCode({ pageId });
|
const { ftlCode } = generateFtlFilesCode({ pageId });
|
||||||
|
|
||||||
|
@ -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 };
|
||||||
|
|
||||||
|
@ -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";
|
||||||
@ -13,7 +13,7 @@ import { LoginOtp } from "./LoginOtp";
|
|||||||
import { LoginUpdateProfile } from "./LoginUpdateProfile";
|
import { LoginUpdateProfile } from "./LoginUpdateProfile";
|
||||||
import { LoginIdpLinkConfirm } from "./LoginIdpLinkConfirm";
|
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 }} />;
|
||||||
|
@ -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")}
|
||||||
|
@ -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 LoginIdpLinkConfirm = memo(({ kcContext, ...props }: { kcContext: KcContext.LoginIdpLinkConfirm; } & KcProps) => {
|
export const LoginIdpLinkConfirm = memo(({ kcContext, ...props }: { kcContext: KcContextBase.LoginIdpLinkConfirm; } & KcProps) => {
|
||||||
|
|
||||||
const { msg } = useKcMessage();
|
const { msg } = useKcMessage();
|
||||||
|
|
||||||
@ -15,6 +15,7 @@ export const LoginIdpLinkConfirm = memo(({ kcContext, ...props }: { kcContext: K
|
|||||||
return (
|
return (
|
||||||
<Template
|
<Template
|
||||||
{...{ kcContext, ...props }}
|
{...{ kcContext, ...props }}
|
||||||
|
doFetchDefaultThemeResources={true}
|
||||||
headerNode={msg("confirmLinkIdpTitle")}
|
headerNode={msg("confirmLinkIdpTitle")}
|
||||||
formNode={
|
formNode={
|
||||||
<form id="kc-register-form" action={url.loginAction} method="post">
|
<form id="kc-register-form" action={url.loginAction} method="post">
|
||||||
|
@ -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={
|
||||||
|
@ -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 LoginUpdateProfile = memo(({ kcContext, ...props }: { kcContext: KcContext.LoginUpdateProfile; } & KcProps) => {
|
export const LoginUpdateProfile = memo(({ kcContext, ...props }: { kcContext: KcContextBase.LoginUpdateProfile; } & KcProps) => {
|
||||||
|
|
||||||
const { msg, msgStr } = useKcMessage();
|
const { msg, msgStr } = useKcMessage();
|
||||||
|
|
||||||
@ -14,6 +14,7 @@ export const LoginUpdateProfile = memo(({ kcContext, ...props }: { kcContext: Kc
|
|||||||
return (
|
return (
|
||||||
<Template
|
<Template
|
||||||
{...{ kcContext, ...props }}
|
{...{ kcContext, ...props }}
|
||||||
|
doFetchDefaultThemeResources={true}
|
||||||
headerNode={msg("loginProfileTitle")}
|
headerNode={msg("loginProfileTitle")}
|
||||||
formNode={
|
formNode={
|
||||||
<form id="kc-update-profile-form" className={cx(props.kcFormClass)} action={url.loginAction} method="post">
|
<form id="kc-update-profile-form" className={cx(props.kcFormClass)} action={url.loginAction} method="post">
|
||||||
|
@ -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,13 +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 | KcContext.LoginUpdateProfile |
|
KcContextBase.Terms | KcContextBase.LoginOtp | KcContextBase.LoginUpdateProfile |
|
||||||
KcContext.LoginIdpLinkConfirm;
|
KcContextBase.LoginIdpLinkConfirm;
|
||||||
|
|
||||||
export declare namespace KcContext {
|
export declare namespace KcContextBase {
|
||||||
|
|
||||||
export type Common = {
|
export type Common = {
|
||||||
url: {
|
url: {
|
||||||
@ -125,11 +123,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 & {
|
||||||
@ -148,7 +150,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 & {
|
||||||
@ -198,9 +201,8 @@ export declare namespace KcContext {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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";
|
256
src/lib/getKcContext/kcContextMocks/kcContextMocks.ts
Normal file
256
src/lib/getKcContext/kcContextMocks/kcContextMocks.ts
Normal file
@ -0,0 +1,256 @@
|
|||||||
|
|
||||||
|
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
|
||||||
|
},
|
||||||
|
"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": {
|
||||||
|
"baseUrl": "#"
|
||||||
|
}
|
||||||
|
|
||||||
|
}),
|
||||||
|
id<KcContextBase.Error>({
|
||||||
|
...kcContextCommonMock,
|
||||||
|
"pageId": "error.ftl",
|
||||||
|
"client": {
|
||||||
|
"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();
|
||||||
|
|
||||||
|
const languageLike =
|
||||||
|
kcContext?.locale?.current ??
|
||||||
|
(
|
||||||
|
typeof navigator === "undefined" ?
|
||||||
|
undefined :
|
||||||
|
navigator.language
|
||||||
|
);
|
||||||
|
|
||||||
|
if (languageLike === undefined) {
|
||||||
|
return "en";
|
||||||
|
}
|
||||||
|
|
||||||
|
return getBestMatchAmongKcLanguageTag(languageLike);
|
||||||
|
|
||||||
|
},
|
||||||
{ "persistance": "localStorage" }
|
{ "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,249 +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"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const kcLoginUpdateProfileContext: KcContext.LoginUpdateProfile = {
|
|
||||||
...kcCommonContext,
|
|
||||||
"pageId": "login-update-profile.ftl",
|
|
||||||
"user": {
|
|
||||||
"editUsernameAllowed": true,
|
|
||||||
"username": "anUsername",
|
|
||||||
"email": "foo@example.com",
|
|
||||||
"firstName": "aFirstName",
|
|
||||||
"lastName": "aLastName"
|
|
||||||
},
|
|
||||||
"messagesPerField": {
|
|
||||||
"printIfExists": () => undefined
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const kcLoginIdpLinkConfirmContext: KcContext.LoginIdpLinkConfirm ={
|
|
||||||
...kcCommonContext,
|
|
||||||
"pageId": "login-idp-link-confirm.ftl",
|
|
||||||
"idpAlias": "FranceConnect"
|
|
||||||
};
|
|
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,7 @@ 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"]
|
||||||
});
|
});
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
57
yarn.lock
57
yarn.lock
@ -43,9 +43,9 @@
|
|||||||
"@babel/helper-plugin-utils" "^7.14.5"
|
"@babel/helper-plugin-utils" "^7.14.5"
|
||||||
|
|
||||||
"@babel/runtime@^7.13.10", "@babel/runtime@^7.7.2":
|
"@babel/runtime@^7.13.10", "@babel/runtime@^7.7.2":
|
||||||
version "7.14.5"
|
version "7.14.6"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.5.tgz#665450911c6031af38f81db530f387ec04cd9a98"
|
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.6.tgz#535203bc0892efc7dec60bdc27b2ecf6e409062d"
|
||||||
integrity sha512-121rumjddw9c3NCQ55KGkyE1h/nzWhU/owjhw0l4mQrkzz4x9SGS1X8gFLraHwX7td3Yo4QTL+qj0NcIzN87BA==
|
integrity sha512-/PCB2uJ7oM44tz8YhC4Z/6PeOKXp4K588f+5M3clr1M4zbqztlo0XEfJ2LEzj/FgwfgGcIdl8n7YYjTCI0BYwg==
|
||||||
dependencies:
|
dependencies:
|
||||||
regenerator-runtime "^0.13.4"
|
regenerator-runtime "^0.13.4"
|
||||||
|
|
||||||
@ -322,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"
|
||||||
|
|
||||||
@ -514,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"
|
||||||
@ -893,14 +902,13 @@ path@^0.12.7:
|
|||||||
process "^0.11.1"
|
process "^0.11.1"
|
||||||
util "^0.10.3"
|
util "^0.10.3"
|
||||||
|
|
||||||
powerhooks@^0.1.0:
|
powerhooks@^0.1.6:
|
||||||
version "0.1.1"
|
version "0.1.6"
|
||||||
resolved "https://registry.yarnpkg.com/powerhooks/-/powerhooks-0.1.1.tgz#5ce825d595673a5c6f86b227e0fcaca1972bd90d"
|
resolved "https://registry.yarnpkg.com/powerhooks/-/powerhooks-0.1.6.tgz#23dfb6c781bbfdd20ea37ff5a2a0fd5a9799d591"
|
||||||
integrity sha512-I8bzTU/uRUyeTkeyAR54g862pwMcAX/ErN3xqdUt7n28riqv93N0Cm900018bNRKBq8ovnb+VxOQ+MXJwhkUOA==
|
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"
|
||||||
minimal-polyfills "^2.2.1"
|
|
||||||
resize-observer-polyfill "^1.5.1"
|
resize-observer-polyfill "^1.5.1"
|
||||||
tsafe "^0.1.0"
|
tsafe "^0.1.0"
|
||||||
|
|
||||||
@ -1122,11 +1130,26 @@ 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:
|
tsafe@^0.1.0:
|
||||||
version "0.1.15"
|
version "0.1.15"
|
||||||
resolved "https://registry.yarnpkg.com/tsafe/-/tsafe-0.1.15.tgz#9e2b6137fb5a49fc7c23cb0bc49ae09bab215f2a"
|
resolved "https://registry.yarnpkg.com/tsafe/-/tsafe-0.1.15.tgz#9e2b6137fb5a49fc7c23cb0bc49ae09bab215f2a"
|
||||||
integrity sha512-aAWMOACHXMmwE2zRcQpBv+whxYqB4zvAbs+dzwbnGaGK9NAOQ65m0+WyO+jw/41JzCX7orJU/ieFHTmgehTOKA==
|
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:
|
tslib@^2.2.0:
|
||||||
version "2.3.0"
|
version "2.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.0.tgz#803b8cdab3e12ba581a4ca41c8839bbb0dacb09e"
|
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.0.tgz#803b8cdab3e12ba581a4ca41c8839bbb0dacb09e"
|
||||||
@ -1150,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.3.2"
|
version "4.3.4"
|
||||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.2.tgz#399ab18aac45802d6f2498de5054fcbbe716a805"
|
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.4.tgz#3f85b986945bcf31071decdd96cf8bfa65f9dcbc"
|
||||||
integrity sha512-zZ4hShnmnoVnAHpVHWpTcxdv7dWP60S2FsydQLV8V5PbS3FifjWFFRiHSWpDJahly88PRyV5teTSLoq4eG7mKw==
|
integrity sha512-uauPG7XZn9F/mo+7MrsRjyvbxFpzemRjKEZXS4AK83oP2KKOJPvb+9cO/gmnv8arWZvhnjVOXz7B49m1l0e9Ew==
|
||||||
|
|
||||||
unified@^9.0.0:
|
unified@^9.0.0:
|
||||||
version "9.2.1"
|
version "9.2.1"
|
||||||
@ -1265,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