@oussemasahbeni/keycloakify-login-shadcn 250004.0.8 → 250004.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/README.md +317 -0
  2. package/keycloak-theme/components/ui/alert.tsx +69 -61
  3. package/keycloak-theme/components/ui/button.tsx +44 -38
  4. package/keycloak-theme/components/ui/card.tsx +60 -45
  5. package/keycloak-theme/components/ui/checkbox.tsx +24 -22
  6. package/keycloak-theme/components/ui/dropdown-menu.tsx +231 -176
  7. package/keycloak-theme/components/ui/field.tsx +51 -48
  8. package/keycloak-theme/components/ui/input-otp.tsx +56 -49
  9. package/keycloak-theme/components/ui/input.tsx +18 -21
  10. package/keycloak-theme/components/ui/label.tsx +19 -20
  11. package/keycloak-theme/components/ui/radio-group.tsx +27 -25
  12. package/keycloak-theme/components/ui/select.tsx +160 -121
  13. package/keycloak-theme/components/ui/separator.tsx +23 -23
  14. package/keycloak-theme/components/ui/tooltip.tsx +54 -24
  15. package/keycloak-theme/login/KcPage.tsx +0 -1
  16. package/keycloak-theme/login/components/Template/Template.tsx +2 -2
  17. package/keycloak-theme/login/components/Template/useInitializeTemplate.ts +3 -19
  18. package/keycloak-theme/login/index.css +3 -20
  19. package/keycloak-theme/login/pages/login/Form.tsx +49 -51
  20. package/keycloak-theme/login/pages/login/SocialProviders.tsx +9 -4
  21. package/keycloak-theme/login/pages/login/providers/github.svg +4 -3
  22. package/keycloak-theme/login/pages/login/providers/x.svg +4 -3
  23. package/keycloak-theme/login/pages/login/useProviderLogos.tsx +2 -3
  24. package/keycloak-theme/login/styleLevelCustomization.tsx +1 -0
  25. package/keycloak-theme/public/keycloak-theme/login/js/authChecker.js +95 -0
  26. package/keycloak-theme/public/keycloak-theme/login/js/passkeysConditionalAuth.js +86 -0
  27. package/keycloak-theme/public/keycloak-theme/login/js/rfc4648.js +185 -0
  28. package/keycloak-theme/public/keycloak-theme/login/js/webauthnAuthenticate.js +113 -0
  29. package/keycloak-theme/public/keycloak-theme/login/js/webauthnRegister.js +153 -0
  30. package/package.json +1 -1
@@ -49,8 +49,8 @@ export function Form() {
49
49
  {!kcContext.realm.loginWithEmailAllowed
50
50
  ? msg("email")
51
51
  : !kcContext.realm.registrationEmailAsUsername
52
- ? msg("usernameOrEmail")
53
- : msg("username")}
52
+ ? msg("usernameOrEmail")
53
+ : msg("username")}
54
54
  </FieldLabel>
55
55
  <Input
56
56
  tabIndex={2}
@@ -69,21 +69,21 @@ export function Form() {
69
69
  "username",
70
70
  "password"
71
71
  ) && (
72
- <FieldError>
73
- <span
74
- id="input-error"
75
- aria-live="polite"
76
- dangerouslySetInnerHTML={{
77
- __html: kcSanitize(
78
- kcContext.messagesPerField.getFirstError(
79
- "username",
80
- "password"
72
+ <FieldError>
73
+ <span
74
+ id="input-error"
75
+ aria-live="polite"
76
+ dangerouslySetInnerHTML={{
77
+ __html: kcSanitize(
78
+ kcContext.messagesPerField.getFirstError(
79
+ "username",
80
+ "password"
81
+ )
81
82
  )
82
- )
83
- }}
84
- />
85
- </FieldError>
86
- )}
83
+ }}
84
+ />
85
+ </FieldError>
86
+ )}
87
87
  </Field>
88
88
  )}
89
89
 
