keycloakify 10.0.0-rc.22 → 10.0.0-rc.24

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 (236) hide show
  1. package/PUBLIC_URL.d.ts +1 -1
  2. package/PUBLIC_URL.js +1 -1
  3. package/PUBLIC_URL.js.map +1 -1
  4. package/account/Fallback.js.map +1 -1
  5. package/account/Template.js +3 -3
  6. package/account/Template.js.map +1 -1
  7. package/account/i18n/baseMessages/index.js.map +1 -1
  8. package/account/i18n/i18n.js.map +1 -1
  9. package/account/index.d.ts +2 -2
  10. package/account/index.js +1 -2
  11. package/account/index.js.map +1 -1
  12. package/account/kcContext/KcContext.d.ts +12 -0
  13. package/account/kcContext/KcContext.js.map +1 -1
  14. package/account/kcContext/getKcContextMock.d.ts +24 -0
  15. package/account/kcContext/getKcContextMock.js +28 -0
  16. package/account/kcContext/getKcContextMock.js.map +1 -0
  17. package/account/kcContext/index.d.ts +2 -1
  18. package/account/kcContext/index.js +1 -1
  19. package/account/kcContext/index.js.map +1 -1
  20. package/account/kcContext/kcContextMocks.js +23 -91
  21. package/account/kcContext/kcContextMocks.js.map +1 -1
  22. package/account/pages/Account.js +1 -1
  23. package/account/pages/Account.js.map +1 -1
  24. package/account/pages/Applications.js +2 -2
  25. package/account/pages/Applications.js.map +1 -1
  26. package/account/pages/FederatedIdentity.js +1 -1
  27. package/account/pages/FederatedIdentity.js.map +1 -1
  28. package/account/pages/Log.js +1 -1
  29. package/account/pages/Log.js.map +1 -1
  30. package/account/pages/Password.js +6 -4
  31. package/account/pages/Password.js.map +1 -1
  32. package/account/pages/Sessions.js +1 -1
  33. package/account/pages/Sessions.js.map +1 -1
  34. package/account/pages/Totp.js +1 -1
  35. package/account/pages/Totp.js.map +1 -1
  36. package/bin/main.js +174 -162
  37. package/lib/BASE_URL.js.map +1 -1
  38. package/login/Fallback.js.map +1 -1
  39. package/login/Template.js +7 -7
  40. package/login/Template.js.map +1 -1
  41. package/login/UserProfileFormFields.js +24 -68
  42. package/login/UserProfileFormFields.js.map +1 -1
  43. package/login/i18n/baseMessages/index.js.map +1 -1
  44. package/login/i18n/i18n.d.ts +1 -1
  45. package/login/i18n/i18n.js +1 -1
  46. package/login/i18n/i18n.js.map +1 -1
  47. package/login/index.d.ts +2 -3
  48. package/login/index.js +1 -2
  49. package/login/index.js.map +1 -1
  50. package/login/kcContext/KcContext.d.ts +23 -13
  51. package/login/kcContext/KcContext.js.map +1 -1
  52. package/login/kcContext/getKcContextMock.d.ts +24 -0
  53. package/login/kcContext/getKcContextMock.js +28 -0
  54. package/login/kcContext/getKcContextMock.js.map +1 -0
  55. package/login/kcContext/index.d.ts +2 -1
  56. package/login/kcContext/index.js +1 -1
  57. package/login/kcContext/index.js.map +1 -1
  58. package/login/kcContext/kcContextMocks.js +29 -103
  59. package/login/kcContext/kcContextMocks.js.map +1 -1
  60. package/login/lib/useDownloadTerms.js +8 -14
  61. package/login/lib/useDownloadTerms.js.map +1 -1
  62. package/login/lib/useGetClassName.js +1 -1
  63. package/login/lib/useGetClassName.js.map +1 -1
  64. package/login/lib/useUserProfileForm.d.ts +9 -1
  65. package/login/lib/useUserProfileForm.js +94 -15
  66. package/login/lib/useUserProfileForm.js.map +1 -1
  67. package/login/pages/Code.js +1 -1
  68. package/login/pages/Code.js.map +1 -1
  69. package/login/pages/DeleteAccountConfirm.js +2 -2
  70. package/login/pages/DeleteAccountConfirm.js.map +1 -1
  71. package/login/pages/DeleteCredential.js +1 -1
  72. package/login/pages/DeleteCredential.js.map +1 -1
  73. package/login/pages/Error.js +1 -1
  74. package/login/pages/Error.js.map +1 -1
  75. package/login/pages/FrontchannelLogout.js +1 -1
  76. package/login/pages/FrontchannelLogout.js.map +1 -1
  77. package/login/pages/IdpReviewUserProfile.js +1 -1
  78. package/login/pages/IdpReviewUserProfile.js.map +1 -1
  79. package/login/pages/Info.js +5 -5
  80. package/login/pages/Info.js.map +1 -1
  81. package/login/pages/Login.js +4 -4
  82. package/login/pages/Login.js.map +1 -1
  83. package/login/pages/LoginConfigTotp.js +2 -2
  84. package/login/pages/LoginConfigTotp.js.map +1 -1
  85. package/login/pages/LoginIdpLinkConfirm.js +1 -1
  86. package/login/pages/LoginIdpLinkConfirm.js.map +1 -1
  87. package/login/pages/LoginIdpLinkEmail.js +1 -1
  88. package/login/pages/LoginIdpLinkEmail.js.map +1 -1
  89. package/login/pages/LoginOauth2DeviceVerifyUserCode.js +1 -1
  90. package/login/pages/LoginOauth2DeviceVerifyUserCode.js.map +1 -1
  91. package/login/pages/LoginOauthGrant.js +2 -2
  92. package/login/pages/LoginOauthGrant.js.map +1 -1
  93. package/login/pages/LoginOtp.js +1 -1
  94. package/login/pages/LoginOtp.js.map +1 -1
  95. package/login/pages/LoginPageExpired.js +1 -1
  96. package/login/pages/LoginPageExpired.js.map +1 -1
  97. package/login/pages/LoginPassword.js +3 -3
  98. package/login/pages/LoginPassword.js.map +1 -1
  99. package/login/pages/LoginRecoveryAuthnCodeConfig.js +10 -10
  100. package/login/pages/LoginRecoveryAuthnCodeConfig.js.map +1 -1
  101. package/login/pages/LoginRecoveryAuthnCodeInput.js +1 -1
  102. package/login/pages/LoginRecoveryAuthnCodeInput.js.map +1 -1
  103. package/login/pages/LoginResetOtp.js +1 -1
  104. package/login/pages/LoginResetOtp.js.map +1 -1
  105. package/login/pages/LoginResetPassword.js +2 -2
  106. package/login/pages/LoginResetPassword.js.map +1 -1
  107. package/login/pages/LoginUpdatePassword.js +3 -3
  108. package/login/pages/LoginUpdatePassword.js.map +1 -1
  109. package/login/pages/LoginUpdateProfile.js +4 -2
  110. package/login/pages/LoginUpdateProfile.js.map +1 -1
  111. package/login/pages/LoginUsername.js +3 -3
  112. package/login/pages/LoginUsername.js.map +1 -1
  113. package/login/pages/LoginVerifyEmail.js +1 -1
  114. package/login/pages/LoginVerifyEmail.js.map +1 -1
  115. package/login/pages/LoginX509Info.js +1 -1
  116. package/login/pages/LoginX509Info.js.map +1 -1
  117. package/login/pages/LogoutConfirm.js +1 -1
  118. package/login/pages/LogoutConfirm.js.map +1 -1
  119. package/login/pages/Register.js +8 -4
  120. package/login/pages/Register.js.map +1 -1
  121. package/login/pages/SamlPostForm.js +1 -1
  122. package/login/pages/SamlPostForm.js.map +1 -1
  123. package/login/pages/SelectAuthenticator.js +2 -2
  124. package/login/pages/SelectAuthenticator.js.map +1 -1
  125. package/login/pages/Terms.js +1 -1
  126. package/login/pages/Terms.js.map +1 -1
  127. package/login/pages/UpdateEmail.js +5 -3
  128. package/login/pages/UpdateEmail.js.map +1 -1
  129. package/login/pages/WebauthnAuthenticate.js +8 -8
  130. package/login/pages/WebauthnAuthenticate.js.map +1 -1
  131. package/login/pages/WebauthnError.js +2 -2
  132. package/login/pages/WebauthnError.js.map +1 -1
  133. package/login/pages/WebauthnRegister.js +5 -5
  134. package/login/pages/WebauthnRegister.js.map +1 -1
  135. package/package.json +26 -38
  136. package/src/PUBLIC_URL.ts +1 -1
  137. package/src/account/Template.tsx +2 -3
  138. package/src/account/index.ts +2 -2
  139. package/src/account/kcContext/KcContext.ts +19 -1
  140. package/src/account/kcContext/getKcContextMock.ts +80 -0
  141. package/src/account/kcContext/index.ts +2 -1
  142. package/src/account/kcContext/kcContextMocks.ts +26 -91
  143. package/src/bin/copy-keycloak-resources-to-public.ts +1 -4
  144. package/src/bin/keycloakify/generateFtl/ftl_object_to_js_code_declaring_an_object.ftl +19 -11
  145. package/src/bin/start-keycloak/start-keycloak.ts +18 -5
  146. package/src/bin/tools/getNpmWorkspaceRootDirPath.ts +25 -25
  147. package/src/login/Template.tsx +4 -5
  148. package/src/login/UserProfileFormFields.tsx +28 -80
  149. package/src/login/i18n/i18n.tsx +3 -3
  150. package/src/login/index.ts +6 -3
  151. package/src/login/kcContext/KcContext.ts +43 -31
  152. package/src/login/kcContext/getKcContextMock.ts +80 -0
  153. package/src/login/kcContext/index.ts +7 -1
  154. package/src/login/kcContext/kcContextMocks.ts +92 -165
  155. package/src/login/lib/useDownloadTerms.ts +10 -24
  156. package/src/login/lib/useGetClassName.ts +1 -1
  157. package/src/login/lib/useUserProfileForm.tsx +117 -13
  158. package/src/login/pages/LoginConfigTotp.tsx +1 -1
  159. package/src/login/pages/LoginRecoveryAuthnCodeConfig.tsx +7 -8
  160. package/src/login/pages/WebauthnAuthenticate.tsx +2 -3
  161. package/src/login/pages/WebauthnRegister.tsx +2 -3
  162. package/src/tools/ExtractAfterStartingWith.ts +4 -0
  163. package/src/tools/StatefulObservable/hooks/useRerenderOnChange.ts +4 -4
  164. package/src/tools/ValueOf.ts +2 -0
  165. package/src/tools/deepAssign.ts +51 -20
  166. package/src/tools/structuredCloneButFunctions.ts +24 -0
  167. package/src/tools/useInsertLinkTags.ts +78 -87
  168. package/src/tools/useInsertScriptTags.ts +69 -78
  169. package/src/tools/useOnFirstMount.ts +18 -0
  170. package/tools/Array.prototype.every.js.map +1 -1
  171. package/tools/ExtractAfterStartingWith.d.ts +1 -0
  172. package/tools/ExtractAfterStartingWith.js +2 -0
  173. package/tools/ExtractAfterStartingWith.js.map +1 -0
  174. package/tools/HTMLElement.prototype.prepend.js.map +1 -1
  175. package/tools/StatefulObservable/StatefulObservable.js.map +1 -1
  176. package/tools/StatefulObservable/hooks/useRerenderOnChange.d.ts +1 -1
  177. package/tools/StatefulObservable/hooks/useRerenderOnChange.js +4 -4
  178. package/tools/StatefulObservable/hooks/useRerenderOnChange.js.map +1 -1
  179. package/tools/ValueOf.d.ts +2 -0
  180. package/tools/ValueOf.js +2 -0
  181. package/tools/ValueOf.js.map +1 -0
  182. package/tools/clsx.js.map +1 -1
  183. package/tools/deepAssign.d.ts +1 -0
  184. package/tools/deepAssign.js +39 -16
  185. package/tools/deepAssign.js.map +1 -1
  186. package/tools/formatNumber.js.map +1 -1
  187. package/tools/structuredCloneButFunctions.d.ts +7 -0
  188. package/tools/structuredCloneButFunctions.js +19 -0
  189. package/tools/structuredCloneButFunctions.js.map +1 -0
  190. package/tools/useInsertLinkTags.d.ts +11 -6
  191. package/tools/useInsertLinkTags.js +53 -53
  192. package/tools/useInsertLinkTags.js.map +1 -1
  193. package/tools/useInsertScriptTags.d.ts +15 -6
  194. package/tools/useInsertScriptTags.js +56 -64
  195. package/tools/useInsertScriptTags.js.map +1 -1
  196. package/tools/useOnFirstMount.d.ts +2 -0
  197. package/tools/useOnFirstMount.js +15 -0
  198. package/tools/useOnFirstMount.js.map +1 -0
  199. package/tools/useSetClassName.js.map +1 -1
  200. package/vite-plugin/index.js +66 -64
  201. package/account/kcContext/createGetKcContext.d.ts +0 -19
  202. package/account/kcContext/createGetKcContext.js +0 -78
  203. package/account/kcContext/createGetKcContext.js.map +0 -1
  204. package/account/kcContext/getKcContext.d.ts +0 -13
  205. package/account/kcContext/getKcContext.js +0 -13
  206. package/account/kcContext/getKcContext.js.map +0 -1
  207. package/account/kcContext/getKcContextFromWindow.d.ts +0 -10
  208. package/account/kcContext/getKcContextFromWindow.js +0 -5
  209. package/account/kcContext/getKcContextFromWindow.js.map +0 -1
  210. package/login/kcContext/createGetKcContext.d.ts +0 -19
  211. package/login/kcContext/createGetKcContext.js +0 -117
  212. package/login/kcContext/createGetKcContext.js.map +0 -1
  213. package/login/kcContext/getKcContext.d.ts +0 -13
  214. package/login/kcContext/getKcContext.js +0 -13
  215. package/login/kcContext/getKcContext.js.map +0 -1
  216. package/login/kcContext/getKcContextFromWindow.d.ts +0 -10
  217. package/login/kcContext/getKcContextFromWindow.js +0 -5
  218. package/login/kcContext/getKcContextFromWindow.js.map +0 -1
  219. package/src/account/kcContext/createGetKcContext.ts +0 -134
  220. package/src/account/kcContext/getKcContext.ts +0 -23
  221. package/src/account/kcContext/getKcContextFromWindow.ts +0 -15
  222. package/src/login/kcContext/createGetKcContext.ts +0 -206
  223. package/src/login/kcContext/getKcContext.ts +0 -23
  224. package/src/login/kcContext/getKcContextFromWindow.ts +0 -15
  225. package/src/tools/AndByDiscriminatingKey.ts +0 -31
  226. package/src/tools/deepClone.ts +0 -19
  227. package/src/tools/memoize.ts +0 -55
  228. package/tools/AndByDiscriminatingKey.d.ts +0 -5
  229. package/tools/AndByDiscriminatingKey.js +0 -2
  230. package/tools/AndByDiscriminatingKey.js.map +0 -1
  231. package/tools/deepClone.d.ts +0 -2
  232. package/tools/deepClone.js +0 -14
  233. package/tools/deepClone.js.map +0 -1
  234. package/tools/memoize.d.ts +0 -7
  235. package/tools/memoize.js +0 -38
  236. package/tools/memoize.js.map +0 -1
