keycloakify 11.5.4 → 11.6.0

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.
@@ -142,15 +142,96 @@ export async function command(params: {
142
142
  ].join("\n")
143
143
  );
144
144
 
145
- const { value: tag } = await cliSelect<string>({
146
- values: latestMajorTags
147
- }).catch(() => {
148
- process.exit(-1);
149
- });
145
+ const tag_userSelected = await (async () => {
146
+ let tag: string;
147
+
148
+ let latestMajorTags_copy = [...latestMajorTags];
149
+
150
+ while (true) {
151
+ const { value } = await cliSelect<string>({
152
+ values: latestMajorTags_copy
153
+ }).catch(() => {
154
+ process.exit(-1);
155
+ });
156
+
157
+ tag = value;
158
+
159
+ {
160
+ const doImplementAccountMpa =
161
+ buildContext.implementedThemeTypes.account.isImplemented &&
162
+ buildContext.implementedThemeTypes.account.type === "Multi-Page";
163
+
164
+ if (doImplementAccountMpa && tag.startsWith("22.")) {
165
+ console.log(
166
+ chalk.yellow(
167
+ `You are implementing a Multi-Page Account theme. Keycloak 22 is not supported, select another version`
168
+ )
169
+ );
170
+ latestMajorTags_copy = latestMajorTags_copy.filter(
171
+ tag => !tag.startsWith("22.")
172
+ );
173
+ continue;
174
+ }
175
+ }
150
176
 
151
- console.log(`→ ${tag}`);
177
+ const readMajor = (tag: string) => {
178
+ const major = parseInt(tag.split(".")[0]);
179
+ assert(!isNaN(major));
180
+ return major;
181
+ };
152
182
 
153
- return { dockerImageTag: tag };
183
+ {
184
+ const major = readMajor(tag);
185
+
186
+ const doImplementAdminTheme =
187
+ buildContext.implementedThemeTypes.admin.isImplemented;
188
+
189
+ const getIsSupported = (major: number) => major >= 23;
190
+
191
+ if (doImplementAdminTheme && !getIsSupported(major)) {
192
+ console.log(
193
+ chalk.yellow(
194
+ `You are implementing an Admin theme. Only Keycloak 23 and later are supported, select another version`
195
+ )
196
+ );
197
+ latestMajorTags_copy = latestMajorTags_copy.filter(tag =>
198
+ getIsSupported(readMajor(tag))
199
+ );
200
+ continue;
201
+ }
202
+ }
203
+
204
+ {
205
+ const doImplementAccountSpa =
206
+ buildContext.implementedThemeTypes.account.isImplemented &&
207
+ buildContext.implementedThemeTypes.account.type === "Single-Page";
208
+
209
+ const major = readMajor(tag);
210
+
211
+ const getIsSupported = (major: number) => major >= 19;
212
+
213
+ if (doImplementAccountSpa && !getIsSupported(major)) {
214
+ console.log(
215
+ chalk.yellow(
216
+ `You are implementing a Single-Page Account theme. Only Keycloak 19 and later are supported, select another version`
217
+ )
218
+ );
219
+ latestMajorTags_copy = latestMajorTags_copy.filter(tag =>
220
+ getIsSupported(readMajor(tag))
221
+ );
222
+ continue;
223
+ }
224
+ }
225
+
226
+ break;
227
+ }
228
+
229
+ return tag;
230
+ })();
231
+
232
+ console.log(`→ ${tag_userSelected}`);
233
+
234
+ return { dockerImageTag: tag_userSelected };
154
235
  })();
155
236
 
