Compare commits

...

142 Commits

Author SHA1 Message Date
59db202fe4 Update changelog v0.3.23 2021-04-08 15:43:34 +00:00
09927afd43 Merge branch 'main' of https://github.com/InseeFrLab/keycloakify into main 2021-04-08 17:40:54 +02:00
13c6122b9b Bump version (changelog ignore) 2021-04-08 17:06:26 +02:00
1bb19f65a2 Allow to lazily load therms 2021-04-08 17:06:09 +02:00
918a80cfb6 Update changelog v0.3.22 2021-04-08 13:48:41 +00:00
ed7d5eabcb Bump version (changelog ignore) 2021-04-08 15:43:12 +02:00
2795109162 update powerhooks 2021-04-08 15:42:41 +02:00
966f277628 Support terms and condition 2021-04-08 15:41:40 +02:00
36d60411f9 Fix info.ftl 2021-04-08 12:54:29 +02:00
60fe33f3fd Merge branch 'main' of https://github.com/InseeFrLab/keycloakify into main 2021-04-08 12:20:14 +02:00
1df685df92 For useKcMessage we prefer returning callbacks with a changing references 2021-04-08 12:20:06 +02:00
7894d95ace Update changelog v0.3.21 2021-04-04 19:20:27 +00:00
a8b4493aa1 update yarn.lock (changelog ignore) 2021-04-04 21:17:35 +02:00
715a7399cf Merge branch 'main' of https://github.com/InseeFrLab/keycloakify into main 2021-04-04 21:12:39 +02:00
a1e59bae23 Update powerhooks 2021-04-04 21:12:30 +02:00
b0819314a1 Update changelog v0.3.20 2021-04-01 22:37:41 +00:00
0099442543 Merge branch 'main' of https://github.com/InseeFrLab/keycloakify into main 2021-04-01 23:43:14 +02:00
66a0b07228 Bump version (changelog ignore) 2021-04-01 23:43:08 +02:00
85f9544754 Always catch freemarker errors 2021-04-01 23:42:31 +02:00
2f16a09ab8 Update changelog v0.3.19 2021-04-01 15:21:10 +00:00
183ae98c30 Merge branch 'main' of https://github.com/InseeFrLab/keycloakify into main 2021-04-01 17:16:01 +02:00
ba15e63879 Bump version (changelog ignore) 2021-04-01 17:15:55 +02:00
654277feda Fix previous release 2021-04-01 17:15:28 +02:00
81279a5cc5 Update changelog v0.3.18 2021-04-01 15:04:08 +00:00
59f0a843b0 Merge branch 'main' of https://github.com/InseeFrLab/keycloakify into main 2021-04-01 16:52:53 +02:00
c094f70171 Bump version (changelog ignore) 2021-04-01 16:51:50 +02:00
0858fe6319 Fix error.ftt, Adopt best effort strategy to convert ftl values into JS 2021-04-01 16:51:28 +02:00
5012ec0ccc Update changelog v0.3.17 2021-03-29 04:33:07 +00:00
990a24fab2 Merge branch 'main' of https://github.com/InseeFrLab/keycloakify into main 2021-03-29 06:27:19 +02:00
036b6bf82a Use push instead of replace in keycloak-js adapter to enable going back 2021-03-29 06:27:12 +02:00
8272a02b52 Update changelog v0.3.15 2021-03-28 12:18:37 +00:00
e346b1d9d2 Merge branch 'main' of https://github.com/InseeFrLab/keycloakify into main 2021-03-28 14:14:52 +02:00
2309bd21c6 Remove all reference to --external-assets, broken feature 2021-03-28 14:14:43 +02:00
7d6476c1b5 Update changelog v0.3.14 2021-03-28 11:39:36 +00:00
e892a0e7e6 Merge branch 'main' of https://github.com/InseeFrLab/keycloakify into main 2021-03-28 13:37:07 +02:00
ca5b41e730 Fix standalone mode: imports from js 2021-03-28 13:37:02 +02:00
9b18234112 Update changelog v0.3.13 2021-03-26 16:54:38 +00:00
5274368f47 Merge branch 'main' of https://github.com/InseeFrLab/keycloakify into main 2021-03-26 17:49:36 +01:00
1415c24028 Bump version (changelog ignore) 2021-03-26 17:49:31 +01:00
4a084f5859 Fix previous release (changelog ignore) 2021-03-26 17:49:09 +01:00
a30c9eb0cd Update changelog v0.3.12 2021-03-26 16:35:41 +00:00
85d3b40b8e Merge branch 'main' of https://github.com/InseeFrLab/keycloakify into main 2021-03-26 17:31:06 +01:00
335afec230 Bump version (changelog ignore) 2021-03-26 17:31:01 +01:00
69fa49848a Fix mocksContext 2021-03-26 17:30:04 +01:00
7a09051127 Update changelog v0.3.11 2021-03-26 14:36:48 +00:00
07ee0ecb8b Merge branch 'main' of https://github.com/InseeFrLab/keycloakify into main 2021-03-26 15:29:23 +01:00
6f133428f8 Fix previous build, improve README 2021-03-26 15:29:17 +01:00
4f733736db fmt (changelog ignore) 2021-03-26 14:06:14 +01:00
d96ff13a67 Update changelog v0.3.10 2021-03-26 13:05:25 +00:00
2c1351ce47 fmt (changelog ignore) 2021-03-26 14:04:45 +01:00
96cd56ec77 Merge branch 'main' of https://github.com/InseeFrLab/keycloakify into main 2021-03-26 14:02:46 +01:00
e5b2096d65 Bump version (changelog ignore) 2021-03-26 14:02:40 +01:00
3aa140335f Handle <style> tag, improve documentation 2021-03-26 14:02:14 +01:00
4cafaa2492 Update changelog v0.3.9 2021-03-25 11:54:13 +00:00
9c633a7521 Update readme 2021-03-25 12:48:40 +01:00
e27845ba91 Merge branch 'main' of https://github.com/InseeFrLab/keycloakify into main 2021-03-25 12:01:15 +01:00
2a8708a45b Document --external-assets 2021-03-25 12:01:11 +01:00
6874fa4c24 Update README.md 2021-03-24 09:12:50 +01:00
ba531a4927 Update README.md 2021-03-24 09:12:04 +01:00
20175b57cf Update README.md 2021-03-24 09:10:44 +01:00
ad275e4c34 Update changelog v0.3.8 2021-03-22 22:36:17 +00:00
060b9fe0de Merge branch 'main' of https://github.com/InseeFrLab/keycloakify into main 2021-03-22 23:34:13 +01:00
17b24d14ed Make standalone mode the default 2021-03-22 23:34:07 +01:00
2d278b0680 Update changelog v0.3.7 2021-03-22 19:57:34 +00:00
fb5975e4f1 Merge branch 'main' of https://github.com/InseeFrLab/keycloakify into main 2021-03-22 20:54:35 +01:00
24fccaf513 (test) external asset mode by default 2021-03-22 20:54:28 +01:00
293953aa1b Update changelog v0.3.6 2021-03-22 19:02:53 +00:00
1049e312f9 Fix previous release 2021-03-22 20:00:58 +01:00
a2db250600 Update changelog v0.3.5 2021-03-22 18:43:18 +00:00
cf7fe8c337 Merge branch 'main' of https://github.com/InseeFrLab/keycloakify into main 2021-03-22 19:41:08 +01:00
f5350097bf Bump version (changelog ignore) 2021-03-22 19:40:58 +01:00
1cb5dd461b support homepage with urlPath 2021-03-22 19:40:38 +01:00
845599a5e8 Update changelog v0.3.4 2021-03-22 06:23:40 +00:00
0cc02c292f Merge branch 'main' of https://github.com/InseeFrLab/keycloakify into main 2021-03-22 07:21:36 +01:00
1919702326 Bugfix: Import assets from CSS 2021-03-22 07:21:31 +01:00
0c0052e1cd Update changelog v0.3.3 2021-03-22 04:27:01 +00:00
78622770ec Merge branch 'main' of https://github.com/InseeFrLab/keycloakify into main 2021-03-22 05:21:22 +01:00
7b86727394 Fix submit not receving correct text 2021-03-22 05:21:05 +01:00
0965f8648e Update changelog v0.3.2 2021-03-21 21:31:01 +00:00
98974b4367 Merge branch 'main' of https://github.com/InseeFrLab/keycloakify into main 2021-03-21 22:25:49 +01:00
597bcadd9e Fix broken previous release 2021-03-21 22:25:47 +01:00
4d9aabcb91 Update changelog v0.3.1 2021-03-21 21:13:36 +00:00
1606c2884d kcHeaderClass can be updated after initial mount 2021-03-21 22:10:33 +01:00
12f69b593f Merge branch 'main' of https://github.com/InseeFrLab/keycloakify into main 2021-03-20 05:07:26 +01:00
1ca45f90d0 Update readme (changelog ignore) 2021-03-20 05:07:22 +01:00
a91a5616f9 Update changelog v0.3.0 2021-03-20 04:02:59 +00:00
c525e09368 Bump version 2021-03-20 05:00:14 +01:00
f5bba4a6a0 Feat: Cary over states using URL search params 2021-03-20 04:59:18 +01:00
77a37fb573 Merge branch 'main' of https://github.com/InseeFrLab/keycloakify into main 2021-03-20 02:54:21 +01:00
6b24c5878c Bugfix: with kcHtmlClass 2021-03-20 02:54:15 +01:00
f4414e1249 Update changelog v0.2.10 2021-03-19 23:19:03 +00:00
b72971f4ce Merge branch 'main' of https://github.com/InseeFrLab/keycloakify into main 2021-03-20 00:17:06 +01:00
b9af4e6804 Bump version (changelog ingnore) 2021-03-20 00:16:58 +01:00
2fd1d42d1e Remove dependency to denoify 2021-03-20 00:16:34 +01:00
3cfc7d7fa9 Update changelog v0.2.9 2021-03-19 23:14:27 +00:00
b5d9055fcf Bump version (changelog ignore) 2021-03-20 00:09:37 +01:00
63d644d95f Update deps and CI workflow 2021-03-20 00:09:12 +01:00
e16192b416 Update changelog v0.2.8 2021-03-19 21:42:31 +00:00
505e018448 Bump version (changelog ignore) 2021-03-19 22:39:48 +01:00
5ced0e2809 Bugfix: keycloak_build that grow and grow in size 2021-03-19 22:39:32 +01:00
0e1d919f7e Add disclaimer about maitainment strategy 2021-03-15 16:22:13 +01:00
a009db998e Merge branch 'develop' of https://github.com/garronej/keycloakify into develop 2021-03-15 16:05:28 +01:00
d6c6bd933b Add a note for tested version support 2021-03-15 16:05:21 +01:00
859cc03f35 Update changelog v0.2.7 2021-03-13 11:56:59 +00:00
1a3b8ae3b8 Merge branch 'develop' of https://github.com/garronej/keycloakify into develop 2021-03-13 12:51:41 +01:00
863a08abf3 Bump version 2021-03-13 12:51:34 +01:00
fd9c6afa5e Update README.md 2021-03-11 00:10:34 +01:00
8f3797407b Update README.md 2021-03-10 23:55:37 +01:00
7eedb23285 Update changelog v0.2.6 2021-03-10 22:04:25 +00:00
e4a2c95dd8 Merge branch 'develop' of https://github.com/garronej/keycloakify into develop 2021-03-10 23:02:30 +01:00
9429228b71 Bump version (changelog ignore) 2021-03-10 23:02:25 +01:00
aafbc60f12 Fix generated gitignore 2021-03-10 23:02:03 +01:00
7170611791 Update changelog v0.2.5 2021-03-10 21:51:07 +00:00
59e57f3dd5 Merge branch 'develop' of https://github.com/garronej/keycloakify into develop 2021-03-10 22:46:06 +01:00
fd0d25b081 Bump version (changelog ignore) 2021-03-10 22:46:01 +01:00
fa529c911a Fix generated .gitignore 2021-03-10 22:45:09 +01:00
dc997b7ef4 Update changelog v0.2.4 2021-03-10 20:33:36 +00:00
0168d32f96 Merge branch 'develop' of https://github.com/garronej/keycloakify into develop 2021-03-10 21:27:48 +01:00
d6fc0c779c Bump version (changelog ignore) 2021-03-10 21:27:43 +01:00
08be36edfa Update deps (changelog ignore) 2021-03-10 21:27:23 +01:00
990d287953 Update README.md 2021-03-09 05:12:45 +01:00
a629d4ab45 Update readme 2021-03-09 04:56:45 +01:00
78b48af886 Update changelog v0.2.3 2021-03-09 02:07:07 +00:00
72d267853c fix gitignore generation 2021-03-09 03:04:59 +01:00
66a218c2ec Update changelog v0.2.2 2021-03-08 02:02:53 +00:00
d4f3ec2245 Add table of content 2021-03-08 02:59:22 +01:00
7bdc19bf4b Update README.md 2021-03-08 02:55:41 +01:00
103ef788fb Update README.md 2021-03-08 02:54:43 +01:00
b6f6d1f3cc Update README.md 2021-03-08 02:52:58 +01:00
ef3d2e4e04 Update publish.yaml 2021-03-08 02:51:50 +01:00
8ec8b91ead Update changelog v0.2.1 2021-03-08 01:49:47 +00:00
819a1d473d Update ci.yaml 2021-03-08 02:48:02 +01:00
c930337255 Update readme 2021-03-08 02:40:25 +01:00
57bb4a9d96 Bump version (changelog ignore) 2021-03-08 02:36:21 +01:00
1776341242 Update readme 2021-03-08 02:35:58 +01:00
983eec6941 Bump version (changelog ignore) 2021-03-08 02:34:31 +01:00
86d390ee1a update deps 2021-03-08 02:33:52 +01:00
91703409d9 Update readme 2021-03-08 02:29:54 +01:00
3322d0e4a5 Add all mocks for testing 2021-03-08 01:02:06 +01:00
d09038fde2 Merge branch 'develop' of https://github.com/garronej/keycloakify into develop 2021-03-08 00:10:57 +01:00
71966deaac Bump version (changelog ignore) 2021-03-08 00:10:51 +01:00
12e83c9468 many small fixes 2021-03-08 00:09:52 +01:00
43 changed files with 1884 additions and 851 deletions

View File