@@ -108,46 +108,44 @@ export function Form() {
108
108
  "username",
109
109
  "password"
110
110
  ) && (
111
- <FieldError>
112
- <span
113
- id="input-error"
114
- aria-live="polite"
115
- dangerouslySetInnerHTML={{
116
- __html: kcSanitize(
117
- kcContext.messagesPerField.getFirstError(
118
- "username",
119
- "password"
111
+ <FieldError>
112
+ <span
113
+ id="input-error"
114
+ aria-live="polite"
115
+ dangerouslySetInnerHTML={{
116
+ __html: kcSanitize(
117
+ kcContext.messagesPerField.getFirstError(
118
+ "username",
119
+ "password"
120
+ )
120
121
  )
121
- )
122
- }}
123
- />
124
- </FieldError>
125
- )}
122
+ }}
123
+ />
124
+ </FieldError>
125
+ )}
126
126
  </Field>
127
127
 
128
128
  <div className=" space-y-1 my-3 flex justify-between text-xs ">
129
- <div>
130
- {kcContext.realm.rememberMe &&
131
- !kcContext.usernameHidden && (
132
- <div className="flex items-center space-x-2 ">
133
- <Checkbox
134
- tabIndex={5}
135
- id="rememberMe"
136
- name="rememberMe"
137
- defaultChecked={
138
- !!kcContext.login.rememberMe
139
- }
140
- />
129
+ {kcContext.realm.rememberMe &&
130
+ !kcContext.usernameHidden && (
131
+ <div className="flex items-center space-x-2 ">
132
+ <Checkbox
133
+ tabIndex={5}
134
+ id="rememberMe"
135
+ name="rememberMe"
136
+ defaultChecked={
137
+ !!kcContext.login.rememberMe
138
+ }
139
+ />
141
140
 
142
- <Label
143
- htmlFor="rememberMe"
144
- className="text-sm font-medium cursor-pointer"
145
- >
146
- {msg("rememberMe")}
147
- </Label>
148
- </div>
149
- )}
150
- </div>
141
+ <Label
142
+ htmlFor="rememberMe"
143
+ className="text-sm font-medium cursor-pointer"
144
+ >
145
+ {msg("rememberMe")}
146
+ </Label>
147
+ </div>
148
+ )}
151
149
  <div className="link-style ">
