keycloakify 7.6.2 → 7.6.4

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 (58) hide show
  1. package/account/kcContext/KcContext.d.ts +1 -0
  2. package/account/kcContext/KcContext.js.map +1 -1
  3. package/account/kcContext/getKcContext.js +3 -1
  4. package/account/kcContext/getKcContext.js.map +1 -1
  5. package/account/kcContext/kcContextMocks.js +1 -0
  6. package/account/kcContext/kcContextMocks.js.map +1 -1
  7. package/bin/eject-keycloak-page.js +7 -7
  8. package/bin/eject-keycloak-page.js.map +1 -1
  9. package/bin/keycloakify/generateFtl/ftl_object_to_js_code_declaring_an_object.ftl +2 -2
  10. package/bin/keycloakify/generateFtl/generateFtl.d.ts +1 -4
  11. package/bin/keycloakify/generateFtl/generateFtl.js +5 -32
  12. package/bin/keycloakify/generateFtl/generateFtl.js.map +1 -1
  13. package/bin/keycloakify/generateFtl/index.d.ts +1 -0
  14. package/bin/keycloakify/generateFtl/index.js +1 -0
  15. package/bin/keycloakify/generateFtl/index.js.map +1 -1
  16. package/bin/keycloakify/generateFtl/pageId.d.ts +4 -0
  17. package/bin/keycloakify/generateFtl/pageId.js +30 -0
  18. package/bin/keycloakify/generateFtl/pageId.js.map +1 -0
  19. package/bin/keycloakify/generateKeycloakThemeResources.d.ts +1 -0
  20. package/bin/keycloakify/generateKeycloakThemeResources.js +4 -3
  21. package/bin/keycloakify/generateKeycloakThemeResources.js.map +1 -1
  22. package/bin/keycloakify/keycloakify.js +7 -1
  23. package/bin/keycloakify/keycloakify.js.map +1 -1
  24. package/bin/tools/jar.d.ts +19 -3
  25. package/bin/tools/jar.js +151 -58
  26. package/bin/tools/jar.js.map +1 -1
  27. package/bin/tools/walk.d.ts +1 -1
  28. package/login/kcContext/KcContext.d.ts +1 -0
  29. package/login/kcContext/KcContext.js.map +1 -1
  30. package/login/kcContext/getKcContext.js +2 -1
  31. package/login/kcContext/getKcContext.js.map +1 -1
  32. package/login/kcContext/kcContextMocks.js +1 -0
  33. package/login/kcContext/kcContextMocks.js.map +1 -1
  34. package/login/lib/useDownloadTerms.d.ts +1 -2
  35. package/login/pages/Terms.js +3 -2
  36. package/login/pages/Terms.js.map +1 -1
  37. package/package.json +7 -5
  38. package/src/account/kcContext/KcContext.ts +1 -0
  39. package/src/account/kcContext/getKcContext.ts +3 -1
  40. package/src/account/kcContext/kcContextMocks.ts +1 -0
  41. package/src/bin/eject-keycloak-page.ts +3 -5
  42. package/src/bin/keycloakify/generateFtl/ftl_object_to_js_code_declaring_an_object.ftl +2 -2
  43. package/src/bin/keycloakify/generateFtl/generateFtl.ts +4 -34
  44. package/src/bin/keycloakify/generateFtl/index.ts +1 -0
  45. package/src/bin/keycloakify/generateFtl/pageId.ts +30 -0
  46. package/src/bin/keycloakify/generateKeycloakThemeResources.ts +4 -2
  47. package/src/bin/keycloakify/keycloakify.ts +9 -1
  48. package/src/bin/tools/jar.ts +58 -65
  49. package/src/bin/tools/walk.ts +1 -1
  50. package/src/login/kcContext/KcContext.ts +1 -0
  51. package/src/login/kcContext/getKcContext.ts +2 -1
  52. package/src/login/kcContext/kcContextMocks.ts +1 -0
  53. package/src/login/lib/useDownloadTerms.ts +1 -1
  54. package/src/login/pages/Terms.tsx +6 -2
  55. package/bin/tools/zip.d.ts +0 -29
  56. package/bin/tools/zip.js +0 -330
  57. package/bin/tools/zip.js.map +0 -1
  58. package/src/bin/tools/zip.ts +0 -246