@ -2,87 +2,135 @@ name: ci
on:
push:
branches:
- develop
- main
pull_request:
branches:
- develop
- main
jobs:
test_node:
runs-on: ubuntu-latest
test:
runs-on: macos-10.15
strategy:
matrix:
node: [ '14', '13', '12' ]
name: Test with Node v${{ matrix.node }}
steps:
- name: Tell if project is using npm or yarn
id: _1
uses: garronej/github_actions_toolkit@v1.11
id: step1
uses: garronej/github_actions_toolkit@v2.2
with:
action_name: tell_if_project_uses_npm_or_yarn
owner: ${{github.repository_owner}}
repo: ${{github.event.repository.name}}
branch: ${{github.ref}}
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
- uses: actions/checkout@v2.3.4
- uses: actions/setup-node@v2.1.3
with:
node-version: ${{ matrix.node }}
- if: steps._1.outputs.npm_or_yarn == 'yarn'
- if: steps.step1.outputs.npm_or_yarn == 'yarn'
run: |
yarn install --frozen-lockfile
yarn run build
yarn run test
- if: steps._1.outputs.npm_or_yarn == 'npm'
yarn build
yarn test
- if: steps.step1.outputs.npm_or_yarn == 'npm'
run: |
npm ci
npm run build
npm run test
trigger_publish:
name: Trigger publish.yaml workflow if package.json version updated ( and secrets.PAT is set ).
npm test
check_if_version_upgraded:
name: Check if version upgrade
if: github.event_name == 'push'
runs-on: ubuntu-latest
env:
PAT: ${{secrets.PAT}}
if: github.event_name == 'push' && github.event.head_commit.author.name != 'ts_ci'
needs: test_node
needs: test
outputs:
from_version: ${{ steps.step1.outputs.from_version }}
to_version: ${{ steps.step1.outputs.to_version }}
is_upgraded_version: ${{steps.step1.outputs.is_upgraded_version }}
steps:
- name: Get version on latest
id: v_latest
uses: garronej/github_actions_toolkit@v1.11
- uses: garronej/github_actions_toolkit@v2.2
id: step1
with:
action_name: get_package_json_version
owner: ${{github.repository_owner}}
repo: ${{github.event.repository.name}}
branch: latest
compare_to_version: '0.0.0'
action_name: is_package_json_version_upgraded
- name: Get version on develop
id: v_develop
uses: garronej/github_actions_toolkit@v1.11
with:
action_name: get_package_json_version
owner: ${{github.repository_owner}}
repo: ${{github.event.repository.name}}
branch: ${{ github.sha }}
compare_to_version: ${{steps.v_latest.outputs.version || '0.0.0'}}
- name: 'Trigger the ''publish'' workflow'
if: ${{ !!env.PAT && steps.v_develop.outputs.compare_result == '1' }}
uses: garronej/github_actions_toolkit@v1.11
env:
GITHUB_TOKEN: ${{ secrets.PAT }}
update_changelog:
runs-on: ubuntu-latest
needs: check_if_version_upgraded
if: needs.check_if_version_upgraded.outputs.is_upgraded_version == 'true'
steps:
- uses: garronej/github_actions_toolkit@v2.2
with:
action_name: dispatch_event
owner: ${{github.repository_owner}}
repo: ${{github.event.repository.name}}
event_type: publish
client_payload_json: |
${{
format(
'{{"from_version":"{0}","to_version":"{1}","repo":"{2}"}}',
steps.v_latest.outputs.version,
steps.v_develop.outputs.version,
github.event.repository.name
)
}}
action_name: update_changelog
branch: ${{ github.ref }}
commit_author_email: ts_ci@github.com
create_github_release:
runs-on: ubuntu-latest
needs:
- update_changelog
- check_if_version_upgraded
steps:
- uses: actions/checkout@v2
with:
ref: ${{ github.ref }}
- name: Build GitHub release body
id: step1
run: |
if [ "$FROM_VERSION" = "0.0.0" ]; then
echo "::set-output name=body::🚀"
else
echo "::set-output name=body::📋 [CHANGELOG](https://github.com/$GITHUB_REPOSITORY/blob/v$TO_VERSION/CHANGELOG.md)"
fi
env:
FROM_VERSION: ${{ needs.check_if_version_upgraded.outputs.from_version }}
TO_VERSION: ${{ needs.check_if_version_upgraded.outputs.to_version }}
- uses: garronej/action-gh-release@v0.2.0
with:
name: Release v${{ needs.check_if_version_upgraded.outputs.to_version }}
tag_name: v${{ needs.check_if_version_upgraded.outputs.to_version }}
target_commitish: ${{ github.ref }}
body: ${{ steps.step1.outputs.body }}
draft: false
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
publish_on_npm:
runs-on: macos-10.15
needs:
- update_changelog
- check_if_version_upgraded
steps:
- uses: actions/checkout@v2.3.4
with:
ref: ${{ github.ref }}
- uses: actions/setup-node@v2.1.3
with:
node-version: '15'
- run: |
PACKAGE_MANAGER=npm
if [ -f "./yarn.lock" ]; then
PACKAGE_MANAGER=yarn
fi
if [ "$PACKAGE_MANAGER" = "yarn" ]; then
yarn install --frozen-lockfile
else
npm ci
fi
$PACKAGE_MANAGER run build
- run: npx -y -p denoify@0.6.5 denoify_enable_short_npm_import_path
env:
DRY_RUN: "0"
- name: Publishing on NPM
run: |
if [ "$(npm show . version)" = "$VERSION" ]; then
echo "This version is already published"
exit 0
fi
if [ "$NPM_TOKEN" = "" ]; then
echo "Can't publish on NPM, You must first create a secret called NPM_TOKEN that contains your NPM auth token. https://help.github.com/en/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets"
false
fi
echo '//registry.npmjs.org/:_authToken=${NPM_TOKEN}' > .npmrc
npm publish
rm .npmrc
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
VERSION: ${{ needs.check_if_version_upgraded.outputs.to_version }}

View File

@ -1,116 +0,0 @@
on:
repository_dispatch:
types: publish
jobs:
update_changelog_and_sync_package_lock_version:
name: Update CHANGELOG.md and make sure package.json and package-lock.json versions matches.
runs-on: ubuntu-latest
steps:
- name: Synchronize package.json and package-lock.json version if needed.
uses: garronej/github_actions_toolkit@v1.11
env:
GITHUB_TOKEN: ${{ secrets.PAT }}
with:
action_name: sync_package_and_package_lock_version
owner: ${{github.repository_owner}}
repo: ${{github.event.client_payload.repo}}
branch: develop
commit_author_email: ts_ci@github.com
- name: Update CHANGELOG.md
if: ${{ !!github.event.client_payload.from_version }}
uses: garronej/github_actions_toolkit@v1.11
env:
GITHUB_TOKEN: ${{ secrets.PAT }}
with:
action_name: update_changelog
owner: ${{github.repository_owner}}
repo: ${{github.event.client_payload.repo}}
branch_behind: latest
branch_ahead: develop
commit_author_email: ts_ci@github.com
exclude_commit_from_author_names_json: '["ts_ci"]'
publish:
runs-on: ubuntu-latest
needs: update_changelog_and_sync_package_lock_version
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
ref: develop
- name: Remove .github directory, useless on 'latest' branch
run: rm -r .github
- name: Remove branch 'latest'
continue-on-error: true
run: |
git branch -d latest || true
git push origin :latest
- name: Create the new 'latest' branch
run: |
git branch latest
git checkout latest
- uses: actions/setup-node@v1
- run: |
if [ -f "./yarn.lock" ]; then
yarn install --frozen-lockfile
else
npm ci
fi
- run: |
PACKAGE_MANAGER=npm
if [ -f "./yarn.lock" ]; then
PACKAGE_MANAGER=yarn
fi
$PACKAGE_MANAGER run enable_short_import_path
env:
DRY_RUN: "0"
- name: (DEBUG) Show how the files have been moved to enable short import
run: ls -lR
- name: Publishing on NPM
run: |
if [ "$(npm show . version)" = "$VERSION" ]; then
echo "This version is already published"
exit 0
fi
if [ "$NPM_TOKEN" = "" ]; then
echo "Can't publish on NPM, You must first create a secret called NPM_TOKEN that contains your NPM auth token. https://help.github.com/en/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets"
false
fi
echo '//registry.npmjs.org/:_authToken=${NPM_TOKEN}' > .npmrc
npm publish
rm .npmrc
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
VERSION: ${{ github.event.client_payload.to_version }}
- name: Commit changes
run: |
git config --local user.email "ts_ci@github.com"
git config --local user.name "ts_ci"
git add -A
git commit -am "Enabling shorter import paths [automatic]"
- run: git push origin latest
- name: Release body
id: id_rb
run: |
if [ "$FROM_VERSION" = "" ]; then
echo "::set-output name=body::🚀"
else
echo "::set-output name=body::📋 [CHANGELOG](https://github.com/$OWNER/$REPO/blob/$REF/CHANGELOG.md)"
fi
env:
FROM_VERSION: ${{ github.event.client_payload.from_version }}
OWNER: ${{github.repository_owner}}
REPO: ${{github.event.client_payload.repo}}
REF: v${{github.event.client_payload.to_version}}
- name: Create Release
uses: garronej/create-release@master
env:
GITHUB_TOKEN: ${{ secrets.PAT }}
with:
tag_name: v${{ github.event.client_payload.to_version }}
release_name: Release v${{ github.event.client_payload.to_version }}
branch: latest
draft: false
prerelease: false
body: ${{ steps.id_rb.outputs.body }}

View File

@ -1,3 +1,156 @@
### **0.3.23** (2021-04-08)
- Allow to lazily load therms
### **0.3.22** (2021-04-08)
- update powerhooks
- Support terms and condition
- Fix info.ftl
- For useKcMessage we prefer returning callbacks with a changing references
### **0.3.21** (2021-04-04)
- Update powerhooks
### **0.3.20** (2021-04-01)
- Always catch freemarker errors
### **0.3.19** (2021-04-01)
- Fix previous release
### **0.3.18** (2021-04-01)
- Fix error.ftt, Adopt best effort strategy to convert ftl values into JS
### **0.3.17** (2021-03-29)
- Use push instead of replace in keycloak-js adapter to enable going back
### **0.3.15** (2021-03-28)
- Remove all reference to --external-assets, broken feature
### **0.3.14** (2021-03-28)
- Fix standalone mode: imports from js
### **0.3.13** (2021-03-26)
### **0.3.12** (2021-03-26)
- Fix mocksContext
### **0.3.11** (2021-03-26)
- Fix previous build, improve README
### **0.3.10** (2021-03-26)
- Handle <style> tag, improve documentation
### **0.3.9** (2021-03-25)
- Update readme
- Document --external-assets
- Update README.md
- Update README.md
- Update README.md
### **0.3.8** (2021-03-22)
- Make standalone mode the default
### **0.3.7** (2021-03-22)
- (test) external asset mode by default
### **0.3.6** (2021-03-22)
- Fix previous release
### **0.3.5** (2021-03-22)
- support homepage with urlPath
### **0.3.4** (2021-03-22)
- Bugfix: Import assets from CSS
### **0.3.3** (2021-03-22)
- Fix submit not receving correct text
### **0.3.2** (2021-03-21)
- Fix broken previous release
### **0.3.1** (2021-03-21)
- kcHeaderClass can be updated after initial mount
## **0.3.0** (2021-03-20)
- Bump version
- Feat: Cary over states using URL search params
- Bugfix: with kcHtmlClass
### **0.2.10** (2021-03-19)
- Remove dependency to denoify
### **0.2.9** (2021-03-19)
- Update deps and CI workflow
### **0.2.8** (2021-03-19)
- Bugfix: keycloak_build that grow and grow in size
- Add disclaimer about maitainment strategy
- Add a note for tested version support
### **0.2.7** (2021-03-13)
- Bump version
- Update README.md
- Update README.md
### **0.2.6** (2021-03-10)
- Fix generated gitignore
### **0.2.5** (2021-03-10)
- Fix generated .gitignore
### **0.2.4** (2021-03-10)
- Update README.md
### **0.2.3** (2021-03-09)
- fix gitignore generation
### **0.2.2** (2021-03-08)
- Add table of content
- Update README.md
- Update README.md
## **0.2.1** (2021-03-08)
- Update ci.yaml
- Update readme
- Update readme
- update deps
- Update readme
- Add all mocks for testing
- many small fixes
### **0.1.6** (2021-03-07)
- Fix Turkish

266
README.md
View File

