commit 566453191dd3f4fb36ed786024848605720024f8 Author: Andreas Knuth Date: Thu Feb 29 15:32:49 2024 -0600 initial release diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..7c1e13a --- /dev/null +++ b/.editorconfig @@ -0,0 +1,11 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +[*] +end_of_line = lf +indent_size = 2 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..22aa52b --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +node_modules/ + +# misc +.DS_Store + +dist \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..5ac85e2 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,4 @@ +{ + "printWidth": 100, + "singleQuote": true +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/META-INF/keycloak-themes.json b/META-INF/keycloak-themes.json new file mode 100644 index 0000000..2352cc0 --- /dev/null +++ b/META-INF/keycloak-themes.json @@ -0,0 +1,8 @@ +{ + "themes": [ + { + "name": "keywind", + "types": ["login"] + } + ] +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..03f842c --- /dev/null +++ b/README.md @@ -0,0 +1,114 @@ +# :wind_face: Keywind + +Keywind is a component-based Keycloak Login Theme built with [Tailwind CSS](https://github.com/tailwindlabs/tailwindcss) and [Alpine.js](https://github.com/alpinejs/alpine). + +![Preview](./preview.png) + +### Styled Pages + +- Error +- Login +- Login Config TOTP +- Login IDP Link Confirm +- Login OAuth Grant +- Login OTP +- Login Page Expired +- Login Password +- Login Recovery Authn Code Config +- Login Recovery Authn Code Input +- Login Reset Password +- Login Update Password +- Login Update Profile +- Login Username +- Login X.509 Info +- Logout Confirm +- Register +- Select Authenticator +- Terms and Conditions +- WebAuthn Authenticate +- WebAuthn Error +- WebAuthn Register + +### Identity Provider Icons + +- Apple +- Bitbucket +- Discord +- Facebook +- GitHub +- GitLab +- Google +- Instagram +- LinkedIn +- Microsoft +- OpenID +- Red Hat OpenShift +- PayPal +- Slack +- Stack Overflow +- Twitter + +## Installation + +Keywind has been designed with component-based architecture from the start, and **you can customize as little or as much Keywind as you need**: + +1. [Deploy Keywind Login Theme](https://www.keycloak.org/docs/latest/server_development/#deploying-themes) +2. [Create your own Login Theme](https://www.keycloak.org/docs/latest/server_development/#creating-a-theme) +3. Specify parent theme in [theme properties](https://www.keycloak.org/docs/latest/server_development/#theme-properties): + +``` +parent=keywind +``` + +4. Brand and customize components with [FreeMarker](https://freemarker.apache.org/docs/dgui_quickstart_template.html) + +## Customization + +### Theme + +When you do need to customize a palette, you can configure your colors under the `colors` key in the `theme` section of Tailwind config file: + +`tailwind.config.js` + +```js +module.exports = { + theme: { + extend: { + colors: { + primary: colors.red, + }, + }, + }, +}; +``` + +Read more about Tailwind CSS configuration in the [documentation](https://tailwindcss.com/docs/configuration). + +### Components + +You can update Keywind components in your own child theme. For example, create a copy of the `body` component and change the background: + +`theme/mytheme/login/components/atoms/body.ftl` + +``` +<#macro kw> + + <#nested> + + +``` + +## Build + +When you're ready to deploy your own theme, run the build command to generate a static production build. + +```bash +pnpm install +pnpm build +``` + +To deploy a theme as an archive, create a JAR archive with the theme resources. + +```bash +pnpm build:jar +``` diff --git a/dump.rdb b/dump.rdb new file mode 100644 index 0000000..9562b9f Binary files /dev/null and b/dump.rdb differ diff --git a/html/login/error.html b/html/login/error.html new file mode 100644 index 0000000..061fb6e --- /dev/null +++ b/html/login/error.html @@ -0,0 +1,49 @@ + + + Sign in to + + + + + + + +
+
+
+
+ Keywind +
+

We are sorry...

+
+
+ +
+ +
+
+
+
+
+ + +
+
+
+ + \ No newline at end of file diff --git a/html/login/login-config-totp.html b/html/login/login-config-totp.html new file mode 100644 index 0000000..f348b1f --- /dev/null +++ b/html/login/login-config-totp.html @@ -0,0 +1,76 @@ + + + Sign in to + + + + + + + +
+
+
+
+ Keywind +
+

Mobile Authenticator Setup

+
+
+ +
    +
  1. +

    Install one of the following applications on your mobile:

    +
      +
    • FreeOTP
    • +
  2. +
  3. +

    Open the application and scan the barcode:

    Figure: Barcode Unable to scan?
  4. +
  5. Enter the one-time code provided by the application and click Submit to finish the setup.
  6. +
  7. Provide a Device Name to help you manage your OTP devices.
  8. +
+
+ +
+ +
+
+
+
+ +
+
+
+
+ +
+
+
+ +
+
+
+
+
+ + +
+
+
+ + \ No newline at end of file diff --git a/html/login/login-idp-link-confirm.html b/html/login/login-idp-link-confirm.html new file mode 100644 index 0000000..149daf8 --- /dev/null +++ b/html/login/login-idp-link-confirm.html @@ -0,0 +1,52 @@ + + + Sign in to + + + + + + + +
+
+
+
+ Keywind +
+

Account already exists

+
+
+ +
+ +
+
+ +
+
+
+
+
+ + +
+
+
+ + \ No newline at end of file diff --git a/html/login/login-oauth-grant.html b/html/login/login-oauth-grant.html new file mode 100644 index 0000000..493fe85 --- /dev/null +++ b/html/login/login-oauth-grant.html @@ -0,0 +1,59 @@ + + + Sign in to + + + + + + + +
+
+
+
+ Keywind +
+

+

Grant Access to null

+
+
+ +

Do you grant these access privileges?

+
    +
+
+ +
+ +
+
+
+ +
+
+
+
+
+ + +
+
+
+ + \ No newline at end of file diff --git a/html/login/login-otp.html b/html/login/login-otp.html new file mode 100644 index 0000000..8476be5 --- /dev/null +++ b/html/login/login-otp.html @@ -0,0 +1,59 @@ + + + Sign in to + + + + + + + +
+
+
+
+ Keywind +
+

Sign In

+
+
+ +
+
+ +
+
+
+
+ +
+
+
+ +
+
+
+
+
+ + +
+
+
+ + \ No newline at end of file diff --git a/html/login/login-page-expired.html b/html/login/login-page-expired.html new file mode 100644 index 0000000..64db4a4 --- /dev/null +++ b/html/login/login-page-expired.html @@ -0,0 +1,52 @@ + + + Sign in to + + + + + + + +
+
+
+
+ Keywind +
+

Page has expired

+
+
+ + +
+ +
+
+
+
+
+ + +
+
+
+ + \ No newline at end of file diff --git a/html/login/login-password.html b/html/login/login-password.html new file mode 100644 index 0000000..cdca5ef --- /dev/null +++ b/html/login/login-password.html @@ -0,0 +1,75 @@ + + + Sign in to + + + + + + + +
+
+
+
+ Keywind +
+

Sign In

+
+
+ +
+
+ +
+ +
+
+
+
+ +
+ +
+
+
+ +
+
+
+
+
+ + +
+
+
+ + \ No newline at end of file diff --git a/html/login/login-recovery-authn-code-config.html b/html/login/login-recovery-authn-code-config.html new file mode 100644 index 0000000..5a7a96a --- /dev/null +++ b/html/login/login-recovery-authn-code-config.html @@ -0,0 +1,84 @@ + + + Sign in to + + + + + + + + +
+
+
+
+ Keywind +
+

Recovery Authentication Codes

+
+
+ +
+ +
    +
  • 0000-0000-0000
  • +
  • 1111-1111-1111
  • +
+
+ +
+
+ +
+ +
+
+ +
+
+
+
+ +
+
+
+
+
+ + +
+
+
+ + + \ No newline at end of file diff --git a/html/login/login-recovery-authn-code-input.html b/html/login/login-recovery-authn-code-input.html new file mode 100644 index 0000000..0f10d6f --- /dev/null +++ b/html/login/login-recovery-authn-code-input.html @@ -0,0 +1,59 @@ + + + Sign in to + + + + + + + +
+
+
+
+ Keywind +
+

Login with a recovery authentication code

+
+
+ +
+
+ +
+
+
+
+ +
+
+
+ +
+
+
+
+
+ + +
+
+
+ + \ No newline at end of file diff --git a/html/login/login-reset-password.html b/html/login/login-reset-password.html new file mode 100644 index 0000000..a56248c --- /dev/null +++ b/html/login/login-reset-password.html @@ -0,0 +1,63 @@ + + + Sign in to + + + + + + + +
+
+
+
+ Keywind +
+

Forgot Your Password?

+
+
+ +
+
+ +
+
+
+
+ +
+
+
+ +
+
+
+ Enter your username and we will send you instructions on how to create a new password. +
+
+
+ « Back to Login +
+ + +
+
+
+ + \ No newline at end of file diff --git a/html/login/login-update-password.html b/html/login/login-update-password.html new file mode 100644 index 0000000..c223fa8 --- /dev/null +++ b/html/login/login-update-password.html @@ -0,0 +1,91 @@ + + + Sign in to + + + + + + + +
+
+
+
+ Keywind +
+

Update password

+
+
+ +
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+
+
+
+
+ +
+
+
+ +
+
+
+
+
+ + +
+
+
+ + \ No newline at end of file diff --git a/html/login/login-update-profile.html b/html/login/login-update-profile.html new file mode 100644 index 0000000..971a3ff --- /dev/null +++ b/html/login/login-update-profile.html @@ -0,0 +1,74 @@ + + + Sign in to + + + + + + + +
+
+
+
+ Keywind +
+

Update Account Information

+
+
+ +
+
+ +
+
+
+
+ +
+
+
+
+ +
+
+
+
+ +
+
+
+
+ +
+
+
+ +
+
+
+
+
+ + +
+
+
+ + \ No newline at end of file diff --git a/html/login/login-username.html b/html/login/login-username.html new file mode 100644 index 0000000..d5db05e --- /dev/null +++ b/html/login/login-username.html @@ -0,0 +1,90 @@ + + + Sign in to + + + + + + + +
+
+
+
+ Keywind +
+

Sign in to your account

+
+
+ +
+
+ +
+
+
+
+
+ +
+
+
+ +
+
+
+ +
+
+ Or sign in with +
+ +
+
+
+ New user? Register +
+
+
+
+
+ + +
+
+
+ + \ No newline at end of file diff --git a/html/login/login-x509-info.html b/html/login/login-x509-info.html new file mode 100644 index 0000000..5d26f88 --- /dev/null +++ b/html/login/login-x509-info.html @@ -0,0 +1,65 @@ + + + Sign in to + + + + + + + +
+
+
+
+ Keywind +
+

Sign In

+
+
+ +
+
+ X509 client certificate: +
+
+ CN=User, C=US, O=Keywind +
+
+
+ You will be logged in as: Username +
+
+
+ +
+
+
+ +
+
+
+
+
+ + +
+
+
+ + \ No newline at end of file diff --git a/html/login/login.html b/html/login/login.html new file mode 100644 index 0000000..d24c358 --- /dev/null +++ b/html/login/login.html @@ -0,0 +1,109 @@ + + + Sign in to + + + + + + + +
+
+
+
+ Keywind +
+

Sign in to your account

+
+
+ +
+ +
+ +
+
+
+
+ +
+ +
+
+
+
+
+
+ +
Forgot Password? +
+
+ +
+
+
+ +
+
+ Or sign in with +
+ +
+
+
+ New user? Register +
+
+
+
+
+ + +
+
+
+ + \ No newline at end of file diff --git a/html/login/logout-confirm.html b/html/login/logout-confirm.html new file mode 100644 index 0000000..e13b12d --- /dev/null +++ b/html/login/logout-confirm.html @@ -0,0 +1,53 @@ + + + Sign in to + + + + + + + +
+
+
+
+ Keywind +
+

Logging out

+
+
+ +

Do you want to log out?

+
+ +
+
+ +
+
+
+
+
+ + +
+
+
+ + \ No newline at end of file diff --git a/html/login/register.html b/html/login/register.html new file mode 100644 index 0000000..b1f3691 --- /dev/null +++ b/html/login/register.html @@ -0,0 +1,70 @@ + + + Sign in to + + + + + + + +
+
+
+
+ Keywind +
+

Register

+
+
+ +
+
+ +
+
+
+
+ +
+
+
+
+ +
+
+
+
+ +
+
+
+ +
+
+
+
+ « Back to Login +
+ + +
+
+
+ + \ No newline at end of file diff --git a/html/login/select-authenticator.html b/html/login/select-authenticator.html new file mode 100644 index 0000000..248bea3 --- /dev/null +++ b/html/login/select-authenticator.html @@ -0,0 +1,60 @@ + + + Sign in to + + + + + + + +
+
+
+
+ Keywind +
+

Select login method

+
+
+ +
+
+ +
+ +
+ Use your security key to sign in. +
+
+
+
+
+ +
+
+
+
+
+ + +
+
+
+ + \ No newline at end of file diff --git a/html/login/webauthn-authenticate.html b/html/login/webauthn-authenticate.html new file mode 100644 index 0000000..dbc3c3e --- /dev/null +++ b/html/login/webauthn-authenticate.html @@ -0,0 +1,70 @@ + + + Sign in to + + + + + + + + +
+
+
+
+ Keywind +
+

Security Key login

+
+
+ +
+
+ +
+
+ +
+
+
+ +
+
+
+
+
+ + +
+
+
+ + + \ No newline at end of file diff --git a/html/login/webauthn-error.html b/html/login/webauthn-error.html new file mode 100644 index 0000000..24e893d --- /dev/null +++ b/html/login/webauthn-error.html @@ -0,0 +1,57 @@ + + + Sign in to + + + + + + + +
+
+
+
+ Keywind +
+

Security Key Error