152
150
  {kcContext.realm.resetPasswordAllowed && (
153
151
  <span className=" underline-offset-4 hover:underline">
@@ -1,3 +1,4 @@
1
+ import { cn } from '@/components/lib/utils';
1
2
  import { Button } from "@/components/ui/button";
2
3
  import { kcSanitize } from "@keycloakify/login-ui/kcSanitize";
3
4
  import { clsx } from "@keycloakify/login-ui/tools/clsx";
@@ -39,7 +40,7 @@ export function SocialProviders() {
39
40
  <div className="mt-px flex-auto border-t"></div>
40
41
  </div>
41
42
  <ul
42
- className={`mt-4! grid gap-2 sm:grid-cols-1 ${(kcContext.social?.providers?.length ?? 0) > 3 ? "sm:grid-cols-2" : ""}`}
43
+ className={`mt-4! grid gap-3 sm:grid-cols-1 ${(kcContext.social?.providers?.length ?? 0) > 3 ? "sm:grid-cols-2" : ""}`}
43
44
  >
44
45
  {kcContext.social.providers.map((...[p, , providers]) => (
45
46
  <li key={p.alias}>
@@ -49,9 +50,9 @@ export function SocialProviders() {
49
50
  className={clsx(
50
51
  kcClsx(
51
52
  providers.length > 3 &&
52
- "kcFormSocialAccountGridItem"
53
+ "kcFormSocialAccountGridItem"
53
54
  ),
54
- "flex items-center justify-center gap-2 "
55
+ "flex items-center justify-center gap-3 "
55
56
  )}
56
57
  type="button"
57
58
  href={p.loginUrl}
@@ -61,7 +62,11 @@ export function SocialProviders() {
61
62
  <img
62
63
  src={providerLogos[p.alias]}
63
64
  alt={`${p.displayName} logo`}
64
- className={"h-full w-auto"}
65
+ className={cn(
66
+ "h-full w-auto",
67
+ // Invert specific icons in dark mode
68
+ (p.alias === "github" || p.alias === "x" || p.alias === "twitter") && "dark:invert"
69
+ )}
65
70
  />
66
71
  ) : (
67
72
  // Fallback to the original iconClasses if the logo is not defined
@@ -1,4 +1,5 @@
1
- <svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2
- <title>GitHub</title>
3
- <path d="M11.8452 0C5.13387 0 0 5.09516 0 11.8065C0 17.1726 3.37742 21.7645 8.20161 23.3806C8.82097 23.4919 9.03871 23.1097 9.03871 22.7952C9.03871 22.4952 9.02419 20.8403 9.02419 19.8242C9.02419 19.8242 5.6371 20.55 4.92581 18.3823C4.92581 18.3823 4.37419 16.9742 3.58065 16.6113C3.58065 16.6113 2.47258 15.8516 3.65806 15.8661C3.65806 15.8661 4.8629 15.9629 5.52581 17.1145C6.58548 18.9823 8.36129 18.4452 9.05323 18.1258C9.16452 17.3516 9.47903 16.8145 9.82742 16.4952C7.12258 16.1952 4.39355 15.8032 4.39355 11.1484C4.39355 9.81774 4.76129 9.15 5.53548 8.29839C5.40968 7.98387 4.99839 6.6871 5.66129 5.0129C6.67258 4.69839 9 6.31936 9 6.31936C9.96774 6.04839 11.0081 5.90806 12.0387 5.90806C13.0694 5.90806 14.1097 6.04839 15.0774 6.31936C15.0774 6.31936 17.4048 4.69355 18.4161 5.0129C19.079 6.69194 18.6677 7.98387 18.5419 8.29839C19.3161 9.15484 19.7903 9.82258 19.7903 11.1484C19.7903 15.8177 16.9403 16.1903 14.2355 16.4952C14.6806 16.8774 15.0581 17.6032 15.0581 18.7403C15.0581 20.371 15.0435 22.3887 15.0435 22.7855C15.0435 23.1 15.2661 23.4823 15.8806 23.371C20.7194 21.7645 24 17.1726 24 11.8065C24 5.09516 18.5565 0 11.8452 0Z" fill="#181717" />
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
2
+ <path
3
+ d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"
4
+ fill="currentColor" />
4
5
  </svg>
@@ -1,4 +1,5 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <svg width="300" height="271" xmlns="http://www.w3.org/2000/svg">
3
- <path d="m236 0h46l-101 115 118 156h-92.6l-72.5-94.8-83 94.8h-46l107-123-113-148h94.9l65.5 86.6zm-16.1 244h25.5l-165-218h-27.4z"/>
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" role="img"
2
+ aria-hidden="true">
3
+ <path
4
+ d="M18.901 1.153h3.68l-8.04 9.19L24 22.846h-7.406l-5.8-7.584-6.638 7.584H.474l8.6-9.83L0 1.154h7.594l5.243 6.932ZM17.61 20.644h2.039L6.486 3.24H4.298Z" />
4
5
  </svg>
@@ -24,11 +24,10 @@ const useProviderLogos: () => Record<string, string> = () => ({
24
24
  gitlab: gitlabLogo,
25
25
  google: googleLogo,
26
26
  instagram: instagramLogo,
27
- "linkedin-openid-connect": linkedinLogo,
27
+ linkedin: linkedinLogo,
28
28
  microsoft: microsoftLogo,
29
29
  oidc: oidcLogo,
30
- "openshift-v3": openshiftLogo,
31
- "openshift-v4": openshiftLogo,
30
+ openshift: openshiftLogo,
32
31
  paypal: paypalLogo,
33
32
  slack: slackLogo,
34
33
  stackoverflow: stackoverflowLogo,
@@ -1,6 +1,7 @@
1
1
  import { ThemeProvider } from "@/components/theme-provider";
2
2
  import type { ClassKey } from "@keycloakify/login-ui/useKcClsx";
3
3
  import type { ReactNode } from "react";
4
+ import "./index.css";
4
5
  import { useKcContext } from "./KcContext";
5
6
  import { getTheme } from "./shared/getColorScheme";
6
7
 
@@ -0,0 +1,95 @@
1
+ /**
2
+ * This file has been claimed for ownership from @keycloakify/login-ui version 250004.6.5.
3
+ * To relinquish ownership and restore this file to its original content, run the following command:
4
+ *
5
+ * $ npx keycloakify own --path "login/js/authChecker.js" --public --revert
6
+ */
7
+
8
+
9
+
10
+ const SESSION_POLLING_INTERVAL = 2000;
11
+ const AUTH_SESSION_TIMEOUT_MILLISECS = 1000;
12
+ const initialSession = getSession();
13
+ const forms = Array.from(document.forms);
14
+ let timeout;
15
+
16
+ // Stop polling for a session when a form is submitted to prevent unexpected redirects.
17
+ // This is required as Safari does not support the 'beforeunload' event properly.
18
+ // See: https://bugs.webkit.org/show_bug.cgi?id=219102
19
+ forms.forEach((form) =>
20
+ form.addEventListener("submit", () => stopSessionPolling()),
21
+ );
22
+
23
+ // Stop polling for a session when the page is unloaded to prevent unexpected redirects.
24
+ globalThis.addEventListener("beforeunload", () => stopSessionPolling());
25
+
26
+ /**
27
+ * Starts polling to check if a new session was started in another context (e.g. a tab or window), and redirects to the specified URL if a session is detected.
28
+ * @param {string} redirectUrl - The URL to redirect to if a new session is detected.
29
+ */
30
+ export function startSessionPolling(redirectUrl) {
31
+ if (initialSession) {
32
+ // We started with a session, so there is nothing to do, exit.
33
+ return;
34
+ }
35
+
36
+ const session = getSession();
37
+
38
+ if (!session) {
39
+ // No new session detected, check again later.
40
+ timeout = setTimeout(
41
+ () => startSessionPolling(redirectUrl),
42
+ SESSION_POLLING_INTERVAL,
43
+ );
44
+ } else {
45
+ // A new session was detected, redirect to the specified URL and stop polling.
46
+ location.href = redirectUrl;
47
+ stopSessionPolling();
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Stops polling the session.
53
+ */
54
+ function stopSessionPolling() {
55
+ if (timeout) {
56
+ clearTimeout(timeout);
57
+ timeout = undefined;
58
+ }
59
+ }
60
+
61
+ export function checkAuthSession(pageAuthSessionHash) {
62
+ setTimeout(() => {
63
+ const cookieAuthSessionHash = getKcAuthSessionHash();
64
+ if (
65
+ cookieAuthSessionHash &&
66
+ cookieAuthSessionHash !== pageAuthSessionHash
67
+ ) {
68
+ location.reload();
69
+ }
70
+ }, AUTH_SESSION_TIMEOUT_MILLISECS);
71
+ }
72
+
73
+ function getKcAuthSessionHash() {
74
+ return getCookieByName("KC_AUTH_SESSION_HASH");
75
+ }
76
+
77
+ function getSession() {
78
+ return getCookieByName("KEYCLOAK_SESSION");
79
+ }
80
+
81
+ function getCookieByName(name) {
82
+ for (const cookie of document.cookie.split(";")) {
83
+ const [key, value] = cookie.split("=").map((value) => value.trim());
84
+ if (key === name) {
85
+ return value.startsWith('"') && value.endsWith('"')
86
+ ? value.slice(1, -1)
87
+ : value;
88
+ }
89
+ }
90
+ return null;
91
+ }
92
+
93
+
94
+
95
+
@@ -0,0 +1,86 @@
1
+ /**
2
+ * This file has been claimed for ownership from @keycloakify/login-ui version 250004.6.5.
3
+ * To relinquish ownership and restore this file to its original content, run the following command:
4
+ *
5
+ * $ npx keycloakify own --path "login/js/passkeysConditionalAuth.js" --public --revert
6
+ */
7
+
8
+ import { base64url } from "./rfc4648.js";
9
+ import { returnSuccess, returnFailure } from "./webauthnAuthenticate.js";
10
+
11
+ export function initAuthenticate(input) {
12
+ // Check if WebAuthn is supported by this browser
13
+ if (!window.PublicKeyCredential) {
14
+ returnFailure(input.errmsg);
15
+ return;
16
+ }
17
+ if (input.isUserIdentified || typeof PublicKeyCredential.isConditionalMediationAvailable === "undefined") {
18
+ document.getElementById("kc-form-passkey-button").style.display = 'block';
19
+ } else {
20
+ tryAutoFillUI(input);
21
+ }
22
+ }
23
+
24
+ function doAuthenticate(input) {
25
+ // Check if WebAuthn is supported by this browser
26
+ if (!window.PublicKeyCredential) {
27
+ returnFailure(input.errmsg);
28
+ return;
29
+ }
30
+
31
+ const publicKey = {
32
+ rpId : input.rpId,
33
+ challenge: base64url.parse(input.challenge, { loose: true })
34
+ };
35
+
36
+ publicKey.allowCredentials = !input.isUserIdentified ? [] : getAllowCredentials();
37
+
38
+ if (input.createTimeout !== 0) {
39
+ publicKey.timeout = input.createTimeout * 1000;
40
+ }
41
+
42
+ if (input.userVerification !== 'not specified') {
43
+ publicKey.userVerification = input.userVerification;
44
+ }
45
+
46
+ return navigator.credentials.get({
47
+ publicKey: publicKey,
48
+ ...input.additionalOptions
49
+ });
50
+ }
51
+
52
+ async function tryAutoFillUI(input) {
53
+ const isConditionalMediationAvailable = await PublicKeyCredential.isConditionalMediationAvailable();
54
+ if (isConditionalMediationAvailable) {
55
+ document.getElementById("kc-form-login").style.display = "block";
56
+ input.additionalOptions = { mediation: 'conditional'};
57
+ try {
58
+ const result = await doAuthenticate(input);
59
+ returnSuccess(result);
60
+ } catch (error) {
61
+ returnFailure(error);
62
+ }
63
+ } else {
64
+ document.getElementById("kc-form-passkey-button").style.display = 'block';
65
+ }
66
+ }
67
+
68
+ function getAllowCredentials() {
69
+ const allowCredentials = [];
70
+ const authnUse = document.forms['authn_select'].authn_use_chk;
71
+ if (authnUse !== undefined) {
72
+ if (authnUse.length === undefined) {
73
+ allowCredentials.push({
74
+ id: base64url.parse(authnUse.value, {loose: true}),
75
+ type: 'public-key',
76
+ });
77
+ } else {
78
+ authnUse.forEach((entry) =>
79
+ allowCredentials.push({
80
+ id: base64url.parse(entry.value, {loose: true}),
81
+ type: 'public-key',
82
+ }));
83
+ }
84
+ }
85
+ return allowCredentials;
86
+ }
@@ -0,0 +1,185 @@
1
+ /**
2
+ * This file has been claimed for ownership from @keycloakify/login-ui version 250004.6.5.
3
+ * To relinquish ownership and restore this file to its original content, run the following command:
4
+ *
5
+ * $ npx keycloakify own --path "login/js/rfc4648.js" --public --revert
6
+ */
7
+
8
+ /* eslint-disable @typescript-eslint/strict-boolean-expressions */
9
+ function parse(string, encoding, opts) {
10
+ var _opts$out;
11
+
12
+ if (opts === void 0) {
13
+ opts = {};
14
+ }
15
+
16
+ // Build the character lookup table:
17
+ if (!encoding.codes) {
18
+ encoding.codes = {};
19
+
20
+ for (var i = 0; i < encoding.chars.length; ++i) {
21
+ encoding.codes[encoding.chars[i]] = i;
22
+ }
23
+ } // The string must have a whole number of bytes:
24
+
25
+
26
+ if (!opts.loose && string.length * encoding.bits & 7) {
27
+ throw new SyntaxError('Invalid padding');
28
+ } // Count the padding bytes:
29
+
30
+
31
+ var end = string.length;
32
+
33
+ while (string[end - 1] === '=') {
34
+ --end; // If we get a whole number of bytes, there is too much padding:
35
+
36
+ if (!opts.loose && !((string.length - end) * encoding.bits & 7)) {
37
+ throw new SyntaxError('Invalid padding');
38
+ }
39
+ } // Allocate the output:
40
+
41
+
42
+ var out = new ((_opts$out = opts.out) != null ? _opts$out : Uint8Array)(end * encoding.bits / 8 | 0); // Parse the data:
43
+
44
+ var bits = 0; // Number of bits currently in the buffer
45
+
46
+ var buffer = 0; // Bits waiting to be written out, MSB first
47
+
48
+ var written = 0; // Next byte to write
49
+
50
+ for (var _i = 0; _i < end; ++_i) {
51
+ // Read one character from the string:
52
+ var value = encoding.codes[string[_i]];
53
+
54
+ if (value === undefined) {
55
+ throw new SyntaxError('Invalid character ' + string[_i]);
56
+ } // Append the bits to the buffer:
57
+
58
+
59
+ buffer = buffer << encoding.bits | value;
60
+ bits += encoding.bits; // Write out some bits if the buffer has a byte's worth:
61
+
62
+ if (bits >= 8) {
63
+ bits -= 8;
64
+ out[written++] = 0xff & buffer >> bits;
65
+ }
66
+ } // Verify that we have received just enough bits:
67
+
68
+
69
+ if (bits >= encoding.bits || 0xff & buffer << 8 - bits) {
70
+ throw new SyntaxError('Unexpected end of data');
71
+ }
72
+
73
+ return out;
74
+ }
75
+ function stringify(data, encoding, opts) {
76
+ if (opts === void 0) {
77
+ opts = {};
78
+ }
79
+
80
+ var _opts = opts,
81
+ _opts$pad = _opts.pad,
82
+ pad = _opts$pad === void 0 ? true : _opts$pad;
83
+ var mask = (1 << encoding.bits) - 1;
84
+ var out = '';
85
+ var bits = 0; // Number of bits currently in the buffer
86
+
87
+ var buffer = 0; // Bits waiting to be written out, MSB first
88
+
89
+ for (var i = 0; i < data.length; ++i) {
90
+ // Slurp data into the buffer:
91
+ buffer = buffer << 8 | 0xff & data[i];
92
+ bits += 8; // Write out as much as we can:
93
+
94
+ while (bits > encoding.bits) {
95
+ bits -= encoding.bits;
96
+ out += encoding.chars[mask & buffer >> bits];
97
+ }
98
+ } // Partial character:
99
+
100
+
101
+ if (bits) {
102
+ out += encoding.chars[mask & buffer << encoding.bits - bits];
103
+ } // Add padding characters until we hit a byte boundary:
104
+
105
+
106
+ if (pad) {
107
+ while (out.length * encoding.bits & 7) {
108
+ out += '=';
109
+ }
110
+ }
111
+
112
+ return out;
113
+ }
114
+
115
+ /* eslint-disable @typescript-eslint/strict-boolean-expressions */
116
+ var base16Encoding = {
117
+ chars: '0123456789ABCDEF',
118
+ bits: 4
119
+ };
120
+ var base32Encoding = {
121
+ chars: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567',
122
+ bits: 5
123
+ };
124
+ var base32HexEncoding = {
125
+ chars: '0123456789ABCDEFGHIJKLMNOPQRSTUV',
126
+ bits: 5
127
+ };
128
+ var base64Encoding = {
129
+ chars: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
130
+ bits: 6
131
+ };
132
+ var base64UrlEncoding = {
133
+ chars: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_',
134
+ bits: 6
135
+ };
136
+ var base16 = {
137
+ parse: function parse$1(string, opts) {
138
+ return parse(string.toUpperCase(), base16Encoding, opts);
139
+ },
140
+ stringify: function stringify$1(data, opts) {
141
+ return stringify(data, base16Encoding, opts);
142
+ }
143
+ };
144
+ var base32 = {
145
+ parse: function parse$1(string, opts) {
146
+ if (opts === void 0) {
147
+ opts = {};
148
+ }
149
+
150
+ return parse(opts.loose ? string.toUpperCase().replace(/0/g, 'O').replace(/1/g, 'L').replace(/8/g, 'B') : string, base32Encoding, opts);
151
+ },
152
+ stringify: function stringify$1(data, opts) {
153
+ return stringify(data, base32Encoding, opts);
154
+ }
155
+ };
156
+ var base32hex = {
157
+ parse: function parse$1(string, opts) {
158
+ return parse(string, base32HexEncoding, opts);
159
+ },
160
+ stringify: function stringify$1(data, opts) {
161
+ return stringify(data, base32HexEncoding, opts);
162
+ }
163
+ };
164
+ var base64 = {
165
+ parse: function parse$1(string, opts) {
166
+ return parse(string, base64Encoding, opts);
167
+ },
168
+ stringify: function stringify$1(data, opts) {
169
+ return stringify(data, base64Encoding, opts);
170
+ }
171
+ };
172
+ var base64url = {
173
+ parse: function parse$1(string, opts) {
174
+ return parse(string, base64UrlEncoding, opts);
175
+ },
176
+ stringify: function stringify$1(data, opts) {
177
+ return stringify(data, base64UrlEncoding, opts);
178
+ }
179
+ };
180
+ var codec = {
181
+ parse: parse,
182
+ stringify: stringify
183
+ };
184
+
185
+ export { base16, base32, base32hex, base64, base64url, codec };