@ -2,7 +2,7 @@
<img src="https://user-images.githubusercontent.com/6702424/109387840-eba11f80-7903-11eb-9050-db1dad883f78.png">
</p>
<p align="center">
<i>🔏 Keycloak theme generator for Reacts app 🔏</i>
<i>🔏 Customize key cloak's pages as if they were part of your App 🔏</i>
<br>
<br>
<img src="https://github.com/garronej/keycloakify/workflows/ci/badge.svg?branch=develop">
@ -11,78 +11,266 @@
<img src="https://img.shields.io/npm/l/keycloakify">
</p>
**!!! This module is under active developement. It is not usable yet!!!**
<p align="center">
<i>Ultimately this build tool Generates a Keycloak theme</i>
<img src="https://user-images.githubusercontent.com/6702424/110260457-a1c3d380-7fac-11eb-853a-80459b65626b.png">
</p>
# Motivations
The problem:
![keycloak_before](https://user-images.githubusercontent.com/6702424/108838381-dbbbcf80-75d3-11eb-8ae8-db41563ef9db.gif)
<p align="center">
<i>Without keycloakify:</i><br>
<img src="https://user-images.githubusercontent.com/6702424/108838381-dbbbcf80-75d3-11eb-8ae8-db41563ef9db.gif">
</p>
When we redirected to Keycloak the user suffers from a harsh context switch.
On je login/register pages the language is set back to default and the theme is different that the one on the app.
Keycloak does offer a way to customize theses pages but it requires a lot of raw HTML/CSS hacking
to reproduce the look and feel of a specific app. Not mentioning the maintenance cost of such an endeavour.
Wouldn't it be great if we could just design the login and register pages as if they where part of our app while
still letting Keycloak handle the heavy lifting of actually authenticating the users?
Wouldn't it be great if we could just design the login and register pages as if they where part of our app?
Here is `yarn add keycloakify` for you 🍸
TODO: Insert video after.
<p align="center">
<i>With keycloakify:</i><br>
<img src="https://github.com/InseeFrLab/keycloakify/releases/download/v0.3.8/keycloakify_after.gif">
</p>
**TL;DR**: [Here](https://github.com/garronej/keycloakify-demo-app) is a Hello World React project with Keycloakify set up.
- [Motivations](#motivations)
- [How to use](#how-to-use)
- [Setting up the build tool](#setting-up-the-build-tool)
- [Developing your login and register pages in your React app](#developing-your-login-and-register-pages-in-your-react-app)
- [Just changing the look](#just-changing-the-look)
- [Changing the look **and** feel](#changing-the-look-and-feel)
- [Hot reload](#hot-reload)
- [GitHub Actions](#github-actions)
- [Requirements](#requirements)
- [Limitations](#limitations)
- [`process.env.PUBLIC_URL` not supported.](#processenvpublic_url-not-supported)
- [`@font-face` importing fonts from the `src/` dir](#font-face-importing-fonts-from-thesrc-dir)
- [Example of setup that **won't** work](#example-of-setup-that-wont-work)
- [Workarounds](#workarounds)
- [Implement context persistence (optional)](#implement-context-persistence-optional)
- [API Reference](#api-reference)
- [The build tool](#the-build-tool)
# How to use
## Setting up the build tool
Add `keycloakify` to the dev dependencies of your project `npm install --save-dev keycloakify` or `yarn add --dev keycloakify`
then configure your `package.json` build's script to build the keycloak's theme by adding `&& build-keycloak-theme`.
Typically you will get:
`package.json`:
[`package.json`](https://github.com/garronej/keycloakify-demo-app/blob/main/package.json)
```json
"devDependencies": {
"homepage": "https://URL.OF/YOUR-APP"
"dependencies": {
"keycloakify": "^0.0.10"
},
"scripts": {
"build": "react-scripts build && build-keycloak-theme"
},
},
"scripts": {
"keycloak": "yarn build && build-keycloak-theme",
},
```
`"homepage"` must be specified only if the url path is not `/`
(Onl `/YOUR-APP` matters `URL.OF` don't have to be the actual domain)
Then build your app with `yarn run build` or `npm run build`, you will be provided with instructions
about how to load the theme into Keycloak.
It is mandatory that you specify the url where your app will be available
using the `homepage` field.
[![kickstart_video](https://user-images.githubusercontent.com/6702424/108877866-f146ee80-75ff-11eb-8120-003b3c5f6dd8.png)](https://youtu.be/xTz0Rj7i2v8)
Once you've edited your `package.json` you can install your new
dependency with `yarn install` and build the keycloak theme with
`yarn keycloak`.
Once the build is complete instructions about how to load
the theme into Keycloak are printed in the console.
## Developing your login and register pages in your React app
TODO
### Just changing the look
# How to implement context persistance
The first approach is to only arr/replace the default class names by your
own.
If you want dark mode preference, language and others users preferences your can do so
very easily by using [`powerhooks/useGlobalState`](https://github.com/garronej/powerhooks)
```tsx
WARNING: `powerhooks` is still a work in progress.
import { App } from "./<wherever>/App";
import {
KcApp,
defaultKcProps,
kcContext
} from "keycloakify";
import { css } from "tss-react";
# REQUIREMENTS
const myClassName = css({ "color": "red" });
reactDom.render(
// Unless the app is currently being served by Keycloak
// kcContext is undefined.
kcContext !== undefined ?
<KcApp
kcContext={kcContext}
{...{
...defaultKcProps,
"kcHeaderWrapperClass": myClassName
}}
/> :
<App />, // Your actual app
document.getElementById("root")
);
```
<i>result:</i>
<p align="center">
<img src="https://user-images.githubusercontent.com/6702424/110261408-688d6280-7fb0-11eb-9822-7003d268b459.png">
</p>
### Changing the look **and** feel
If you want to really re-implement the pages the best approach is to
create you own version of the [`<KcApp />`](https://github.com/garronej/keycloakify/blob/develop/src/lib/components/KcApp.tsx).
Copy/past some of [the components](https://github.com/garronej/keycloakify/tree/develop/src/lib/components) provided by this module and start hacking around.
### Hot reload
By default, in order to see your changes you will have to wait for
`yarn build` to complete which can takes sevrall minute.
If you want to test your login screens outside of Keycloak, in [storybook](https://storybook.js.org/)
for example you can use `kcContextMocks`.
```tsx
import {
KcApp,
defaultKcProps,
kcContextMocks
} from "keycloakify";
reactDom.render(
kcContext !== undefined ?
<KcApp
kcContext={kcContextMocks.kcLoginContext}
{...defaultKcProps}
/>
document.getElementById("root")
);
```
then `yarn start` ...
Checkout [this concrete example](https://github.com/garronej/keycloakify-demo-app/blob/main/src/index.tsx)
*NOTE: keycloak-react-theming was renamed keycloakify since this video was recorded*
[![kickstart_video](https://user-images.githubusercontent.com/6702424/108877866-f146ee80-75ff-11eb-8120-003b3c5f6dd8.png)](https://youtu.be/xTz0Rj7i2v8)
# GitHub Actions
![image](https://user-images.githubusercontent.com/6702424/110708305-c44b2c00-81fa-11eb-8152-eeaaac0883d6.png)
[Here is a demo repo](https://github.com/garronej/keycloakify-demo-app) to show how to automate
the building and publishing of the theme (the .jar file).
# Requirements
Tested with the following Keycloak versions:
- [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)
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 the Keycloak v11.
This tools assumes you are bundling your app with Webpack (tested with 4.44.2) .
It assumes there is a `build/` directory at the root of your react project directory containing a `index.html` file
and a `static/` directory generated by webpack.
and a `build/static/` directory generated by webpack.
**All this is defaults with [`create-react-app`](https://create-react-app.dev)** (tested with 4.0.3=)
- For building the theme: `mvn` (Maven) must be installed
- For development, (testing the theme in a local container ): `rm`, `mkdir`, `wget`, `unzip` are assumed to be available
- For development (testing the theme in a local container ): `rm`, `mkdir`, `wget`, `unzip` are assumed to be available
and `docker` up and running.
NOTE: This build tool has only be tested on MacOS.
# Limitations
## `process.env.PUBLIC_URL` not supported.
You won't be able to [import things from your public directory in your JavaScript code](https://create-react-app.dev/docs/using-the-public-folder/#adding-assets-outside-of-the-module-system). (This isn't recommended anyway).
## `@font-face` importing fonts from the `src/` dir
### Example of setup that **won't** work
- We have a `fonts/` directory in `src/`
- We import the font like this [`src: url("/fonts/my-font.woff2") format("woff2");`(https://github.com/garronej/keycloakify-demo-app/blob/07d54a3012ef354ee12b1374c6f7ad1cb125d56b/src/fonts.scss#L4) in a `.scss` a file.
### Workarounds
If it is possible, use Google Fonts or any other font provider.
If you want to host your font recommended approach is to move your fonts into the `public`
directory and to place your `@font-face` statements in the `public/index.html`.
Example [here]().
You can also [use your explicit url](https://github.com/garronej/keycloakify-demo-app/blob/2de8a9eb6f5de9c94f9cd3991faad0377e63268c/src/fonts.scss#L16) but don't forget [`Access-Control-Allow-Origin`](https://github.com/garronej/keycloakify-demo-app/blob/2de8a9eb6f5de9c94f9cd3991faad0377e63268c/nginx.conf#L17-L19).
# Implement context persistence (optional)
If, before logging in, a user has selected a specific language
you don't want it to be reset to default when the user gets redirected to
the login or register pages.
Same goes for the dark mode, you don't want, if the user had it enabled
to show the login page with light themes.
The problem is that you are probably using `localStorage` to persist theses values across
reload but, as the Keycloak pages are not served on the same domain that the rest of your
app you won't be able to carry over states using `localStorage`.
The only reliable solution is to inject parameters into the URL before
redirecting to Keycloak. We integrate with
[`keycloak-js`](https://github.com/keycloak/keycloak-documentation/blob/master/securing_apps/topics/oidc/javascript-adapter.adoc),
by providing you a way to tell `keycloak-js` that you would like to inject
some search parameters before redirecting.
The method also works with [`@react-keycloak/web`](https://www.npmjs.com/package/@react-keycloak/web) (use the `initOptions`).
You can implement your own mechanism to pass the states in the URL and
restore it on the other side but we recommend using `powerhooks/useGlobalState`
from the library [`powerhooks`](https://www.powerhooks.dev) that provide an elegant
way to handle states such as `isDarkModeEnabled` or `selectedLanguage`.
Let's modify [the example](https://github.com/keycloak/keycloak-documentation/blob/master/securing_apps/topics/oidc/javascript-adapter.adoc) from the official `keycloak-js` documentation to
enables the states of `useGlobalStates` to be injected in the URL before redirecting.
Note that the states are automatically restored on the other side by `powerhooks`
```typescript
import keycloak_js from "keycloak-js";
import { injectGlobalStatesInSearchParams } from "powerhooks/useGlobalState";
import { createKeycloakAdapter } from "keycloakify";
//...
const keycloakInstance = keycloak_js({
"url": "http://keycloak-server/auth",
"realm": "myrealm",
"clientId": "myapp"
});
keycloakInstance.init({
"onLoad": 'check-sso',
"silentCheckSsoRedirectUri": window.location.origin + "/silent-check-sso.html",
"adapter": createKeycloakAdapter({
"transformUrlBeforeRedirect": injectGlobalStatesInSearchParams,
keycloakInstance
})
});
//...
```
If you really want to go the extra miles and avoid having the white
flash of the blank html before the js bundle have been evaluated
[here is a snippet](https://github.com/InseeFrLab/onyxia-ui/blob/a77eb502870cfe6878edd0d956c646d28746d053/public/index.html#L5-L54) that you can place in your `public/index.html` if you are using `powerhooks/useGlobalState`.
# API Reference
## The build tool
@ -90,10 +278,4 @@ NOTE: This build tool has only be tested on MacOS.
Part of the lib that runs with node, at build time.
- `npx build-keycloak-theme`: Builds the theme, the CWD is assumed to be the root of your react project.
- `npx download-sample-keycloak-themes`: Downloads the keycloak default themes (for development purposes)
## The fronted lib ( imported into your react app )
Part of the lib that you import in your react project and runs on the browser.
**TODO**
- `npx download-sample-keycloak-themes`: Downloads the keycloak default themes (for development purposes)

View File

@ -1,6 +1,6 @@
{
"name": "keycloakify",
"version": "0.1.6",
"version": "0.3.23",
"description": "Keycloak theme generator for Reacts app",
"repository": {
"type": "git",
@ -13,10 +13,8 @@
"build": "yarn clean && tsc && yarn grant-exec-perms && yarn copy-files",
"grant-exec-perms": "node dist/bin/tools/grant-exec-perms.js",
"test": "node dist/test",
"enable_short_import_path": "yarn build && denoify_enable_short_npm_import_path",
"copy-files": "copyfiles -u 1 src/**/*.ftl src/**/*.xml src/**/*.js dist/",
"generate-messages": "node dist/bin/generate-i18n-messages.js",
"watch": "tsc -w"
"generate-messages": "node dist/bin/generate-i18n-messages.js"
},
"bin": {
"build-keycloak-theme": "dist/bin/build-keycloak-theme/index.js",
@ -45,18 +43,18 @@
"@types/node": "^10.0.0",
"@types/react": "^17.0.0",
"copyfiles": "^2.4.1",
"denoify": "^0.6.5",
"properties-parser": "^0.3.1",
"react": "^17.0.1",
"rimraf": "^3.0.2",
"typescript": "^4.1.5"
"typescript": "^4.2.3"
},
"dependencies": {
"cheerio": "^1.0.0-rc.5",
"evt": "2.0.0-beta.15",
"minimal-polyfills": "^2.1.6",
"powerhooks": "^0.0.19",
"path": "^0.12.7",
"powerhooks": "^0.0.36",
"scripting-tools": "^0.19.13",
"tss-react": "^0.0.11"
"tss-react": "^0.0.12"
}
}

View File

@ -0,0 +1,263 @@
<script>const _=
{
"url": {
"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 (){
<#attempt>
<#if realm.internationalizationEnabled>
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>
</#attempt>
})(),
"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>
<#if auth.showUsername() && !auth.showResetCredentials()>
Object.assign(
out,
{
"attemptedUsername": (function (){
<#attempt>
return "${auth.attemptedUsername}";
<#recover>
</#attempt>
})()
}
);
</#if>
<#recover>
</#attempt>
return out;
</#if>
<#recover>
</#attempt>
})(),
"scripts": (function(){
var out = [];
<#attempt>
<#if scripts??>
<#attempt>
<#list scripts as script>
out.push((function (){
<#attempt>
return "${script}";
<#recover>
</#attempt>
})());
</#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>

View File

@ -2,13 +2,21 @@
{
"client": (function (){
<#attempt>
<#if client??>
return {
"baseUrl": "${(client.baseUrl!'')?no_esc}" || undefined
"baseUrl": (function (){
<#attempt>
return "${(client.baseUrl!'')?no_esc}" || undefined;
<#recover>
</#attempt>
})()
};
</#if>
return undefined;
<#recover>
</#attempt>
})()
}

View File

@ -2,23 +2,28 @@
import cheerio from "cheerio";
import {
replaceImportFromStaticInJsCode,
replaceImportsFromStaticInJsCode,
replaceImportsInInlineCssCode,
generateCssCodeToDefineGlobals
} from "../replaceImportFromStatic";
import fs from "fs";
import { join as pathJoin } from "path";
import { objectKeys } from "evt/tools/typeSafety/objectKeys";
export const pageIds = ["login.ftl", "register.ftl", "info.ftl", "error.ftl", "login-reset-password.ftl", "login-verify-email.ftl"] as const;
export const pageIds = [
"login.ftl", "register.ftl", "info.ftl",
"error.ftl", "login-reset-password.ftl",
"login-verify-email.ftl", "terms.ftl"
] as const;
export type PageId = typeof pageIds[number];
export type PageId = typeof pageIds[number];
function loadAdjacentFile(fileBasename: string){
function loadAdjacentFile(fileBasename: string) {
return fs.readFileSync(pathJoin(__dirname, fileBasename))
.toString("utf8");
};
function loadFtlFile(ftlFileBasename: PageId | "template.ftl") {
function loadFtlFile(ftlFileBasename: PageId | "common.ftl") {
try {
return loadAdjacentFile(ftlFileBasename)
@ -29,21 +34,23 @@ function loadFtlFile(ftlFileBasename: PageId | "template.ftl") {
}
}
export function generateFtlFilesCodeFactory(
params: {
ftlValuesGlobalName: string;
cssGlobalsToDefine: Record<string, string>;
indexHtmlCode: string;
urlPathname: string;
}
) {
const { ftlValuesGlobalName, cssGlobalsToDefine, indexHtmlCode } = params;
const { ftlValuesGlobalName, cssGlobalsToDefine, indexHtmlCode, urlPathname } = params;
const $ = cheerio.load(indexHtmlCode);
$("script:not([src])").each((...[, element]) => {
const { fixedJsCode } = replaceImportFromStaticInJsCode({
const { fixedJsCode } = replaceImportsFromStaticInJsCode({
ftlValuesGlobalName,
"jsCode": $(element).html()!
});
@ -52,6 +59,17 @@ export function generateFtlFilesCodeFactory(
});
$("style").each((...[, element]) => {
const { fixedCssCode } = replaceImportsInInlineCssCode({
"cssCode": $(element).html()!,
"urlPathname": params.urlPathname
});
$(element).text(fixedCssCode);
});
([
["link", "href"],
["script", "src"],
@ -60,18 +78,24 @@ export function generateFtlFilesCodeFactory(
const href = $(element).attr(attrName);
if (!href?.startsWith("/")) {
if (href === undefined) {
return;
}
$(element).attr(attrName, "${url.resourcesPath}/build" + href);
$(element).attr(
attrName,
href.replace(
new RegExp(`^${urlPathname.replace(/\//g, "\\/")}`),
"${url.resourcesPath}/build/"
)
);
})
);
//FTL is no valid html, we can't insert with cheerio, we put placeholder for injecting later.
const ftlCommonPlaceholders = {
'{ "x": "vIdLqMeOed9sdLdIdOxdK0d" }': loadFtlFile("template.ftl"),
'{ "x": "vIdLqMeOed9sdLdIdOxdK0d" }': loadFtlFile("common.ftl"),
'<!-- xIdLqMeOedErIdLsPdNdI9dSlxI -->':
[
'<#if scripts??>',
@ -89,9 +113,10 @@ export function generateFtlFilesCodeFactory(
...(Object.keys(cssGlobalsToDefine).length === 0 ? [] : [
'',
'<style>',
generateCssCodeToDefineGlobals(
{ cssGlobalsToDefine }
).cssCodeToPrependInHead,
generateCssCodeToDefineGlobals({
cssGlobalsToDefine,
urlPathname
}).cssCodeToPrependInHead,
'</style>',
''
]),

View File

@ -1,37 +1,82 @@
<script>const _=
{
"messageHeader": "${messageHeader!''}" || undefined,
"messageHeader": (function (){
<#attempt>
return "${messageHeader!''}" || undefined;
<#recover>
</#attempt>
})(),
"requiredActions": (function (){
<#attempt>
<#if requiredActions??>
var out =[];
<#attempt>
<#list requiredActions>
<#attempt>
<#items as reqActionItem>
out.push("${reqActionItem}");
</#items></b>
out.push((function (){
<#attempt>
return "${reqActionItem}";
<#recover>
</#attempt>
})());
</#items>
<#recover>
</#attempt>
</#list>
<#recover>
</#attempt>
return out;
<#else>
return undefined;
</#if>
<#recover>
</#attempt>
})(),
"skipLink": (function (){
<#attempt>
<#if skipLink??>
return true;
</#if>
<#recover>
</#attempt>
return false;
})(),
"pageRedirectUri": "${(pageRedirectUri!'')?no_esc}" || undefined,
"actionUri": "${(actionUri!'')?no_esc}" || undefined,
"pageRedirectUri": (function (){
<#attempt>
return "${(pageRedirectUri!'')?no_esc}" || undefined;
<#recover>
</#attempt>
})(),
"actionUri": (function (){
<#attempt>
return "${(actionUri!'')?no_esc}" || undefined;
<#recover>
</#attempt>
})(),
"client": {
"baseUrl": "${(client.baseUrl!'')?no_esc}" || undefined
"baseUrl": (function(){
<#attempt>
return "${(client.baseUrl!'')?no_esc}" || undefined;
<#recover>
</#attempt>
})()
}
}
</script>

View File

@ -1,7 +1,14 @@
<script>const _=
{
"realm": {
"loginWithEmailAllowed": ${realm.loginWithEmailAllowed?c}
},
"loginWithEmailAllowed": (function (){
<#attempt>
return ${realm.loginWithEmailAllowed?c};
<#recover>
</#attempt>
})()
}
}
</script>

View File

@ -1,83 +1,160 @@
<script>const _=
{
"url": {
"loginResetCredentialsUrl": "${url.loginResetCredentialsUrl?no_esc}",
"registrationUrl": "${url.registrationUrl?no_esc}"
"loginResetCredentialsUrl": (function (){
<#attempt>
return "${url.loginResetCredentialsUrl?no_esc}";
<#recover>
</#attempt>
})(),
"registrationUrl": (function (){
<#attempt>
return "${url.registrationUrl?no_esc}";
<#recover>
</#attempt>
})()
},
"realm": {
"loginWithEmailAllowed": ${realm.loginWithEmailAllowed?c},
"rememberMe": ${realm.rememberMe?c},
"password": ${realm.password?c},
"resetPasswordAllowed": ${realm.resetPasswordAllowed?c},
"registrationAllowed": ${realm.registrationAllowed?c}
"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>
var out= {
"selectedCredential": "${auth.selectedCredential!''}" || undefined
return {
"selectedCredential": (function (){
<#attempt>
return "${auth.selectedCredential!''}" || undefined;
<#recover>
</#attempt>
})()
};
return out;
</#if>
return undefined;
<#recover>
</#attempt>
})(),
"social": {
"displayInfo": ${social.displayInfo?c},
"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": "${p.loginUrl?no_esc}",
"alias": "${p.alias}",
"providerId": "${p.providerId}",
"displayName": "${p.displayName}"
"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>
return undefined;
<#recover>
</#attempt>
})()
},
"usernameEditDisabled": (function () {
<#attempt>
<#if usernameEditDisabled??>
return true;
</#if>
<#recover>
</#attempt>
return false;
})(),
"login": {
"username": "${login.username!''}" || undefined,
"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>

View File

@ -1,46 +1,152 @@
<script>const _=
{
"url": {
"registrationAction": "${url.registrationAction?no_esc}"
"registrationAction": (function (){
<#attempt>
return "${url.registrationAction?no_esc}";
<#recover>
</#attempt>
})()
},
"messagesPerField": {
"printIfExists": function (key, x) {
switch(key){
case "userLabel": "${messagesPerField.printIfExists('userLabel','1')}" ? x : undefined;
case "username": "${messagesPerField.printIfExists('username','1')}" ? x : undefined;
case "email": "${messagesPerField.printIfExists('email','1')}" ? x : undefined;
case "firstName": "${messagesPerField.printIfExists('firstName','1')}" ? x : undefined;
case "lastName": "${messagesPerField.printIfExists('lastName','1')}" ? x : undefined;
case "password": "${messagesPerField.printIfExists('password','1')}" ? x : undefined;
case "password-confirm": "${messagesPerField.printIfExists('password-confirm','1')}" ? x : undefined;
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": "${register.formData.firstName!''}" || undefined,
"displayName": "${register.formData.displayName!''}" || undefined,
"lastName": "${register.formData.lastName!''}" || undefined,
"email": "${register.formData.email!''}" || undefined,
"username": "${register.formData.username!''}" || undefined
"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": "${recaptchaSiteKey!''}" || undefined
"recaptchaSiteKey": (function (){
<#attempt>
return "${recaptchaSiteKey!''}" || undefined;
<#recover>
</#attempt>
})()
}
</script>

View File

@ -1,114 +0,0 @@
<script>const _=
{
"url": {
"loginAction": "${url.loginAction?no_esc}",
"resourcesPath": "${url.resourcesPath?no_esc}",
"resourcesCommonPath": "${url.resourcesCommonPath?no_esc}",
"loginRestartFlowUrl": "${url.loginRestartFlowUrl?no_esc}",
"loginUrl": "${url.loginUrl?no_esc}"
},
"realm": {
"displayName": "${realm.displayName!''}" || undefined,
"displayNameHtml": "${realm.displayNameHtml!''}" || undefined,
"internationalizationEnabled": ${realm.internationalizationEnabled?c},
"registrationEmailAsUsername": ${realm.registrationEmailAsUsername?c},
},
"locale": (function (){
<#if realm.internationalizationEnabled>
return {
"supported": (function(){
<#if realm.internationalizationEnabled>
var out= [];
<#list locale.supported as lng>
out.push({
"url": "${lng.url?no_esc}",
"label": "${lng.label}",
"languageTag": "${lng.languageTag}"
});
</#list>
return out;
</#if>
return undefined;
})(),
"current": "${locale.current}"
};
</#if>
return undefined;
})(),
"auth": (function (){
<#if auth?has_content>
var out= {
"showUsername": ${auth.showUsername()?c},
"showResetCredentials": ${auth.showResetCredentials()?c},
"showTryAnotherWayLink": ${auth.showTryAnotherWayLink()?c},
};
<#if auth.showUsername() && !auth.showResetCredentials()>
Object.assign(
out,
{
"attemptedUsername": "${auth.attemptedUsername}"
}
);
</#if>
return out;
</#if>
return undefined;
})(),
"scripts": (function(){
var out = [];
<#if scripts??>
<#list scripts as script>
out.push("${script}");
</#list>
</#if>
return out;
})(),
"message": (function (){
<#if message?has_content>
return { 
"type": "${message.type}",
"summary": String.htmlUnescape("${message.summary}")
};
</#if>
return undefined;
})(),
"isAppInitiatedAction": (function (){
<#if isAppInitiatedAction??>
return true;
</#if>
return false;
})()
}
</script>

View File

@ -3,24 +3,28 @@ import { transformCodebase } from "../tools/transformCodebase";
import * as fs from "fs";
import { join as pathJoin } from "path";
import {
replaceImportFromStaticInCssCode,
replaceImportFromStaticInJsCode
replaceImportsInCssCode,
replaceImportsFromStaticInJsCode
} from "./replaceImportFromStatic";
import { generateFtlFilesCodeFactory, pageIds } from "./generateFtl";
import { builtinThemesUrl } from "../install-builtin-keycloak-themes";
import { downloadAndUnzip } from "../tools/downloadAndUnzip";
import { builtinThemesUrl } from "../install-builtin-keycloak-themes";
import { downloadAndUnzip } from "../tools/downloadAndUnzip";
import * as child_process from "child_process";
import { ftlValuesGlobalName } from "./ftlValuesGlobalName";
import { ftlValuesGlobalName } from "./ftlValuesGlobalName";
import { resourcesCommonPath, resourcesPath, subDirOfPublicDirBasename } from "../../lib/kcContextMocks/urlResourcesPath";
import { isInside } from "../tools/isInside";
export function generateKeycloakThemeResources(
params: {
themeName: string;
reactAppBuildDirPath: string;
keycloakThemeBuildingDirPath: string;
urlPathname: string;
}
) {
const { themeName, reactAppBuildDirPath, keycloakThemeBuildingDirPath } = params;
const { themeName, reactAppBuildDirPath, keycloakThemeBuildingDirPath, urlPathname } = params;
const themeDirPath = pathJoin(keycloakThemeBuildingDirPath, "src", "main", "resources", "theme", themeName, "login");
@ -31,9 +35,20 @@ export function generateKeycloakThemeResources(
"srcDirPath": reactAppBuildDirPath,
"transformSourceCode": ({ filePath, sourceCode }) => {
//NOTE: Prevent cycles, excludes the folder we generated for debug in public/
if (
isInside({
"dirPath": pathJoin(reactAppBuildDirPath, subDirOfPublicDirBasename),
filePath
})
) {
return undefined;
}
if (/\.css?$/i.test(filePath)) {
const { cssGlobalsToDefine, fixedCssCode } = replaceImportFromStaticInCssCode(
const { cssGlobalsToDefine, fixedCssCode } = replaceImportsInCssCode(
{ "cssCode": sourceCode.toString("utf8") }
);
@ -46,9 +61,10 @@ export function generateKeycloakThemeResources(
}
if (/\.js?$/i.test(filePath)) {
const { fixedJsCode } = replaceImportFromStaticInJsCode({
const { fixedJsCode } = replaceImportsFromStaticInJsCode({
"jsCode": sourceCode.toString("utf8"),
ftlValuesGlobalName
});
@ -67,7 +83,8 @@ export function generateKeycloakThemeResources(
ftlValuesGlobalName,
"indexHtmlCode": fs.readFileSync(
pathJoin(reactAppBuildDirPath, "index.html")
).toString("utf8")
).toString("utf8"),
urlPathname
});
pageIds.forEach(pageId => {
@ -90,11 +107,48 @@ export function generateKeycloakThemeResources(
"destDirPath": tmpDirPath
});
const themeResourcesDirPath = pathJoin(themeDirPath, "resources");
transformCodebase({
"srcDirPath": pathJoin(tmpDirPath, "keycloak", "login", "resources"),
"destDirPath": pathJoin(themeDirPath, "resources")
"destDirPath": themeResourcesDirPath
});
const reactAppPublicDirPath = pathJoin(reactAppBuildDirPath, "..", "public");
transformCodebase({
"srcDirPath": themeResourcesDirPath,
"destDirPath": pathJoin(
reactAppPublicDirPath,
resourcesPath
)
});
transformCodebase({
"srcDirPath": pathJoin(tmpDirPath, "keycloak", "common", "resources"),
"destDirPath": pathJoin(
reactAppPublicDirPath,
resourcesCommonPath
)
});
const keycloakResourcesWithinPublicDirPath =
pathJoin(reactAppPublicDirPath, subDirOfPublicDirBasename);
fs.writeFileSync(
pathJoin(keycloakResourcesWithinPublicDirPath, "README.txt"),
Buffer.from([
"This is just a test folder that helps develop",
"the login and register page without having to yarn build"
].join(" "))
);
fs.writeFileSync(
pathJoin(keycloakResourcesWithinPublicDirPath, ".gitignore"),
Buffer.from("*", "utf8")
);
child_process.execSync(`rm -r ${tmpDirPath}`);
}

View File

@ -6,6 +6,7 @@ import type { ParsedPackageJson } from "./generateJavaStackFiles";
import { join as pathJoin, relative as pathRelative, basename as pathBasename } from "path";
import * as child_process from "child_process";
import { generateDebugFiles, containerLaunchScriptBasename } from "./generateDebugFiles";
import { URL } from "url";
const reactProjectDirPath = process.cwd();
@ -22,7 +23,25 @@ if (require.main === module) {
generateKeycloakThemeResources({
keycloakThemeBuildingDirPath,
"reactAppBuildDirPath": pathJoin(reactProjectDirPath, "build"),
"themeName": parsedPackageJson.name
"themeName": parsedPackageJson.name,
"urlPathname": (() => {
const url = (() => {
const { homepage } = parsedPackageJson;
return homepage === undefined ?
undefined :
new URL(homepage);
})();
return url === undefined ?
"/" :
url.pathname.replace(/([^/])$/, "$1/");
})()
});
const { jarFilePath } = generateJavaStackFiles({

View File

@ -1,7 +1,7 @@
import * as crypto from "crypto";
export function replaceImportFromStaticInJsCode(
export function replaceImportsFromStaticInJsCode(
params: {
ftlValuesGlobalName: string;
jsCode: string;
@ -10,16 +10,36 @@ export function replaceImportFromStaticInJsCode(
const { jsCode, ftlValuesGlobalName } = params;
const fixedJsCode = jsCode!.replace(
/"static\//g,
`window.${ftlValuesGlobalName}.url.resourcesPath.replace(/^\\//,"") + "/build/static/`
const fixedJsCode = jsCode.replace(
/[a-z]+\.[a-z]+\+"static\//g,
`window.${ftlValuesGlobalName}.url.resourcesPath + "/build/static/`
);
return { fixedJsCode };
}
export function replaceImportFromStaticInCssCode(
export function replaceImportsInInlineCssCode(
params: {
cssCode: string;
urlPathname: string;
}
): { fixedCssCode: string; } {
const { cssCode, urlPathname } = params;
const fixedCssCode = cssCode.replace(
urlPathname === "/" ?
/url\(\/([^/][^)]+)\)/g :
new RegExp(`url\\(${urlPathname}([^)]+)\\)`, "g"),
(...[, group]) => `url(${"${url.resourcesPath}/build/" + group})`
);
return { fixedCssCode };
}
export function replaceImportsInCssCode(
params: {
cssCode: string;
}
@ -32,7 +52,7 @@ export function replaceImportFromStaticInCssCode(
const cssGlobalsToDefine: Record<string, string> = {};
new Set(cssCode.match(/(url\(\/[^)]+\))/g) ?? [])
new Set(cssCode.match(/url\(\/[^/][^)]+\)[^;}]*/g) ?? [])
.forEach(match =>
cssGlobalsToDefine[
"url" + crypto
@ -60,12 +80,13 @@ export function replaceImportFromStaticInCssCode(
export function generateCssCodeToDefineGlobals(
params: {
cssGlobalsToDefine: Record<string, string>;
urlPathname: string;
}
): {
cssCodeToPrependInHead: string;
} {
const { cssGlobalsToDefine } = params;
const { cssGlobalsToDefine, urlPathname } = params;
return {
"cssCodeToPrependInHead": [
@ -73,12 +94,8 @@ export function generateCssCodeToDefineGlobals(
...Object.keys(cssGlobalsToDefine)
.map(cssVariableName => [
`--${cssVariableName}:`,
[
"url(",
"${url.resourcesPath}/build" +
cssGlobalsToDefine[cssVariableName].match(/^url\(([^)]+)\)$/)![1],
")"
].join("")
cssGlobalsToDefine[cssVariableName]
.replace(new RegExp(`url\\(${urlPathname.replace(/\//g, "\\/")}`, "g"), "url(${url.resourcesPath}/build/")
].join(" "))
.map(line => ` ${line};`),
"}"

View File

@ -49,7 +49,7 @@ crawl(".").forEach(filePath => {
child_process.execSync(`rm -r ${tmpDirPath}`);
const targetDirPath = pathJoin(getProjectRoot(), "src", "lib", "i18n", "generated_messages");
const targetDirPath = pathJoin(getProjectRoot(), "src", "lib", "i18n", "generated_kcMessages");
fs.mkdirSync(targetDirPath, { "recursive": true });
@ -65,7 +65,7 @@ Object.keys(record).forEach(pageType => {
'//PLEASE DO NOT EDIT MANUALLY',
'',
'/* spell-checker: disable */',
`export const messages= ${JSON.stringify(record[pageType], null, 2)} as const;`,
`export const kcMessages= ${JSON.stringify(record[pageType], null, 2)};`,
'/* spell-checker: enable */'
].join("\n"), "utf8")
);

14
src/bin/tools/isInside.ts Normal file
View File

@ -0,0 +1,14 @@
import { relative as pathRelative } from "path";
export function isInside(
params: {
dirPath: string;
filePath: string;
}
) {
const { dirPath, filePath } = params;
return !pathRelative(dirPath, filePath).startsWith("..");
}

View File

@ -3,24 +3,20 @@ import { memo } from "react";
import { Template } from "./Template";
import type { KcProps } from "./KcProps";
import { assert } from "../tools/assert";
import { kcContext } from "../kcContext";
import type { KcContext } from "../KcContext";
import { useKcMessage } from "../i18n/useKcMessage";
export const Error = memo((props: KcProps) => {
export const Error = memo(({ kcContext, ...props }: { kcContext: KcContext.Error; } & KcProps) => {
const { msg } = useKcMessage();
assert(
kcContext !== undefined &&
kcContext.pageId === "error.ftl" &&
kcContext.message !== undefined
);
assert(kcContext.message !== undefined);
const { message, client } = kcContext;
return (
<Template
{...props}
{...{ kcContext, ...props }}
displayMessage={false}
headerNode={msg("errorTitle")}
formNode={

View File

@ -3,18 +3,14 @@ import { memo } from "react";
import { Template } from "./Template";
import type { KcProps } from "./KcProps";
import { assert } from "../tools/assert";
import { kcContext } from "../kcContext";
import type { KcContext } from "../KcContext";
import { useKcMessage } from "../i18n/useKcMessage";
export const Info = memo((props: KcProps) => {
export const Info = memo(({ kcContext, ...props }: { kcContext: KcContext.Info; } & KcProps) => {
const { msg } = useKcMessage();
assert(
kcContext !== undefined &&
kcContext.pageId === "info.ftl" &&
kcContext.message !== undefined
);
assert(kcContext.message !== undefined);
const {
messageHeader,
@ -28,7 +24,7 @@ export const Info = memo((props: KcProps) => {
return (
<Template
{...props}
{...{ kcContext, ...props }}
displayMessage={false}
headerNode={
messageHeader !== undefined ?
@ -57,13 +53,13 @@ export const Info = memo((props: KcProps) => {
{
!skipLink &&
pageRedirectUri !== undefined ?
<p><a href="${pageRedirectUri}">${(msg("backToApplication"))}</a></p>
<p><a href={pageRedirectUri}>{(msg("backToApplication"))}</a></p>
:
actionUri !== undefined ?
<p><a href="${actionUri}">${msg("proceedWithAction")}</a></p>
<p><a href={actionUri}>{msg("proceedWithAction")}</a></p>
:
client.baseUrl !== undefined &&
<p><a href="${client.baseUrl}">${msg("backToApplication")}</a></p>
<p><a href={client.baseUrl}>{msg("backToApplication")}</a></p>
}
</div>

View File

@ -1,7 +1,6 @@
import { memo } from "react";
import { kcContext } from "../kcContext";
import { assert } from "../tools/assert";
import type { KcContext } from "../KcContext";
import type { KcProps } from "./KcProps";
import { Login } from "./Login";
import { Register } from "./Register";
@ -9,18 +8,16 @@ import { Info } from "./Info";
import { Error } from "./Error";
import { LoginResetPassword } from "./LoginResetPassword";
import { LoginVerifyEmail } from "./LoginVerifyEmail";
import { Terms } from "./Terms";
export const KcApp = memo((props: KcProps) => {
assert(kcContext !== undefined, "App is not currently served by a Keycloak server");
export const KcApp = memo(({ kcContext, ...props }: { kcContext: KcContext; } & KcProps ) => {
switch (kcContext.pageId) {
case "login.ftl": return <Login {...props} />;
case "register.ftl": return <Register {...props} />;
case "info.ftl": return <Info {...props} />;
case "error.ftl": return <Error {...props} />;
case "login-reset-password.ftl": return <LoginResetPassword {...props} />;
case "login-verify-email.ftl": return <LoginVerifyEmail {...props} />;
case "login.ftl": return <Login {...{ kcContext, ...props }} />;
case "register.ftl": return <Register {...{ kcContext, ...props }} />;
case "info.ftl": return <Info {...{ kcContext, ...props }} />;
case "error.ftl": return <Error {...{ kcContext, ...props }} />;
case "login-reset-password.ftl": return <LoginResetPassword {...{ kcContext, ...props }} />;
case "login-verify-email.ftl": return <LoginVerifyEmail {...{ kcContext, ...props }} />;
case "terms.ftl": return <Terms {...{ kcContext, ...props }}/>;
}
});

View File

@ -2,21 +2,15 @@
import { useState, memo } from "react";
import { Template } from "./Template";
import type { KcProps } from "./KcProps";
import { assert } from "../tools/assert";
import { kcContext } from "../kcContext";
import type { KcContext } from "../KcContext";
import { useKcMessage } from "../i18n/useKcMessage";
import { cx } from "tss-react";
import { useConstCallback } from "powerhooks";
export const Login = memo((props: KcProps) => {
export const Login = memo(({ kcContext, ...props }: { kcContext: KcContext.Login; } & KcProps) => {
const { msg, msgStr } = useKcMessage();
assert(
kcContext !== undefined &&
kcContext.pageId === "login.ftl"
);
const {
social, realm, url,
usernameEditDisabled, login,
@ -31,7 +25,7 @@ export const Login = memo((props: KcProps) => {
return (
<Template
{...props}
{...{ kcContext, ...props }}
displayInfo={social.displayInfo}
displayWide={realm.password && social.providers !== undefined}
headerNode={msg("doLogIn")}
@ -68,7 +62,7 @@ export const Login = memo((props: KcProps) => {
name="username"
defaultValue={login.username ?? ''}
type="text"
{...(usernameEditDisabled ? { "disabled": true } : { "autoFocus": true, "autocomplete": "off" })}
{...(usernameEditDisabled ? { "disabled": true } : { "autoFocus": true, "autoComplete": "off" })}
/>
</div>
<div className={cx(props.kcFormGroupClass)}>

View File

@ -2,20 +2,14 @@
import { memo } from "react";
import { Template } from "./Template";
import type { KcProps } from "./KcProps";
import { assert } from "../tools/assert";
import { kcContext } from "../kcContext";
import type { KcContext } from "../KcContext";
import { useKcMessage } from "../i18n/useKcMessage";
import { cx } from "tss-react";
export const LoginResetPassword = memo((props: KcProps) => {
export const LoginResetPassword = memo(({ kcContext, ...props }: { kcContext: KcContext.LoginResetPassword; } & KcProps) => {
const { msg, msgStr } = useKcMessage();
assert(
kcContext !== undefined &&
kcContext.pageId === "login-reset-password.ftl"
);
const {
url,
realm,
@ -24,7 +18,7 @@ export const LoginResetPassword = memo((props: KcProps) => {
return (
<Template
{...props}
{...{ kcContext, ...props }}
displayMessage={false}
headerNode={msg("emailForgotTitle")}
formNode={
@ -72,7 +66,7 @@ export const LoginResetPassword = memo((props: KcProps) => {
props.kcButtonBlockClass, props.kcButtonLargeClass
)}
type="submit"
defaultValue={msgStr("doSubmit")}
value={msgStr("doSubmit")}
/>
</div>
</div>

View File

@ -2,26 +2,20 @@
import { memo } from "react";
import { Template } from "./Template";
import type { KcProps } from "./KcProps";
import { assert } from "../tools/assert";
import { kcContext } from "../kcContext";
import type { KcContext } from "../KcContext";
import { useKcMessage } from "../i18n/useKcMessage";
export const LoginVerifyEmail = memo((props: KcProps) => {
export const LoginVerifyEmail = memo(({ kcContext, ...props }: { kcContext: KcContext.LoginVerifyEmail; } & KcProps) => {
const { msg } = useKcMessage();
assert(
kcContext !== undefined &&
kcContext.pageId === "login-verify-email.ftl"
);
const {
url
} = kcContext;
return (
<Template
{...props}
{...{ kcContext, ...props }}
displayMessage={false}
headerNode={msg("emailVerifyTitle")}
formNode={

View File

@ -1,20 +1,14 @@
import { memo } from "react";
import { Template } from "./Template";
import type { KcProps } from "./KcProps";
import { assert } from "../tools/assert";
import { kcContext } from "../kcContext";
import type { KcContext } from "../KcContext";
import { useKcMessage } from "../i18n/useKcMessage";
import { cx } from "tss-react";
export const Register = memo((props: KcProps) => {
export const Register = memo(({ kcContext, ...props }: { kcContext: KcContext.Register; } & KcProps) => {
const { msg, msgStr } = useKcMessage();
assert(
kcContext !== undefined &&
kcContext.pageId === "register.ftl"
);
const {
url,
messagesPerField,
@ -27,7 +21,7 @@ export const Register = memo((props: KcProps) => {
return (
<Template
{...props}
{...{ kcContext, ...props }}
headerNode={msg("registerTitle")}
formNode={
<form id="kc-register-form" className={cx(props.kcFormClass)} action={url.registrationAction} method="post">
@ -119,7 +113,7 @@ export const Register = memo((props: KcProps) => {
<div id="kc-form-buttons" className={cx(props.kcFormButtonsClass)}>
<input className={cx(props.kcButtonClass, props.kcButtonPrimaryClass, props.kcButtonBlockClass, props.kcButtonLargeClass)} type="submit"
defaultValue={msgStr("doRegister")} />
value={msgStr("doRegister")} />
</div>
</div>
</form >

View File

@ -3,7 +3,7 @@ import { useReducer, useEffect, memo } from "react";
import type { ReactNode } from "react";
import { useKcMessage } from "../i18n/useKcMessage";
import { useKcLanguageTag } from "../i18n/useKcLanguageTag";
import { kcContext } from "../kcContext";
import type { KcContext } from "../KcContext";
import { assert } from "../tools/assert";
import { cx } from "tss-react";
import type { KcLanguageTag } from "../i18n/KcLanguageTag";
@ -25,8 +25,7 @@ export type TemplateProps = {
showUsernameNode?: ReactNode;
formNode: ReactNode;
infoNode?: ReactNode;
} & KcTemplateProps;
} & { kcContext: KcContext; } & KcTemplateProps;
export const Template = memo((props: TemplateProps) => {
@ -39,7 +38,8 @@ export const Template = memo((props: TemplateProps) => {
headerNode,
showUsernameNode = null,
formNode,
infoNode = null
infoNode = null,
kcContext
} = props;
useEffect(() => { console.log("Rendering this page with react using keycloakify") }, []);
@ -58,37 +58,36 @@ export const Template = memo((props: TemplateProps) => {
(document.forms["kc-select-try-another-way-form" as never].submit(), false)
);
assert(kcContext !== undefined);
const {
realm, locale, auth,
realm, locale, auth,
url, message, isAppInitiatedAction
}= kcContext;
} = kcContext;
useEffect(()=>{
useEffect(() => {
if( !realm.internationalizationEnabled ){
if (!realm.internationalizationEnabled) {
return;
}
assert( locale !== undefined );
assert(locale !== undefined);
if( kcLanguageTag === getBestMatchAmongKcLanguageTag(locale.current) ){
if (kcLanguageTag === getBestMatchAmongKcLanguageTag(locale.current)) {
return;
}
window.location.href =
window.location.href =
locale.supported.find(({ languageTag }) => languageTag === kcLanguageTag)!.url;
},[kcLanguageTag]);
}, [kcLanguageTag]);
const [isExtraCssLoaded, setExtraCssLoaded] = useReducer(() => true, false);
useEffect(() => {
let isUnmounted = false;
const cleanups: (() => void)[] = [];
const toArr = (x: string | readonly string[] | undefined) =>
const toArr = (x: string | readonly string[] | undefined) =>
typeof x === "string" ? x.split(" ") : x ?? [];
Promise.all(
@ -115,13 +114,29 @@ export const Template = memo((props: TemplateProps) => {
})
);
document.getElementsByTagName("html")[0]
.classList
.add(cx(props.kcHtmlClass));
if (props.kcHtmlClass !== undefined) {
return () => { isUnmounted = true; };
const htmlClassList =
document.getElementsByTagName("html")[0]
.classList;
}, []);
const tokens = cx(props.kcHtmlClass).split(" ")
htmlClassList.add(...tokens);
cleanups.push(() => htmlClassList.remove(...tokens));
}
return () => {
isUnmounted = true;
cleanups.forEach(f => f());
};
}, [props.kcHtmlClass]);
if (!isExtraCssLoaded) {
return null;
@ -154,7 +169,7 @@ export const Template = memo((props: TemplateProps) => {
{
locale.supported.map(
({ languageTag }) =>
<li className="kc-dropdown-item">
<li key={languageTag} className="kc-dropdown-item">
<a href="#" onClick={onChangeLanguageClickFactory(languageTag)}>
{getKcLanguageTagLabel(languageTag)}
</a>
@ -219,21 +234,21 @@ export const Template = memo((props: TemplateProps) => {
</div>
</div>
) : (
<>
{showUsernameNode}
<div className={cx(props.kcFormGroupClass)}>
<div id="kc-username">
<label id="kc-attempted-username">{auth?.attemptedUsername}</label>
<a id="reset-login" href={url.loginRestartFlowUrl}>
<div className="kc-login-tooltip">
<i className={cx(props.kcResetFlowIcon)}></i>
<span className="kc-tooltip-text">{msg("restartLoginTooltip")}</span>
</div>
</a>
</div>
<>
{showUsernameNode}
<div className={cx(props.kcFormGroupClass)}>
<div id="kc-username">
<label id="kc-attempted-username">{auth?.attemptedUsername}</label>
<a id="reset-login" href={url.loginRestartFlowUrl}>
<div className="kc-login-tooltip">
<i className={cx(props.kcResetFlowIcon)}></i>
<span className="kc-tooltip-text">{msg("restartLoginTooltip")}</span>
</div>
</a>
</div>
</>
)
</div>
</>
)
)
}
</header>

View File

@ -0,0 +1,57 @@
import { memo } from "react";
import { Template } from "./Template";
import type { KcProps } from "./KcProps";
import type { KcContext } from "../KcContext";
import { useKcMessage } from "../i18n/useKcMessage";
import { cx } from "tss-react";
export const Terms = memo(({ kcContext, ...props }: { kcContext: KcContext.Terms; } & KcProps) => {
const { msg, msgStr } = useKcMessage();
const { url } = kcContext;
return (
<Template
{...{ kcContext, ...props }}
displayMessage={false}
headerNode={msg("termsTitle")}
formNode={
<>
<div id="kc-terms-text">
{msg("termsText")}
</div>
<form className="form-actions" action={url.loginAction} method="POST">
<input
className={cx(
props.kcButtonClass,
props.kcButtonClass,
props.kcButtonClass,
props.kcButtonPrimaryClass,
props.kcButtonLargeClass
)}
name="accept"
id="kc-accept"
type="submit"
value={msgStr("doAccept")}
/>
<input
className={cx(
props.kcButtonClass,
props.kcButtonDefaultClass,
props.kcButtonLargeClass
)}
name="cancel"
id="kc-decline"
type="submit"
value={msgStr("doDecline")}
/>
</form>
<div className="clearfix" />
</>
}
/>
);
});

View File

@ -1,8 +1,8 @@
import { objectKeys } from "evt/tools/typeSafety/objectKeys";
import { messages } from "./generated_messages/login";
import { kcMessages } from "./kcMessages/login";
export type KcLanguageTag = keyof typeof messages;
export type KcLanguageTag = keyof typeof kcMessages;
export type LanguageLabel =
/* spell-checker: disable */
@ -40,7 +40,7 @@ export function getKcLanguageTagLabel(language: KcLanguageTag): LanguageLabel {
}
const availableLanguages = objectKeys(messages);
const availableLanguages = objectKeys(kcMessages);
/**
* Pass in "fr-FR" or "français" for example, it will return the AvailableLanguage

View File

@ -2,7 +2,7 @@
//PLEASE DO NOT EDIT MANUALLY
/* spell-checker: disable */
export const messages= {
export const kcMessages= {
"ca": {
"doSave": "Desa",
"doCancel": "Cancel·la",
@ -3060,5 +3060,5 @@ export const messages= {
"locale_ru": "Русский",
"locale_zh-CN": "中文简体"
}
} as const;
};
/* spell-checker: enable */

View File

@ -2,7 +2,7 @@
//PLEASE DO NOT EDIT MANUALLY
/* spell-checker: disable */
export const messages= {
export const kcMessages= {
"ca": {
"invalidPasswordHistoryMessage": "Contrasenya incorrecta: no pot ser igual a cap de les últimes {0} contrasenyes.",
"invalidPasswordMinDigitsMessage": "Contraseña incorrecta: debe contener al menos {0} caracteres numéricos.",
@ -240,5 +240,5 @@ export const messages= {
"pairwiseFailedToGetRedirectURIs": "无法从服务器获得重定向URL",
"pairwiseRedirectURIsMismatch": "客户端的重定向URI与服务器端获取的URI配置不匹配。"
}
} as const;
};
/* spell-checker: enable */

View File

@ -2,7 +2,7 @@
//PLEASE DO NOT EDIT MANUALLY
/* spell-checker: disable */
export const messages= {
export const kcMessages= {
"ca": {
"emailVerificationSubject": "Verificació d'email",
"emailVerificationBody": "Algú ha creat un compte de {2} amb aquesta adreça de correu electrònic. Si has estat tu, fes clic a l'enllaç següent per verificar la teva adreça de correu electrònic.\n\n{0}\n\nAquest enllaç expirarà en {1} minuts.\n\nSi tu no has creat aquest compte, simplement ignora aquest missatge.",
@ -634,5 +634,5 @@ export const messages= {
"eventUpdateTotpBody": "您账户的OTP 配置在{0} 由 {1}更改. 如非本人操作,请联系管理员。",
"eventUpdateTotpBodyHtml": "<p>您账户的OTP 配置在{0} 由 {1}更改. 如非本人操作,请联系管理员。</p>"
}
} as const;
};
/* spell-checker: enable */

View File

@ -2,7 +2,7 @@
//PLEASE DO NOT EDIT MANUALLY
/* spell-checker: disable */
export const messages= {
export const kcMessages= {
"ca": {
"doLogIn": "Inicia sessió",
"doRegister": "Registra't",
@ -4360,5 +4360,5 @@ export const messages= {
"invalidParameterMessage": "无效的参数 : {0}",
"alreadyLoggedIn": "您已经登录"
}
} as const;
};
/* spell-checker: enable */

View File

@ -0,0 +1,33 @@
import { kcMessages } from "../generated_kcMessages/login";
import { Evt } from "evt";
import { objectKeys } from "evt/tools/typeSafety/objectKeys";
export const evtTermsUpdated = Evt.asNonPostable(Evt.create<void>());
objectKeys(kcMessages).forEach(kcLanguage =>
Object.defineProperty(
kcMessages[kcLanguage],
"termsText",
(() => {
let value = kcMessages[kcLanguage].termsText;
return {
"enumerable": true,
"get": () => value,
"set": (newValue: string) => {
Evt.asPostable(evtTermsUpdated).post();
value = newValue;
}
};
})()
)
);
export { kcMessages };

View File

@ -1,9 +1,10 @@
import { createUseGlobalState } from "powerhooks";
import { kcContext } from "../kcContext";
import { kcContext } from "../KcContext";
import { getBestMatchAmongKcLanguageTag } from "./KcLanguageTag";
export const { useKcLanguageTag } = createUseGlobalState(
//export const { useKcLanguageTag, evtKcLanguageTag } = createUseGlobalState(
const wrap = createUseGlobalState(
"kcLanguageTag",
() => getBestMatchAmongKcLanguageTag(
kcContext?.locale?.current ??
@ -11,3 +12,12 @@ export const { useKcLanguageTag } = createUseGlobalState(
),
{ "persistance": "cookie" }
);
export const { useKcLanguageTag } = wrap;
export function getEvtKcLanguage() {
return wrap.evtKcLanguageTag;
}

View File

@ -1,20 +1,26 @@
import { useCallback, useReducer } from "react";
import { useKcLanguageTag } from "./useKcLanguageTag";
import { messages } from "./generated_messages/login";
import { useConstCallback } from "powerhooks";
import { kcMessages, evtTermsUpdated } from "./kcMessages/login";
import type { ReactNode } from "react";
import { id } from "evt/tools/typeSafety/id";
import { useEvt } from "evt/hooks";
//@ts-ignore
import * as markdown from "markdown";
export type MessageKey = keyof typeof messages["en"];
export type MessageKey = keyof typeof kcMessages["en"];
export function useKcMessage() {
const { kcLanguageTag } = useKcLanguageTag();
const msgStr = useConstCallback(
const [trigger, forceUpdate] = useReducer((counter: number) => counter + 1, 0);
useEvt(ctx => evtTermsUpdated.attach(ctx, forceUpdate), []);
const msgStr = useCallback(
(key: MessageKey, ...args: (string | undefined)[]): string => {
let str: string = messages[kcLanguageTag as any as "en"][key] ?? messages["en"][key];
let str: string = kcMessages[kcLanguageTag as any as "en"][key] ?? kcMessages["en"][key];
args.forEach((arg, i) => {
@ -28,14 +34,21 @@ export function useKcMessage() {
return str;
}
},
[kcLanguageTag, trigger]
);
const msg = useConstCallback(
id<(...args: Parameters<typeof msgStr>) => ReactNode>(
(key, ...args) =>
<span className={key} dangerouslySetInnerHTML={{ "__html": msgStr(key, ...args) }} />
)
const msg = useCallback<(...args: Parameters<typeof msgStr>) => ReactNode>(
(key, ...args) =>
<span
className={key}
dangerouslySetInnerHTML={{
"__html":
markdown.toHTML(msgStr(key, ...args))
}}
/>
,
[kcLanguageTag, trigger]
);
return { msg, msgStr };

View File

@ -1,8 +1,9 @@
export * from "./kcContext";
export * from "./KcContext";
export * from "./i18n/KcLanguageTag";
export * from "./i18n/useKcLanguageTag";
export * from "./i18n/useKcMessage";
export * from "./i18n/kcMessages/login";
export * from "./components/KcProps";
export * from "./components/Login";
@ -12,5 +13,9 @@ export * from "./components/Info";
export * from "./components/Error";
export * from "./components/LoginResetPassword";
export * from "./components/LoginVerifyEmail";
export * from "./keycloakJsAdapter";
export * from "./tools/assert";
export * from "./tools/assert";
export * as kcContextMocks from "./kcContextMocks";

View File

@ -10,12 +10,18 @@ import type { LanguageLabel } from "./i18n/KcLanguageTag";
type ExtractAfterStartingWith<Prefix extends string, StrEnum> =
StrEnum extends `${Prefix}${infer U}` ? U : never;
/** Take theses type definition with a grain of salt.
* Some values might be undefined on some pages.
* (ex: url.loginAction is undefined on error.ftl)
*/
export type KcContext =
KcContext.Login | KcContext.Register | KcContext.Info |
KcContext.Error | KcContext.LoginResetPassword | KcContext.LoginVerifyEmail;
KcContext.Error | KcContext.LoginResetPassword | KcContext.LoginVerifyEmail |
KcContext.Terms;
export declare namespace KcContext {
export type Template = {
export type Common = {
url: {
loginAction: string;
resourcesPath: string;
@ -27,7 +33,7 @@ export declare namespace KcContext {
displayName?: string;
displayNameHtml?: string;
internationalizationEnabled: boolean;
registrationEmailAsUsername: boolean; //<---
registrationEmailAsUsername: boolean;
};
/** Undefined if !realm.internationalizationEnabled */
locale?: {
@ -55,7 +61,7 @@ export declare namespace KcContext {
isAppInitiatedAction: boolean;
};
export type Login = Template & {
export type Login = Common & {
pageId: "login.ftl";
url: {
loginResetCredentialsUrl: string;
@ -88,7 +94,7 @@ export declare namespace KcContext {
};
};
export type Register = Template & {
export type Register = Common & {
pageId: "register.ftl";
url: {
registrationAction: string;
@ -117,11 +123,10 @@ export declare namespace KcContext {
};
passwordRequired: boolean;
recaptchaRequired: boolean;
/** undefined if !recaptchaRequired */
recaptchaSiteKey?: string;
};
export type Info = Template & {
export type Info = Common & {
pageId: "info.ftl";
messageHeader?: string;
requiredActions?: ExtractAfterStartingWith<"requiredAction.", MessageKey>[];
@ -133,27 +138,33 @@ export declare namespace KcContext {
}
};
export type Error = Template & {
export type Error = Common & {
pageId: "error.ftl";
client?: {
baseUrl?: string;
}
};
export type LoginResetPassword = Template & {
export type LoginResetPassword = Common & {
pageId: "login-reset-password.ftl";
realm: {
loginWithEmailAllowed: boolean;
}
};
export type LoginVerifyEmail = Template & {
export type LoginVerifyEmail = Common & {
pageId: "login-verify-email.ftl";
};
export type Terms = Common & {
pageId: "terms.ftl";
};
}
doExtends<KcContext["pageId"], PageId>();
doExtends<PageId, KcContext["pageId"]>();
export const kcContext = id<KcContext | undefined>((window as any)[ftlValuesGlobalName]);

View File

@ -0,0 +1,206 @@
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
};
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 kcTermContext: KcContext.Terms = {
...kcCommonContext,
"pageId": "terms.ftl"
};

View File

@ -0,0 +1,6 @@
import { join as pathJoin } from "path";
export const subDirOfPublicDirBasename = "keycloak_static";
export const resourcesPath = pathJoin(subDirOfPublicDirBasename, "/resources");
export const resourcesCommonPath = pathJoin(subDirOfPublicDirBasename, "/resources_common");

View File

@ -0,0 +1,118 @@
export declare namespace keycloak_js {
export type KeycloakPromiseCallback<T> = (result: T) => void;
export class KeycloakPromise<TSuccess, TError> extends Promise<TSuccess> {
success(callback: KeycloakPromiseCallback<TSuccess>): KeycloakPromise<TSuccess, TError>;
error(callback: KeycloakPromiseCallback<TError>): KeycloakPromise<TSuccess, TError>;
}
export interface KeycloakAdapter {
login(options?: KeycloakLoginOptions): KeycloakPromise<void, void>;
logout(options?: KeycloakLogoutOptions): KeycloakPromise<void, void>;
register(options?: KeycloakLoginOptions): KeycloakPromise<void, void>;
accountManagement(): KeycloakPromise<void, void>;
redirectUri(options: { redirectUri: string; }, encodeHash: boolean): string;
}
export interface KeycloakLogoutOptions {
redirectUri?: string;
}
export interface KeycloakLoginOptions {
scope?: string;
redirectUri?: string;
prompt?: 'none' | 'login';
action?: string;
maxAge?: number;
loginHint?: string;
idpHint?: string;
locale?: string;
cordovaOptions?: { [optionName: string]: string };
}
export type KeycloakInstance = Record<
"createLoginUrl" |
"createLogoutUrl" |
"createRegisterUrl",
(options: KeycloakLoginOptions | undefined) => string
> & {
createAccountUrl(): string;
redirectUri?: string;
}
}
/**
* NOTE: This is just a slightly modified version of the default adapter in keycloak-js
* The goal here is just to be able to inject search param in url before keycloak redirect.
* Our use case for it is to pass over the login screen the states of useGlobalState
* namely isDarkModeEnabled, lgn...
*/
export function createKeycloakAdapter(
params: {
keycloakInstance: keycloak_js.KeycloakInstance;
transformUrlBeforeRedirect(url: string): string;
}
): keycloak_js.KeycloakAdapter {
const { keycloakInstance, transformUrlBeforeRedirect } = params;
const neverResolvingPromise: keycloak_js.KeycloakPromise<void, void> = Object.defineProperties(
new Promise(() => { }),
{
"success": { "value": () => { } },
"error": { "value": () => { } }
}
);
return {
"login": options => {
window.location.href=
transformUrlBeforeRedirect(
keycloakInstance.createLoginUrl(
options
)
);
return neverResolvingPromise;
},
"logout": options => {
window.location.replace(
transformUrlBeforeRedirect(
keycloakInstance.createLogoutUrl(
options
)
)
);
return neverResolvingPromise;
},
"register": options => {
window.location.href =
transformUrlBeforeRedirect(
keycloakInstance.createRegisterUrl(
options
)
);
return neverResolvingPromise;
},
"accountManagement": () => {
var accountUrl = transformUrlBeforeRedirect(keycloakInstance.createAccountUrl());
if (typeof accountUrl !== 'undefined') {
window.location.href = accountUrl;
} else {
throw new Error("Not supported by the OIDC server");
}
return neverResolvingPromise;
},
"redirectUri": options => {
if (options && options.redirectUri) {
return options.redirectUri;
} else if (keycloakInstance.redirectUri) {
return keycloakInstance.redirectUri;
} else {
return window.location.href;
}
}
};
}

View File

@ -1,7 +1,7 @@
import { join as pathJoin } from "path";
import { generateKeycloakThemeResources } from "../bin/build-keycloak-theme/generateKeycloakThemeResources";
import {
import {
setupSampleReactProject,
sampleReactProjectDirPath
} from "./setupSampleReactProject";
@ -9,8 +9,9 @@ import {
setupSampleReactProject();
generateKeycloakThemeResources({
"themeName": "onyxia-ui",
"themeName": "keycloakify-demo-app",
"reactAppBuildDirPath": pathJoin(sampleReactProjectDirPath, "build"),
"keycloakThemeBuildingDirPath": pathJoin(sampleReactProjectDirPath, "build_keycloak_theme")
"keycloakThemeBuildingDirPath": pathJoin(sampleReactProjectDirPath, "build_keycloak_theme"),
"urlPathname": "/keycloakify-demo-app/"
});

View File

@ -1,11 +1,11 @@
import { 
replaceImportFromStaticInJsCode,
replaceImportFromStaticInCssCode,
replaceImportsFromStaticInJsCode,
replaceImportsInCssCode,
generateCssCodeToDefineGlobals
} from "../bin/build-keycloak-theme/replaceImportFromStatic";
const { fixedJsCode } = replaceImportFromStaticInJsCode({
const { fixedJsCode } = replaceImportsFromStaticInJsCode({
"ftlValuesGlobalName": "keycloakFtlValues",
"jsCode": `
function f() {
@ -24,7 +24,7 @@ const { fixedJsCode } = replaceImportFromStaticInJsCode({
console.log({ fixedJsCode });
const { fixedCssCode, cssGlobalsToDefine } = replaceImportFromStaticInCssCode({
const { fixedCssCode, cssGlobalsToDefine } = replaceImportsInCssCode({
"cssCode": `
.my-div {
@ -45,6 +45,6 @@ const { fixedCssCode, cssGlobalsToDefine } = replaceImportFromStaticInCssCode({
console.log({ fixedCssCode, cssGlobalsToDefine });
const { cssCodeToPrependInHead } = generateCssCodeToDefineGlobals({ cssGlobalsToDefine });
const { cssCodeToPrependInHead } = generateCssCodeToDefineGlobals({ cssGlobalsToDefine, "urlPathname": "/" });
console.log({ cssCodeToPrependInHead });

343
yarn.lock
View File

@ -27,9 +27,9 @@
integrity sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==
"@babel/highlight@^7.12.13":
version "7.13.8"
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.13.8.tgz#10b2dac78526424dfc1f47650d0e415dfd9dc481"
integrity sha512-4vrIhfJyfNf+lCtXC2ck1rKSzDwciqF7IWFhXXrSOUC2O5DrVp+w4c6ed4AllTxhTkUP5x2tYj41VaxdVMMRDw==
version "7.13.10"
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.13.10.tgz#a8b2a66148f5b27d666b15d81774347a731d52d1"
integrity sha512-5aPpe5XQPzflQrFwL1/QoeHkP2MsA4JCntcXHRhEsdsfPVkvPi2w7Qix4iV7t5S/oC9OodGrggd8aco1g3SZFg==
dependencies:
"@babel/helper-validator-identifier" "^7.12.11"
chalk "^2.0.0"
@ -43,9 +43,9 @@
"@babel/helper-plugin-utils" "^7.12.13"
"@babel/runtime@^7.7.2":
version "7.13.9"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.13.9.tgz#97dbe2116e2630c489f22e0656decd60aaa1fcee"
integrity sha512-aY2kU+xgJ3dJ1eU6FMB9EH8dIe8dmusF1xEku52joLvw6eAFN0AI+WxCLDnpev2LEejWBAy2sBvBOBAjI3zmvA==
version "7.13.10"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.13.10.tgz#47d42a57b6095f4468da440388fdbad8bebf0d7d"
integrity sha512-4QPkjJq6Ns3V/RgpEahRk+AGfL0eO6RHHtTWoNNr5mO49G6B5+X6d6THgWEAvTrznU5xYpbAlVKRYcsCgh/Akw==
dependencies:
regenerator-runtime "^0.13.4"
@ -109,9 +109,9 @@
integrity sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ==
"@emotion/serialize@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.0.0.tgz#1a61f4f037cf39995c97fc80ebe99abc7b191ca9"
integrity sha512-zt1gm4rhdo5Sry8QpCOpopIUIKU+mUSpV9WNmFILUraatm5dttNEaYzUWWSboSMUE6PtN2j1cAsuvcugfdI3mw==
version "1.0.1"
resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.0.1.tgz#322cdebfdbb5a88946f17006548191859b9b0855"
integrity sha512-TXlKs5sgUKhFlszp/rg4lIAZd7UUSmJpwaf9/lAEFcUh2vPi32i7x4wk7O8TN8L8v2Ol8k0CxnhRBY0zQalTxA==
dependencies:
"@emotion/hash" "^0.8.0"
"@emotion/memoize" "^0.7.4"
@ -139,117 +139,10 @@
resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz#8eed982e2ee6f7f4e44c253e12962980791efd46"
integrity sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==
"@octokit/auth-token@^2.4.4":
version "2.4.5"
resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-2.4.5.tgz#568ccfb8cb46f36441fac094ce34f7a875b197f3"
integrity sha512-BpGYsPgJt05M7/L/5FoE1PiAbdxXFZkX/3kDYcsvd1v6UhlnE5e96dTDr0ezX/EFwciQxf3cNV0loipsURU+WA==
dependencies:
"@octokit/types" "^6.0.3"
"@octokit/core@^3.2.3":
version "3.2.5"
resolved "https://registry.yarnpkg.com/@octokit/core/-/core-3.2.5.tgz#57becbd5fd789b0592b915840855f3a5f233d554"
integrity sha512-+DCtPykGnvXKWWQI0E1XD+CCeWSBhB6kwItXqfFmNBlIlhczuDPbg+P6BtLnVBaRJDAjv+1mrUJuRsFSjktopg==
dependencies:
"@octokit/auth-token" "^2.4.4"
"@octokit/graphql" "^4.5.8"
"@octokit/request" "^5.4.12"
"@octokit/types" "^6.0.3"
before-after-hook "^2.1.0"
universal-user-agent "^6.0.0"
"@octokit/endpoint@^6.0.1":
version "6.0.11"
resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-6.0.11.tgz#082adc2aebca6dcefa1fb383f5efb3ed081949d1"
integrity sha512-fUIPpx+pZyoLW4GCs3yMnlj2LfoXTWDUVPTC4V3MUEKZm48W+XYpeWSZCv+vYF1ZABUm2CqnDVf1sFtIYrj7KQ==
dependencies:
"@octokit/types" "^6.0.3"
is-plain-object "^5.0.0"
universal-user-agent "^6.0.0"
"@octokit/graphql@^4.5.8":
version "4.6.0"
resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-4.6.0.tgz#f9abca55f82183964a33439d5264674c701c3327"
integrity sha512-CJ6n7izLFXLvPZaWzCQDjU/RP+vHiZmWdOunaCS87v+2jxMsW9FB5ktfIxybRBxZjxuJGRnxk7xJecWTVxFUYQ==
dependencies:
"@octokit/request" "^5.3.0"
"@octokit/types" "^6.0.3"
universal-user-agent "^6.0.0"
"@octokit/openapi-types@^5.3.0":
version "5.3.1"
resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-5.3.1.tgz#a49d119a1b9e47b7a9f5133ab14be2d8afaa183d"
integrity sha512-TvVk2QuIA0lQZcIMd6xbdGaGDVeNYIOa3l1ZVagAIk5K3t/WMYbcg4BISNDhzdVhm/TgQB26frAgd/GV81aHJA==
"@octokit/plugin-paginate-rest@^2.6.2":
version "2.11.0"
resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.11.0.tgz#3568c43896a3355f4a0bbb3a64f443b2abdc760d"
integrity sha512-7L9xQank2G3r1dGqrVPo1z62V5utbykOUzlmNHPz87Pww/JpZQ9KyG5CHtUzgmB4n5iDRKYNK/86A8D98HP0yA==
dependencies:
"@octokit/types" "^6.11.0"
"@octokit/plugin-request-log@^1.0.2":
version "1.0.3"
resolved "https://registry.yarnpkg.com/@octokit/plugin-request-log/-/plugin-request-log-1.0.3.tgz#70a62be213e1edc04bb8897ee48c311482f9700d"
integrity sha512-4RFU4li238jMJAzLgAwkBAw+4Loile5haQMQr+uhFq27BmyJXcXSKvoQKqh0agsZEiUlW6iSv3FAgvmGkur7OQ==
"@octokit/plugin-rest-endpoint-methods@4.13.4":
version "4.13.4"
resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-4.13.4.tgz#093b130d558760f6bc912c6f622ce502522410af"
integrity sha512-MGxptzVfiP8O+aydC/riheYzS/yJ9P16M29OuvtZep/sF5sKuOCQP8Wf83YCKXRsQF+ZpYfke2snbPPSIMZKzg==
dependencies:
"@octokit/types" "^6.12.0"
deprecation "^2.3.1"
"@octokit/request-error@^2.0.0":
version "2.0.5"
resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-2.0.5.tgz#72cc91edc870281ad583a42619256b380c600143"
integrity sha512-T/2wcCFyM7SkXzNoyVNWjyVlUwBvW3igM3Btr/eKYiPmucXTtkxt2RBsf6gn3LTzaLSLTQtNmvg+dGsOxQrjZg==
dependencies:
"@octokit/types" "^6.0.3"
deprecation "^2.0.0"
once "^1.4.0"
"@octokit/request@^5.3.0", "@octokit/request@^5.4.12":
version "5.4.14"
resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.4.14.tgz#ec5f96f78333bb2af390afa5ff66f114b063bc96"
integrity sha512-VkmtacOIQp9daSnBmDI92xNIeLuSRDOIuplp/CJomkvzt7M18NXgG044Cx/LFKLgjKt9T2tZR6AtJayba9GTSA==
dependencies:
"@octokit/endpoint" "^6.0.1"
"@octokit/request-error" "^2.0.0"
"@octokit/types" "^6.7.1"
deprecation "^2.0.0"
is-plain-object "^5.0.0"
node-fetch "^2.6.1"
once "^1.4.0"
universal-user-agent "^6.0.0"
"@octokit/rest@^18.0.0":
version "18.3.4"
resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-18.3.4.tgz#8e7ab02cd509e2fe7e71917a54254a8f8b827f20"
integrity sha512-NES0pHbwyFB1D0jrLkdnIXgEmze/gLE0JoSNgfAe4vwD77/qaQGO/lRWNuPPsoBVBjiW6mmA9CU5cYHujJTKQA==
dependencies:
"@octokit/core" "^3.2.3"
"@octokit/plugin-paginate-rest" "^2.6.2"
"@octokit/plugin-request-log" "^1.0.2"
"@octokit/plugin-rest-endpoint-methods" "4.13.4"
"@octokit/types@^6.0.3", "@octokit/types@^6.11.0", "@octokit/types@^6.12.0", "@octokit/types@^6.7.1":
version "6.12.0"
resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.12.0.tgz#8376fd60edfd5d1eebfeedb994c6bcb5b862c7a1"
integrity sha512-KwOf16soD7aDEEi/PgNeJlHzjZPfrmmNy+7WezSdrpnqZ7YImBJcNnX9+5RUHt1MnA4h8oISRHTqaZDGsX9DRQ==
dependencies:
"@octokit/openapi-types" "^5.3.0"
"@types/comment-json@^1.1.1":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@types/comment-json/-/comment-json-1.1.1.tgz#b4ae889912a93e64619f97989aecaff8ce889dca"
integrity sha512-U70oEqvnkeSSp8BIJwJclERtT13rd9ejK7XkIzMCQQePZe3VW1b7iQggXyW4ZvfGtGeXD0pZw24q5iWNe++HqQ==
"@types/node@^10.0.0":
version "10.17.54"
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.54.tgz#a737488631aca3ec7bd9f6229d77f1079e444793"
integrity sha512-c8Lm7+hXdSPmWH4B9z/P/xIXhFK3mCQin4yCYMd2p1qpMG5AfgyJuYZ+3q2dT7qLiMMMGMd5dnkFpdqJARlvtQ==
version "10.17.55"
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.55.tgz#a147f282edec679b894d4694edb5abeb595fecbd"
integrity sha512-koZJ89uLZufDvToeWO5BrC4CR4OUfHnUz2qoPs/daQH6qq3IN62QFxCTZ+bKaCE0xaoCAJYE4AXre8AbghCrhg==
"@types/parse-json@^4.0.0":
version "4.0.0"
@ -262,13 +155,19 @@
integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==
"@types/react@^17.0.0":
version "17.0.2"
resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.2.tgz#3de24c4efef902dd9795a49c75f760cbe4f7a5a8"
integrity sha512-Xt40xQsrkdvjn1EyWe1Bc0dJLcil/9x2vAuW7ya+PuQip4UYUaXyhzWmAbwRsdMgwOFHpfp7/FFZebDU6Y8VHA==
version "17.0.3"
resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.3.tgz#ba6e215368501ac3826951eef2904574c262cc79"
integrity sha512-wYOUxIgs2HZZ0ACNiIayItyluADNbONl7kt8lkLjVK8IitMH5QMyAh75Fwhmo37r1m7L2JaFj03sIfxBVDvRAg==
dependencies:
"@types/prop-types" "*"
"@types/scheduler" "*"
csstype "^3.0.2"
"@types/scheduler@*":
version "0.16.1"
resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.1.tgz#18845205e86ff0038517aab7a18a62a6b9f71275"
integrity sha512-EaCxbanVeyxDRTQBkdLb3Bvl/HK7PBK6UJjsSixB0iHKoWxE5uu2Q/DgtpOhPIojN0Zl1whvOd7PoHs2P0s5eA==
ansi-regex@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75"
@ -302,11 +201,6 @@ balanced-match@^1.0.0:
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
before-after-hook@^2.1.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.0.tgz#09c40d92e936c64777aa385c4e9b904f8147eaf0"
integrity sha512-jH6rKQIfroBbhEXVmI7XmXe3ix5S/PgJqpzdDPnR8JGLHWNYLsYZ6tK5iWOF/Ra3oqEX0NobXGlzbiylIzVphQ==
boolbase@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
@ -391,21 +285,6 @@ color-name@~1.1.4:
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
commander@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068"
integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==
comment-json@^3.0.2:
version "3.0.3"
resolved "https://registry.yarnpkg.com/comment-json/-/comment-json-3.0.3.tgz#0cadacd6278602b57b8c51b1814dc5d311d228c4"
integrity sha512-P7XwYkC3qjIK45EAa9c5Y3lR7SMXhJqwFdWg3niAIAcbk3zlpKDdajV8Hyz/Y3sGNn3l+YNMl8A2N/OubSArHg==
dependencies:
core-util-is "^1.0.2"
esprima "^4.0.1"
has-own-prop "^2.0.0"
repeat-string "^1.6.1"
concat-map@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
@ -431,7 +310,7 @@ copyfiles@^2.4.1:
untildify "^4.0.0"
yargs "^16.1.0"
core-util-is@^1.0.2, core-util-is@~1.0.0:
core-util-is@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
@ -476,29 +355,6 @@ d@1, d@^1.0.1:
es5-ext "^0.10.50"
type "^1.0.1"
denoify@^0.6.5:
version "0.6.5"
resolved "https://registry.yarnpkg.com/denoify/-/denoify-0.6.5.tgz#06512be38026ec0f2033db9566d07428e63d56bf"
integrity sha512-AhKBlvO91QdP5T/fRcxiMj3b6cDe5ucr+7YVlePXCuV/kImbiR0V1qjGTCfmVuC3wvRn5Xf6r34qJJRbo86AqA==
dependencies:
"@octokit/rest" "^18.0.0"
"@types/comment-json" "^1.1.1"
commander "^4.1.1"
comment-json "^3.0.2"
evt "^1.9.2"
get-github-default-branch-name "^0.0.4"
gitignore-parser "0.0.2"
glob "^7.1.6"
node-fetch "^2.6.0"
path-depth "^1.0.0"
scripting-tools "^0.19.13"
url-join "^4.0.1"
deprecation@^2.0.0, deprecation@^2.3.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919"
integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==
dom-serializer@^1.0.1, dom-serializer@~1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.2.0.tgz#3433d9136aeb3c627981daa385fc7f32d27c48f1"
@ -521,9 +377,9 @@ domhandler@^4.0.0:
domelementtype "^2.1.0"
domutils@^2.4.3, domutils@^2.4.4:
version "2.4.4"
resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.4.4.tgz#282739c4b150d022d34699797369aad8d19bbbd3"
integrity sha512-jBC0vOsECI4OMdD0GC9mGn7NXPLb+Qt6KW1YDQzeQYRUFKmNG8lh7mO5HiELfr+lLQE7loDVI4QcAxV80HS+RA==
version "2.5.0"
resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.5.0.tgz#42f49cffdabb92ad243278b331fd761c1c2d3039"
integrity sha512-Ho16rzNMOFk2fPwChGh3D2D9OEHAfG19HgmRR2l+WLSsIstNsAYBzePH412bL0y5T44ejABIVfTHQ8nqi/tBCg==
dependencies:
dom-serializer "^1.0.1"
domelementtype "^2.0.1"
@ -602,11 +458,6 @@ escape-string-regexp@^4.0.0:
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
esprima@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
event-emitter@^0.3.5:
version "0.3.5"
resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39"
@ -615,14 +466,6 @@ event-emitter@^0.3.5:
d "1"
es5-ext "~0.10.14"
evt@2.0.0-beta.13:
version "2.0.0-beta.13"
resolved "https://registry.yarnpkg.com/evt/-/evt-2.0.0-beta.13.tgz#a84828cc15b535fabdeaf551982d0705ed428a6f"
integrity sha512-Y71l8BU1r/hs7MOyaNb5PTf8Jp+5h8Vt6T8ixvJSqDmKhwCjjCVKP2b1E3UE+Qlr1bZnCOWnVTucTeTeJghWEQ==
dependencies:
minimal-polyfills "^2.1.5"
run-exclusive "^2.2.14"
evt@2.0.0-beta.15:
version "2.0.0-beta.15"
resolved "https://registry.yarnpkg.com/evt/-/evt-2.0.0-beta.15.tgz#a34ef224c827152c06f29157a9af5a41644e1a36"
@ -631,14 +474,6 @@ evt@2.0.0-beta.15:
minimal-polyfills "^2.1.5"
run-exclusive "^2.2.14"
evt@^1.9.2:
version "1.9.12"
resolved "https://registry.yarnpkg.com/evt/-/evt-1.9.12.tgz#8d06177259cbcb09ef936e18945bf7ddd087170c"
integrity sha512-u8wC4Xif2pcDJ9cEm0wzWCIQb+Y214m1eUgsgm2hVIuXuvC6LToryA0Ecl1O8Slii2E9l6USLsyxXWntjlnIbw==
dependencies:
minimal-polyfills "^2.1.5"
run-exclusive "^2.2.14"
ext@^1.1.2:
version "1.4.0"
resolved "https://registry.yarnpkg.com/ext/-/ext-1.4.0.tgz#89ae7a07158f79d35517882904324077e4379244"
@ -666,20 +501,7 @@ get-caller-file@^2.0.5:
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
get-github-default-branch-name@^0.0.4:
version "0.0.4"
resolved "https://registry.yarnpkg.com/get-github-default-branch-name/-/get-github-default-branch-name-0.0.4.tgz#9c0c6606ba606edb136d2fd26e4d515b69f2de90"
integrity sha512-ltOGC9Jk0k8boe48Gk7SkJErwxt7MhwXtbNrBUyNCZcwcXSmGRdkKb2u0YO250PGvPsUtdqRjg7lVuIk1VtpCg==
dependencies:
"@octokit/rest" "^18.0.0"
scripting-tools "^0.19.12"
gitignore-parser@0.0.2:
version "0.0.2"
resolved "https://registry.yarnpkg.com/gitignore-parser/-/gitignore-parser-0.0.2.tgz#f61259b985dd91414b9a7168faef9171c2eec5df"
integrity sha1-9hJZuYXdkUFLmnFo+u+RccLuxd8=
glob@^7.0.5, glob@^7.1.3, glob@^7.1.6:
glob@^7.0.5, glob@^7.1.3:
version "7.1.6"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
@ -696,11 +518,6 @@ has-flag@^3.0.0:
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
has-own-prop@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/has-own-prop/-/has-own-prop-2.0.0.tgz#f0f95d58f65804f5d218db32563bb85b8e0417af"
integrity sha512-Pq0h+hvsVm6dDEa8x82GnLSYHOzNDt7f0ddFa3FqcQlgzEiptPqL+XrOJNavjOzSYiYWIrgeVYYgGlLmnxwilQ==
has@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
@ -708,15 +525,10 @@ has@^1.0.3:
dependencies:
function-bind "^1.1.1"
html-escaper@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-3.0.3.tgz#4d336674652beb1dcbc29ef6b6ba7f6be6fdfed6"
integrity sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==
htmlparser2@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.0.0.tgz#c2da005030390908ca4c91e5629e418e0665ac01"
integrity sha512-numTQtDZMoh78zJpaNdJ9MXb2cv5G3jwUoe3dMQODubZvLoGvTE/Ofp6sHvH8OGKcN/8A47pGLi/k58xHP/Tfw==
version "6.0.1"
resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.0.1.tgz#422521231ef6d42e56bd411da8ba40aa36e91446"
integrity sha512-GDKPd+vk4jvSuvCbyuzx/unmXkk090Azec7LovXP8as1Hn8q9p3hbjmDGbUqqhknw0ajwit6LiiWqfiTUPMK7w==
dependencies:
domelementtype "^2.0.1"
domhandler "^4.0.0"
@ -744,6 +556,11 @@ inherits@2, inherits@^2.0.1, inherits@~2.0.1, inherits@~2.0.3:
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
inherits@2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
is-arrayish@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
@ -761,11 +578,6 @@ is-fullwidth-code-point@^3.0.0:
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d"
integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
is-plain-object@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344"
integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==
is-promise@^2.2.2:
version "2.2.2"
resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1"
@ -856,11 +668,6 @@ next-tick@~1.0.0:
resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c"
integrity sha1-yobR/ogoFpsBICCOPchCS524NCw=
node-fetch@^2.6.0, node-fetch@^2.6.1:
version "2.6.1"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
noms@0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/noms/-/noms-0.0.0.tgz#da8ebd9f3af9d6760919b27d9cdc8092a7332859"
@ -881,7 +688,7 @@ object-assign@^4.1.1:
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
once@^1.3.0, once@^1.4.0:
once@^1.3.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
@ -917,11 +724,6 @@ parse5@^6.0.0, parse5@^6.0.1:
resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b"
integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==
path-depth@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/path-depth/-/path-depth-1.0.0.tgz#88cf881097e171b8b54d450d2167ea76063d3086"
integrity sha512-dEiwdXAQyLvOi6ktLqhFhjVelJiVsdp2xBX3BaUtYCCkMRZTwUiq7cha+A0myvAVXRHbXfjhfTf4mNoAWzm2iA==
path-is-absolute@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
@ -937,12 +739,20 @@ path-type@^4.0.0:
resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
powerhooks@^0.0.19:
version "0.0.19"
resolved "https://registry.yarnpkg.com/powerhooks/-/powerhooks-0.0.19.tgz#86f4157dbde32cd44082c756ab747c64f6045449"
integrity sha512-yaODFWkflrZCSz4lvRQ2O4AjolheiE6oXa1F4mny2LUOwai4ip+zer16fgXEM53R+IiDnqj6ff8wooU5x4GslQ==
path@^0.12.7:
version "0.12.7"
resolved "https://registry.yarnpkg.com/path/-/path-0.12.7.tgz#d4dc2a506c4ce2197eb481ebfcd5b36c0140b10f"
integrity sha1-1NwqUGxM4hl+tIHr/NWzbAFAsQ8=
dependencies:
evt "2.0.0-beta.13"
process "^0.11.1"
util "^0.10.3"
powerhooks@^0.0.36:
version "0.0.36"
resolved "https://registry.yarnpkg.com/powerhooks/-/powerhooks-0.0.36.tgz#d973d339ad8ca7ce52ea9d288ebe8e138df7d769"
integrity sha512-0fEGKLfJmuFeEYDGsAlTuvGKKdqOH059xezosmYRoGurfC1bbUlaNJD+TuzOT5cTGURi88DCLcDnhk/2eLyCyA==
dependencies:
evt "2.0.0-beta.15"
memoizee "^0.4.15"
resize-observer-polyfill "^1.5.1"
@ -951,6 +761,11 @@ process-nextick-args@~2.0.0:
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
process@^0.11.1:
version "0.11.10"
resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI=
properties-parser@^0.3.1:
version "0.3.1"
resolved "https://registry.yarnpkg.com/properties-parser/-/properties-parser-0.3.1.tgz#1316e9539ffbfd93845e369b211022abd478771a"
@ -994,11 +809,6 @@ regenerator-runtime@^0.13.4:
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55"
integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==
repeat-string@^1.6.1:
version "1.6.1"
resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637"
integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc=
require-directory@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
@ -1041,7 +851,7 @@ safe-buffer@~5.1.0, safe-buffer@~5.1.1:
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
scripting-tools@^0.19.12, scripting-tools@^0.19.13:
scripting-tools@^0.19.13:
version "0.19.13"
resolved "https://registry.yarnpkg.com/scripting-tools/-/scripting-tools-0.19.13.tgz#836df7d9c2ec99aea91984d1d5b2bd110670afec"
integrity sha512-d09H8vzSVa8p4XUTJqHZDbjKDyl5TG3SyPfNPUUkfyOwjwykStmfK8AXyWq7VRWjcgzTpkTiJ9uMk1NytMQY7w==
@ -1117,10 +927,10 @@ to-fast-properties@^2.0.0:
resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=
tss-react@^0.0.11:
version "0.0.11"
resolved "https://registry.yarnpkg.com/tss-react/-/tss-react-0.0.11.tgz#1cd061927744cd4fc9b7346e2fd1cfcf896a18d5"
integrity sha512-j8CDpHHIl6S6/mX+AmK08v7waPqwgNA7urHOD3qknCgbY79LRlS7he5DF4NUNE/5B4/Btc3F25w+KqgChNbyGw==
tss-react@^0.0.12:
version "0.0.12"
resolved "https://registry.yarnpkg.com/tss-react/-/tss-react-0.0.12.tgz#6463617ae5e7f670742e48e497d8825d59e2a2e9"
integrity sha512-oHekukqdaE71uhHx4XEdHy6aMnDYhoHLWB94iy2Fy9X8btH2lJH1joPj0zS1q7+1Xy2TydkLEZsTq3ElVd7ZqA==
dependencies:
"@emotion/css" "^11.1.3"
@ -1130,35 +940,32 @@ type@^1.0.1:
integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==
type@^2.0.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/type/-/type-2.3.0.tgz#ada7c045f07ead08abf9e2edd29be1a0c0661132"
integrity sha512-rgPIqOdfK/4J9FhiVrZ3cveAjRRo5rsQBAIhnylX874y1DX/kEKSVdLsnuHB6l1KTjHyU01VjiMBHgU2adejyg==
version "2.5.0"
resolved "https://registry.yarnpkg.com/type/-/type-2.5.0.tgz#0a2e78c2e77907b252abe5f298c1b01c63f0db3d"
integrity sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw==
typescript@^4.1.5:
typescript@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.3.tgz#39062d8019912d43726298f09493d598048c1ce3"
integrity sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw==
universal-user-agent@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.0.tgz#3381f8503b251c0d9cd21bc1de939ec9df5480ee"
integrity sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==
untildify@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b"
integrity sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==
url-join@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/url-join/-/url-join-4.0.1.tgz#b642e21a2646808ffa178c4c5fda39844e12cde7"
integrity sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==
util-deprecate@~1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
util@^0.10.3:
version "0.10.4"
resolved "https://registry.yarnpkg.com/util/-/util-0.10.4.tgz#3aa0125bfe668a4672de58857d3ace27ecb76901"
integrity sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==
dependencies:
inherits "2.0.3"
wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
@ -1184,14 +991,14 @@ y18n@^5.0.5:
integrity sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg==
yaml@^1.7.2:
version "1.10.0"
resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.0.tgz#3b593add944876077d4d683fee01081bd9fff31e"
integrity sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg==
version "1.10.2"
resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"
integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==
yargs-parser@^20.2.2:
version "20.2.6"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.6.tgz#69f920addf61aafc0b8b89002f5d66e28f2d8b20"
integrity sha512-AP1+fQIWSM/sMiET8fyayjx/J+JmTPt2Mr0FkrgqB4todtfa53sOsrSAcIrJRD5XS20bKUwaDIuMkWKCEiQLKA==
version "20.2.7"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.7.tgz#61df85c113edfb5a7a4e36eb8aa60ef423cbc90a"
integrity sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw==
yargs@^16.1.0:
version "16.2.0"