@@ -9,75 +9,72 @@ import { id } from "tsafe/id";
9
9
  import { assert, type Equals } from "tsafe/assert";
10
10
  import { BASE_URL } from "keycloakify/lib/BASE_URL";
11
11
 
12
- const attributes: Attribute[] = [
13
- {
14
- validators: {
15
- length: {
16
- "ignore.empty.value": true,
17
- min: "3",
18
- max: "255"
19
- }
20
- },
21
- displayName: "${username}",
22
- annotations: {},
23
- required: true,
24
- autocomplete: "username",
25
- readOnly: false,
26
- name: "username",
27
- value: "xxxx"
28
- },
29
- {
30
- validators: {
31
- length: {
32
- max: "255",
33
- "ignore.empty.value": true
34
- },
35
- email: {
36
- "ignore.empty.value": true
12
+ const attributesByName = Object.fromEntries(
13
+ id<Attribute[]>([
14
+ {
15
+ validators: {
16
+ length: {
17
+ "ignore.empty.value": true,
18
+ min: "3",
19
+ max: "255"
20
+ }
37
21
  },
38
- pattern: {
39
- "ignore.empty.value": true,
40
- pattern: "gmail\\.com$"
41
- }
22
+ displayName: "${username}",
23
+ annotations: {},
24
+ required: true,
25
+ autocomplete: "username",
26
+ readOnly: false,
27
+ name: "username"
42
28
  },
43
- displayName: "${email}",
44
- annotations: {},
45
- required: true,
46
- autocomplete: "email",
47
- readOnly: false,
48
- name: "email"
49
- },
50
- {
51
- validators: {
52
- length: {
53
- max: "255",
54
- "ignore.empty.value": true
55
- }
29
+ {
30
+ validators: {
31
+ length: {
32
+ max: "255",
33
+ "ignore.empty.value": true
34
+ },
35
+ email: {
36
+ "ignore.empty.value": true
37
+ },
38
+ pattern: {
39
+ "ignore.empty.value": true,
40
+ pattern: "gmail\\.com$"
41
+ }
42
+ },
43
+ displayName: "${email}",
44
+ annotations: {},
45
+ required: true,
46
+ autocomplete: "email",
47
+ readOnly: false,
48
+ name: "email"
56
49
  },
57
- displayName: "${firstName}",
58
- annotations: {},
59
- required: true,
60
- readOnly: false,
61
- name: "firstName"
62
- },
63
- {
64
- validators: {
65
- length: {
66
- max: "255",
67
- "ignore.empty.value": true
68
- }
50
+ {
51
+ validators: {
52
+ length: {
53
+ max: "255",
54
+ "ignore.empty.value": true
55
+ }
56
+ },
57
+ displayName: "${firstName}",
58
+ annotations: {},
59
+ required: true,
60
+ readOnly: false,
61
+ name: "firstName"
69
62
  },
70
- displayName: "${lastName}",
71
- annotations: {},
72
- required: true,
73
- readOnly: false,
74
- name: "lastName"
75
- }
76
- ];
77
-
78
- const attributesByName = Object.fromEntries(
79
- attributes.map(attribute => [attribute.name, attribute])
80
- ) as any;
63
+ {
64
+ validators: {
65
+ length: {
66
+ max: "255",
67
+ "ignore.empty.value": true
68
+ }
69
+ },
70
+ displayName: "${lastName}",
71
+ annotations: {},
72
+ required: true,
73
+ readOnly: false,
74
+ name: "lastName"
75
+ }
76
+ ]).map(attribute => [attribute.name, attribute])
77
+ );
81
78
 
82
79
  const resourcesPath = `${BASE_URL}${keycloak_resources}/login/resources`;
83
80
 
@@ -116,98 +113,34 @@ export const kcContextCommonMock: KcContext.Common = {
116
113
  locale: {
117
114
  supported: [
118
115
  /* spell-checker: disable */
119
- {
120
- url: "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=de",
121
- label: "Deutsch",
122
- languageTag: "de"
123
- },
124
- {
125
- url: "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=no",
126
- label: "Norsk",
127
- languageTag: "no"
128
- },
129
- {
130
- url: "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=ru",
131
- label: "Русский",
132
- languageTag: "ru"
133
- },
134
- {
135
- url: "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=sv",
136
- label: "Svenska",
137
- languageTag: "sv"
138
- },
139
- {
140
- url: "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=pt-BR",
141
- label: "Português (Brasil)",
142
- languageTag: "pt-BR"
143
- },
144
- {
145
- url: "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=lt",
146
- label: "Lietuvių",
147
- languageTag: "lt"
148
- },
149
- {
150
- url: "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=en",
151
- label: "English",
152
- languageTag: "en"
153
- },
154
- {
155
- url: "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=it",
156
- label: "Italiano",
157
- languageTag: "it"
158
- },
159
- {
160
- url: "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=fr",
161
- label: "Français",
162
- languageTag: "fr"
163
- },
164
- {
165
- url: "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=zh-CN",
166
- label: "中文简体",
167
- languageTag: "zh-CN"
168
- },
169
- {
170
- url: "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=es",
171
- label: "Español",
172
- languageTag: "es"
173
- },
174
- {
175
- url: "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=cs",
176
- label: "Čeština",
177
- languageTag: "cs"
178
- },
179
- {
180
- url: "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=ja",
181
- label: "日本語",
182
- languageTag: "ja"
183
- },
184
- {
185
- url: "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=sk",
186
- label: "Slovenčina",
187
- languageTag: "sk"
188
- },
189
- {
190
- url: "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=pl",
191
- label: "Polski",
192
- languageTag: "pl"
193
- },
194
- {
195
- url: "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=ca",
196
- label: "Català",
197
- languageTag: "ca"
198
- },
199
- {
200
- url: "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=nl",
201
- label: "Nederlands",
202
- languageTag: "nl"
203
- },
204
- {
205
- url: "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=tr",
206
- label: "Türkçe",
207
- languageTag: "tr"
208
- }
116
+ ["de", "Deutsch"],
117
+ ["no", "Norsk"],
118
+ ["ru", "Русский"],
119
+ ["sv", "Svenska"],
120
+ ["pt-BR", "Português (Brasil)"],
121
+ ["lt", "Lietuvių"],
122
+ ["en", "English"],
123
+ ["it", "Italiano"],
124
+ ["fr", "Français"],
125
+ ["zh-CN", "中文简体"],
126
+ ["es", "Español"],
127
+ ["cs", "Čeština"],
128
+ ["ja", "日本語"],
129
+ ["sk", "Slovenčina"],
130
+ ["pl", "Polski"],
131
+ ["ca", "Català"],
132
+ ["nl", "Nederlands"],
133
+ ["tr", "Türkçe"]
209
134
  /* spell-checker: enable */
210
- ],
135
+ ].map(
136
+ ([languageTag, label]) =>
137
+ ({
138
+ languageTag,
139
+ label,
140
+ url: "https://gist.github.com/garronej/52baaca1bb925f2296ab32741e062b8e"
141
+ }) as const
142
+ ),
143
+
211
144
  currentLanguageTag: "en"
212
145
  },
213
146
  auth: {
@@ -269,7 +202,6 @@ export const kcContextMocks = [
269
202
  recaptchaRequired: false,
270
203
  pageId: "register.ftl",
271
204
  profile: {
272
- attributes,
273
205
  attributesByName
274
206
  },
275
207
  scripts: [
@@ -421,7 +353,6 @@ export const kcContextMocks = [
421
353
  ...kcContextCommonMock,
422
354
  pageId: "login-update-profile.ftl",
423
355
  profile: {
424
- attributes,
425
356
  attributesByName
426
357
  }
427
358
  }),
@@ -478,7 +409,6 @@ export const kcContextMocks = [
478
409
  ...kcContextCommonMock,
479
410
  pageId: "idp-review-user-profile.ftl",
480
411
  profile: {
481
- attributes,
482
412
  attributesByName
483
413
  }
484
414
  }),
@@ -486,12 +416,9 @@ export const kcContextMocks = [
486
416
  ...kcContextCommonMock,
487
417
  pageId: "update-email.ftl",
488
418
  profile: {
489
- attributes: attributes.filter(attribute => attribute.name === "email"),
490
- attributesByName: Object.fromEntries(
491
- attributes
492
- .filter(attribute => attribute.name === "email")
493
- .map(attribute => [attribute.name, attribute])
494
- )
419
+ attributesByName: {
420
+ email: attributesByName["email"]
421
+ }
495
422
  }
496
423
  }),
497
424
  id<KcContext.SelectAuthenticator>({
@@ -1,14 +1,11 @@
1
- import { useEffect } from "react";
2
- import { memoize } from "keycloakify/tools/memoize";
3
1
  import { fallbackLanguageTag } from "keycloakify/login/i18n/i18n";
4
- import { useConst } from "keycloakify/tools/useConst";
5
- import { useConstCallback } from "keycloakify/tools/useConstCallback";
6
2
  import { assert } from "tsafe/assert";
7
3
  import {
8
4
  createStatefulObservable,
9
5
  useRerenderOnChange
10
6
  } from "keycloakify/tools/StatefulObservable";
11
7
  import { KcContext } from "../kcContext";
8
+ import { useOnFistMount } from "keycloakify/tools/useOnFirstMount";
12
9
 
13
10
  const obsTermsMarkdown = createStatefulObservable<string | undefined>(() => undefined);
14
11
 
@@ -27,29 +24,18 @@ export function useDownloadTerms(params: {
27
24
  kcContext: KcContextLike;
28
25
  downloadTermMarkdown: (params: { currentLanguageTag: string }) => Promise<string>;
29
26
  }) {
30
- const { kcContext } = params;
27
+ const { kcContext, downloadTermMarkdown } = params;
31
28
 
32
- const { downloadTermMarkdownMemoized } = (function useClosure() {
33
- const { downloadTermMarkdown } = params;
34
-
35
- const downloadTermMarkdownConst = useConstCallback(downloadTermMarkdown);
36
-
37
- const downloadTermMarkdownMemoized = useConst(() =>
38
- memoize((currentLanguageTag: string) =>
39
- downloadTermMarkdownConst({ currentLanguageTag })
40
- )
41
- );
42
-
43
- return { downloadTermMarkdownMemoized };
44
- })();
45
-
46
- useEffect(() => {
29
+ useOnFistMount(async () => {
47
30
  if (kcContext.pageId === "terms.ftl" || kcContext.termsAcceptanceRequired) {
48
- downloadTermMarkdownMemoized(
49
- kcContext.locale?.currentLanguageTag ?? fallbackLanguageTag
50
- ).then(thermMarkdown => (obsTermsMarkdown.current = thermMarkdown));
31
+ const termsMarkdown = await downloadTermMarkdown({
32
+ currentLanguageTag:
33
+ kcContext.locale?.currentLanguageTag ?? fallbackLanguageTag
34
+ });
35
+
36
+ obsTermsMarkdown.current = termsMarkdown;
51
37
  }
52
- }, []);
38
+ });
53
39
  }
54
40
 
55
41
  export function useTermsMarkdown() {
@@ -3,6 +3,7 @@ import type { ClassKey } from "keycloakify/login/TemplateProps";
3
3
 
4
4
  export const { useGetClassName } = createUseClassName<ClassKey>({
5
5
  defaultClasses: {
6
+ kcHtmlClass: "login-pf",
6
7
  kcBodyClass: undefined,
7
8
  kcHeaderWrapperClass: undefined,
8
9
  kcLocaleWrapperClass: undefined,
@@ -54,7 +55,6 @@ export const { useGetClassName } = createUseClassName<ClassKey>({
54
55
  kcLogoLink: "http://www.keycloak.org",
55
56
  kcContainerClass: "container-fluid",
56
57
  kcSelectAuthListItemTitle: "select-auth-box-paragraph",
57
- kcHtmlClass: "login-pf",
58
58
  kcLoginOTPListItemTitleClass: "pf-c-tile__title",
59
59
  "kcLogoIdP-openshift-v4": "pf-icon pf-icon-openshift",
60
60
  kcWebAuthnUnknownIcon: "pficon pficon-key unknown-transport-class",
@@ -8,7 +8,8 @@ import { emailRegexp } from "keycloakify/tools/emailRegExp";
8
8
  import type { KcContext, PasswordPolicies } from "keycloakify/login/kcContext/KcContext";
9
9
  import { assert, type Equals } from "tsafe/assert";
10
10
  import { formatNumber } from "keycloakify/tools/formatNumber";
11
- import { createUseInsertScriptTags } from "keycloakify/tools/useInsertScriptTags";
11
+ import { useInsertScriptTags } from "keycloakify/tools/useInsertScriptTags";
12
+ import { structuredCloneButFunctions } from "keycloakify/tools/structuredCloneButFunctions";
12
13
  import type { I18n } from "../i18n";
13
14
 
14
15
  export type FormFieldError = {
@@ -67,7 +68,7 @@ export type FormAction =
67
68
  export type KcContextLike = {
68
69
  messagesPerField: Pick<KcContext.Common["messagesPerField"], "existsError" | "get">;
69
70
  profile: {
70
- attributes: Attribute[];
71
+ attributesByName: Record<string, Attribute>;
71
72
  html5DataAnnotations?: Record<string, string>;
72
73
  };
73
74
  passwordRequired?: boolean;
@@ -102,12 +103,11 @@ namespace internal {
102
103
  };
103
104
  }
104
105
 
105
- const { useInsertScriptTags } = createUseInsertScriptTags();
106
-
107
106
  export function useUserProfileForm(params: ParamsOfUseUserProfileForm): ReturnTypeOfUseUserProfileForm {
108
107
  const { kcContext, i18n, doMakeUserConfirmPassword } = params;
109
108
 
110
109
  const { insertScriptTags } = useInsertScriptTags({
110
+ componentOrHookName: "useUserProfileForm",
111
111
  scriptTags: Object.keys(kcContext.profile?.html5DataAnnotations ?? {})
112
112
  .filter(key => key !== "kcMultivalued" && key !== "kcNumberFormat") // NOTE: Keycloakify handles it.
113
113
  .map(key => ({
@@ -136,7 +136,11 @@ export function useUserProfileForm(params: ParamsOfUseUserProfileForm): ReturnTy
136
136
 
137
137
  const attributes = (() => {
138
138
  retrocompat_patch: {
139
- if ("profile" in kcContext && "attributes" in kcContext.profile && kcContext.profile.attributes.length !== 0) {
139
+ if (
140
+ "profile" in kcContext &&
141
+ "attributesByName" in kcContext.profile &&
142
+ Object.keys(kcContext.profile.attributesByName).length !== 0
143
+ ) {
140
144
  break retrocompat_patch;
141
145
  }
142
146
 
@@ -216,7 +220,7 @@ export function useUserProfileForm(params: ParamsOfUseUserProfileForm): ReturnTy
216
220
  assert(false, "Unable to mock user profile from the current kcContext");
217
221
  }
218
222
 
219
- return kcContext.profile.attributes.map(attribute_pre_group_patch => {
223
+ return Object.values(kcContext.profile.attributesByName).map(attribute_pre_group_patch => {
220
224
  if (typeof attribute_pre_group_patch.group === "string" && attribute_pre_group_patch.group !== "") {
221
225
  const { group, groupDisplayHeader, groupDisplayDescription, groupAnnotations, ...rest } =
222
226
  attribute_pre_group_patch as Attribute & {
@@ -242,7 +246,7 @@ export function useUserProfileForm(params: ParamsOfUseUserProfileForm): ReturnTy
242
246
  })();
243
247
 
244
248
  for (const attribute of attributes) {
245
- syntheticAttributes.push(attribute);
249
+ syntheticAttributes.push(structuredCloneButFunctions(attribute));
246
250
 
247
251
  add_password_and_password_confirm: {
248
252
  if (!kcContext.passwordRequired) {
@@ -284,6 +288,21 @@ export function useUserProfileForm(params: ParamsOfUseUserProfileForm): ReturnTy
284
288
  }
285
289
  }
286
290
 
291
+ // NOTE: Consistency patch
292
+ syntheticAttributes.forEach(attribute => {
293
+ if (getIsMultivaluedSingleField({ attribute })) {
294
+ attribute.multivalued = true;
295
+ }
296
+
297
+ if (attribute.multivalued) {
298
+ attribute.values ??= attribute.value !== undefined ? [attribute.value] : [];
299
+ delete attribute.value;
300
+ } else {
301
+ attribute.value ??= attribute.values?.[0];
302
+ delete attribute.values;
303
+ }
304
+ });
305
+
287
306
  return syntheticAttributes;
288
307
  })();
289
308
 
@@ -299,10 +318,10 @@ export function useUserProfileForm(params: ParamsOfUseUserProfileForm): ReturnTy
299
318
  break handle_multi_valued_attribute;
300
319
  }
301
320
 
302
- const values = attribute.values ?? [""];
321
+ const values = attribute.values?.length ? attribute.values : [""];
303
322
 
304
323
  apply_validator_min_range: {
305
- if (attribute.annotations.inputType?.startsWith("multiselect")) {
324
+ if (getIsMultivaluedSingleField({ attribute })) {
306
325
  break apply_validator_min_range;
307
326
  }
308
327
 
@@ -349,7 +368,8 @@ export function useUserProfileForm(params: ParamsOfUseUserProfileForm): ReturnTy
349
368
  attributeName: attribute.name,
350
369
  formFieldStates: initialFormFieldState
351
370
  }),
352
- hasLostFocusAtLeastOnce: valueOrValues instanceof Array ? valueOrValues.map(() => false) : false,
371
+ hasLostFocusAtLeastOnce:
372
+ valueOrValues instanceof Array && !getIsMultivaluedSingleField({ attribute }) ? valueOrValues.map(() => false) : false,
353
373
  valueOrValues: valueOrValues
354
374
  }))
355
375
  };
@@ -543,7 +563,7 @@ function useGetErrors(params: { kcContext: Pick<KcContextLike, "messagesPerField
543
563
 
544
564
  server_side_error: {
545
565
  if (attribute.multivalued) {
546
- const defaultValues = attribute.values ?? [""];
566
+ const defaultValues = attribute.values?.length ? attribute.values : [""];
547
567
 
548
568
  assert(valueOrValues instanceof Array);
549
569
 
@@ -595,7 +615,7 @@ function useGetErrors(params: { kcContext: Pick<KcContextLike, "messagesPerField
595
615
  break handle_multi_valued_multi_fields;
596
616
  }
597
617
 
598
- if (attribute.annotations.inputType?.startsWith("multiselect")) {
618
+ if (getIsMultivaluedSingleField({ attribute })) {
599
619
  break handle_multi_valued_multi_fields;
600
620
  }
601
621
 
@@ -674,7 +694,7 @@ function useGetErrors(params: { kcContext: Pick<KcContextLike, "messagesPerField
674
694
  break handle_multi_valued_single_field;
675
695
  }
676
696
 
677
- if (!attribute.annotations.inputType?.startsWith("multiselect")) {
697
+ if (!getIsMultivaluedSingleField({ attribute })) {
678
698
  break handle_multi_valued_single_field;
679
699
  }
680
700
 
@@ -913,6 +933,10 @@ function useGetErrors(params: { kcContext: Pick<KcContextLike, "messagesPerField
913
933
  return valueOrValues;
914
934
  })();
915
935
 
936
+ if (usernameValue === "") {
937
+ break check_password_policy_x;
938
+ }
939
+
916
940
  if (value !== usernameValue) {
917
941
  break check_password_policy_x;
918
942
  }
@@ -950,6 +974,10 @@ function useGetErrors(params: { kcContext: Pick<KcContextLike, "messagesPerField
950
974
  {
951
975
  const emailValue = emailFormFieldState.valueOrValues;
952
976
 
977
+ if (emailValue === "") {
978
+ break check_password_policy_x;
979
+ }
980
+
953
981
  if (value !== emailValue) {
954
982
  break check_password_policy_x;
955
983
  }
@@ -1239,3 +1267,79 @@ function useGetErrors(params: { kcContext: Pick<KcContextLike, "messagesPerField
1239
1267
 
1240
1268
  return { getErrors };
1241
1269
  }
1270
+
1271
+ function getIsMultivaluedSingleField(params: { attribute: Attribute }) {
1272
+ const { attribute } = params;
1273
+
1274
+ return attribute.annotations.inputType?.startsWith("multiselect") ?? false;
1275
+ }
1276
+
1277
+ export function getButtonToDisplayForMultivaluedAttributeField(params: { attribute: Attribute; values: string[]; fieldIndex: number }) {
1278
+ const { attribute, values, fieldIndex } = params;
1279
+
1280
+ const hasRemove = (() => {
1281
+ if (values.length === 1) {
1282
+ return false;
1283
+ }
1284
+
1285
+ const minCount = (() => {
1286
+ const { multivalued } = attribute.validators;
1287
+
1288
+ if (multivalued === undefined) {
1289
+ return undefined;
1290
+ }
1291
+
1292
+ const minStr = multivalued.min;
1293
+
1294
+ if (minStr === undefined) {
1295
+ return undefined;
1296
+ }
1297
+
1298
+ return parseInt(`${minStr}`);
1299
+ })();
1300
+
1301
+ if (minCount === undefined) {
1302
+ return true;
1303
+ }
1304
+
1305
+ if (values.length === minCount) {
1306
+ return false;
1307
+ }
1308
+
1309
+ return true;
1310
+ })();
1311
+
1312
+ const hasAdd = (() => {
1313
+ if (fieldIndex + 1 !== values.length) {
1314
+ return false;
1315
+ }
1316
+
1317
+ const maxCount = (() => {
1318
+ const { multivalued } = attribute.validators;
1319
+
1320
+ if (multivalued === undefined) {
1321
+ return undefined;
1322
+ }
1323
+
1324
+ const maxStr = multivalued.max;
1325
+
1326
+ if (maxStr === undefined) {
1327
+ return undefined;
1328
+ }
1329
+
1330
+ return parseInt(`${maxStr}`);
1331
+ })();
1332
+
1333
+ if (maxCount === undefined) {
1334
+ return false;
1335
+ }
1336
+
1337
+ if (values.length === maxCount) {
1338
+ return false;
1339
+ }
1340
+
1341
+ return true;
1342
+ })();
1343
+
1344
+ return { hasRemove, hasAdd };
1345
+ }
@@ -25,7 +25,7 @@ export default function LoginConfigTotp(props: PageProps<Extract<KcContext, { pa
25
25
 
26
26
  <ul id="kc-totp-supported-apps">
27
27
  {totp.supportedApplications.map(app => (
28
- <li>{advancedMsg(app)}</li>
28
+ <li key={app}>{advancedMsg(app)}</li>
29
29
  ))}
30
30
  </ul>
31
31
  </li>
@@ -2,12 +2,10 @@ import { useEffect } from "react";
2
2
  import { clsx } from "keycloakify/tools/clsx";
3
3
  import type { PageProps } from "keycloakify/login/pages/PageProps";
4
4
  import { useGetClassName } from "keycloakify/login/lib/useGetClassName";
5
- import { createUseInsertScriptTags } from "keycloakify/tools/useInsertScriptTags";
5
+ import { useInsertScriptTags } from "keycloakify/tools/useInsertScriptTags";
6
6
  import type { KcContext } from "../kcContext";
7
7
  import type { I18n } from "../i18n";
8
8
 
9
- const { useInsertScriptTags } = createUseInsertScriptTags();
10
-
11
9
  export default function LoginRecoveryAuthnCodeConfig(props: PageProps<Extract<KcContext, { pageId: "login-recovery-authn-code-config.ftl" }>, I18n>) {
12
10
  const { kcContext, i18n, doUseDefaultCss, Template, classes } = props;
13
11
 
@@ -21,6 +19,7 @@ export default function LoginRecoveryAuthnCodeConfig(props: PageProps<Extract<Kc
21
19
  const { msg, msgStr } = i18n;
22
20
 
23
21
  const { insertScriptTags } = useInsertScriptTags({
22
+ componentOrHookName: "LoginRecoveryAuthnCodeConfig",
24
23
  scriptTags: [
25
24
  {
26
25
  type: "text/javascript",
@@ -31,7 +30,7 @@ export default function LoginRecoveryAuthnCodeConfig(props: PageProps<Extract<Kc
31
30
  var tmpTextarea = document.createElement("textarea");
32
31
  var codes = document.getElementById("kc-recovery-codes-list").getElementsByTagName("li");
33
32
  for (i = 0; i < codes.length; i++) {
34
- tmpTextarea.value = tmpTextarea.value + codes[i].innerText + "\n";
33
+ tmpTextarea.value = tmpTextarea.value + codes[i].innerText + "\\n";
35
34
  }
36
35
  document.body.appendChild(tmpTextarea);
37
36
  tmpTextarea.select();
@@ -65,7 +64,7 @@ export default function LoginRecoveryAuthnCodeConfig(props: PageProps<Extract<Kc
65
64
 
66
65
  for (var i = 0; i < recoveryCodes.length; i++) {
67
66
  var recoveryCodeLiElement = recoveryCodes[i].innerText;
68
- recoveryCodeList += recoveryCodeLiElement + "\r\n";
67
+ recoveryCodeList += recoveryCodeLiElement + "\\r\\n";
69
68
  }
70
69
 
71
70
  return recoveryCodeList;
@@ -84,9 +83,9 @@ export default function LoginRecoveryAuthnCodeConfig(props: PageProps<Extract<Kc
84
83
  };
85
84
 
86
85
  return fileBodyContent =
87
- "${msgStr("recovery-codes-download-file-header")}\n\n" +
88
- recoveryCodeList + "\n" +
89
- "${msgStr("recovery-codes-download-file-description")}\n\n" +
86
+ "${msgStr("recovery-codes-download-file-header")}\\n\\n" +
87
+ recoveryCodeList + "\\n" +
88
+ "${msgStr("recovery-codes-download-file-description")}\\n\\n" +
90
89
  "${msgStr("recovery-codes-download-file-date")} " + formatCurrentDateTime();
91
90
  }
92
91