@@ -13,37 +13,6 @@ export const themeTypes = ["login", "account"] as const;
13
13
 
14
14
  export type ThemeType = (typeof themeTypes)[number];
15
15
 
16
- export const loginThemePageIds = [
17
- "login.ftl",
18
- "login-username.ftl",
19
- "login-password.ftl",
20
- "webauthn-authenticate.ftl",
21
- "register.ftl",
22
- "register-user-profile.ftl",
23
- "info.ftl",
24
- "error.ftl",
25
- "login-reset-password.ftl",
26
- "login-verify-email.ftl",
27
- "terms.ftl",
28
- "login-otp.ftl",
29
- "login-update-profile.ftl",
30
- "login-update-password.ftl",
31
- "login-idp-link-confirm.ftl",
32
- "login-idp-link-email.ftl",
33
- "login-page-expired.ftl",
34
- "login-config-totp.ftl",
35
- "logout-confirm.ftl",
36
- "update-user-profile.ftl",
37
- "idp-review-user-profile.ftl",
38
- "update-email.ftl",
39
- "select-authenticator.ftl"
40
- ] as const;
41
-
42
- export const accountThemePageIds = ["password.ftl", "account.ftl"] as const;
43
-
44
- export type LoginThemePageId = (typeof loginThemePageIds)[number];
45
- export type AccountThemePageId = (typeof accountThemePageIds)[number];
46
-
47
16
  export type BuildOptionsLike = BuildOptionsLike.Standalone | BuildOptionsLike.ExternalAssets;
48
17
 