+
+
+ +
+
+ +
+
+ +
+
+
+ +
+
+
+
+
+ + +
+
+
+ + \ No newline at end of file diff --git a/out/keywind.jar b/out/keywind.jar new file mode 100644 index 0000000..761ed46 Binary files /dev/null and b/out/keywind.jar differ diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..3b23107 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2562 @@ +{ + "name": "keywind", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "keywind", + "dependencies": { + "alpinejs": "^3.13.0", + "rfc4648": "^1.5.2" + }, + "devDependencies": { + "@tailwindcss/forms": "^0.5.6", + "@types/alpinejs": "^3.13.2", + "@types/archiver": "^5.3.3", + "@types/node": "^20.6.5", + "archiver": "^6.0.1", + "autoprefixer": "^10.4.16", + "postcss": "^8.4.30", + "tailwindcss": "^3.3.3", + "typescript": "^5.2.2", + "vite": "^4.4.9", + "vite-node": "^0.34.5" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", + "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", + "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", + "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", + "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", + "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", + "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", + "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", + "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", + "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", + "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", + "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", + "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", + "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", + "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", + "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", + "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", + "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", + "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", + "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", + "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", + "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", + "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.22", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.22.tgz", + "integrity": "sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@tailwindcss/forms": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.7.tgz", + "integrity": "sha512-QE7X69iQI+ZXwldE+rzasvbJiyV/ju1FGHH0Qn2W3FKbuYtqp8LKcy6iSw79fVUT5/Vvf+0XgLCeYVG+UV6hOw==", + "dev": true, + "dependencies": { + "mini-svg-data-uri": "^1.2.3" + }, + "peerDependencies": { + "tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1" + } + }, + "node_modules/@types/alpinejs": { + "version": "3.13.6", + "resolved": "https://registry.npmjs.org/@types/alpinejs/-/alpinejs-3.13.6.tgz", + "integrity": "sha512-BMi1/2uQz7mp30VFn69SzjN7YwQ0QzE4Hn3RMBt4iMpQeasdbMiImv1f5yvK1bYmvjIyG/YFg+CgPxbjIXZk0g==", + "dev": true + }, + "node_modules/@types/archiver": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/@types/archiver/-/archiver-5.3.4.tgz", + "integrity": "sha512-Lj7fLBIMwYFgViVVZHEdExZC3lVYsl+QL0VmdNdIzGZH544jHveYWij6qdnBgJQDnR7pMKliN9z2cPZFEbhyPw==", + "dev": true, + "dependencies": { + "@types/readdir-glob": "*" + } + }, + "node_modules/@types/node": { + "version": "20.11.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.18.tgz", + "integrity": "sha512-ABT5VWnnYneSBcNWYSCuR05M826RoMyMSGiFivXGx6ZUIsXb9vn4643IEwkg2zbEOSgAiSogtapN2fgc4mAPlw==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/readdir-glob": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@types/readdir-glob/-/readdir-glob-1.1.5.tgz", + "integrity": "sha512-raiuEPUYqXu+nvtY2Pe8s8FEmZ3x5yAH4VkLdihcPdalvsHltomrRC9BzuStrJ9yk06470hS0Crw0f1pXqD+Hg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@vue/reactivity": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.1.5.tgz", + "integrity": "sha512-1tdfLmNjWG6t/CsPldh+foumYFo3cpyCHgBYQ34ylaMsJ+SNHQ1kApMIa8jN+i593zQuaw3AdWH0nJTARzCFhg==", + "dependencies": { + "@vue/shared": "3.1.5" + } + }, + "node_modules/@vue/shared": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.1.5.tgz", + "integrity": "sha512-oJ4F3TnvpXaQwZJNF3ZK+kLPHKarDmJjJ6jyzVNDKH9md1dptjC7lWR//jrGuLdek/U6iltWxqAnYOu8gCiOvA==" + }, + "node_modules/acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/alpinejs": { + "version": "3.13.5", + "resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.13.5.tgz", + "integrity": "sha512-1d2XeNGN+Zn7j4mUAKXtAgdc4/rLeadyTMWeJGXF5DzwawPBxwTiBhFFm6w/Ei8eJxUZeyNWWSD9zknfdz1kEw==", + "dependencies": { + "@vue/reactivity": "~3.1.1" + } + }, + "node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/archiver": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-6.0.1.tgz", + "integrity": "sha512-CXGy4poOLBKptiZH//VlWdFuUC1RESbdZjGjILwBuZ73P7WkAUN0htfSfBq/7k6FRFlpu7bg4JOkj1vU9G6jcQ==", + "dev": true, + "dependencies": { + "archiver-utils": "^4.0.1", + "async": "^3.2.4", + "buffer-crc32": "^0.2.1", + "readable-stream": "^3.6.0", + "readdir-glob": "^1.1.2", + "tar-stream": "^3.0.0", + "zip-stream": "^5.0.1" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/archiver-utils": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-4.0.1.tgz", + "integrity": "sha512-Q4Q99idbvzmgCTEAAhi32BkOyq8iVI5EwdO0PmBDSGIzzjYNdcFn7Q7k3OzbLy4kLUPXfJtG6fO2RjftXbobBg==", + "dev": true, + "dependencies": { + "glob": "^8.0.0", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash": "^4.17.15", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true + }, + "node_modules/async": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", + "dev": true + }, + "node_modules/autoprefixer": { + "version": "10.4.17", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.17.tgz", + "integrity": "sha512-/cpVNRLSfhOtcGflT13P2794gVSgmPgTR+erw5ifnMLZb0UnSlkK4tquLmkd3BhA+nLo5tX8Cu0upUsGKvKbmg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "browserslist": "^4.22.2", + "caniuse-lite": "^1.0.30001578", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.0", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/b4a": { + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.6.tgz", + "integrity": "sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==", + "dev": true + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/bare-events": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.2.0.tgz", + "integrity": "sha512-Yyyqff4PIFfSuthCZqLlPISTWHmnQxoPuAvkmgzsJEmG3CesdIv6Xweayl0JkCZJSB2yYIdJyEz97tpxNhgjbg==", + "dev": true, + "optional": true + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", + "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001587", + "electron-to-chromium": "^1.4.668", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001587", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001587.tgz", + "integrity": "sha512-HMFNotUmLXn71BQxg8cijvqxnIAofforZOwGsxyXJ0qugTdspUF4sPSJ2vhgprHCB996tIDzEq1ubumPDV8ULA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/compress-commons": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-5.0.1.tgz", + "integrity": "sha512-MPh//1cERdLtqwO3pOFLeXtpuai0Y2WCd5AhtKxznqM7WtaMYaOEMSgn45d9D10sIHSfIKE603HlOp8OPGrvag==", + "dev": true, + "dependencies": { + "crc-32": "^1.2.0", + "crc32-stream": "^5.0.0", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "dev": true, + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/crc32-stream": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-5.0.0.tgz", + "integrity": "sha512-B0EPa1UK+qnpBZpG+7FgPCu0J2ETLpXq09o9BkLkEAhdB6Z61Qo4pJ3JYu0c+Qi+/SAL7QThqnzS06pmSSyZaw==", + "dev": true, + "dependencies": { + "crc-32": "^1.2.0", + "readable-stream": "^3.4.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/electron-to-chromium": { + "version": "1.4.670", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.670.tgz", + "integrity": "sha512-hcijYOWjOtjKrKPtNA6tuLlA/bTLO3heFG8pQA6mLpq7dRydSWicXova5lyxDzp1iVJaYhK7J2OQlGE52KYn7A==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/esbuild": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", + "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.18.20", + "@esbuild/android-arm64": "0.18.20", + "@esbuild/android-x64": "0.18.20", + "@esbuild/darwin-arm64": "0.18.20", + "@esbuild/darwin-x64": "0.18.20", + "@esbuild/freebsd-arm64": "0.18.20", + "@esbuild/freebsd-x64": "0.18.20", + "@esbuild/linux-arm": "0.18.20", + "@esbuild/linux-arm64": "0.18.20", + "@esbuild/linux-ia32": "0.18.20", + "@esbuild/linux-loong64": "0.18.20", + "@esbuild/linux-mips64el": "0.18.20", + "@esbuild/linux-ppc64": "0.18.20", + "@esbuild/linux-riscv64": "0.18.20", + "@esbuild/linux-s390x": "0.18.20", + "@esbuild/linux-x64": "0.18.20", + "@esbuild/netbsd-x64": "0.18.20", + "@esbuild/openbsd-x64": "0.18.20", + "@esbuild/sunos-x64": "0.18.20", + "@esbuild/win32-arm64": "0.18.20", + "@esbuild/win32-ia32": "0.18.20", + "@esbuild/win32-x64": "0.18.20" + } + }, + "node_modules/escalade": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "dev": true, + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/hasown": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz", + "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/jackspeak": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jiti": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz", + "integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==", + "dev": true, + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/jsonc-parser": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz", + "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==", + "dev": true + }, + "node_modules/lazystream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "dev": true, + "dependencies": { + "readable-stream": "^2.0.5" + }, + "engines": { + "node": ">= 0.6.3" + } + }, + "node_modules/lazystream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/lazystream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/lazystream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lru-cache": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", + "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", + "dev": true, + "engines": { + "node": "14 || >=16.14" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mini-svg-data-uri": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz", + "integrity": "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==", + "dev": true, + "bin": { + "mini-svg-data-uri": "cli.js" + } + }, + "node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/minipass": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", + "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mlly": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.5.0.tgz", + "integrity": "sha512-NPVQvAY1xr1QoVeG0cy8yUYC7FQcOx6evl/RjT1wL5FvzPnzOysoqB/jmx/DhssT2dYa8nxECLAaFI/+gVLhDQ==", + "dev": true, + "dependencies": { + "acorn": "^8.11.3", + "pathe": "^1.1.2", + "pkg-types": "^1.0.3", + "ufo": "^1.3.2" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-releases": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", + "dev": true + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-scurry": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", + "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", + "dev": true, + "dependencies": { + "lru-cache": "^9.1.1 || ^10.0.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-types": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.0.3.tgz", + "integrity": "sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==", + "dev": true, + "dependencies": { + "jsonc-parser": "^3.2.0", + "mlly": "^1.2.0", + "pathe": "^1.1.0" + } + }, + "node_modules/postcss": { + "version": "8.4.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz", + "integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "dev": true, + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", + "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "lilconfig": "^3.0.0", + "yaml": "^2.3.4" + }, + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-load-config/node_modules/lilconfig": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.0.tgz", + "integrity": "sha512-p3cz0JV5vw/XeouBU3Ldnp+ZkBjE+n8ydJ4mcwBrOiXXPqNlrzGBqWs9X4MWF7f+iKUBu794Y8Hh8yawiJbCjw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/postcss-nested": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz", + "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.11" + }, + "engines": { + "node": ">=12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.15", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.15.tgz", + "integrity": "sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/queue-tick": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", + "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==", + "dev": true + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdir-glob": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", + "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", + "dev": true, + "dependencies": { + "minimatch": "^5.1.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rfc4648": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/rfc4648/-/rfc4648-1.5.3.tgz", + "integrity": "sha512-MjOWxM065+WswwnmNONOT+bD1nXzY9Km6u3kzvnx8F8/HXGZdz3T6e6vZJ8Q/RIMUSp/nxqjH3GwvJDy8ijeQQ==" + }, + "node_modules/rollup": { + "version": "3.29.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", + "integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==", + "dev": true, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=14.18.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/streamx": { + "version": "2.15.8", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.15.8.tgz", + "integrity": "sha512-6pwMeMY/SuISiRsuS8TeIrAzyFbG5gGPHFQsYjUr/pbBadaL1PCWmzKw+CHZSwainfvcF6Si6cVLq4XTEwswFQ==", + "dev": true, + "dependencies": { + "fast-fifo": "^1.1.0", + "queue-tick": "^1.0.1" + }, + "optionalDependencies": { + "bare-events": "^2.2.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/sucrase": { + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", + "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "^10.3.10", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/sucrase/node_modules/glob": { + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sucrase/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tailwindcss": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.1.tgz", + "integrity": "sha512-qAYmXRfk3ENzuPBakNK0SRrUDipP8NQnEY6772uDhflcQz5EhRdD7JNZxyrFHVQNCwULPBn6FNPp9brpO7ctcA==", + "dev": true, + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.5.3", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.0", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.19.1", + "lilconfig": "^2.1.0", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.23", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.1", + "postcss-nested": "^6.0.1", + "postcss-selector-parser": "^6.0.11", + "resolve": "^1.22.2", + "sucrase": "^3.32.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tar-stream": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", + "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", + "dev": true, + "dependencies": { + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true + }, + "node_modules/typescript": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/ufo": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.4.0.tgz", + "integrity": "sha512-Hhy+BhRBleFjpJ2vchUNN40qgkh0366FWJGqVLYBHev0vpHTrXSA0ryT+74UiW6KWsldNurQMKGqCm1M2zBciQ==", + "dev": true + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, + "node_modules/update-browserslist-db": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/vite": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.2.tgz", + "integrity": "sha512-tBCZBNSBbHQkaGyhGCDUGqeo2ph8Fstyp6FMSvTtsXeZSPpSMGlviAOav2hxVTqFcx8Hj/twtWKsMJXNY0xI8w==", + "dev": true, + "dependencies": { + "esbuild": "^0.18.10", + "postcss": "^8.4.27", + "rollup": "^3.27.1" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + }, + "peerDependencies": { + "@types/node": ">= 14", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite-node": { + "version": "0.34.7", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-0.34.7.tgz", + "integrity": "sha512-0Yzb96QzHmqIKIs/x2q/sqG750V/EF6yDkS2p1WjJc1W2bgRSuQjf5vB9HY8h2nVb5j4pO5paS5Npcv3s69YUg==", + "dev": true, + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.3.4", + "mlly": "^1.4.0", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0-0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": ">=v14.18.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/yaml": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", + "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==", + "dev": true, + "engines": { + "node": ">= 14" + } + }, + "node_modules/zip-stream": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-5.0.1.tgz", + "integrity": "sha512-UfZ0oa0C8LI58wJ+moL46BDIMgCQbnsb+2PoiJYtonhBsMh2bq1eRBVkvjfVsqbEHd9/EgKPUuL9saSSsec8OA==", + "dev": true, + "dependencies": { + "archiver-utils": "^4.0.1", + "compress-commons": "^5.0.1", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 12.0.0" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..7f84d04 --- /dev/null +++ b/package.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://json.schemastore.org/package", + "name": "keywind", + "scripts": { + "build": "tsc && vite build", + "build:jar": "vite-node scripts/build", + "dev": "vite dev --host", + "test": "mvn test" + }, + "dependencies": { + "alpinejs": "^3.13.0", + "rfc4648": "^1.5.2" + }, + "devDependencies": { + "@tailwindcss/forms": "^0.5.6", + "@types/alpinejs": "^3.13.2", + "@types/archiver": "^5.3.3", + "@types/node": "^20.6.5", + "archiver": "^6.0.1", + "autoprefixer": "^10.4.16", + "postcss": "^8.4.30", + "tailwindcss": "^3.3.3", + "typescript": "^5.2.2", + "vite": "^4.4.9", + "vite-node": "^0.34.5" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..3c60020 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,1261 @@ +lockfileVersion: '6.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +dependencies: + alpinejs: + specifier: ^3.13.0 + version: 3.13.0 + rfc4648: + specifier: ^1.5.2 + version: 1.5.2 + +devDependencies: + '@tailwindcss/forms': + specifier: ^0.5.6 + version: 0.5.6(tailwindcss@3.3.3) + '@types/alpinejs': + specifier: ^3.13.2 + version: 3.13.2 + '@types/archiver': + specifier: ^5.3.3 + version: 5.3.3 + '@types/node': + specifier: ^20.6.5 + version: 20.6.5 + archiver: + specifier: ^6.0.1 + version: 6.0.1 + autoprefixer: + specifier: ^10.4.16 + version: 10.4.16(postcss@8.4.30) + postcss: + specifier: ^8.4.30 + version: 8.4.30 + tailwindcss: + specifier: ^3.3.3 + version: 3.3.3 + typescript: + specifier: ^5.2.2 + version: 5.2.2 + vite: + specifier: ^4.4.9 + version: 4.4.9(@types/node@20.6.5) + vite-node: + specifier: ^0.34.5 + version: 0.34.5(@types/node@20.6.5) + +packages: + + /@alloc/quick-lru@5.2.0: + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + engines: {node: '>=10'} + dev: true + + /@esbuild/android-arm64@0.18.20: + resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-arm@0.18.20: + resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-x64@0.18.20: + resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-arm64@0.18.20: + resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-x64@0.18.20: + resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-arm64@0.18.20: + resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-x64@0.18.20: + resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm64@0.18.20: + resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm@0.18.20: + resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ia32@0.18.20: + resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-loong64@0.18.20: + resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-mips64el@0.18.20: + resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ppc64@0.18.20: + resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-riscv64@0.18.20: + resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-s390x@0.18.20: + resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-x64@0.18.20: + resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/netbsd-x64@0.18.20: + resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/openbsd-x64@0.18.20: + resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/sunos-x64@0.18.20: + resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-arm64@0.18.20: + resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-ia32@0.18.20: + resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-x64@0.18.20: + resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@jridgewell/gen-mapping@0.3.3: + resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/set-array': 1.1.2 + '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/trace-mapping': 0.3.19 + dev: true + + /@jridgewell/resolve-uri@3.1.1: + resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} + engines: {node: '>=6.0.0'} + dev: true + + /@jridgewell/set-array@1.1.2: + resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} + engines: {node: '>=6.0.0'} + dev: true + + /@jridgewell/sourcemap-codec@1.4.15: + resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + dev: true + + /@jridgewell/trace-mapping@0.3.19: + resolution: {integrity: sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==} + dependencies: + '@jridgewell/resolve-uri': 3.1.1 + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + + /@nodelib/fs.scandir@2.1.5: + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + dev: true + + /@nodelib/fs.stat@2.0.5: + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + dev: true + + /@nodelib/fs.walk@1.2.8: + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.15.0 + dev: true + + /@tailwindcss/forms@0.5.6(tailwindcss@3.3.3): + resolution: {integrity: sha512-Fw+2BJ0tmAwK/w01tEFL5TiaJBX1NLT1/YbWgvm7ws3Qcn11kiXxzNTEQDMs5V3mQemhB56l3u0i9dwdzSQldA==} + peerDependencies: + tailwindcss: '>=3.0.0 || >= 3.0.0-alpha.1' + dependencies: + mini-svg-data-uri: 1.4.4 + tailwindcss: 3.3.3 + dev: true + + /@types/alpinejs@3.13.2: + resolution: {integrity: sha512-kRGGjngGMCw8H4BIDbaDLwuGNIsrzdvt54kKqLe6O8ZLTNW5swEMr6j2ByH/vUo46ajqf2Qbxh6fcxPDrfEU+Q==} + dev: true + + /@types/archiver@5.3.3: + resolution: {integrity: sha512-0ABdVcXL6jOwNGY+hjWPqrxUvKelBEwNLcuv/SV2vZ4YCH8w9NttFCt+/QqI5zgMX+iX/XqVy89/r7EmLJmMpQ==} + dependencies: + '@types/readdir-glob': 1.1.1 + dev: true + + /@types/node@20.6.5: + resolution: {integrity: sha512-2qGq5LAOTh9izcc0+F+dToFigBWiK1phKPt7rNhOqJSr35y8rlIBjDwGtFSgAI6MGIhjwOVNSQZVdJsZJ2uR1w==} + dev: true + + /@types/readdir-glob@1.1.1: + resolution: {integrity: sha512-ImM6TmoF8bgOwvehGviEj3tRdRBbQujr1N+0ypaln/GWjaerOB26jb93vsRHmdMtvVQZQebOlqt2HROark87mQ==} + dependencies: + '@types/node': 20.6.5 + dev: true + + /@vue/reactivity@3.1.5: + resolution: {integrity: sha512-1tdfLmNjWG6t/CsPldh+foumYFo3cpyCHgBYQ34ylaMsJ+SNHQ1kApMIa8jN+i593zQuaw3AdWH0nJTARzCFhg==} + dependencies: + '@vue/shared': 3.1.5 + dev: false + + /@vue/shared@3.1.5: + resolution: {integrity: sha512-oJ4F3TnvpXaQwZJNF3ZK+kLPHKarDmJjJ6jyzVNDKH9md1dptjC7lWR//jrGuLdek/U6iltWxqAnYOu8gCiOvA==} + dev: false + + /acorn@8.10.0: + resolution: {integrity: sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: true + + /alpinejs@3.13.0: + resolution: {integrity: sha512-7FYR1Yz3evIjlJD1mZ3SYWSw+jlOmQGeQ1QiSufSQ6J84XMQFkzxm6OobiZ928SfqhGdoIp2SsABNsS4rXMMJw==} + dependencies: + '@vue/reactivity': 3.1.5 + dev: false + + /any-promise@1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + dev: true + + /anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + dev: true + + /archiver-utils@4.0.1: + resolution: {integrity: sha512-Q4Q99idbvzmgCTEAAhi32BkOyq8iVI5EwdO0PmBDSGIzzjYNdcFn7Q7k3OzbLy4kLUPXfJtG6fO2RjftXbobBg==} + engines: {node: '>= 12.0.0'} + dependencies: + glob: 8.1.0 + graceful-fs: 4.2.11 + lazystream: 1.0.1 + lodash: 4.17.21 + normalize-path: 3.0.0 + readable-stream: 3.6.2 + dev: true + + /archiver@6.0.1: + resolution: {integrity: sha512-CXGy4poOLBKptiZH//VlWdFuUC1RESbdZjGjILwBuZ73P7WkAUN0htfSfBq/7k6FRFlpu7bg4JOkj1vU9G6jcQ==} + engines: {node: '>= 12.0.0'} + dependencies: + archiver-utils: 4.0.1 + async: 3.2.4 + buffer-crc32: 0.2.13 + readable-stream: 3.6.2 + readdir-glob: 1.1.3 + tar-stream: 3.1.6 + zip-stream: 5.0.1 + dev: true + + /arg@5.0.2: + resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + dev: true + + /async@3.2.4: + resolution: {integrity: sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==} + dev: true + + /autoprefixer@10.4.16(postcss@8.4.30): + resolution: {integrity: sha512-7vd3UC6xKp0HLfua5IjZlcXvGAGy7cBAXTg2lyQ/8WpNhd6SiZ8Be+xm3FyBSYJx5GKcpRCzBh7RH4/0dnY+uQ==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + dependencies: + browserslist: 4.21.11 + caniuse-lite: 1.0.30001539 + fraction.js: 4.3.6 + normalize-range: 0.1.2 + picocolors: 1.0.0 + postcss: 8.4.30 + postcss-value-parser: 4.2.0 + dev: true + + /b4a@1.6.4: + resolution: {integrity: sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw==} + dev: true + + /balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + dev: true + + /binary-extensions@2.2.0: + resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} + engines: {node: '>=8'} + dev: true + + /brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + dev: true + + /brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + dependencies: + balanced-match: 1.0.2 + dev: true + + /braces@3.0.2: + resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} + engines: {node: '>=8'} + dependencies: + fill-range: 7.0.1 + dev: true + + /browserslist@4.21.11: + resolution: {integrity: sha512-xn1UXOKUz7DjdGlg9RrUr0GGiWzI97UQJnugHtH0OLDfJB7jMgoIkYvRIEO1l9EeEERVqeqLYOcFBW9ldjypbQ==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + dependencies: + caniuse-lite: 1.0.30001539 + electron-to-chromium: 1.4.528 + node-releases: 2.0.13 + update-browserslist-db: 1.0.13(browserslist@4.21.11) + dev: true + + /buffer-crc32@0.2.13: + resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} + dev: true + + /cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + dev: true + + /camelcase-css@2.0.1: + resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} + engines: {node: '>= 6'} + dev: true + + /caniuse-lite@1.0.30001539: + resolution: {integrity: sha512-hfS5tE8bnNiNvEOEkm8HElUHroYwlqMMENEzELymy77+tJ6m+gA2krtHl5hxJaj71OlpC2cHZbdSMX1/YEqEkA==} + dev: true + + /chokidar@3.5.3: + resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} + engines: {node: '>= 8.10.0'} + dependencies: + anymatch: 3.1.3 + braces: 3.0.2 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + dev: true + + /commander@4.1.1: + resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} + engines: {node: '>= 6'} + dev: true + + /compress-commons@5.0.1: + resolution: {integrity: sha512-MPh//1cERdLtqwO3pOFLeXtpuai0Y2WCd5AhtKxznqM7WtaMYaOEMSgn45d9D10sIHSfIKE603HlOp8OPGrvag==} + engines: {node: '>= 12.0.0'} + dependencies: + crc-32: 1.2.2 + crc32-stream: 5.0.0 + normalize-path: 3.0.0 + readable-stream: 3.6.2 + dev: true + + /concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + dev: true + + /core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + dev: true + + /crc-32@1.2.2: + resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} + engines: {node: '>=0.8'} + hasBin: true + dev: true + + /crc32-stream@5.0.0: + resolution: {integrity: sha512-B0EPa1UK+qnpBZpG+7FgPCu0J2ETLpXq09o9BkLkEAhdB6Z61Qo4pJ3JYu0c+Qi+/SAL7QThqnzS06pmSSyZaw==} + engines: {node: '>= 12.0.0'} + dependencies: + crc-32: 1.2.2 + readable-stream: 3.6.2 + dev: true + + /cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + dev: true + + /debug@4.3.4: + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.2 + dev: true + + /didyoumean@1.2.2: + resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} + dev: true + + /dlv@1.1.3: + resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} + dev: true + + /electron-to-chromium@1.4.528: + resolution: {integrity: sha512-UdREXMXzLkREF4jA8t89FQjA8WHI6ssP38PMY4/4KhXFQbtImnghh4GkCgrtiZwLKUKVD2iTVXvDVQjfomEQuA==} + dev: true + + /esbuild@0.18.20: + resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/android-arm': 0.18.20 + '@esbuild/android-arm64': 0.18.20 + '@esbuild/android-x64': 0.18.20 + '@esbuild/darwin-arm64': 0.18.20 + '@esbuild/darwin-x64': 0.18.20 + '@esbuild/freebsd-arm64': 0.18.20 + '@esbuild/freebsd-x64': 0.18.20 + '@esbuild/linux-arm': 0.18.20 + '@esbuild/linux-arm64': 0.18.20 + '@esbuild/linux-ia32': 0.18.20 + '@esbuild/linux-loong64': 0.18.20 + '@esbuild/linux-mips64el': 0.18.20 + '@esbuild/linux-ppc64': 0.18.20 + '@esbuild/linux-riscv64': 0.18.20 + '@esbuild/linux-s390x': 0.18.20 + '@esbuild/linux-x64': 0.18.20 + '@esbuild/netbsd-x64': 0.18.20 + '@esbuild/openbsd-x64': 0.18.20 + '@esbuild/sunos-x64': 0.18.20 + '@esbuild/win32-arm64': 0.18.20 + '@esbuild/win32-ia32': 0.18.20 + '@esbuild/win32-x64': 0.18.20 + dev: true + + /escalade@3.1.1: + resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} + engines: {node: '>=6'} + dev: true + + /fast-fifo@1.3.2: + resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} + dev: true + + /fast-glob@3.3.1: + resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==} + engines: {node: '>=8.6.0'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.5 + dev: true + + /fastq@1.15.0: + resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} + dependencies: + reusify: 1.0.4 + dev: true + + /fill-range@7.0.1: + resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} + engines: {node: '>=8'} + dependencies: + to-regex-range: 5.0.1 + dev: true + + /fraction.js@4.3.6: + resolution: {integrity: sha512-n2aZ9tNfYDwaHhvFTkhFErqOMIb8uyzSQ+vGJBjZyanAKZVbGUQ1sngfk9FdkBw7G26O7AgNjLcecLffD1c7eg==} + dev: true + + /fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + dev: true + + /fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /function-bind@1.1.1: + resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} + dev: true + + /glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + dependencies: + is-glob: 4.0.3 + dev: true + + /glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + dependencies: + is-glob: 4.0.3 + dev: true + + /glob@7.1.6: + resolution: {integrity: sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + dev: true + + /glob@8.1.0: + resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} + engines: {node: '>=12'} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 5.1.6 + once: 1.4.0 + dev: true + + /graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + dev: true + + /has@1.0.3: + resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} + engines: {node: '>= 0.4.0'} + dependencies: + function-bind: 1.1.1 + dev: true + + /inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + dev: true + + /inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + dev: true + + /is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + dependencies: + binary-extensions: 2.2.0 + dev: true + + /is-core-module@2.13.0: + resolution: {integrity: sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==} + dependencies: + has: 1.0.3 + dev: true + + /is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + dev: true + + /is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + dependencies: + is-extglob: 2.1.1 + dev: true + + /is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + dev: true + + /isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + dev: true + + /jiti@1.20.0: + resolution: {integrity: sha512-3TV69ZbrvV6U5DfQimop50jE9Dl6J8O1ja1dvBbMba/sZ3YBEQqJ2VZRoQPVnhlzjNtU1vaXRZVrVjU4qtm8yA==} + hasBin: true + dev: true + + /jsonc-parser@3.2.0: + resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==} + dev: true + + /lazystream@1.0.1: + resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==} + engines: {node: '>= 0.6.3'} + dependencies: + readable-stream: 2.3.8 + dev: true + + /lilconfig@2.1.0: + resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} + engines: {node: '>=10'} + dev: true + + /lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + dev: true + + /lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + dev: true + + /merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + dev: true + + /micromatch@4.0.5: + resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} + engines: {node: '>=8.6'} + dependencies: + braces: 3.0.2 + picomatch: 2.3.1 + dev: true + + /mini-svg-data-uri@1.4.4: + resolution: {integrity: sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==} + hasBin: true + dev: true + + /minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + dependencies: + brace-expansion: 1.1.11 + dev: true + + /minimatch@5.1.6: + resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} + engines: {node: '>=10'} + dependencies: + brace-expansion: 2.0.1 + dev: true + + /mlly@1.4.2: + resolution: {integrity: sha512-i/Ykufi2t1EZ6NaPLdfnZk2AX8cs0d+mTzVKuPfqPKPatxLApaBoxJQ9x1/uckXtrS/U5oisPMDkNs0yQTaBRg==} + dependencies: + acorn: 8.10.0 + pathe: 1.1.1 + pkg-types: 1.0.3 + ufo: 1.3.0 + dev: true + + /ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + dev: true + + /mz@2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + dependencies: + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 + dev: true + + /nanoid@3.3.6: + resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + dev: true + + /node-releases@2.0.13: + resolution: {integrity: sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==} + dev: true + + /normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + dev: true + + /normalize-range@0.1.2: + resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} + engines: {node: '>=0.10.0'} + dev: true + + /object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + dev: true + + /object-hash@3.0.0: + resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} + engines: {node: '>= 6'} + dev: true + + /once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + dependencies: + wrappy: 1.0.2 + dev: true + + /path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + dev: true + + /path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + dev: true + + /pathe@1.1.1: + resolution: {integrity: sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q==} + dev: true + + /picocolors@1.0.0: + resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + dev: true + + /picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + dev: true + + /pify@2.3.0: + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} + engines: {node: '>=0.10.0'} + dev: true + + /pirates@4.0.6: + resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} + engines: {node: '>= 6'} + dev: true + + /pkg-types@1.0.3: + resolution: {integrity: sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==} + dependencies: + jsonc-parser: 3.2.0 + mlly: 1.4.2 + pathe: 1.1.1 + dev: true + + /postcss-import@15.1.0(postcss@8.4.30): + resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} + engines: {node: '>=14.0.0'} + peerDependencies: + postcss: ^8.0.0 + dependencies: + postcss: 8.4.30 + postcss-value-parser: 4.2.0 + read-cache: 1.0.0 + resolve: 1.22.6 + dev: true + + /postcss-js@4.0.1(postcss@8.4.30): + resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} + engines: {node: ^12 || ^14 || >= 16} + peerDependencies: + postcss: ^8.4.21 + dependencies: + camelcase-css: 2.0.1 + postcss: 8.4.30 + dev: true + + /postcss-load-config@4.0.1(postcss@8.4.30): + resolution: {integrity: sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==} + engines: {node: '>= 14'} + peerDependencies: + postcss: '>=8.0.9' + ts-node: '>=9.0.0' + peerDependenciesMeta: + postcss: + optional: true + ts-node: + optional: true + dependencies: + lilconfig: 2.1.0 + postcss: 8.4.30 + yaml: 2.3.2 + dev: true + + /postcss-nested@6.0.1(postcss@8.4.30): + resolution: {integrity: sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.2.14 + dependencies: + postcss: 8.4.30 + postcss-selector-parser: 6.0.13 + dev: true + + /postcss-selector-parser@6.0.13: + resolution: {integrity: sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==} + engines: {node: '>=4'} + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + dev: true + + /postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + dev: true + + /postcss@8.4.30: + resolution: {integrity: sha512-7ZEao1g4kd68l97aWG/etQKPKq07us0ieSZ2TnFDk11i0ZfDW2AwKHYU8qv4MZKqN2fdBfg+7q0ES06UA73C1g==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.6 + picocolors: 1.0.0 + source-map-js: 1.0.2 + dev: true + + /process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + dev: true + + /queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + dev: true + + /queue-tick@1.0.1: + resolution: {integrity: sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==} + dev: true + + /read-cache@1.0.0: + resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} + dependencies: + pify: 2.3.0 + dev: true + + /readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + dev: true + + /readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + dev: true + + /readdir-glob@1.1.3: + resolution: {integrity: sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==} + dependencies: + minimatch: 5.1.6 + dev: true + + /readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + dependencies: + picomatch: 2.3.1 + dev: true + + /resolve@1.22.6: + resolution: {integrity: sha512-njhxM7mV12JfufShqGy3Rz8j11RPdLy4xi15UurGJeoHLfJpVXKdh3ueuOqbYUcDZnffr6X739JBo5LzyahEsw==} + hasBin: true + dependencies: + is-core-module: 2.13.0 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + dev: true + + /reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + dev: true + + /rfc4648@1.5.2: + resolution: {integrity: sha512-tLOizhR6YGovrEBLatX1sdcuhoSCXddw3mqNVAcKxGJ+J0hFeJ+SjeWCv5UPA/WU3YzWPPuCVYgXBKZUPGpKtg==} + dev: false + + /rollup@3.29.3: + resolution: {integrity: sha512-T7du6Hum8jOkSWetjRgbwpM6Sy0nECYrYRSmZjayFcOddtKJWU4d17AC3HNUk7HRuqy4p+G7aEZclSHytqUmEg==} + engines: {node: '>=14.18.0', npm: '>=8.0.0'} + hasBin: true + optionalDependencies: + fsevents: 2.3.3 + dev: true + + /run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + dependencies: + queue-microtask: 1.2.3 + dev: true + + /safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + dev: true + + /safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + dev: true + + /source-map-js@1.0.2: + resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} + engines: {node: '>=0.10.0'} + dev: true + + /streamx@2.15.1: + resolution: {integrity: sha512-fQMzy2O/Q47rgwErk/eGeLu/roaFWV0jVsogDmrszM9uIw8L5OA+t+V93MgYlufNptfjmYR1tOMWhei/Eh7TQA==} + dependencies: + fast-fifo: 1.3.2 + queue-tick: 1.0.1 + dev: true + + /string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + dependencies: + safe-buffer: 5.1.2 + dev: true + + /string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + dependencies: + safe-buffer: 5.2.1 + dev: true + + /sucrase@3.34.0: + resolution: {integrity: sha512-70/LQEZ07TEcxiU2dz51FKaE6hCTWC6vr7FOk3Gr0U60C3shtAN+H+BFr9XlYe5xqf3RA8nrc+VIwzCfnxuXJw==} + engines: {node: '>=8'} + hasBin: true + dependencies: + '@jridgewell/gen-mapping': 0.3.3 + commander: 4.1.1 + glob: 7.1.6 + lines-and-columns: 1.2.4 + mz: 2.7.0 + pirates: 4.0.6 + ts-interface-checker: 0.1.13 + dev: true + + /supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + dev: true + + /tailwindcss@3.3.3: + resolution: {integrity: sha512-A0KgSkef7eE4Mf+nKJ83i75TMyq8HqY3qmFIJSWy8bNt0v1lG7jUcpGpoTFxAwYcWOphcTBLPPJg+bDfhDf52w==} + engines: {node: '>=14.0.0'} + hasBin: true + dependencies: + '@alloc/quick-lru': 5.2.0 + arg: 5.0.2 + chokidar: 3.5.3 + didyoumean: 1.2.2 + dlv: 1.1.3 + fast-glob: 3.3.1 + glob-parent: 6.0.2 + is-glob: 4.0.3 + jiti: 1.20.0 + lilconfig: 2.1.0 + micromatch: 4.0.5 + normalize-path: 3.0.0 + object-hash: 3.0.0 + picocolors: 1.0.0 + postcss: 8.4.30 + postcss-import: 15.1.0(postcss@8.4.30) + postcss-js: 4.0.1(postcss@8.4.30) + postcss-load-config: 4.0.1(postcss@8.4.30) + postcss-nested: 6.0.1(postcss@8.4.30) + postcss-selector-parser: 6.0.13 + resolve: 1.22.6 + sucrase: 3.34.0 + transitivePeerDependencies: + - ts-node + dev: true + + /tar-stream@3.1.6: + resolution: {integrity: sha512-B/UyjYwPpMBv+PaFSWAmtYjwdrlEaZQEhMIBFNC5oEG8lpiW8XjcSdmEaClj28ArfKScKHs2nshz3k2le6crsg==} + dependencies: + b4a: 1.6.4 + fast-fifo: 1.3.2 + streamx: 2.15.1 + dev: true + + /thenify-all@1.6.0: + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} + dependencies: + thenify: 3.3.1 + dev: true + + /thenify@3.3.1: + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + dependencies: + any-promise: 1.3.0 + dev: true + + /to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + dependencies: + is-number: 7.0.0 + dev: true + + /ts-interface-checker@0.1.13: + resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + dev: true + + /typescript@5.2.2: + resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==} + engines: {node: '>=14.17'} + hasBin: true + dev: true + + /ufo@1.3.0: + resolution: {integrity: sha512-bRn3CsoojyNStCZe0BG0Mt4Nr/4KF+rhFlnNXybgqt5pXHNFRlqinSoQaTrGyzE4X8aHplSb+TorH+COin9Yxw==} + dev: true + + /update-browserslist-db@1.0.13(browserslist@4.21.11): + resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + dependencies: + browserslist: 4.21.11 + escalade: 3.1.1 + picocolors: 1.0.0 + dev: true + + /util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + dev: true + + /vite-node@0.34.5(@types/node@20.6.5): + resolution: {integrity: sha512-RNZ+DwbCvDoI5CbCSQSyRyzDTfFvFauvMs6Yq4ObJROKlIKuat1KgSX/Ako5rlDMfVCyMcpMRMTkJBxd6z8YRA==} + engines: {node: '>=v14.18.0'} + hasBin: true + dependencies: + cac: 6.7.14 + debug: 4.3.4 + mlly: 1.4.2 + pathe: 1.1.1 + picocolors: 1.0.0 + vite: 4.4.9(@types/node@20.6.5) + transitivePeerDependencies: + - '@types/node' + - less + - lightningcss + - sass + - stylus + - sugarss + - supports-color + - terser + dev: true + + /vite@4.4.9(@types/node@20.6.5): + resolution: {integrity: sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA==} + engines: {node: ^14.18.0 || >=16.0.0} + hasBin: true + peerDependencies: + '@types/node': '>= 14' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + '@types/node': 20.6.5 + esbuild: 0.18.20 + postcss: 8.4.30 + rollup: 3.29.3 + optionalDependencies: + fsevents: 2.3.3 + dev: true + + /wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + dev: true + + /yaml@2.3.2: + resolution: {integrity: sha512-N/lyzTPaJasoDmfV7YTrYCI0G/3ivm/9wdG0aHuheKowWQwGTsK0Eoiw6utmzAnI6pkJa0DUVygvp3spqqEKXg==} + engines: {node: '>= 14'} + dev: true + + /zip-stream@5.0.1: + resolution: {integrity: sha512-UfZ0oa0C8LI58wJ+moL46BDIMgCQbnsb+2PoiJYtonhBsMh2bq1eRBVkvjfVsqbEHd9/EgKPUuL9saSSsec8OA==} + engines: {node: '>= 12.0.0'} + dependencies: + archiver-utils: 4.0.1 + compress-commons: 5.0.1 + readable-stream: 3.6.2 + dev: true diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..8d36684 --- /dev/null +++ b/pom.xml @@ -0,0 +1,71 @@ + + + + 4.0.0 + + org.keywind.theme + keywind + 1.0-SNAPSHOT + + keywind + https://keywind.org + + + UTF-8 + 20 + 20 + + + + + org.freemarker + freemarker + 2.3.32 + + + org.jsoup + jsoup + 1.16.1 + + + org.junit.jupiter + junit-jupiter-engine + 5.10.0 + test + + + org.keycloak + keycloak-server-spi-private + 22.0.1 + + + org.keycloak + keycloak-services + 22.0.1 + + + org.keycloak + keycloak-themes + 22.0.1 + + + + + + + maven-clean-plugin + 3.3.1 + + + auto-clean + test + + clean + + + + + + + diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 0000000..12a703d --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/preview.png b/preview.png new file mode 100644 index 0000000..981558a Binary files /dev/null and b/preview.png differ diff --git a/scripts/build.ts b/scripts/build.ts new file mode 100644 index 0000000..ecf4b68 --- /dev/null +++ b/scripts/build.ts @@ -0,0 +1,25 @@ +import archiver from 'archiver'; +import { createWriteStream, existsSync, mkdirSync } from 'fs'; + +import { name } from '../package.json'; + +const dir = 'out'; +const file = `${name}.jar`; +const path = `${dir}/${file}`; + +!existsSync(dir) && mkdirSync(dir); + +const output = createWriteStream(`${__dirname}/../${path}`); + +const archive = archiver('zip'); + +archive.on('error', (error) => { + throw error; +}); + +archive.pipe(output); + +archive.directory('META-INF', 'META-INF'); +archive.directory('theme', 'theme'); + +archive.finalize(); diff --git a/src/data/recoveryCodes.ts b/src/data/recoveryCodes.ts new file mode 100644 index 0000000..b329381 --- /dev/null +++ b/src/data/recoveryCodes.ts @@ -0,0 +1,59 @@ +import Alpine from 'alpinejs'; + +type DataType = { + $refs: RefsType; + $store: StoreType; +}; + +type RefsType = { + codeList: HTMLUListElement; +}; + +type StoreType = { + recoveryCodes: { + downloadFileDate: string; + downloadFileDescription: string; + downloadFileHeader: string; + downloadFileName: string; + }; +}; + +document.addEventListener('alpine:init', () => { + Alpine.data('recoveryCodes', function (this: DataType) { + const { codeList } = this.$refs; + const { downloadFileDate, downloadFileDescription, downloadFileHeader, downloadFileName } = + this.$store.recoveryCodes; + + const date = new Date().toLocaleString(navigator.language); + + const codeElements = codeList.getElementsByTagName('li'); + const codes = Array.from(codeElements) + .map((codeElement) => codeElement.innerText) + .join('\n'); + + return { + copy: () => navigator.clipboard.writeText(codes), + download: () => { + const element = document.createElement('a'); + const text = `${downloadFileHeader}\n\n${codes}\n\n${downloadFileDescription}\n\n${downloadFileDate} ${date}`; + + element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text)); + element.setAttribute('download', `${downloadFileName}.txt`); + element.click(); + }, + print: () => { + const codeListHTML = codeList.innerHTML; + const styles = 'div { font-family: monospace; list-style-type: none }'; + const content = `${downloadFileName}

${downloadFileHeader}

${codeListHTML}

${downloadFileDescription}

${downloadFileDate} ${date}

`; + + const printWindow = window.open(); + + if (printWindow) { + printWindow.document.write(content); + printWindow.print(); + printWindow.close(); + } + }, + }; + }); +}); diff --git a/src/data/webAuthnAuthenticate.ts b/src/data/webAuthnAuthenticate.ts new file mode 100644 index 0000000..8aab5bb --- /dev/null +++ b/src/data/webAuthnAuthenticate.ts @@ -0,0 +1,147 @@ +import Alpine from 'alpinejs'; +import { base64url } from 'rfc4648'; + +type DataType = { + $refs: RefsType; + $store: StoreType; +}; + +type RefsType = { + authenticatorDataInput: HTMLInputElement; + authnSelectForm?: HTMLFormElement; + clientDataJSONInput: HTMLInputElement; + credentialIdInput: HTMLInputElement; + errorInput: HTMLInputElement; + signatureInput: HTMLInputElement; + userHandleInput: HTMLInputElement; + webAuthnForm: HTMLFormElement; +}; + +type StoreType = { + webAuthnAuthenticate: { + challenge: string; + createTimeout: string; + isUserIdentified: string; + rpId: string; + unsupportedBrowserText: string; + userVerification: UserVerificationRequirement | 'not specified'; + }; +}; + +document.addEventListener('alpine:init', () => { + Alpine.data('webAuthnAuthenticate', function (this: DataType) { + const { + authenticatorDataInput, + authnSelectForm, + clientDataJSONInput, + credentialIdInput, + errorInput, + signatureInput, + userHandleInput, + webAuthnForm, + } = this.$refs; + const { + challenge, + createTimeout, + isUserIdentified, + rpId, + unsupportedBrowserText, + userVerification, + } = this.$store.webAuthnAuthenticate; + + const doAuthenticate = (allowCredentials: PublicKeyCredentialDescriptor[]) => { + if (!window.PublicKeyCredential) { + errorInput.value = unsupportedBrowserText; + webAuthnForm.submit(); + + return; + } + + const publicKey: PublicKeyCredentialRequestOptions = { + challenge: base64url.parse(challenge, { loose: true }), + rpId: rpId, + }; + + if (allowCredentials.length) { + publicKey.allowCredentials = allowCredentials; + } + + if (parseInt(createTimeout) !== 0) publicKey.timeout = parseInt(createTimeout) * 1000; + + if (userVerification !== 'not specified') publicKey.userVerification = userVerification; + + navigator.credentials + .get({ publicKey }) + .then((result) => { + if ( + result instanceof PublicKeyCredential && + result.response instanceof AuthenticatorAssertionResponse + ) { + window.result = result; + + authenticatorDataInput.value = base64url.stringify( + new Uint8Array(result.response.authenticatorData), + { pad: false } + ); + + clientDataJSONInput.value = base64url.stringify( + new Uint8Array(result.response.clientDataJSON), + { pad: false } + ); + + signatureInput.value = base64url.stringify(new Uint8Array(result.response.signature), { + pad: false, + }); + + credentialIdInput.value = result.id; + + if (result.response.userHandle) { + userHandleInput.value = base64url.stringify( + new Uint8Array(result.response.userHandle), + { pad: false } + ); + } + + webAuthnForm.submit(); + } + }) + .catch((error) => { + errorInput.value = error; + webAuthnForm.submit(); + }); + }; + + const checkAllowCredentials = () => { + const allowCredentials: PublicKeyCredentialDescriptor[] = []; + + if (authnSelectForm) { + const authnSelectFormElements = Array.from(authnSelectForm.elements); + + if (authnSelectFormElements.length) { + authnSelectFormElements.forEach((element) => { + if (element instanceof HTMLInputElement) { + allowCredentials.push({ + id: base64url.parse(element.value, { loose: true }), + type: 'public-key', + }); + } + }); + } + } + + doAuthenticate(allowCredentials); + }; + + return { + webAuthnAuthenticate: () => { + if (!isUserIdentified) { + doAuthenticate([]); + + return; + } + + checkAllowCredentials(); + }, + }; + }); +}); diff --git a/src/data/webAuthnRegister.ts b/src/data/webAuthnRegister.ts new file mode 100644 index 0000000..23744df --- /dev/null +++ b/src/data/webAuthnRegister.ts @@ -0,0 +1,225 @@ +import Alpine from 'alpinejs'; +import { base64url } from 'rfc4648'; + +type DataType = { + $refs: RefsType; + $store: StoreType; +}; + +type RefsType = { + attestationObjectInput: HTMLInputElement; + authenticatorLabelInput: HTMLInputElement; + clientDataJSONInput: HTMLInputElement; + errorInput: HTMLInputElement; + publicKeyCredentialIdInput: HTMLInputElement; + registerForm: HTMLFormElement; + transportsInput: HTMLInputElement; +}; + +type StoreType = { + webAuthnRegister: { + attestationConveyancePreference: AttestationConveyancePreference | 'not specified'; + authenticatorAttachment: AuthenticatorAttachment | 'not specified'; + challenge: string; + createTimeout: string; + excludeCredentialIds: string; + requireResidentKey: string; + rpEntityName: string; + rpId: string; + signatureAlgorithms: string; + unsupportedBrowserText: string; + userId: string; + userVerificationRequirement: UserVerificationRequirement | 'not specified'; + username: string; + }; +}; + +document.addEventListener('alpine:init', () => { + Alpine.data('webAuthnRegister', function (this: DataType) { + const { + attestationObjectInput, + authenticatorLabelInput, + clientDataJSONInput, + errorInput, + publicKeyCredentialIdInput, + registerForm, + transportsInput, + } = this.$refs; + const { + attestationConveyancePreference, + authenticatorAttachment, + challenge, + createTimeout, + excludeCredentialIds, + requireResidentKey, + rpEntityName, + rpId, + signatureAlgorithms, + unsupportedBrowserText, + userId, + userVerificationRequirement, + username, + } = this.$store.webAuthnRegister; + + const getPubKeyCredParams = (signatureAlgorithms: string) => { + const pubKeyCredParams: PublicKeyCredentialParameters[] = []; + + if (signatureAlgorithms === '') { + pubKeyCredParams.push({ alg: -7, type: 'public-key' }); + + return pubKeyCredParams; + } + + const signatureAlgorithmsList = signatureAlgorithms.split(','); + + signatureAlgorithmsList.forEach((value) => { + pubKeyCredParams.push({ + alg: parseInt(value), + type: 'public-key', + }); + }); + + return pubKeyCredParams; + }; + + const getExcludeCredentials = (excludeCredentialIds: string) => { + const excludeCredentials: PublicKeyCredentialDescriptor[] = []; + + if (excludeCredentialIds === '') return excludeCredentials; + + const excludeCredentialIdsList = excludeCredentialIds.split(','); + + excludeCredentialIdsList.forEach((value) => { + excludeCredentials.push({ + id: base64url.parse(value, { loose: true }), + type: 'public-key', + }); + }); + + return excludeCredentials; + }; + + const getTransportsAsString = (transportsList: string | string[]) => { + if (transportsList === '' || transportsList.constructor !== Array) return ''; + + let transportsString = ''; + + transportsList.forEach((value) => { + transportsString += value + ','; + }); + + return transportsString.slice(0, -1); + }; + + return { + registerSecurityKey: () => { + if (!window.PublicKeyCredential) { + errorInput.value = unsupportedBrowserText; + registerForm.submit(); + + return; + } + + const publicKey: PublicKeyCredentialCreationOptions = { + challenge: base64url.parse(challenge, { loose: true }), + pubKeyCredParams: getPubKeyCredParams(signatureAlgorithms), + rp: { + id: rpId, + name: rpEntityName, + }, + user: { + displayName: username, + id: base64url.parse(userId, { loose: true }), + name: username, + }, + }; + + if (attestationConveyancePreference !== 'not specified') + publicKey.attestation = attestationConveyancePreference; + + const authenticatorSelection: AuthenticatorSelectionCriteria = {}; + let isAuthenticatorSelectionSpecified = false; + + if (authenticatorAttachment !== 'not specified') { + authenticatorSelection.authenticatorAttachment = authenticatorAttachment; + isAuthenticatorSelectionSpecified = true; + } + + if (requireResidentKey !== 'not specified') { + if (requireResidentKey === 'Yes') authenticatorSelection.requireResidentKey = true; + else authenticatorSelection.requireResidentKey = false; + isAuthenticatorSelectionSpecified = true; + } + + if (userVerificationRequirement !== 'not specified') { + authenticatorSelection.userVerification = userVerificationRequirement; + isAuthenticatorSelectionSpecified = true; + } + + if (isAuthenticatorSelectionSpecified) + publicKey.authenticatorSelection = authenticatorSelection; + + const excludeCredentials = getExcludeCredentials(excludeCredentialIds); + if (excludeCredentials.length > 0) publicKey.excludeCredentials = excludeCredentials; + + if (parseInt(createTimeout) !== 0) publicKey.timeout = parseInt(createTimeout) * 1000; + + navigator.credentials + .create({ publicKey }) + .then((result) => { + if ( + result instanceof PublicKeyCredential && + result.response instanceof AuthenticatorAttestationResponse + ) { + const { getTransports } = result.response; + + window.result = result; + + const publicKeyCredentialId = result.rawId; + + attestationObjectInput.value = base64url.stringify( + new Uint8Array(result.response.attestationObject), + { pad: false } + ); + + clientDataJSONInput.value = base64url.stringify( + new Uint8Array(result.response.clientDataJSON), + { pad: false } + ); + + publicKeyCredentialIdInput.value = base64url.stringify( + new Uint8Array(publicKeyCredentialId), + { pad: false } + ); + + if (typeof getTransports === 'function') { + const transports = getTransports(); + + if (transports) { + transportsInput.value = getTransportsAsString(transports); + } + } else { + console.log( + 'Your browser is not able to recognize supported transport media for the authenticator.' + ); + } + + const initLabel = 'WebAuthn Authenticator (Default Label)'; + let labelResult = window.prompt( + "Please input your registered authenticator's label", + initLabel + ); + if (labelResult === null) labelResult = initLabel; + + authenticatorLabelInput.value = labelResult; + registerForm.submit(); + } + }) + .catch(function (error) { + error.value = error; + registerForm.submit(); + }); + }, + }; + }); +}); diff --git a/src/global.d.ts b/src/global.d.ts new file mode 100644 index 0000000..a858d3e --- /dev/null +++ b/src/global.d.ts @@ -0,0 +1,8 @@ +import type { Alpine } from 'alpinejs'; + +declare global { + interface Window { + Alpine: Alpine; + result?: PublicKeyCredential; + } +} diff --git a/src/index.css b/src/index.css new file mode 100644 index 0000000..0624862 --- /dev/null +++ b/src/index.css @@ -0,0 +1,33 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer components { + /* Alpine.js + ======================================================================== */ + + [x-cloak] { + @apply hidden !important; + } + + /* Separate + ======================================================================== */ + + .separate { + @apply flex items-center text-center; + } + + .separate::after, + .separate::before { + content: ''; + @apply border-b border-secondary-200 flex-1; + } + + .separate:not(:empty)::after { + @apply ml-2; + } + + .separate:not(:empty)::before { + @apply mr-2; + } +} diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..2133481 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,7 @@ +import './index.css'; + +import Alpine from 'alpinejs'; + +window.Alpine = Alpine; + +Alpine.start(); diff --git a/src/test/java/org/keywind/theme/AuthenticationUtil.java b/src/test/java/org/keywind/theme/AuthenticationUtil.java new file mode 100644 index 0000000..197168d --- /dev/null +++ b/src/test/java/org/keywind/theme/AuthenticationUtil.java @@ -0,0 +1,13 @@ +package org.keywind.theme; + +import java.util.List; + +import freemarker.template.TemplateMethodModelEx; +import freemarker.template.TemplateModelException; + +public class AuthenticationUtil implements TemplateMethodModelEx { + @Override + public Object exec(List arguments) throws TemplateModelException { + return true; + } +} diff --git a/src/test/java/org/keywind/theme/LoginDataModel.java b/src/test/java/org/keywind/theme/LoginDataModel.java new file mode 100644 index 0000000..fd973b4 --- /dev/null +++ b/src/test/java/org/keywind/theme/LoginDataModel.java @@ -0,0 +1,265 @@ +package org.keywind.theme; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class LoginDataModel { + public static Map createDataModel() { + Map dataModel = new HashMap<>(); + dataModel.put("auth", createAuthModel()); + dataModel.put("client", createClientModel()); + dataModel.put("locale", createLocaleModel()); + dataModel.put("login", createLoginModel()); + dataModel.put("logoutConfirm", createLogoutConfirmModel()); + dataModel.put("message", createMessageModel()); + dataModel.put("oauth", createOAuthModel()); + dataModel.put("otpLogin", createOtpLoginModel()); + dataModel.put("properties", createPropertiesModel()); + dataModel.put("realm", createRealmModel()); + dataModel.put("recoveryAuthnCodesConfigBean", createRecoveryAuthnCodesConfigBeanModel()); + dataModel.put("recoveryAuthnCodesInputBean", createRecoveryAuthnCodesInputBeanModel()); + dataModel.put("social", createSocialModel()); + dataModel.put("totp", createTotpModel()); + dataModel.put("url", createUrlModel()); + dataModel.put("user", createUserModel()); + dataModel.put("username", "Username"); + dataModel.put("x509", createX509Model()); + dataModel.putAll(createWebAuthnModel()); + + return dataModel; + } + + private static Map createAuthModel() { + Map securityKey = new HashMap<>(); + securityKey.put("authExecId", "authExecId"); + securityKey.put("displayName", "Security Key"); + securityKey.put("helpText", "Use your security key to sign in."); + + List> authenticationSelections = new ArrayList<>(); + authenticationSelections.add(securityKey); + + Map auth = new HashMap<>(); + auth.put("attemptedUsername", "Attempted Username"); + auth.put("authenticationSelections", authenticationSelections); + auth.put("showResetCredentials", new AuthenticationUtil()); + auth.put("showTryAnotherWayLink", new AuthenticationUtil()); + auth.put("showUsername", new AuthenticationUtil()); + + return auth; + } + + private static Map createClientModel() { + Map attributes = new HashMap<>(); + + Map client = new HashMap<>(); + client.put("attributes", attributes); + + return client; + } + + private static Map createLocaleModel() { + Map de = new HashMap<>(); + de.put("label", "Deutsch"); + de.put("url", "url"); + + Map en = new HashMap<>(); + en.put("label", "English"); + en.put("url", "url"); + + Map fr = new HashMap<>(); + fr.put("label", "Français"); + fr.put("url", "url"); + + List> supported = new ArrayList<>(); + supported.add(de); + supported.add(en); + supported.add(fr); + + Map locale = new HashMap<>(); + locale.put("current", "English"); + locale.put("currentLanguageTag", "en"); + locale.put("supported", supported); + + return locale; + } + + private static Map createLoginModel() { + Map login = new HashMap<>(); + login.put("rememberMe", true); + + return login; + } + + private static Map createLogoutConfirmModel() { + Map logoutConfirm = new HashMap<>(); + logoutConfirm.put("code", "code"); + logoutConfirm.put("skipLink", false); + + return logoutConfirm; + } + + private static Map createMessageModel() { + Map message = new HashMap<>(); + message.put("summary", "Example of an error message"); + message.put("type", "error"); + + return message; + } + + private static Map createOAuthModel() { + List> clientScopesRequested = new ArrayList<>(); + + Map oauth = new HashMap<>(); + oauth.put("clientScopesRequested", clientScopesRequested); + oauth.put("code", "code"); + + return oauth; + } + + private static Map createOtpLoginModel() { + List> userOtpCredentials = new ArrayList<>(); + + Map otpLogin = new HashMap<>(); + otpLogin.put("selectedCredentialId", 1); + otpLogin.put("userOtpCredentials", userOtpCredentials); + + return otpLogin; + } + + private static Map createPropertiesModel() { + Map properties = new HashMap<>(); + properties.put("scripts", "src/index.ts"); + properties.put("styles", "src/index.css"); + + return properties; + } + + private static Map createRealmModel() { + Map realm = new HashMap<>(); + realm.put("displayNameHtml", "Keywind"); + realm.put("internationalizationEnabled", true); + realm.put("loginWithEmailAllowed", true); + realm.put("password", true); + realm.put("registrationAllowed", true); + realm.put("registrationEmailAsUsername", true); + realm.put("rememberMe", true); + realm.put("resetPasswordAllowed", true); + + return realm; + } + + public static Map createRecoveryAuthnCodesConfigBeanModel() { + List generatedRecoveryAuthnCodesList = new ArrayList<>(); + generatedRecoveryAuthnCodesList.add("000000000000"); + generatedRecoveryAuthnCodesList.add("111111111111"); + + Map recoveryAuthnCodesConfigBean = new HashMap<>(); + recoveryAuthnCodesConfigBean.put("generatedAt", "generatedAt"); + recoveryAuthnCodesConfigBean.put("generatedRecoveryAuthnCodesAsString", "generatedRecoveryAuthnCodesAsString"); + recoveryAuthnCodesConfigBean.put("generatedRecoveryAuthnCodesList", generatedRecoveryAuthnCodesList); + + return recoveryAuthnCodesConfigBean; + } + + public static Map createRecoveryAuthnCodesInputBeanModel() { + Map recoveryAuthnCodesInputBean = new HashMap<>(); + recoveryAuthnCodesInputBean.put("codeNumber", "codeNumber"); + + return recoveryAuthnCodesInputBean; + } + + private static Map createSocialModel() { + Map facebook = new HashMap<>(); + facebook.put("alias", "facebook"); + facebook.put("displayName", "Facebook"); + facebook.put("loginUrl", "loginUrl"); + + Map github = new HashMap<>(); + github.put("alias", "github"); + github.put("displayName", "GitHub"); + github.put("loginUrl", "loginUrl"); + + Map google = new HashMap<>(); + google.put("alias", "google"); + google.put("displayName", "Google"); + google.put("loginUrl", "loginUrl"); + + List> providers = new ArrayList<>(); + providers.add(facebook); + providers.add(github); + providers.add(google); + + Map social = new HashMap<>(); + social.put("providers", providers); + + return social; + } + + private static Map createTotpModel() { + Map otpCredential = new HashMap<>(); + + List> otpCredentials = new ArrayList<>(); + otpCredentials.add(otpCredential); + + List supportedApplications = new ArrayList<>(); + supportedApplications.add("totpAppFreeOTPName"); + + Map totp = new HashMap<>(); + totp.put("manualUrl", "manualUrl"); + totp.put("otpCredentials", otpCredentials); + totp.put("supportedApplications", supportedApplications); + totp.put("totpSecret", "totpSecret"); + totp.put("totpSecretQrCode", + "iVBORw0KGgoAAAANSUhEUgAAASwAAAEsCAYAAAB5fY51AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABmJLR0QAAAAAAAD5Q7t/AAAACXBIWXMAAABgAAAAYADwa0LPAAAF70lEQVR42u3d0W3DOBQAwfhwPcj9V2e4CV8D96EgBPVWnilAphRlwY8H6vH5fD4/AAH/XL0AgLMEC8gQLCBDsIAMwQIyBAvIECwgQ7CADMECMgQLyBAsIEOwgAzBAjIEC8gQLCBDsIAMwQIyBAvIECwgQ7CADMECMgQLyBAsIEOwgAzBAjIEC8gQLCBDsIAMwQIyBAvIECwgQ7CADMECMgQLyBAsIEOwgIx/r17A/zmO4+f9fl+9jEt8Pp8l13k8HrnfOnOdM1bdl/dwHjssIEOwgAzBAjIEC8gQLCBDsIAMwQIyBAvIGDk4esbr9fo5juPqZfzKqkHEaUOhO6+zagB11fP55vfwCnZYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQkR0cPWPVsOIZ005o3Hnvq+w8lXSnb34PV7PDAjIEC8gQLCBDsIAMwQIyBAvIECwgQ7CAjFsPjt7VtAHLaaeb3n148pvZYQEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIbB0aCdn2Ivrrn4fDjHDgvIECwgQ7CADMECMgQLyBAsIEOwgAzBAjJuPTj6zcOBq4YnV13nm4dCp62nzA4LyBAsIEOwgAzBAjIEC8gQLCBDsIAMwQIysoOjz+fz6iWMtnMo9K7XOcN7uJcdFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZDw+jkPMOTMYeca0U0CLp4mylx0WkCFYQIZgARmCBWQIFpAhWECGYAEZggVkjDxxdOdg5M5hxbsOfBaf86o1r7JzPeXhWzssIEOwgAzBAjIEC8gQLCBDsIAMwQIyBAvIGDk4esaq4bedQ4/ThhXPcAro3+992iB0mR0WkCFYQIZgARmCBWQIFpAhWECGYAEZggVkZD9Vbxhvj2mvh9NE//5bZXZYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQkR0cPXVzwWG84pqn3dddh0tXKZ8ia4cFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZ2U/VTxtE/GbThl13XmfnEKb30A4LCBEsIEOwgAzBAjIEC8gQLCBDsIAMwQIysoOjd/3896o1TxsynDagu3M9O4dLpz3n1eywgAzBAjIEC8gQLCBDsIAMwQIyBAvIECwgY+Tg6KpBu50nPU4b2CsOu057hjvXXHzHrmCHBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGY/PzgnDlQsfNmg3bUh1528Vn+FO0X+xkeywgAzBAjIEC8gQLCBDsIAMwQIyBAvIECwgI3vi6CrTPlm+8zqr7n3aqZvTTLuvaev5DTssIEOwgAzBAjIEC8gQLCBDsIAMwQIyBAvIyJ44+s12DnMWBxrvel/YYQEhggVkCBaQIVhAhmABGYIFZAgWkCFYQMbIE0eP4/h5v99XL+MSd/1c+86h0Glr3vlbO0+jvYIdFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZIwcHD3j9Xr9HMdx9TJ+ZdVA7DeflvnNw5PThoGvYIcFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZ2cHRM3YO2hWHDIunZd71RNZvHoj9DTssIEOwgAzBAjIEC8gQLCBDsIAMwQIyBAvIuPXg6F1N+6z5zjWvUhxALQ98rmKHBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGQZHg+46QLjzRM1pA5/T7n3qO2aHBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGbceHJ06/PZX04YMV5n29yquZ+ff6wp2WECGYAEZggVkCBaQIVhAhmABGYIFZAgWkJEdHH0+n1cvYbRpp1yeUT4J86/3xTl2WECGYAEZggVkCBaQIVhAhmABGYIFZAgWkPH4FCfxgK9khwVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVk/AeoXLE8BnySdAAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAyMy0wOS0yNVQyMDoxMjo1OSswMDowMLyvm1kAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMjMtMDktMjVUMjA6MTI6NTkrMDA6MDDN8iPlAAAAAElFTkSuQmCC"); + + return totp; + } + + private static Map createUrlModel() { + Map url = new HashMap<>(); + url.put("loginAction", "loginAction"); + url.put("loginResetCredentialsUrl", "loginResetCredentialsUrl"); + url.put("loginRestartFlowUrl", "loginRestartFlowUrl"); + url.put("loginUrl", "loginUrl"); + url.put("logoutConfirmAction", "logoutConfirmAction"); + url.put("oauthAction", "oauthAction"); + url.put("registrationAction", "registrationAction"); + url.put("registrationUrl", "registrationUrl"); + url.put("resourcesPath", "http://localhost:5173"); + + return url; + } + + private static Map createUserModel() { + Map user = new HashMap<>(); + user.put("editUsernameAllowed", true); + + return user; + } + + private static Map createWebAuthnModel() { + Map webAuthn = new HashMap<>(); + webAuthn.put("challenge", "challenge"); + webAuthn.put("execution", "execution"); + webAuthn.put("createTimeout", "60000"); + webAuthn.put("isUserIdentified", "true"); + webAuthn.put("rpId", "https://webauthn.me"); + webAuthn.put("userVerification", "preferred"); + + return webAuthn; + } + + private static Map createX509Model() { + Map formData = new HashMap<>(); + formData.put("isUserEnabled", "true"); + formData.put("subjectDN", "CN=User, C=US, O=Keywind"); + formData.put("username", "Username"); + + Map x509 = new HashMap<>(); + x509.put("formData", formData); + + return x509; + } +} diff --git a/src/test/java/org/keywind/theme/LoginThemeTest.java b/src/test/java/org/keywind/theme/LoginThemeTest.java new file mode 100644 index 0000000..61237a3 --- /dev/null +++ b/src/test/java/org/keywind/theme/LoginThemeTest.java @@ -0,0 +1,113 @@ +package org.keywind.theme; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.StringWriter; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import java.util.ResourceBundle; + +import freemarker.core.HTMLOutputFormat; +import freemarker.template.Configuration; +import freemarker.template.Template; +import freemarker.template.TemplateException; +import freemarker.template.TemplateModelException; +import freemarker.template.TemplateNotFoundException; + +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.junit.jupiter.api.Test; +import org.keycloak.forms.login.LoginFormsPages; +import org.keycloak.forms.login.freemarker.Templates; +import org.keycloak.theme.KeycloakSanitizerMethod; +import org.keycloak.theme.beans.MessageFormatterMethod; +import org.keycloak.theme.beans.MessagesPerFieldBean; + +public class LoginThemeTest { + private static final String LANGUAGE = "English"; + private static final String MESSAGE_PATH = "theme/base/login/messages/messages"; + private static final String OUTPUT_PATH = "html/login"; + private static final String THEME_PATH = "theme/keywind/login"; + + @Test + public void shouldTestTemplates() throws IOException, TemplateException { + Configuration configuration = createFreeMarkerConfiguration(); + + for (String templateName : getTemplateNames()) { + try { + Template template = configuration.getTemplate(templateName); + String renderedTemplate = renderTemplate(template); + Document document = formatHtml(renderedTemplate); + + saveHtmlToFile(templateName, document); + } catch (TemplateNotFoundException e) { + System.out.println("Template not found: " + templateName); + } + } + } + + private Configuration createFreeMarkerConfiguration() throws IOException, TemplateModelException { + Configuration configuration = new Configuration(Configuration.VERSION_2_3_32); + configuration.setDirectoryForTemplateLoading(new File(THEME_PATH)); + configuration.setOutputFormat(HTMLOutputFormat.INSTANCE); + + Locale locale = Locale.of(LANGUAGE); + Properties messages = loadMessages(locale); + + configuration.setSharedVariable("kcSanitize", new KeycloakSanitizerMethod()); + configuration.setSharedVariable("messagesPerField", new MessagesPerFieldBean()); + configuration.setSharedVariable("msg", new MessageFormatterMethod(locale, messages)); + + return configuration; + } + + private Properties loadMessages(Locale locale) { + ResourceBundle resourceBundle = ResourceBundle.getBundle(MESSAGE_PATH, locale); + Properties properties = new Properties(); + + Enumeration keys = resourceBundle.getKeys(); + while (keys.hasMoreElements()) { + String key = keys.nextElement(); + String value = resourceBundle.getString(key); + + properties.setProperty(key, value); + } + + return properties; + } + + private String[] getTemplateNames() { + return Arrays.stream(LoginFormsPages.values()) + .map(Templates::getTemplate) + .toArray(String[]::new); + } + + private String renderTemplate(Template template) throws IOException, TemplateException { + Map dataModel = LoginDataModel.createDataModel(); + + try (StringWriter writer = new StringWriter()) { + template.process(dataModel, writer); + + return writer.toString(); + } + } + + private Document formatHtml(String html) { + Document document = Jsoup.parse(html); + document.outputSettings().indentAmount(2); + + return document; + } + + private void saveHtmlToFile(String templateName, Document document) throws IOException { + File outputFile = new File(OUTPUT_PATH, templateName.replace(".ftl", ".html")); + + try (FileWriter fileWriter = new FileWriter(outputFile)) { + fileWriter.write(document.outerHtml()); + } + } +} diff --git a/tailwind.config.ts b/tailwind.config.ts new file mode 100644 index 0000000..cb7d1a0 --- /dev/null +++ b/tailwind.config.ts @@ -0,0 +1,37 @@ +import type { Config } from 'tailwindcss'; +import colors from 'tailwindcss/colors'; + +export default { + content: ['./theme/**/*.ftl'], + experimental: { + optimizeUniversalDefaults: true, + }, + plugins: [require('@tailwindcss/forms')], + theme: { + extend: { + colors: { + primary: colors.blue, + secondary: colors.gray, + + provider: { + apple: '#000000', + bitbucket: '#0052CC', + discord: '#5865F2', + facebook: '#1877F2', + github: '#181717', + gitlab: '#FC6D26', + google: '#4285F4', + instagram: '#E4405F', + linkedin: '#0A66C2', + microsoft: '#5E5E5E', + oidc: '#F78C40', + openshift: '#EE0000', + paypal: '#00457C', + slack: '#4A154B', + stackoverflow: '#F58025', + twitter: '#1DA1F2', + }, + }, + }, + }, +} satisfies Config; diff --git a/theme/keywind/login/assets/icons/arrow-top-right-on-square.ftl b/theme/keywind/login/assets/icons/arrow-top-right-on-square.ftl new file mode 100644 index 0000000..81c4bf8 --- /dev/null +++ b/theme/keywind/login/assets/icons/arrow-top-right-on-square.ftl @@ -0,0 +1,7 @@ +<#-- https://github.com/tailwindlabs/heroicons/blob/master/src/20/solid/arrow-top-right-on-square.svg --> +<#macro kw> + + + + + diff --git a/theme/keywind/login/assets/icons/chevron-down.ftl b/theme/keywind/login/assets/icons/chevron-down.ftl new file mode 100644 index 0000000..673ef11 --- /dev/null +++ b/theme/keywind/login/assets/icons/chevron-down.ftl @@ -0,0 +1,6 @@ +<#-- https://github.com/tailwindlabs/heroicons/blob/master/src/20/solid/chevron-down.svg --> +<#macro kw> + + + + diff --git a/theme/keywind/login/assets/icons/eye-slash.ftl b/theme/keywind/login/assets/icons/eye-slash.ftl new file mode 100644 index 0000000..74a9f78 --- /dev/null +++ b/theme/keywind/login/assets/icons/eye-slash.ftl @@ -0,0 +1,7 @@ +<#-- https://github.com/tailwindlabs/heroicons/blob/master/src/20/solid/eye.svg --> +<#macro kw> + + + + + diff --git a/theme/keywind/login/assets/icons/eye.ftl b/theme/keywind/login/assets/icons/eye.ftl new file mode 100644 index 0000000..0bd4d06 --- /dev/null +++ b/theme/keywind/login/assets/icons/eye.ftl @@ -0,0 +1,7 @@ +<#-- https://github.com/tailwindlabs/heroicons/blob/master/src/20/solid/eye.svg --> +<#macro kw> + + + + + diff --git a/theme/keywind/login/assets/icons/header-logo.png b/theme/keywind/login/assets/icons/header-logo.png new file mode 100644 index 0000000..aba9071 Binary files /dev/null and b/theme/keywind/login/assets/icons/header-logo.png differ diff --git a/theme/keywind/login/assets/providers/apple.ftl b/theme/keywind/login/assets/providers/apple.ftl new file mode 100644 index 0000000..2444359 --- /dev/null +++ b/theme/keywind/login/assets/providers/apple.ftl @@ -0,0 +1,7 @@ +<#-- https://apple.com --> +<#macro kw name="Apple"> + + ${name} + + + diff --git a/theme/keywind/login/assets/providers/bitbucket.ftl b/theme/keywind/login/assets/providers/bitbucket.ftl new file mode 100644 index 0000000..068bc73 --- /dev/null +++ b/theme/keywind/login/assets/providers/bitbucket.ftl @@ -0,0 +1,14 @@ +<#-- https://atlassian.design/resources/logo-library --> +<#macro kw name="Bitbucket"> + + ${name} + + + + + + + + + + diff --git a/theme/keywind/login/assets/providers/discord.ftl b/theme/keywind/login/assets/providers/discord.ftl new file mode 100644 index 0000000..8ebecaa --- /dev/null +++ b/theme/keywind/login/assets/providers/discord.ftl @@ -0,0 +1,7 @@ +<#-- https://discord.com/branding --> +<#macro kw name="Discord"> + + ${name} + + + diff --git a/theme/keywind/login/assets/providers/facebook.ftl b/theme/keywind/login/assets/providers/facebook.ftl new file mode 100644 index 0000000..bc692e7 --- /dev/null +++ b/theme/keywind/login/assets/providers/facebook.ftl @@ -0,0 +1,8 @@ +<#-- https://www.facebook.com/brand/resources/facebookapp/logo --> +<#macro kw name="Facebook"> + + ${name} + + + + diff --git a/theme/keywind/login/assets/providers/github.ftl b/theme/keywind/login/assets/providers/github.ftl new file mode 100644 index 0000000..9523103 --- /dev/null +++ b/theme/keywind/login/assets/providers/github.ftl @@ -0,0 +1,7 @@ +<#-- https://github.com/logos --> +<#macro kw name="GitHub"> + + ${name} + + + diff --git a/theme/keywind/login/assets/providers/gitlab.ftl b/theme/keywind/login/assets/providers/gitlab.ftl new file mode 100644 index 0000000..4acfc13 --- /dev/null +++ b/theme/keywind/login/assets/providers/gitlab.ftl @@ -0,0 +1,10 @@ +<#-- https://about.gitlab.com/press/press-kit --> +<#macro kw name="GitLab"> + + ${name} + + + + + + diff --git a/theme/keywind/login/assets/providers/google.ftl b/theme/keywind/login/assets/providers/google.ftl new file mode 100644 index 0000000..b536cdb --- /dev/null +++ b/theme/keywind/login/assets/providers/google.ftl @@ -0,0 +1,10 @@ +<#-- https://developers.google.com/identity/branding-guidelines --> +<#macro kw name="Google"> + + ${name} + + + + + + diff --git a/theme/keywind/login/assets/providers/instagram.ftl b/theme/keywind/login/assets/providers/instagram.ftl new file mode 100644 index 0000000..c4996d8 --- /dev/null +++ b/theme/keywind/login/assets/providers/instagram.ftl @@ -0,0 +1,35 @@ +<#-- https://www.facebook.com/brand/resources/instagram/instagram-brand --> +<#macro kw name="Instagram"> + + ${name} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/theme/keywind/login/assets/providers/linkedin.ftl b/theme/keywind/login/assets/providers/linkedin.ftl new file mode 100644 index 0000000..944d143 --- /dev/null +++ b/theme/keywind/login/assets/providers/linkedin.ftl @@ -0,0 +1,7 @@ +<#-- https://brand.linkedin.com/downloads --> +<#macro kw name="LinkedIn"> + + ${name} + + + diff --git a/theme/keywind/login/assets/providers/microsoft.ftl b/theme/keywind/login/assets/providers/microsoft.ftl new file mode 100644 index 0000000..408635b --- /dev/null +++ b/theme/keywind/login/assets/providers/microsoft.ftl @@ -0,0 +1,10 @@ +<#-- https://learn.microsoft.com/azure/active-directory/develop/howto-add-branding-in-azure-ad-apps --> +<#macro kw name="Microsoft"> + + ${name} + + + + + + diff --git a/theme/keywind/login/assets/providers/oidc.ftl b/theme/keywind/login/assets/providers/oidc.ftl new file mode 100644 index 0000000..f7954ff --- /dev/null +++ b/theme/keywind/login/assets/providers/oidc.ftl @@ -0,0 +1,9 @@ +<#-- https://openid.net/add-openid/logos --> +<#macro kw name="OpenID"> + + ${name} + + + + + diff --git a/theme/keywind/login/assets/providers/openshift.ftl b/theme/keywind/login/assets/providers/openshift.ftl new file mode 100644 index 0000000..e85ddef --- /dev/null +++ b/theme/keywind/login/assets/providers/openshift.ftl @@ -0,0 +1,11 @@ +<#-- https://www.redhat.com/technologies/cloud-computing/openshift --> +<#macro kw name="Red Hat OpenShift"> + + ${name} + + + + + + + diff --git a/theme/keywind/login/assets/providers/paypal.ftl b/theme/keywind/login/assets/providers/paypal.ftl new file mode 100644 index 0000000..7946e03 --- /dev/null +++ b/theme/keywind/login/assets/providers/paypal.ftl @@ -0,0 +1,9 @@ +<#-- https://www.paypal.com --> +<#macro kw name="PayPal"> + + ${name} + + + + + diff --git a/theme/keywind/login/assets/providers/providers.ftl b/theme/keywind/login/assets/providers/providers.ftl new file mode 100644 index 0000000..b2d943a --- /dev/null +++ b/theme/keywind/login/assets/providers/providers.ftl @@ -0,0 +1,84 @@ +<#import "./apple.ftl" as appleIcon> +<#import "./bitbucket.ftl" as bitbucketIcon> +<#import "./discord.ftl" as discordIcon> +<#import "./facebook.ftl" as facebookIcon> +<#import "./github.ftl" as githubIcon> +<#import "./gitlab.ftl" as gitlabIcon> +<#import "./google.ftl" as googleIcon> +<#import "./instagram.ftl" as instagramIcon> +<#import "./linkedin.ftl" as linkedinIcon> +<#import "./microsoft.ftl" as microsoftIcon> +<#import "./oidc.ftl" as oidcIcon> +<#import "./openshift.ftl" as openshiftIcon> +<#import "./paypal.ftl" as paypalIcon> +<#import "./slack.ftl" as slackIcon> +<#import "./stackoverflow.ftl" as stackoverflowIcon> +<#import "./twitter.ftl" as twitterIcon> + +<#macro apple> + <@appleIcon.kw /> + + +<#macro bitbucket> + <@bitbucketIcon.kw /> + + +<#macro discord> + <@discordIcon.kw /> + + +<#macro facebook> + <@facebookIcon.kw /> + + +<#macro github> + <@githubIcon.kw /> + + +<#macro gitlab> + <@gitlabIcon.kw /> + + +<#macro google> + <@googleIcon.kw /> + + +<#macro instagram> + <@instagramIcon.kw /> + + +<#macro "linkedin-openid-connect"> + <@linkedinIcon.kw /> + + +<#macro microsoft> + <@microsoftIcon.kw /> + + +<#macro oidc> + <@oidcIcon.kw /> + + +<#macro "openshift-v3"> + <@openshiftIcon.kw /> + + +<#macro "openshift-v4"> + <@openshiftIcon.kw /> + + +<#macro paypal> + <@paypalIcon.kw /> + + +<#macro slack> + <@slackIcon.kw /> + + +<#macro stackoverflow> + <@stackoverflowIcon.kw /> + + +<#macro twitter> + <@twitterIcon.kw /> + diff --git a/theme/keywind/login/assets/providers/slack.ftl b/theme/keywind/login/assets/providers/slack.ftl new file mode 100644 index 0000000..d4dffe3 --- /dev/null +++ b/theme/keywind/login/assets/providers/slack.ftl @@ -0,0 +1,14 @@ +<#-- https://slack.com/media-kit --> +<#macro kw name="Slack"> + + ${name} + + + + + + + + + + diff --git a/theme/keywind/login/assets/providers/stackoverflow.ftl b/theme/keywind/login/assets/providers/stackoverflow.ftl new file mode 100644 index 0000000..1ffad8d --- /dev/null +++ b/theme/keywind/login/assets/providers/stackoverflow.ftl @@ -0,0 +1,8 @@ +<#-- https://stackoverflow.design/brand/logo --> +<#macro kw name="Stack Overflow"> + + ${name} + + + + diff --git a/theme/keywind/login/assets/providers/twitter.ftl b/theme/keywind/login/assets/providers/twitter.ftl new file mode 100644 index 0000000..2bc7e7e --- /dev/null +++ b/theme/keywind/login/assets/providers/twitter.ftl @@ -0,0 +1,7 @@ +<#-- https://about.twitter.com/en/who-we-are/brand-toolkit --> +<#macro kw name="Twitter"> + + ${name} + + + diff --git a/theme/keywind/login/components/atoms/alert.ftl b/theme/keywind/login/components/atoms/alert.ftl new file mode 100644 index 0000000..58e8309 --- /dev/null +++ b/theme/keywind/login/components/atoms/alert.ftl @@ -0,0 +1,22 @@ +<#macro kw color=""> + <#switch color> + <#case "error"> + <#assign colorClass="bg-red-100 text-red-600"> + <#break> + <#case "info"> + <#assign colorClass="bg-blue-100 text-blue-600"> + <#break> + <#case "success"> + <#assign colorClass="bg-green-100 text-green-600"> + <#break> + <#case "warning"> + <#assign colorClass="bg-orange-100 text-orange-600"> + <#break> + <#default> + <#assign colorClass="bg-blue-100 text-blue-600"> + + + + diff --git a/theme/keywind/login/components/atoms/body.ftl b/theme/keywind/login/components/atoms/body.ftl new file mode 100644 index 0000000..dcc94a0 --- /dev/null +++ b/theme/keywind/login/components/atoms/body.ftl @@ -0,0 +1,5 @@ +<#macro kw> + + <#nested> + + diff --git a/theme/keywind/login/components/atoms/button-group.ftl b/theme/keywind/login/components/atoms/button-group.ftl new file mode 100644 index 0000000..4591209 --- /dev/null +++ b/theme/keywind/login/components/atoms/button-group.ftl @@ -0,0 +1,5 @@ +<#macro kw> +
+ <#nested> +
+ diff --git a/theme/keywind/login/components/atoms/button.ftl b/theme/keywind/login/components/atoms/button.ftl new file mode 100644 index 0000000..eeb0af7 --- /dev/null +++ b/theme/keywind/login/components/atoms/button.ftl @@ -0,0 +1,33 @@ +<#macro kw color="" component="button" size="" rest...> + <#switch color> + <#case "primary"> + <#assign colorClass="bg-primary-600 text-white focus:ring-primary-600 hover:bg-primary-700"> + <#break> + <#case "secondary"> + <#assign colorClass="bg-secondary-100 text-secondary-600 focus:ring-secondary-600 hover:bg-secondary-200 hover:text-secondary-900"> + <#break> + <#default> + <#assign colorClass="bg-primary-600 text-white focus:ring-primary-600 hover:bg-primary-700"> + + + <#switch size> + <#case "medium"> + <#assign sizeClass="px-4 py-2 text-sm"> + <#break> + <#case "small"> + <#assign sizeClass="px-2 py-1 text-xs"> + <#break> + <#default> + <#assign sizeClass="px-4 py-2 text-sm"> + + + <${component} + class="${colorClass} ${sizeClass} flex justify-center relative rounded-lg w-full focus:outline-none focus:ring-2 focus:ring-offset-2" + + <#list rest as attrName, attrValue> + ${attrName}="${attrValue}" + + > + <#nested> + + diff --git a/theme/keywind/login/components/atoms/card.ftl b/theme/keywind/login/components/atoms/card.ftl new file mode 100644 index 0000000..c1e808d --- /dev/null +++ b/theme/keywind/login/components/atoms/card.ftl @@ -0,0 +1,19 @@ +<#macro kw content="" footer="" header=""> +
+ <#if header?has_content> +
+ ${header} +
+ + <#if content?has_content> +
+ ${content} +
+ + <#if footer?has_content> +
+ ${footer} +
+ +
+ diff --git a/theme/keywind/login/components/atoms/checkbox.ftl b/theme/keywind/login/components/atoms/checkbox.ftl new file mode 100644 index 0000000..e47fd61 --- /dev/null +++ b/theme/keywind/login/components/atoms/checkbox.ftl @@ -0,0 +1,19 @@ +<#macro kw checked=false label="" name="" rest...> +
+ checked + + class="border-secondary-200 h-4 rounded text-primary-600 w-4 focus:ring-primary-200 focus:ring-opacity-50" + id="${name}" + name="${name}" + type="checkbox" + + <#list rest as attrName, attrValue> + ${attrName}="${attrValue}" + + > + +
+ diff --git a/theme/keywind/login/components/atoms/container.ftl b/theme/keywind/login/components/atoms/container.ftl new file mode 100644 index 0000000..34ead18 --- /dev/null +++ b/theme/keywind/login/components/atoms/container.ftl @@ -0,0 +1,5 @@ +<#macro kw> +
+ <#nested> +
+ diff --git a/theme/keywind/login/components/atoms/form.ftl b/theme/keywind/login/components/atoms/form.ftl new file mode 100644 index 0000000..014bb4f --- /dev/null +++ b/theme/keywind/login/components/atoms/form.ftl @@ -0,0 +1,11 @@ +<#macro kw rest...> +
+ ${attrName}="${attrValue}" + + > + <#nested> +
+ diff --git a/theme/keywind/login/components/atoms/heading.ftl b/theme/keywind/login/components/atoms/heading.ftl new file mode 100644 index 0000000..7665c01 --- /dev/null +++ b/theme/keywind/login/components/atoms/heading.ftl @@ -0,0 +1,5 @@ +<#macro kw> +

+ <#nested> +

+ diff --git a/theme/keywind/login/components/atoms/input.ftl b/theme/keywind/login/components/atoms/input.ftl new file mode 100644 index 0000000..714f832 --- /dev/null +++ b/theme/keywind/login/components/atoms/input.ftl @@ -0,0 +1,78 @@ +<#import "/assets/icons/eye.ftl" as iconEye> +<#import "/assets/icons/eye-slash.ftl" as iconEyeSlash> + +<#macro + kw + autofocus=false + class="block border-secondary-200 mt-1 rounded-md w-full focus:border-primary-300 focus:ring focus:ring-primary-200 focus:ring-opacity-50 sm:text-sm" + disabled=false + invalid=false + label="" + message="" + name="" + required=true + type="text" + rest... +> +
+ + <#if type == "password"> +
+ autofocus + <#if disabled>disabled + <#if required>required + + aria-invalid="${invalid?c}" + class="${class}" + id="${name}" + name="${name}" + placeholder="${label}" + :type="show ? 'text' : 'password'" + + <#list rest as attrName, attrValue> + ${attrName}="${attrValue}" + + > + +
+ <#else> + autofocus + <#if disabled>disabled + <#if required>required + + aria-invalid="${invalid?c}" + class="${class}" + id="${name}" + name="${name}" + placeholder="${label}" + type="${type}" + + <#list rest as attrName, attrValue> + ${attrName}="${attrValue}" + + > + + <#if invalid?? && message??> +
+ ${message?no_esc} +
+ +
+ diff --git a/theme/keywind/login/components/atoms/link.ftl b/theme/keywind/login/components/atoms/link.ftl new file mode 100644 index 0000000..bde7666 --- /dev/null +++ b/theme/keywind/login/components/atoms/link.ftl @@ -0,0 +1,30 @@ +<#macro kw color="" component="a" size="" rest...> + <#switch color> + <#case "primary"> + <#assign colorClass="text-primary-600 hover:text-primary-500"> + <#break> + <#case "secondary"> + <#assign colorClass="text-secondary-600 hover:text-secondary-900"> + <#break> + <#default> + <#assign colorClass="text-primary-600 hover:text-primary-500"> + + + <#switch size> + <#case "small"> + <#assign sizeClass="text-sm"> + <#break> + <#default> + <#assign sizeClass=""> + + + <${component} + class="<#compress>${colorClass} ${sizeClass} inline-flex" + + <#list rest as attrName, attrValue> + ${attrName}="${attrValue}" + + > + <#nested> + + diff --git a/theme/keywind/login/components/atoms/logo.ftl b/theme/keywind/login/components/atoms/logo.ftl new file mode 100644 index 0000000..e883322 --- /dev/null +++ b/theme/keywind/login/components/atoms/logo.ftl @@ -0,0 +1,8 @@ +<#macro kw> +
+ <#--<#nested>--> +
+ Company Logo +
+
+ diff --git a/theme/keywind/login/components/atoms/nav.ftl b/theme/keywind/login/components/atoms/nav.ftl new file mode 100644 index 0000000..81a4abf --- /dev/null +++ b/theme/keywind/login/components/atoms/nav.ftl @@ -0,0 +1,5 @@ +<#macro kw> +
+ <#nested> +
+ diff --git a/theme/keywind/login/components/atoms/radio.ftl b/theme/keywind/login/components/atoms/radio.ftl new file mode 100644 index 0000000..5596d5c --- /dev/null +++ b/theme/keywind/login/components/atoms/radio.ftl @@ -0,0 +1,18 @@ +<#macro kw checked=false id="" label="" rest...> +
+ checked + + class="border-secondary-200 focus:ring-primary-600" + id="${id}" + type="radio" + + <#list rest as attrName, attrValue> + ${attrName}="${attrValue}" + + > + +
+ diff --git a/theme/keywind/login/components/molecules/identity-provider.ftl b/theme/keywind/login/components/molecules/identity-provider.ftl new file mode 100644 index 0000000..b59962e --- /dev/null +++ b/theme/keywind/login/components/molecules/identity-provider.ftl @@ -0,0 +1,81 @@ +<#import "/assets/providers/providers.ftl" as providerIcons> + +<#macro kw providers=[]> +
+ ${msg("identity-provider-login-label")} +
+
+ <#list providers as provider> + <#switch provider.alias> + <#case "apple"> + <#assign colorClass="hover:bg-provider-apple/10"> + <#break> + <#case "bitbucket"> + <#assign colorClass="hover:bg-provider-bitbucket/10"> + <#break> + <#case "discord"> + <#assign colorClass="hover:bg-provider-discord/10"> + <#break> + <#case "facebook"> + <#assign colorClass="hover:bg-provider-facebook/10"> + <#break> + <#case "github"> + <#assign colorClass="hover:bg-provider-github/10"> + <#break> + <#case "gitlab"> + <#assign colorClass="hover:bg-provider-gitlab/10"> + <#break> + <#case "google"> + <#assign colorClass="hover:bg-provider-google/10"> + <#break> + <#case "instagram"> + <#assign colorClass="hover:bg-provider-instagram/10"> + <#break> + <#case "linkedin-openid-connect"> + <#assign colorClass="hover:bg-provider-linkedin/10"> + <#break> + <#case "microsoft"> + <#assign colorClass="hover:bg-provider-microsoft/10"> + <#break> + <#case "oidc"> + <#assign colorClass="hover:bg-provider-oidc/10"> + <#break> + <#case "openshift-v3"> + <#assign colorClass="hover:bg-provider-openshift/10"> + <#break> + <#case "openshift-v4"> + <#assign colorClass="hover:bg-provider-openshift/10"> + <#break> + <#case "paypal"> + <#assign colorClass="hover:bg-provider-paypal/10"> + <#break> + <#case "slack"> + <#assign colorClass="hover:bg-provider-slack/10"> + <#break> + <#case "stackoverflow"> + <#assign colorClass="hover:bg-provider-stackoverflow/10"> + <#break> + <#case "twitter"> + <#assign colorClass="hover:bg-provider-twitter/10"> + <#break> + <#default> + <#assign colorClass="hover:bg-secondary-100"> + + + + <#if providerIcons[provider.alias]??> +
+ <@providerIcons[provider.alias] /> +
+ <#else> + ${provider.displayName!} + +
+ +
+ diff --git a/theme/keywind/login/components/molecules/locale-provider.ftl b/theme/keywind/login/components/molecules/locale-provider.ftl new file mode 100644 index 0000000..198e5be --- /dev/null +++ b/theme/keywind/login/components/molecules/locale-provider.ftl @@ -0,0 +1,29 @@ +<#import "/assets/icons/chevron-down.ftl" as icon> +<#import "/components/atoms/link.ftl" as link> + +<#macro kw currentLocale="" locales=[]> +
+ <@link.kw @click="open = true" color="secondary" component="button" type="button"> +
+ ${currentLocale} + <@icon.kw /> +
+ +
+ <#list locales as locale> + <#if currentLocale != locale.label> +
+ <@link.kw color="secondary" href=locale.url size="small"> + ${locale.label} + +
+ + +
+
+ diff --git a/theme/keywind/login/components/molecules/username.ftl b/theme/keywind/login/components/molecules/username.ftl new file mode 100644 index 0000000..ba63393 --- /dev/null +++ b/theme/keywind/login/components/molecules/username.ftl @@ -0,0 +1,15 @@ +<#import "/assets/icons/arrow-top-right-on-square.ftl" as icon> +<#import "/components/atoms/link.ftl" as link> + +<#macro kw linkHref="" linkTitle="" name=""> +
+ ${name} + <@link.kw + color="primary" + href=linkHref + title=linkTitle + > + <@icon.kw /> + +
+ diff --git a/theme/keywind/login/document.ftl b/theme/keywind/login/document.ftl new file mode 100644 index 0000000..188e16a --- /dev/null +++ b/theme/keywind/login/document.ftl @@ -0,0 +1,35 @@ +<#macro kw script=""> + ${msg("loginTitle", (realm.displayName!""))} + + + + + + <#if properties.meta?has_content> + <#list properties.meta?split(" ") as meta> + + + + + <#if properties.favicons?has_content> + <#list properties.favicons?split(" ") as favicon> + + + + + <#if properties.styles?has_content> + <#list properties.styles?split(" ") as style> + + + + + <#if script?has_content> + + + + <#if properties.scripts?has_content> + <#list properties.scripts?split(" ") as script> + + + + diff --git a/theme/keywind/login/error.ftl b/theme/keywind/login/error.ftl new file mode 100644 index 0000000..52af9c1 --- /dev/null +++ b/theme/keywind/login/error.ftl @@ -0,0 +1,18 @@ +<#import "template.ftl" as layout> +<#import "components/atoms/alert.ftl" as alert> +<#import "components/atoms/link.ftl" as link> + +<@layout.registrationLayout displayMessage=false; section> + <#if section="header"> + ${kcSanitize(msg("errorTitle"))?no_esc} + <#elseif section="form"> + <@alert.kw color="error">${kcSanitize(message.summary)?no_esc} + <#if !skipLink??> + <#if client?? && client.baseUrl?has_content> + <@link.kw color="secondary" href=client.baseUrl size="small"> + ${kcSanitize(msg("backToApplication"))?no_esc} + + + + + diff --git a/theme/keywind/login/features/labels/totp-device.ftl b/theme/keywind/login/features/labels/totp-device.ftl new file mode 100644 index 0000000..98ae12f --- /dev/null +++ b/theme/keywind/login/features/labels/totp-device.ftl @@ -0,0 +1,5 @@ +<#macro kw> + <#compress> + ${msg("loginTotpDeviceName")} <#if totp.otpCredentials?size gte 1>* + + diff --git a/theme/keywind/login/features/labels/totp.ftl b/theme/keywind/login/features/labels/totp.ftl new file mode 100644 index 0000000..be5158e --- /dev/null +++ b/theme/keywind/login/features/labels/totp.ftl @@ -0,0 +1,5 @@ +<#macro kw> + <#compress> + ${msg("authenticatorCode")} * + + diff --git a/theme/keywind/login/features/labels/username.ftl b/theme/keywind/login/features/labels/username.ftl new file mode 100644 index 0000000..6c01d6b --- /dev/null +++ b/theme/keywind/login/features/labels/username.ftl @@ -0,0 +1,11 @@ +<#macro kw> + <#compress> + <#if !realm.loginWithEmailAllowed> + ${msg("username")} + <#elseif !realm.registrationEmailAsUsername> + ${msg("usernameOrEmail")} + <#else> + ${msg("email")} + + + diff --git a/theme/keywind/login/login-config-totp.ftl b/theme/keywind/login/login-config-totp.ftl new file mode 100644 index 0000000..e0b64c6 --- /dev/null +++ b/theme/keywind/login/login-config-totp.ftl @@ -0,0 +1,110 @@ +<#import "template.ftl" as layout> +<#import "components/atoms/button.ftl" as button> +<#import "components/atoms/button-group.ftl" as buttonGroup> +<#import "components/atoms/form.ftl" as form> +<#import "components/atoms/input.ftl" as input> +<#import "components/atoms/link.ftl" as link> +<#import "features/labels/totp.ftl" as totpLabel> +<#import "features/labels/totp-device.ftl" as totpDeviceLabel> + +<#assign totpLabel><@totpLabel.kw /> +<#assign totpDeviceLabel><@totpDeviceLabel.kw /> + +<@layout.registrationLayout + displayMessage=!messagesPerField.existsError("totp", "userLabel") + displayRequiredFields=false + ; + section +> + <#if section="header"> + ${msg("loginTotpTitle")} + <#elseif section="form"> +
    +
  1. +

    ${msg("loginTotpStep1")}

    +
      + <#list totp.supportedApplications as app> +
    • ${msg(app)}
    • + +
    +
  2. + <#if mode?? && mode="manual"> +
  3. +

    ${msg("loginTotpManualStep2")}

    +

    ${totp.totpSecretEncoded}

    +
  4. +
  5. + <@link.kw color="primary" href=totp.qrUrl> + ${msg("loginTotpScanBarcode")} + +
  6. +
  7. +

    ${msg("loginTotpManualStep3")}

    +
      +
    • ${msg("loginTotpType")}: ${msg("loginTotp." + totp.policy.type)}
    • +
    • ${msg("loginTotpAlgorithm")}: ${totp.policy.getAlgorithmKey()}
    • +
    • ${msg("loginTotpDigits")}: ${totp.policy.digits}
    • + <#if totp.policy.type="totp"> +
    • ${msg("loginTotpInterval")}: ${totp.policy.period}
    • + <#elseif totp.policy.type="hotp"> +
    • ${msg("loginTotpCounter")}: ${totp.policy.initialCounter}
    • + +
    +
  8. + <#else> +
  9. +

    ${msg("loginTotpStep2")}

    + Figure: Barcode + <@link.kw color="primary" href=totp.manualUrl> + ${msg("loginTotpUnableToScan")} + +
  10. + +
  11. ${msg("loginTotpStep3")}
  12. +
  13. ${msg("loginTotpStep3DeviceName")}
  14. +
+ <@form.kw action=url.loginAction method="post"> + + <#if mode??> + + + <@input.kw + autocomplete="off" + autofocus=true + invalid=messagesPerField.existsError("totp") + label=totpLabel + message=kcSanitize(messagesPerField.get("totp")) + name="totp" + required=false + type="text" + /> + <@input.kw + autocomplete="off" + invalid=messagesPerField.existsError("userLabel") + label=totpDeviceLabel + message=kcSanitize(messagesPerField.get("userLabel")) + name="userLabel" + required=false + type="text" + /> + <@buttonGroup.kw> + <#if isAppInitiatedAction??> + <@button.kw color="primary" type="submit"> + ${msg("doSubmit")} + + <@button.kw color="secondary" name="cancel-aia" type="submit" value="true"> + ${msg("doCancel")} + + <#else> + <@button.kw color="primary" type="submit"> + ${msg("doSubmit")} + + + + + + diff --git a/theme/keywind/login/login-idp-link-confirm.ftl b/theme/keywind/login/login-idp-link-confirm.ftl new file mode 100644 index 0000000..9a2554d --- /dev/null +++ b/theme/keywind/login/login-idp-link-confirm.ftl @@ -0,0 +1,18 @@ +<#import "template.ftl" as layout> +<#import "components/atoms/button.ftl" as button> +<#import "components/atoms/form.ftl" as form> + +<@layout.registrationLayout; section> + <#if section="header"> + ${msg("confirmLinkIdpTitle")} + <#elseif section="form"> + <@form.kw action=url.loginAction method="post"> + <@button.kw color="primary" name="submitAction" type="submit" value="updateProfile"> + ${msg("confirmLinkIdpReviewProfile")} + + <@button.kw color="primary" name="submitAction" type="submit" value="linkAccount"> + ${msg("confirmLinkIdpContinue", idpDisplayName)} + + + + diff --git a/theme/keywind/login/login-oauth-grant.ftl b/theme/keywind/login/login-oauth-grant.ftl new file mode 100644 index 0000000..aa4173c --- /dev/null +++ b/theme/keywind/login/login-oauth-grant.ftl @@ -0,0 +1,62 @@ +<#import "template.ftl" as layout> +<#import "components/atoms/button.ftl" as button> +<#import "components/atoms/button-group.ftl" as buttonGroup> +<#import "components/atoms/form.ftl" as form> + +<@layout.registrationLayout; section> + <#if section="header"> + <#if client.attributes.logoUri??> + + +

+ <#if client.name?has_content> + ${msg("oauthGrantTitle", advancedMsg(client.name))} + <#else> + ${msg("oauthGrantTitle", client.clientId)} + +

+ <#elseif section="form"> +

${msg("oauthGrantRequest")}

+
    + <#if oauth.clientScopesRequested??> + <#list oauth.clientScopesRequested as clientScope> +
  • + <#if !clientScope.dynamicScopeParameter??> + ${advancedMsg(clientScope.consentScreenText)} + <#else> + ${advancedMsg(clientScope.consentScreenText)}: ${clientScope.dynamicScopeParameter} + +
  • + + +
+ <#if client.attributes.policyUri?? || client.attributes.tosUri??> +

+ <#if client.name?has_content> + ${msg("oauthGrantInformation",advancedMsg(client.name))} + <#else> + ${msg("oauthGrantInformation",client.clientId)} + + <#if client.attributes.tosUri??> + ${msg("oauthGrantReview")} + ${msg("oauthGrantTos")} + + <#if client.attributes.policyUri??> + ${msg("oauthGrantReview")} + ${msg("oauthGrantPolicy")} + +

+ + <@form.kw action=url.oauthAction method="post"> + + <@buttonGroup.kw> + <@button.kw color="primary" name="accept" type="submit"> + ${msg("doYes")} + + <@button.kw color="secondary" name="cancel" type="submit"> + ${msg("doNo")} + + + + + diff --git a/theme/keywind/login/login-otp.ftl b/theme/keywind/login/login-otp.ftl new file mode 100644 index 0000000..b1bb3b9 --- /dev/null +++ b/theme/keywind/login/login-otp.ftl @@ -0,0 +1,50 @@ +<#import "template.ftl" as layout> +<#import "components/atoms/button.ftl" as button> +<#import "components/atoms/button-group.ftl" as buttonGroup> +<#import "components/atoms/form.ftl" as form> +<#import "components/atoms/input.ftl" as input> +<#import "components/atoms/radio.ftl" as radio> +<#import "features/labels/totp.ftl" as totpLabel> + +<#assign totpLabel><@totpLabel.kw /> + +<@layout.registrationLayout + displayMessage=!messagesPerField.existsError("totp") + ; + section +> + <#if section="header"> + ${msg("doLogIn")} + <#elseif section="form"> + <@form.kw action=url.loginAction method="post"> + <#if otpLogin.userOtpCredentials?size gt 1> +
+ <#list otpLogin.userOtpCredentials as otpCredential> + <@radio.kw + checked=(otpCredential.id == otpLogin.selectedCredentialId) + id="kw-otp-credential-${otpCredential?index}" + label=otpCredential.userLabel + name="selectedCredentialId" + tabindex=otpCredential?index + value=otpCredential.id + /> + +
+ + <@input.kw + autocomplete="off" + autofocus=true + invalid=messagesPerField.existsError("totp") + label=totpLabel + message=kcSanitize(messagesPerField.get("totp")) + name="otp" + type="text" + /> + <@buttonGroup.kw> + <@button.kw color="primary" name="submitAction" type="submit"> + ${msg("doLogIn")} + + + + + diff --git a/theme/keywind/login/login-page-expired.ftl b/theme/keywind/login/login-page-expired.ftl new file mode 100644 index 0000000..2b6288d --- /dev/null +++ b/theme/keywind/login/login-page-expired.ftl @@ -0,0 +1,18 @@ +<#import "template.ftl" as layout> +<#import "components/atoms/button.ftl" as button> +<#import "components/atoms/button-group.ftl" as buttonGroup> + +<@layout.registrationLayout; section> + <#if section="header"> + ${msg("pageExpiredTitle")} + <#elseif section="form"> + <@buttonGroup.kw> + <@button.kw color="primary" component="a" href=url.loginRestartFlowUrl> + ${msg("doTryAgain")} + + <@button.kw color="secondary" component="a" href=url.loginAction> + ${msg("doContinue")} + + + + diff --git a/theme/keywind/login/login-password.ftl b/theme/keywind/login/login-password.ftl new file mode 100644 index 0000000..54e7d9d --- /dev/null +++ b/theme/keywind/login/login-password.ftl @@ -0,0 +1,39 @@ +<#import "template.ftl" as layout> +<#import "components/atoms/button.ftl" as button> +<#import "components/atoms/button-group.ftl" as buttonGroup> +<#import "components/atoms/form.ftl" as form> +<#import "components/atoms/input.ftl" as input> +<#import "components/atoms/link.ftl" as link> + +<@layout.registrationLayout displayMessage=!messagesPerField.existsError("password"); section> + <#if section="header"> + ${msg("doLogIn")} + <#elseif section="form"> + <@form.kw + action=url.loginAction + method="post" + onsubmit="login.disabled = true; return true;" + > + <@input.kw + autofocus=true + invalid=messagesPerField.existsError("password") + label=msg("password") + message=kcSanitize(messagesPerField.get("password"))?no_esc + name="password" + type="password" + /> + <#if realm.resetPasswordAllowed> +
+ <@link.kw color="primary" href=url.loginResetCredentialsUrl size="small"> + ${msg("doForgotPassword")} + +
+ + <@buttonGroup.kw> + <@button.kw color="primary" name="login" type="submit"> + ${msg("doLogIn")} + + + + + diff --git a/theme/keywind/login/login-recovery-authn-code-config.ftl b/theme/keywind/login/login-recovery-authn-code-config.ftl new file mode 100644 index 0000000..186d710 --- /dev/null +++ b/theme/keywind/login/login-recovery-authn-code-config.ftl @@ -0,0 +1,91 @@ +<#import "template.ftl" as layout> +<#import "components/atoms/alert.ftl" as alert> +<#import "components/atoms/button.ftl" as button> +<#import "components/atoms/button-group.ftl" as buttonGroup> +<#import "components/atoms/checkbox.ftl" as checkbox> +<#import "components/atoms/form.ftl" as form> + +<@layout.registrationLayout script="dist/recoveryCodes.js"; section> + <#if section="header"> + ${msg("recovery-code-config-header")} + <#elseif section="form"> +
+ <@alert.kw color="warning"> +
+

${msg("recovery-code-config-warning-title")}

+

${msg("recovery-code-config-warning-message")}

+
+ +
    + <#list recoveryAuthnCodesConfigBean.generatedRecoveryAuthnCodesList as code> +
  • ${code[0..3]}-${code[4..7]}-${code[8..]}
  • + +
+
+ <@button.kw @click="print" color="secondary" size="small" type="button"> + ${msg("recovery-codes-print")} + + <@button.kw @click="download" color="secondary" size="small" type="button"> + ${msg("recovery-codes-download")} + + <@button.kw @click="copy" color="secondary" size="small" type="button"> + ${msg("recovery-codes-copy")} + +
+ <@form.kw action=url.loginAction method="post"> + + + + <@checkbox.kw + label=msg("recovery-codes-confirmation-message") + name="kcRecoveryCodesConfirmationCheck" + required="required" + x\-ref="confirmationCheck" + /> + <@buttonGroup.kw> + <#if isAppInitiatedAction??> + <@button.kw color="primary" type="submit"> + ${msg("recovery-codes-action-complete")} + + <@button.kw + @click="$refs.confirmationCheck.required = false" + color="secondary" + name="cancel-aia" + type="submit" + value="true" + > + ${msg("recovery-codes-action-cancel")} + + <#else> + <@button.kw color="primary" type="submit"> + ${msg("recovery-codes-action-complete")} + + + + +
+ + + + diff --git a/theme/keywind/login/login-recovery-authn-code-input.ftl b/theme/keywind/login/login-recovery-authn-code-input.ftl new file mode 100644 index 0000000..a46bcfa --- /dev/null +++ b/theme/keywind/login/login-recovery-authn-code-input.ftl @@ -0,0 +1,26 @@ +<#import "template.ftl" as layout> +<#import "components/atoms/button.ftl" as button> +<#import "components/atoms/button-group.ftl" as buttonGroup> +<#import "components/atoms/form.ftl" as form> +<#import "components/atoms/input.ftl" as input> + +<@layout.registrationLayout; section> + <#if section="header"> + ${msg("auth-recovery-code-header")} + <#elseif section="form"> + <@form.kw action=url.loginAction method="post"> + <@input.kw + autocomplete="off" + autofocus=true + label=msg("auth-recovery-code-prompt", recoveryAuthnCodesInputBean.codeNumber?c) + name="recoveryCodeInput" + type="text" + /> + <@buttonGroup.kw> + <@button.kw color="primary" name="login" type="submit"> + ${msg("doLogIn")} + + + + + diff --git a/theme/keywind/login/login-reset-password.ftl b/theme/keywind/login/login-reset-password.ftl new file mode 100644 index 0000000..9c95003 --- /dev/null +++ b/theme/keywind/login/login-reset-password.ftl @@ -0,0 +1,48 @@ +<#import "template.ftl" as layout> +<#import "components/atoms/button.ftl" as button> +<#import "components/atoms/button-group.ftl" as buttonGroup> +<#import "components/atoms/form.ftl" as form> +<#import "components/atoms/input.ftl" as input> +<#import "components/atoms/link.ftl" as link> +<#import "features/labels/username.ftl" as usernameLabel> + +<#assign usernameLabel><@usernameLabel.kw /> + +<@layout.registrationLayout + displayInfo=true + displayMessage=!messagesPerField.existsError("username") + ; + section +> + <#if section="header"> + ${msg("emailForgotTitle")} + <#elseif section="form"> + <@form.kw action=url.loginAction method="post"> + <@input.kw + autocomplete=realm.loginWithEmailAllowed?string("email", "username") + autofocus=true + invalid=messagesPerField.existsError("username") + label=usernameLabel + message=kcSanitize(messagesPerField.get("username")) + name="username" + type="text" + value=(auth?has_content && auth.showUsername())?then(auth.attemptedUsername, '') + /> + <@buttonGroup.kw> + <@button.kw color="primary" type="submit"> + ${msg("doSubmit")} + + + + <#elseif section="info"> + <#if realm.loginWithEmailAllowed> + ${msg("emailInstruction")} + <#else> + ${msg("emailInstructionUsername")} + + <#elseif section="nav"> + <@link.kw color="secondary" href=url.loginUrl size="small"> + ${kcSanitize(msg("backToLogin"))?no_esc} + + + diff --git a/theme/keywind/login/login-update-password.ftl b/theme/keywind/login/login-update-password.ftl new file mode 100644 index 0000000..ed82380 --- /dev/null +++ b/theme/keywind/login/login-update-password.ftl @@ -0,0 +1,64 @@ +<#import "template.ftl" as layout> +<#import "components/atoms/button.ftl" as button> +<#import "components/atoms/button-group.ftl" as buttonGroup> +<#import "components/atoms/checkbox.ftl" as checkbox> +<#import "components/atoms/form.ftl" as form> +<#import "components/atoms/input.ftl" as input> + +<@layout.registrationLayout + displayMessage=!messagesPerField.existsError("password", "password-confirm") + ; + section +> + <#if section="header"> + ${msg("updatePasswordTitle")} + <#elseif section="form"> + <@form.kw action=url.loginAction method="post"> + + + <@input.kw + autocomplete="new-password" + autofocus=true + invalid=messagesPerField.existsError("password", "password-confirm") + label=msg("passwordNew") + name="password-new" + type="password" + /> + <@input.kw + autocomplete="new-password" + invalid=messagesPerField.existsError("password-confirm") + label=msg("passwordConfirm") + message=kcSanitize(messagesPerField.get("password-confirm")) + name="password-confirm" + type="password" + /> + <#if isAppInitiatedAction??> + <@checkbox.kw + checked=true + label=msg("logoutOtherSessions") + name="logout-sessions" + value="on" + /> + + <@buttonGroup.kw> + <#if isAppInitiatedAction??> + <@button.kw color="primary" type="submit"> + ${msg("doSubmit")} + + <@button.kw color="secondary" name="cancel-aia" type="submit" value="true"> + ${msg("doCancel")} + + <#else> + <@button.kw color="primary" type="submit"> + ${msg("doSubmit")} + + + + + + diff --git a/theme/keywind/login/login-update-profile.ftl b/theme/keywind/login/login-update-profile.ftl new file mode 100644 index 0000000..306bad9 --- /dev/null +++ b/theme/keywind/login/login-update-profile.ftl @@ -0,0 +1,71 @@ +<#import "template.ftl" as layout> +<#import "components/atoms/button.ftl" as button> +<#import "components/atoms/button-group.ftl" as buttonGroup> +<#import "components/atoms/form.ftl" as form> +<#import "components/atoms/input.ftl" as input> + +<@layout.registrationLayout + displayMessage=!messagesPerField.existsError("email", "firstName", "lastName", "username") + ; + section +> + <#if section="header"> + ${msg("loginProfileTitle")} + <#elseif section="form"> + <@form.kw action=url.loginAction method="post"> + <#if user.editUsernameAllowed> + <@input.kw + autocomplete="username" + autofocus=true + invalid=messagesPerField.existsError("username") + label=msg("username") + message=kcSanitize(messagesPerField.get("username")) + name="username" + type="text" + value=(user.username)!'' + /> + + <@input.kw + autocomplete="email" + invalid=messagesPerField.existsError("email") + label=msg("email") + message=kcSanitize(messagesPerField.get("email")) + name="email" + type="email" + value=(user.email)!'' + /> + <@input.kw + autocomplete="given-name" + invalid=messagesPerField.existsError("firstName") + label=msg("firstName") + message=kcSanitize(messagesPerField.get("firstName")) + name="firstName" + type="text" + value=(user.firstName)!'' + /> + <@input.kw + autocomplete="family-name" + invalid=messagesPerField.existsError("lastName") + label=msg("lastName") + message=kcSanitize(messagesPerField.get("lastName")) + name="lastName" + type="text" + value=(user.lastName)!'' + /> + <@buttonGroup.kw> + <#if isAppInitiatedAction??> + <@button.kw color="primary" type="submit"> + ${msg("doSubmit")} + + <@button.kw color="secondary" name="cancel-aia" type="submit" value="true"> + ${msg("doCancel")} + + <#else> + <@button.kw color="primary" type="submit"> + ${msg("doSubmit")} + + + + + + diff --git a/theme/keywind/login/login-username.ftl b/theme/keywind/login/login-username.ftl new file mode 100644 index 0000000..b8064b2 --- /dev/null +++ b/theme/keywind/login/login-username.ftl @@ -0,0 +1,71 @@ +<#import "template.ftl" as layout> +<#import "components/atoms/button.ftl" as button> +<#import "components/atoms/button-group.ftl" as buttonGroup> +<#import "components/atoms/checkbox.ftl" as checkbox> +<#import "components/atoms/form.ftl" as form> +<#import "components/atoms/input.ftl" as input> +<#import "components/atoms/link.ftl" as link> +<#import "components/molecules/identity-provider.ftl" as identityProvider> +<#import "features/labels/username.ftl" as usernameLabel> + +<#assign usernameLabel><@usernameLabel.kw /> + +<@layout.registrationLayout + displayInfo=realm.password && realm.registrationAllowed && !registrationDisabled?? + displayMessage=!messagesPerField.existsError("username") + ; + section +> + <#if section="header"> + ${msg("loginAccountTitle")} + <#elseif section="form"> + <#if realm.password> + <@form.kw + action=url.loginAction + method="post" + onsubmit="login.disabled = true; return true;" + > + <#if !usernameHidden??> + <@input.kw + autocomplete=realm.loginWithEmailAllowed?string("email", "username") + autofocus=true + disabled=usernameEditDisabled?? + invalid=messagesPerField.existsError("username") + label=usernameLabel + message=kcSanitize(messagesPerField.get("username"))?no_esc + name="username" + type="text" + value=(login.username)!'' + /> + + <#if realm.rememberMe && !usernameHidden??> +
+ <@checkbox.kw + checked=login.rememberMe?? + label=msg("rememberMe") + name="rememberMe" + /> +
+ + <@buttonGroup.kw> + <@button.kw color="primary" name="login" type="submit"> + ${msg("doLogIn")} + + + + + <#elseif section="info"> + <#if realm.password && realm.registrationAllowed && !registrationDisabled??> +
+ ${msg("noAccount")} + <@link.kw color="primary" href=url.registrationUrl> + ${msg("doRegister")} + +
+ + <#elseif section="socialProviders"> + <#if realm.password && social.providers??> + <@identityProvider.kw providers=social.providers /> + + + diff --git a/theme/keywind/login/login-x509-info.ftl b/theme/keywind/login/login-x509-info.ftl new file mode 100644 index 0000000..70d8432 --- /dev/null +++ b/theme/keywind/login/login-x509-info.ftl @@ -0,0 +1,40 @@ +<#import "template.ftl" as layout> +<#import "components/atoms/button.ftl" as button> +<#import "components/atoms/button-group.ftl" as buttonGroup> +<#import "components/atoms/form.ftl" as form> +<#import "components/atoms/link.ftl" as link> + +<@layout.registrationLayout; section> + <#if section = "header"> + ${msg("doLogIn")} + <#elseif section = "form"> +
+
${msg("clientCertificate")}
+
+ <#if x509.formData.subjectDN??> + ${(x509.formData.subjectDN!"")} + <#else> + ${msg("noCertificate")} + +
+
+ <#if x509.formData.isUserEnabled??> +
+ ${msg("doX509Login")} + ${(x509.formData.username!'')} +
+ + <@form.kw action=url.loginAction method="post"> + <@buttonGroup.kw> + <@button.kw color="primary" name="login" type="submit"> + ${msg("doContinue")} + + <#if x509.formData.isUserEnabled??> + <@button.kw color="secondary" name="cancel" type="submit"> + ${msg("doIgnore")} + + + + + + diff --git a/theme/keywind/login/login.ftl b/theme/keywind/login/login.ftl new file mode 100644 index 0000000..331da06 --- /dev/null +++ b/theme/keywind/login/login.ftl @@ -0,0 +1,87 @@ +<#import "template.ftl" as layout> +<#import "components/atoms/button.ftl" as button> +<#import "components/atoms/button-group.ftl" as buttonGroup> +<#import "components/atoms/checkbox.ftl" as checkbox> +<#import "components/atoms/form.ftl" as form> +<#import "components/atoms/input.ftl" as input> +<#import "components/atoms/link.ftl" as link> +<#import "components/molecules/identity-provider.ftl" as identityProvider> +<#import "features/labels/username.ftl" as usernameLabel> + +<#assign usernameLabel><@usernameLabel.kw /> + +<@layout.registrationLayout + displayInfo=realm.password && realm.registrationAllowed && !registrationDisabled?? + displayMessage=!messagesPerField.existsError("username", "password") + ; + section +> + <#if section="header"> + ${msg("loginAccountTitle")} + <#elseif section="form"> + <#if realm.password> + <@form.kw + action=url.loginAction + method="post" + onsubmit="login.disabled = true; return true;" + > + + <@input.kw + autocomplete=realm.loginWithEmailAllowed?string("email", "username") + autofocus=true + disabled=usernameEditDisabled?? + invalid=messagesPerField.existsError("username", "password") + label=usernameLabel + message=kcSanitize(messagesPerField.getFirstError("username", "password")) + name="username" + type="text" + value=(login.username)!'' + /> + <@input.kw + invalid=messagesPerField.existsError("username", "password") + label=msg("password") + name="password" + type="password" + /> + <#if realm.rememberMe && !usernameEditDisabled?? || realm.resetPasswordAllowed> +
+ <#if realm.rememberMe && !usernameEditDisabled??> + <@checkbox.kw + checked=login.rememberMe?? + label=msg("rememberMe") + name="rememberMe" + /> + + <#if realm.resetPasswordAllowed> + <@link.kw color="primary" href=url.loginResetCredentialsUrl size="small"> + ${msg("doForgotPassword")} + + +
+ + <@buttonGroup.kw> + <@button.kw color="primary" name="login" type="submit"> + ${msg("doLogIn")} + + + + + <#elseif section="info"> + <#if realm.password && realm.registrationAllowed && !registrationDisabled??> +
+ ${msg("noAccount")} + <@link.kw color="primary" href=url.registrationUrl> + ${msg("doRegister")} + +
+ + <#elseif section="socialProviders"> + <#if realm.password && social.providers??> + <@identityProvider.kw providers=social.providers /> + + + diff --git a/theme/keywind/login/logout-confirm.ftl b/theme/keywind/login/logout-confirm.ftl new file mode 100644 index 0000000..e7ec486 --- /dev/null +++ b/theme/keywind/login/logout-confirm.ftl @@ -0,0 +1,25 @@ +<#import "template.ftl" as layout> +<#import "components/atoms/button.ftl" as button> +<#import "components/atoms/form.ftl" as form> +<#import "components/atoms/link.ftl" as link> + +<@layout.registrationLayout; section> + <#if section="header"> + ${msg("logoutConfirmTitle")} + <#elseif section="form"> +

${msg("logoutConfirmHeader")}

+ <@form.kw action=url.logoutConfirmAction method="post"> + + <@button.kw color="primary" name="confirmLogout" type="submit" value=msg('doLogout')> + ${msg("doLogout")} + + + <#if !logoutConfirm.skipLink> + <#if (client.baseUrl)?has_content> + <@link.kw color="secondary" href=client.baseUrl size="small"> + ${kcSanitize(msg("backToApplication"))?no_esc} + + + + + diff --git a/theme/keywind/login/register.ftl b/theme/keywind/login/register.ftl new file mode 100644 index 0000000..c1a2f06 --- /dev/null +++ b/theme/keywind/login/register.ftl @@ -0,0 +1,88 @@ +<#import "template.ftl" as layout> +<#import "components/atoms/button.ftl" as button> +<#import "components/atoms/button-group.ftl" as buttonGroup> +<#import "components/atoms/form.ftl" as form> +<#import "components/atoms/input.ftl" as input> +<#import "components/atoms/link.ftl" as link> + +<@layout.registrationLayout + displayMessage=!messagesPerField.existsError("firstName", "lastName", "email", "username", "password", "password-confirm") + ; + section +> + <#if section="header"> + ${msg("registerTitle")} + <#elseif section="form"> + <@form.kw action=url.registrationAction method="post"> + <@input.kw + autocomplete="given-name" + autofocus=true + invalid=messagesPerField.existsError("firstName") + label=msg("firstName") + message=kcSanitize(messagesPerField.get("firstName")) + name="firstName" + type="text" + value=(register.formData.firstName)!'' + /> + <@input.kw + autocomplete="family-name" + invalid=messagesPerField.existsError("lastName") + label=msg("lastName") + message=kcSanitize(messagesPerField.get("lastName")) + name="lastName" + type="text" + value=(register.formData.lastName)!'' + /> + <@input.kw + autocomplete="email" + invalid=messagesPerField.existsError("email") + label=msg("email") + message=kcSanitize(messagesPerField.get("email")) + name="email" + type="email" + value=(register.formData.email)!'' + /> + <#if !realm.registrationEmailAsUsername> + <@input.kw + autocomplete="username" + invalid=messagesPerField.existsError("username") + label=msg("username") + message=kcSanitize(messagesPerField.get("username")) + name="username" + type="text" + value=(register.formData.username)!'' + /> + + <#if passwordRequired??> + <@input.kw + autocomplete="new-password" + invalid=messagesPerField.existsError("password", "password-confirm") + label=msg("password") + message=kcSanitize(messagesPerField.getFirstError("password", "password-confirm")) + name="password" + type="password" + /> + <@input.kw + autocomplete="new-password" + invalid=messagesPerField.existsError("password-confirm") + label=msg("passwordConfirm") + message=kcSanitize(messagesPerField.get("password-confirm")) + name="password-confirm" + type="password" + /> + + <#if recaptchaRequired??> +
+ + <@buttonGroup.kw> + <@button.kw color="primary" type="submit"> + ${msg("doRegister")} + + + + <#elseif section="nav"> + <@link.kw color="secondary" href=url.loginUrl size="small"> + ${kcSanitize(msg("backToLogin"))?no_esc} + + + diff --git a/theme/keywind/login/resources/header-logo.png b/theme/keywind/login/resources/header-logo.png new file mode 100644 index 0000000..aba9071 Binary files /dev/null and b/theme/keywind/login/resources/header-logo.png differ diff --git a/theme/keywind/login/select-authenticator.ftl b/theme/keywind/login/select-authenticator.ftl new file mode 100644 index 0000000..26a25fe --- /dev/null +++ b/theme/keywind/login/select-authenticator.ftl @@ -0,0 +1,28 @@ +<#import "template.ftl" as layout> +<#import "components/atoms/form.ftl" as form> +<#import "components/atoms/link.ftl" as link> + +<@layout.registrationLayout displayInfo=false; section> + <#if section="header"> + ${msg("loginChooseAuthenticator")} + <#elseif section="form"> +
+ <@form.kw action=url.loginAction method="post" x\-ref="selectCredentialForm"> + + <#list auth.authenticationSelections as authenticationSelection> +
+ <@link.kw + @click="$refs.authExecInput.value = '${authenticationSelection.authExecId}'; $refs.selectCredentialForm.submit()" + color="primary" + component="button" + type="button" + > + ${msg("${authenticationSelection.displayName}")} + +
${msg("${authenticationSelection.helpText}")}
+
+ + +
+ + diff --git a/theme/keywind/login/template.ftl b/theme/keywind/login/template.ftl new file mode 100644 index 0000000..ddb0035 --- /dev/null +++ b/theme/keywind/login/template.ftl @@ -0,0 +1,84 @@ +<#import "document.ftl" as document> +<#import "components/atoms/alert.ftl" as alert> +<#import "components/atoms/body.ftl" as body> +<#import "components/atoms/button.ftl" as button> +<#import "components/atoms/card.ftl" as card> +<#import "components/atoms/container.ftl" as container> +<#import "components/atoms/heading.ftl" as heading> +<#import "components/atoms/logo.ftl" as logo> +<#import "components/atoms/nav.ftl" as nav> +<#import "components/molecules/locale-provider.ftl" as localeProvider> +<#import "components/molecules/username.ftl" as username> + +<#macro + registrationLayout + displayInfo=false + displayMessage=true + displayRequiredFields=false + script="" + showAnotherWayIfPresent=true +> + <#assign cardHeader> + <@logo.kw> + ${kcSanitize(msg("loginTitleHtml", (realm.displayNameHtml!"")))?no_esc} + + <#if !(auth?has_content && auth.showUsername() && !auth.showResetCredentials())> + <@heading.kw> + <#nested "header"> + + <#else> + <#nested "show-username"> + <@username.kw + linkHref=url.loginRestartFlowUrl + linkTitle=msg("restartLoginTooltip") + name=auth.attemptedUsername + /> + + + + <#assign cardContent> + <#if displayMessage && message?has_content && (message.type != "warning" || !isAppInitiatedAction??)> + <@alert.kw color=message.type> + ${kcSanitize(message.summary)?no_esc} + + + <#nested "form"> + <#if displayRequiredFields> +

+ * ${msg("requiredFields")} +

+ + <#if auth?has_content && auth.showTryAnotherWayLink() && showAnotherWayIfPresent> +
+ + <@button.kw color="secondary" type="submit"> + ${msg("doTryAnotherWay")} + +
+ + <#nested "socialProviders"> + + + <#assign cardFooter> + <#if displayInfo> + <#nested "info"> + + + + lang="${locale.currentLanguageTag}"> + + <@document.kw script=script /> + + <@body.kw> + <@container.kw> + <@card.kw content=cardContent footer=cardFooter header=cardHeader /> + <@nav.kw> + <#nested "nav"> + <#if realm.internationalizationEnabled && locale.supported?size gt 1> + <@localeProvider.kw currentLocale=locale.current locales=locale.supported /> + + + + + + diff --git a/theme/keywind/login/terms.ftl b/theme/keywind/login/terms.ftl new file mode 100644 index 0000000..23386b8 --- /dev/null +++ b/theme/keywind/login/terms.ftl @@ -0,0 +1,22 @@ +<#import "template.ftl" as layout> +<#import "components/atoms/button.ftl" as button> +<#import "components/atoms/button-group.ftl" as buttonGroup> +<#import "components/atoms/form.ftl" as form> + +<@layout.registrationLayout displayMessage=false; section> + <#if section="header"> + ${msg("termsTitle")} + <#elseif section="form"> + ${kcSanitize(msg("termsText"))?no_esc} + <@form.kw action=url.loginAction method="post"> + <@buttonGroup.kw> + <@button.kw color="primary" name="accept" type="submit"> + ${msg("doAccept")} + + <@button.kw color="secondary" name="cancel" type="submit"> + ${msg("doDecline")} + + + + + diff --git a/theme/keywind/login/theme.properties b/theme/keywind/login/theme.properties new file mode 100644 index 0000000..5e2a492 --- /dev/null +++ b/theme/keywind/login/theme.properties @@ -0,0 +1,4 @@ +parent=base + +styles=dist/index.css +scripts=dist/index.js diff --git a/theme/keywind/login/webauthn-authenticate.ftl b/theme/keywind/login/webauthn-authenticate.ftl new file mode 100644 index 0000000..189de50 --- /dev/null +++ b/theme/keywind/login/webauthn-authenticate.ftl @@ -0,0 +1,71 @@ +<#import "template.ftl" as layout> +<#import "components/atoms/button.ftl" as button> +<#import "components/atoms/button-group.ftl" as buttonGroup> + +<@layout.registrationLayout script="dist/webAuthnAuthenticate.js"; section> + <#if section="title"> + title + <#elseif section="header"> + ${kcSanitize(msg("webauthn-login-title"))?no_esc} + <#elseif section="form"> +
+
+ + + + + + +
+ <#if authenticators??> +
+ <#list authenticators.authenticators as authenticator> + + +
+ <#if shouldDisplayAuthenticators?? && shouldDisplayAuthenticators> + <#if authenticators.authenticators?size gt 1> +

${kcSanitize(msg("webauthn-available-authenticators"))?no_esc}

+ + <#list authenticators.authenticators as authenticator> +
+
${kcSanitize(msg("${authenticator.label}"))?no_esc}
+ <#if authenticator.transports?? && authenticator.transports.displayNameProperties?has_content> +
+ <#list authenticator.transports.displayNameProperties as nameProperty> + ${kcSanitize(msg("${nameProperty!}"))?no_esc} + <#if nameProperty?has_next> + , + + +
+ +
+ ${kcSanitize(msg("webauthn-createdAt-label"))?no_esc} + ${kcSanitize(authenticator.createdAt)?no_esc} +
+
+ + + + <@buttonGroup.kw> + <@button.kw @click="webAuthnAuthenticate" color="primary" type="button"> + ${kcSanitize(msg("webauthn-doAuthenticate"))} + + +
+ + + + diff --git a/theme/keywind/login/webauthn-error.ftl b/theme/keywind/login/webauthn-error.ftl new file mode 100644 index 0000000..852d1e3 --- /dev/null +++ b/theme/keywind/login/webauthn-error.ftl @@ -0,0 +1,34 @@ +<#import "template.ftl" as layout> +<#import "components/atoms/button.ftl" as button> +<#import "components/atoms/button-group.ftl" as buttonGroup> + +<@layout.registrationLayout displayMessage=true; section> + <#if section="header"> + ${kcSanitize(msg("webauthn-error-title"))?no_esc} + <#elseif section="form"> +
+
+ + +
+ <@buttonGroup.kw> + <@button.kw + @click="$refs.executionValueInput.value = '${execution}'; $refs.isSetRetryInput.value = 'retry'; $refs.errorCredentialForm.submit()" + color="primary" + name="try-again" + tabindex="4" + type="button" + > + ${kcSanitize(msg("doTryAgain"))?no_esc} + + <#if isAppInitiatedAction??> +
+ <@button.kw color="secondary" name="cancel-aia" type="submit" value="true"> + ${msg("doCancel")} + +
+ + +
+ + diff --git a/theme/keywind/login/webauthn-register.ftl b/theme/keywind/login/webauthn-register.ftl new file mode 100644 index 0000000..57f4dad --- /dev/null +++ b/theme/keywind/login/webauthn-register.ftl @@ -0,0 +1,54 @@ +<#import "template.ftl" as layout> +<#import "components/atoms/button.ftl" as button> +<#import "components/atoms/button-group.ftl" as buttonGroup> + +<@layout.registrationLayout script="dist/webAuthnRegister.js"; section> + <#if section="title"> + title + <#elseif section="header"> + ${kcSanitize(msg("webauthn-registration-title"))?no_esc} + <#elseif section="form"> +
+
+ + + + + + +
+ <@buttonGroup.kw> + <@button.kw @click="registerSecurityKey" color="primary" type="submit"> + ${msg("doRegister")} + + <#if !isSetRetry?has_content && isAppInitiatedAction?has_content> +
+ <@button.kw color="secondary" name="cancel-aia" type="submit" value="true"> + ${msg("doCancel")} + +
+ + +
+ + + + diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..0e78c76 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "compilerOptions": { + "allowImportingTsExtensions": true, + "isolatedModules": true, + "lib": ["ESNext", "DOM"], + "module": "ESNext", + "moduleResolution": "bundler", + "noEmit": true, + "noFallthroughCasesInSwitch": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "strict": true, + "target": "ESNext", + "useDefineForClassFields": true + }, + "include": ["src"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/tsconfig.node.json b/tsconfig.node.json new file mode 100644 index 0000000..c134a6f --- /dev/null +++ b/tsconfig.node.json @@ -0,0 +1,11 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "compilerOptions": { + "allowSyntheticDefaultImports": true, + "composite": true, + "module": "ESNext", + "moduleResolution": "bundler", + "skipLibCheck": true + }, + "include": ["vite.config.ts"] +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..3a18d54 --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,19 @@ +import { defineConfig } from 'vite'; + +export default defineConfig({ + build: { + rollupOptions: { + input: [ + 'src/index.ts', + 'src/data/recoveryCodes.ts', + 'src/data/webAuthnAuthenticate.ts', + 'src/data/webAuthnRegister.ts', + ], + output: { + assetFileNames: '[name][extname]', + dir: 'theme/keywind/login/resources/dist', + entryFileNames: '[name].js', + }, + }, + }, +});