156
237
  const keycloakMajorVersionNumber = (() => {
@@ -623,42 +704,56 @@ export async function command(params: {
623
704
  }
624
705
  )
625
706
  .on("all", async (...[, filePath]) => {
626
- ignore_account_spa: {
627
- const doImplementAccountSpa =
628
- buildContext.implementedThemeTypes.account.isImplemented &&
629
- buildContext.implementedThemeTypes.account.type === "Single-Page";
630
-
631
- if (!doImplementAccountSpa) {
632
- break ignore_account_spa;
707
+ ignore_path_covered_by_hmr: {
708
+ if (filePath.endsWith(".properties")) {
709
+ break ignore_path_covered_by_hmr;
633
710
  }
634
711
 
635
- if (
636
- !isInside({
637
- dirPath: pathJoin(buildContext.themeSrcDirPath, "account"),
638
- filePath
639
- })
640
- ) {
641
- break ignore_account_spa;
712
+ if (!doStartDevServer) {
713
+ break ignore_path_covered_by_hmr;
642
714
  }
643
715
 
644
- return;
645
- }
646
-
647
- ignore_admin: {
648
- if (!buildContext.implementedThemeTypes.admin.isImplemented) {
649
- break ignore_admin;
716
+ ignore_account_spa: {
717
+ const doImplementAccountSpa =
718
+ buildContext.implementedThemeTypes.account.isImplemented &&
719
+ buildContext.implementedThemeTypes.account.type ===
720
+ "Single-Page";
721
+
722
+ if (!doImplementAccountSpa) {
723
+ break ignore_account_spa;
724
+ }
725
+
726
+ if (
727
+ !isInside({
728
+ dirPath: pathJoin(
729
+ buildContext.themeSrcDirPath,
730
+ "account"
731
+ ),
732
+ filePath
733
+ })
734
+ ) {
735
+ break ignore_account_spa;
736
+ }
737
+
738
+ return;
650
739
  }
651
740
 
652
- if (
653
- !isInside({
654
- dirPath: pathJoin(buildContext.themeSrcDirPath, "admin"),
655
- filePath
656
- })
657
- ) {
658
- break ignore_admin;
741
+ ignore_admin: {
742
+ if (!buildContext.implementedThemeTypes.admin.isImplemented) {
743
+ break ignore_admin;
744
+ }
745
+
746
+ if (
747
+ !isInside({
748
+ dirPath: pathJoin(buildContext.themeSrcDirPath, "admin"),
749
+ filePath
750
+ })
751
+ ) {
752
+ break ignore_admin;
753
+ }
754
+
755
+ return;
659
756
  }
660
-
661
- return;
662
757
  }
663
758
 
664
759
  console.log(`Detected changes in ${filePath}`);
@@ -0,0 +1,90 @@
1
+ const keyIsTrapped = "isTrapped_zSskDe9d";
2
+
3
+ export class AccessError extends Error {
4
+ constructor(message: string) {
5
+ super(message);
6
+ Object.setPrototypeOf(this, new.target.prototype);
7
+ }
8
+ }
9
+
10
+ export function createObjectThatThrowsIfAccessed<T extends object>(params?: {
11
+ debugMessage?: string;
12
+ isPropertyWhitelisted?: (prop: string | number | symbol) => boolean;
13
+ }): T {
14
+ const { debugMessage = "", isPropertyWhitelisted = () => false } = params ?? {};
15
+
16
+ const get: NonNullable<ProxyHandler<T>["get"]> = (...args) => {
17
+ const [, prop] = args;
18
+
19
+ if (isPropertyWhitelisted(prop)) {
20
+ return Reflect.get(...args);
21
+ }
22
+
23
+ if (prop === keyIsTrapped) {
24
+ return true;
25
+ }
26
+
27
+ throw new AccessError(`Cannot access ${String(prop)} yet ${debugMessage}`);
28
+ };
29
+
30
+ const trappedObject = new Proxy<T>({} as any, {
31
+ get,
32
+ set: get
33
+ });
34
+
35
+ return trappedObject;
36
+ }
37
+
38
+ export function createObjectThatThrowsIfAccessedFactory(params: {
39
+ isPropertyWhitelisted?: (prop: string | number | symbol) => boolean;
40
+ }) {
41
+ const { isPropertyWhitelisted } = params;
42
+
43
+ return {
44
+ createObjectThatThrowsIfAccessed: <T extends object>(params?: {
45
+ debugMessage?: string;
46
+ }) => {
47
+ const { debugMessage } = params ?? {};
48
+
49
+ return createObjectThatThrowsIfAccessed<T>({
50
+ debugMessage,
51
+ isPropertyWhitelisted
52
+ });
53
+ }
54
+ };
55
+ }
56
+
57
+ export function isObjectThatThrowIfAccessed(obj: object) {
58
+ return (obj as any)[keyIsTrapped] === true;
59
+ }
60
+
61
+ export const THROW_IF_ACCESSED = {
62
+ __brand: "THROW_IF_ACCESSED"
63
+ };
64
+
65
+ export function createObjectWithSomePropertiesThatThrowIfAccessed<
66
+ T extends Record<string, unknown>
67
+ >(obj: { [K in keyof T]: T[K] | typeof THROW_IF_ACCESSED }, debugMessage?: string): T {
68
+ return Object.defineProperties(
69
+ obj,
70
+ Object.fromEntries(
71
+ Object.entries(obj)
72
+ .filter(([, value]) => value === THROW_IF_ACCESSED)
73
+ .map(([key]) => {
74
+ const getAndSet = () => {
75
+ throw new AccessError(
76
+ `Cannot access ${key} yet ${debugMessage ?? ""}`
77
+ );
78
+ };
79
+
80
+ const pd = {
81
+ get: getAndSet,
82
+ set: getAndSet,
83
+ enumerable: true
84
+ };
85
+
86
+ return [key, pd];
87
+ })
88
+ )
89
+ ) as any;
90
+ }