49
18
  export namespace BuildOptionsLike {
@@ -84,8 +53,9 @@ export function generateFtlFilesCodeFactory(params: {
84
53
  //NOTE: Expected to be an empty object if external assets mode is enabled.
85
54
  cssGlobalsToDefine: Record<string, string>;
86
55
  buildOptions: BuildOptionsLike;
56
+ keycloakifyVersion: string;
87
57
  }) {
88
- const { cssGlobalsToDefine, indexHtmlCode, buildOptions } = params;
58
+ const { cssGlobalsToDefine, indexHtmlCode, buildOptions, keycloakifyVersion } = params;
89
59
 
90
60
  const $ = cheerio.load(indexHtmlCode);
91
61
 
@@ -159,7 +129,8 @@ export function generateFtlFilesCodeFactory(params: {
159
129
  .replace(
160
130
  "CUSTOM_USER_ATTRIBUTES_eKsIY4ZsZ4xeM",
161
131
  buildOptions.customUserAttributes.length === 0 ? "" : ", " + buildOptions.customUserAttributes.map(name => `"${name}"`).join(", ")
162
- ),
132
+ )
133
+ .replace("KEYCLOAKIFY_VERSION_xEdKd3xEdr", keycloakifyVersion),
163
134
  "<!-- xIdLqMeOedErIdLsPdNdI9dSlxI -->": [
164
135
  "<#if scripts??>",
165
136
  " <#list scripts as script>",
@@ -192,7 +163,6 @@ export function generateFtlFilesCodeFactory(params: {
192
163
 
193
164
  Object.entries({
194
165
  ...replaceValueBySearchValue,
195
- //If updated, don't forget to change in the ftl script as well.
196
166
  "PAGE_ID_xIgLsPgGId9D8e": pageId
197
167
  }).map(([searchValue, replaceValue]) => (ftlCode = ftlCode.replace(searchValue, replaceValue)));
198
168
 
@@ -1 +1,2 @@
1
1
  export * from "./generateFtl";
2
+ export * from "./pageId";
@@ -0,0 +1,30 @@
1
+ export const loginThemePageIds = [
2
+ "login.ftl",
3
+ "login-username.ftl",
4
+ "login-password.ftl",
5
+ "webauthn-authenticate.ftl",
6
+ "register.ftl",
7
+ "register-user-profile.ftl",
8
+ "info.ftl",
9
+ "error.ftl",
10
+ "login-reset-password.ftl",
11
+ "login-verify-email.ftl",
12
+ "terms.ftl",
13
+ "login-otp.ftl",
14
+ "login-update-profile.ftl",
15
+ "login-update-password.ftl",
16
+ "login-idp-link-confirm.ftl",
17
+ "login-idp-link-email.ftl",
18
+ "login-page-expired.ftl",
19
+ "login-config-totp.ftl",
20
+ "logout-confirm.ftl",
21
+ "update-user-profile.ftl",
22
+ "idp-review-user-profile.ftl",
23
+ "update-email.ftl",
24
+ "select-authenticator.ftl"
25
+ ] as const;
26
+
27
+ export const accountThemePageIds = ["password.ftl", "account.ftl"] as const;
28
+
29
+ export type LoginThemePageId = (typeof loginThemePageIds)[number];
30
+ export type AccountThemePageId = (typeof accountThemePageIds)[number];
@@ -54,8 +54,9 @@ export async function generateKeycloakThemeResources(params: {
54
54
  emailThemeSrcDirPath: string | undefined;
55
55
  keycloakVersion: string;
56
56
  buildOptions: BuildOptionsLike;
57
+ keycloakifyVersion: string;
57
58
  }): Promise<{ doBundlesEmailTemplate: boolean }> {
58
- const { reactAppBuildDirPath, keycloakThemeBuildingDirPath, emailThemeSrcDirPath, keycloakVersion, buildOptions } = params;
59
+ const { reactAppBuildDirPath, keycloakThemeBuildingDirPath, emailThemeSrcDirPath, keycloakVersion, buildOptions, keycloakifyVersion } = params;
59
60
 
60
61
  const getThemeDirPath = (themeType: ThemeType | "email") =>
61
62
  pathJoin(keycloakThemeBuildingDirPath, "src", "main", "resources", "theme", buildOptions.themeName, themeType);
@@ -138,7 +139,8 @@ export async function generateKeycloakThemeResources(params: {
138
139
  const { generateFtlFilesCode } = generateFtlFilesCodeFactory({
139
140
  "indexHtmlCode": fs.readFileSync(pathJoin(reactAppBuildDirPath, "index.html")).toString("utf8"),
140
141
  "cssGlobalsToDefine": allCssGlobalsToDefine,
141
- buildOptions
142
+ buildOptions,
143
+ keycloakifyVersion
142
144
  });
143
145
 
144
146
  return generateFtlFilesCode;
@@ -11,6 +11,7 @@ import jar from "../tools/jar";
11
11
  import { assert } from "tsafe/assert";
12
12
  import { Equals } from "tsafe";
13
13
  import { getEmailThemeSrcDirPath } from "../getSrcDirPath";
14
+ import { getProjectRoot } from "../tools/getProjectRoot";
14
15
 
15
16
  export async function main() {
16
17
  const { isSilent, hasExternalAssets } = getCliOptions(process.argv.slice(2));
@@ -38,7 +39,14 @@ export async function main() {
38
39
  })(),
39
40
  "reactAppBuildDirPath": buildOptions.reactAppBuildDirPath,
40
41
  buildOptions,
41
- "keycloakVersion": buildOptions.keycloakVersionDefaultAssets
42
+ "keycloakVersion": buildOptions.keycloakVersionDefaultAssets,
43
+ "keycloakifyVersion": (() => {
44
+ const version = JSON.parse(fs.readFileSync(pathJoin(getProjectRoot(), "package.json")).toString("utf8"))["version"];
45
+
46
+ assert(typeof version === "string");
47
+
48
+ return version;
49
+ })()
42
50
  });
43
51
 
44
52
  const { jarFilePath } = generateJavaStackFiles({
@@ -1,94 +1,87 @@
1
- import { Readable, Transform } from "stream";
2
1
  import { dirname, relative, sep } from "path";
3
2
  import { createWriteStream } from "fs";
4
3
 
5
4
  import walk from "./walk";
6
- import zip, { type ZipSource } from "./zip";
5
+ import { ZipFile } from "yazl";
7
6
  import { mkdir } from "fs/promises";
8
7
  import trimIndent from "./trimIndent";
9
8
 
10
- type JarArgs = {
11
- rootPath: string;
12
- targetPath: string;
9
+ export type ZipEntry = { zipPath: string } & ({ fsPath: string } | { buffer: Buffer });
10
+ export type ZipEntryGenerator = AsyncGenerator<ZipEntry, void, unknown>;
11
+
12
+ type CommonJarArgs = {
13
13
  groupId: string;
14
14
  artifactId: string;
15
15
  version: string;
16
16
  };
17
17
 
18
- /**
19
- * Create a jar archive, using the resources found at `rootPath` (a directory) and write the
20
- * archive to `targetPath` (a file). Use `groupId`, `artifactId` and `version` to define
21
- * the contents of the pom.properties file which is going to be added to the archive.
22
- */
23
- export default async function jar({ groupId, artifactId, version, rootPath, targetPath }: JarArgs) {
24
- const manifest: ZipSource = {
25
- path: "META-INF/MANIFEST.MF",
26
- data: Buffer.from(trimIndent`
18
+ export type JarStreamArgs = CommonJarArgs & {
19
+ asyncPathGeneratorFn(): ZipEntryGenerator;
20
+ };
21
+
22
+ export type JarArgs = CommonJarArgs & {
23
+ targetPath: string;
24
+ rootPath: string;
25
+ };
26
+
27
+ export async function jarStream({ groupId, artifactId, version, asyncPathGeneratorFn }: JarStreamArgs) {
28
+ const manifestPath = "META-INF/MANIFEST.MF";
29
+ const manifestData = Buffer.from(trimIndent`
27
30
  Manifest-Version: 1.0
28
31
  Archiver-Version: Plexus Archiver
29
32
  Created-By: Keycloakify
30
33
  Built-By: unknown
31
34
  Build-Jdk: 19.0.0
32
- `)
33
- };
35
+ `);
34
36
 
35
- const pomProps: ZipSource = {
36
- path: `META-INF/maven/${groupId}/${artifactId}/pom.properties`,
37
- data: Buffer.from(trimIndent`# Generated by keycloakify
38
- # ${new Date().toString()}
37
+ const pomPropsPath = `META-INF/maven/${groupId}/${artifactId}/pom.properties`;
38
+ const pomPropsData = Buffer.from(trimIndent`
39
+ # Generated by keycloakify
40
+ # ${new Date()}
39
41
  artifactId=${artifactId}
40
42
  groupId=${groupId}
41
43
  version=${version}
42
- `)
43
- };
44
+ `);
44
45
 
45
- /**
46
- * Convert every path entry to a ZipSource record, and when all records are
47
- * processed, append records for MANIFEST.mf and pom.properties
48
- */
49
- const pathToRecord = () =>
50
- new Transform({
51
- objectMode: true,
52
- transform: function (fsPath, _, cb) {
53
- const path = relative(rootPath, fsPath).split(sep).join("/");
54
- this.push({ path, fsPath });
55
- cb();
56
- },
57
- final: function () {
58
- this.push(manifest);
59
- this.push(pomProps);
60
- this.push(null);
61
- }
62
- });
46
+ const zipFile = new ZipFile();
63
47
 
64
- await mkdir(dirname(targetPath), { recursive: true });
48
+ for await (const entry of asyncPathGeneratorFn()) {
49
+ if ("buffer" in entry) {
50
+ zipFile.addBuffer(entry.buffer, entry.zipPath);
51
+ } else if ("fsPath" in entry && entry.fsPath.endsWith(sep)) {
52
+ zipFile.addFile(entry.fsPath, entry.zipPath);
53
+ }
54
+ }
65
55
 
66
- // Create an async pipeline, wait until everything is fully processed
67
- await new Promise<void>((resolve, reject) => {
68
- // walk all files in `rootPath` recursively
69
- Readable.from(walk(rootPath))
70
- // transform every path into a ZipSource object
71
- .pipe(pathToRecord())
72
- // let the zip lib convert all ZipSource objects into a byte stream
73
- .pipe(zip())
74
- // write that byte stream to targetPath
75
- .pipe(createWriteStream(targetPath, { encoding: "binary" }))
76
- .on("finish", () => resolve())
77
- .on("error", e => reject(e));
78
- });
56
+ zipFile.addBuffer(manifestData, manifestPath);
57
+ zipFile.addBuffer(pomPropsData, pomPropsPath);
58
+
59
+ zipFile.end();
60
+
61
+ return zipFile;
79
62
  }
80
63
 
81
64
  /**
82
- * Standalone usage, call e.g. `ts-node jar.ts dirWithSources some-jar.jar`
65
+ * Create a jar archive, using the resources found at `rootPath` (a directory) and write the
66
+ * archive to `targetPath` (a file). Use `groupId`, `artifactId` and `version` to define
67
+ * the contents of the pom.properties file which is going to be added to the archive.
83
68
  */
84
- if (require.main === module) {
85
- const main = () =>
86
- jar({
87
- rootPath: process.argv[2],
88
- targetPath: process.argv[3],
89
- artifactId: process.env.ARTIFACT_ID ?? "artifact",
90
- groupId: process.env.GROUP_ID ?? "group",
91
- version: process.env.VERSION ?? "1.0.0"
92
- });
93
- main();
69
+ export default async function jar({ groupId, artifactId, version, rootPath, targetPath }: JarArgs) {
70
+ await mkdir(dirname(targetPath), { recursive: true });
71
+
72
+ const asyncPathGeneratorFn = async function* (): ZipEntryGenerator {
73
+ for await (const fsPath of walk(rootPath)) {
74
+ const zipPath = relative(rootPath, fsPath).split(sep).join("/");
75
+ yield { fsPath, zipPath };
76
+ }
77
+ };
78
+
79
+ const zipFile = await jarStream({ groupId, artifactId, version, asyncPathGeneratorFn });
80
+
81
+ await new Promise<void>(async (resolve, reject) => {
82
+ zipFile.outputStream
83
+ .pipe(createWriteStream(targetPath, { encoding: "binary" }))
84
+ .on("close", () => resolve())
85
+ .on("error", e => reject(e));
86
+ });
94
87
  }
@@ -8,7 +8,7 @@ import { resolve } from "path";
8
8
  * @param root the starting directory
9
9
  * @returns AsyncGenerator
10
10
  */
11
- export default async function* walk(root: string): AsyncGenerator<string, void, void> {
11
+ export default async function* walk(root: string): AsyncGenerator<string, void, unknown> {
12
12
  for (const entry of await readdir(root, { withFileTypes: true })) {
13
13
  const absolutePath = resolve(root, entry.name);
14
14
  if (entry.isDirectory()) {
@@ -36,6 +36,7 @@ export type KcContext =
36
36
 
37
37
  export declare namespace KcContext {
38
38
  export type Common = {
39
+ keycloakifyVersion: string;
39
40
  url: {
40
41
  loginAction: string;
41
42
  resourcesPath: string;
@@ -11,6 +11,7 @@ import { pathJoin } from "keycloakify/bin/tools/pathJoin";
11
11
  import { pathBasename } from "keycloakify/tools/pathBasename";
12
12
  import { mockTestingResourcesCommonPath } from "keycloakify/bin/mockTestingResourcesPath";
13
13
  import { symToStr } from "tsafe/symToStr";
14
+ import { loginThemePageIds } from "keycloakify/bin/keycloakify/generateFtl/pageId";
14
15
 
15
16
  export function getKcContext<KcContextExtension extends { pageId: string } = never>(params?: {
16
17
  mockPageId?: ExtendKcContext<KcContextExtension>["pageId"];
@@ -121,7 +122,7 @@ export function getKcContext<KcContextExtension extends { pageId: string } = nev
121
122
  return { "kcContext": undefined };
122
123
  }
123
124
 
124
- if (!("login" in realKcContext)) {
125
+ if (id<readonly string[]>(loginThemePageIds).indexOf(realKcContext.pageId) < 0 && !("login" in realKcContext)) {
125
126
  return { "kcContext": undefined };
126
127
  }
127
128
 
@@ -101,6 +101,7 @@ const attributes: Attribute[] = [
101
101
  const attributesByName = Object.fromEntries(attributes.map(attribute => [attribute.name, attribute])) as any;
102
102
 
103
103
  export const kcContextCommonMock: KcContext.Common = {
104
+ "keycloakifyVersion": "0.0.0",
104
105
  "url": {
105
106
  "loginAction": "#",
106
107
  "resourcesPath": pathJoin(PUBLIC_URL, mockTestingResourcesPath),
@@ -10,7 +10,7 @@ import { KcContext } from "../kcContext";
10
10
  export const evtTermMarkdown = Evt.create<string | undefined>(undefined);
11
11
 
12
12
  export type KcContextLike = {
13
- pageId: KcContext["pageId"];
13
+ pageId: string;
14
14
  locale?: {
15
15
  currentLanguageTag: string;
16
16
  };
@@ -21,13 +21,17 @@ export default function Terms(props: PageProps<Extract<KcContext, { pageId: "ter
21
21
 
22
22
  const { url } = kcContext;
23
23
 
24
- if (evtTermMarkdown.state === undefined) {
24
+ const termMarkdown = evtTermMarkdown.state;
25
+
26
+ if (termMarkdown === undefined) {
25
27
  return null;
26
28
  }
27
29
 
28
30
  return (
29
31
  <Template {...{ kcContext, i18n, doUseDefaultCss, classes }} displayMessage={false} headerNode={msg("termsTitle")}>
30
- <div id="kc-terms-text">{evtTermMarkdown.state && <Markdown>{evtTermMarkdown.state}</Markdown>}</div>
32
+ <div id="kc-terms-text">
33
+ <Markdown>{termMarkdown}</Markdown>
34
+ </div>
31
35
  <form className="form-actions" action={url.loginAction} method="POST">
32
36
  <input
33
37
  className={clsx(
@@ -1,29 +0,0 @@
1
- /// <reference types="node" />
2
- /// <reference types="node" />
3
- import { Transform } from "stream";
4
- /**
5
- * Zip source
6
- * @property filename the name of the entry in the archie
7
- * @property path of the source file, if the source is an actual file
8
- * @property data the actual data buffer, if the source is constructed in-memory
9
- */
10
- export type ZipSource = {
11
- path: string;
12
- } & ({
13
- fsPath: string;
14
- } | {
15
- data: Buffer;
16
- });
17
- export type ZipRecord = {
18
- path: string;
19
- compression: "deflate" | undefined;
20
- uncompressedSize: number;
21
- compressedSize?: number;
22
- crc32?: number;
23
- offset?: number;
24
- };
25
- /**
26
- * @returns a stream Transform, which reads a stream of ZipRecords and
27
- * writes a bytestream
28
- */
29
- export default function zip(): Transform;