keycloakify 10.0.0-rc.44 → 10.0.0-rc.46

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 (35) hide show
  1. package/bin/{322.index.js → 190.index.js} +58 -61
  2. package/bin/193.index.js +0 -51
  3. package/bin/3.index.js +93 -96
  4. package/bin/526.index.js +80 -49
  5. package/bin/538.index.js +5 -3
  6. package/bin/932.index.js +535 -1
  7. package/bin/98.index.js +5 -3
  8. package/bin/{944.index.js → 991.index.js} +74 -5
  9. package/bin/main.js +4 -16
  10. package/bin/shared/buildContext.d.ts +1 -1
  11. package/bin/shared/constants.d.ts +1 -1
  12. package/bin/shared/constants.js +1 -1
  13. package/bin/shared/constants.js.map +1 -1
  14. package/package.json +3 -6
  15. package/src/bin/keycloakify/buildJars/buildJar.ts +9 -21
  16. package/src/bin/keycloakify/buildJars/buildJars.ts +15 -4
  17. package/src/bin/keycloakify/buildJars/extensionVersions.ts +1 -1
  18. package/src/bin/keycloakify/buildJars/getKeycloakVersionRangeForJar.ts +9 -2
  19. package/src/bin/keycloakify/generateSrcMainResources/bringInAccountV1.ts +6 -7
  20. package/src/bin/keycloakify/generateSrcMainResources/generateSrcMainResources.ts +11 -3
  21. package/src/bin/keycloakify/generateSrcMainResources/generateSrcMainResourcesForMainTheme.ts +6 -18
  22. package/src/bin/keycloakify/generateSrcMainResources/generateSrcMainResourcesForThemeVariant.ts +9 -15
  23. package/src/bin/keycloakify/keycloakify.ts +20 -9
  24. package/src/bin/main.ts +0 -14
  25. package/src/bin/shared/KeycloakVersionRange.ts +1 -1
  26. package/src/bin/shared/buildContext.ts +7 -3
  27. package/src/bin/shared/constants.ts +1 -1
  28. package/src/bin/shared/metaInfKeycloakThemes.ts +37 -14
  29. package/src/bin/start-keycloak/keycloakifyBuild.ts +4 -4
  30. package/src/bin/start-keycloak/start-keycloak.ts +62 -67
  31. package/src/vite-plugin/vite-plugin.ts +4 -0
  32. package/vite-plugin/index.js +6 -3
  33. package/bin/961.index.js +0 -263
  34. package/src/bin/download-keycloak-default-theme.ts +0 -63
  35. package/src/bin/keycloakify/generateStartKeycloakTestingContainer.ts +0 -74
@@ -3,12 +3,16 @@ import { join as pathJoin, relative as pathRelative, sep as pathSep } from "path
3
3
  import * as child_process from "child_process";
4
4
  import * as fs from "fs";
5
5
  import { getBuildContext } from "../shared/buildContext";
6
- import { vitePluginSubScriptEnvNames, skipBuildJarsEnvName } from "../shared/constants";
6
+ import {
7
+ vitePluginSubScriptEnvNames,
8
+ onlyBuildJarFileBasenameEnvName
9
+ } from "../shared/constants";
7
10
  import { buildJars } from "./buildJars";
8
11
  import type { CliCommandOptions } from "../main";
9
12
  import chalk from "chalk";
10
13
  import { readThisNpmPackageVersion } from "../tools/readThisNpmPackageVersion";
11
14
  import * as os from "os";
15
+ import { rmSync } from "../tools/fs.rmSync";
12
16
 
13
17
  export async function command(params: { cliCommandOptions: CliCommandOptions }) {
14
18
  exit_if_maven_not_installed: {
@@ -76,7 +80,12 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
76
80
  );
77
81
  }
78
82
 
79
- await generateSrcMainResources({ buildContext });
83
+ const resourcesDirPath = pathJoin(buildContext.keycloakifyBuildDirPath, "resources");
84
+
85
+ await generateSrcMainResources({
86
+ resourcesDirPath,
87
+ buildContext
88
+ });
80
89
 
81
90
  run_post_build_script: {
82
91
  if (buildContext.bundler !== "vite") {
@@ -93,15 +102,17 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
93
102
  });
94
103
  }
95
104
 
96
- build_jars: {
97
- if (process.env[skipBuildJarsEnvName]) {
98
- break build_jars;
99
- }
105
+ await buildJars({
106
+ resourcesDirPath,
107
+ buildContext,
108
+ onlyBuildJarFileBasename: process.env[onlyBuildJarFileBasenameEnvName]
109
+ });
100
110
 
101
- await buildJars({ buildContext });
102
- }
111
+ rmSync(resourcesDirPath, { recursive: true });
103
112
 
104
113
  console.log(
105
- chalk.green(`✓ built in ${((Date.now() - startTime) / 1000).toFixed(2)}s`)
114
+ chalk.green(
115
+ `✓ keycloak theme built in ${((Date.now() - startTime) / 1000).toFixed(2)}s`
116
+ )
106
117
  );
107
118
  }
package/src/bin/main.ts CHANGED
@@ -134,20 +134,6 @@ program
134
134
  }
135
135
  });
136
136
 
137
- program
138
- .command({
139
- name: "download-keycloak-default-theme",
140
- description: "Download the built-in Keycloak theme."
141
- })
142
- .task({
143
- skip,
144
- handler: async cliCommandOptions => {
145
- const { command } = await import("./download-keycloak-default-theme");
146
-
147
- await command({ cliCommandOptions });
148
- }
149
- });
150
-
151
137
  program
152
138
  .command({
153
139
  name: "eject-page",
@@ -5,5 +5,5 @@ export type KeycloakVersionRange =
5
5
  export namespace KeycloakVersionRange {
6
6
  export type WithoutAccountTheme = "21-and-below" | "22-and-above";
7
7
 
8
- export type WithAccountTheme = "21-and-below" | "23" | "24-and-above";
8
+ export type WithAccountTheme = "21-and-below" | "23" | "24" | "25-and-above";
9
9
  }
@@ -12,7 +12,7 @@ import { vitePluginSubScriptEnvNames } from "./constants";
12
12
  export type BuildContext = {
13
13
  bundler: "vite" | "webpack";
14
14
  themeVersion: string;
15
- themeNames: string[];
15
+ themeNames: [string, ...string[]];
16
16
  extraThemeProperties: string[] | undefined;
17
17
  groupId: string;
18
18
  artifactId: string;
@@ -147,7 +147,7 @@ export function getBuildContext(params: {
147
147
  ...resolvedViteConfig?.buildOptions
148
148
  };
149
149
 
150
- const themeNames = (() => {
150
+ const themeNames = ((): [string, ...string[]] => {
151
151
  if (buildOptions.themeName === undefined) {
152
152
  return [
153
153
  parsedPackageJson.name
@@ -161,7 +161,11 @@ export function getBuildContext(params: {
161
161
  return [buildOptions.themeName];
162
162
  }
163
163
 
164
- return buildOptions.themeName;
164
+ const [mainThemeName, ...themeVariantNames] = buildOptions.themeName;
165
+
166
+ assert(mainThemeName !== undefined);
167
+
168
+ return [mainThemeName, ...themeVariantNames];
165
169
  })();
166
170
 
167
171
  const projectBuildDirPath = (() => {
@@ -16,7 +16,7 @@ export const vitePluginSubScriptEnvNames = {
16
16
  resolveViteConfig: "KEYCLOAKIFY_RESOLVE_VITE_CONFIG"
17
17
  } as const;
18
18
 
19
- export const skipBuildJarsEnvName = "KEYCLOAKIFY_SKIP_BUILD_JAR";
19
+ export const onlyBuildJarFileBasenameEnvName = "KEYCLOAKIFY_ONLY_BUILD_JAR_FILE_BASENAME";
20
20
 
21
21
  export const loginThemePageIds = [
22
22
  "login.ftl",
@@ -1,50 +1,73 @@
1
1
  import { join as pathJoin, dirname as pathDirname } from "path";
2
2
  import type { ThemeType } from "./constants";
3
3
  import * as fs from "fs";
4
+ import { assert } from "tsafe/assert";
5
+ import { extractArchive } from "../tools/extractArchive";
4
6
 
5
7
  export type MetaInfKeycloakTheme = {
6
8
  themes: { name: string; types: (ThemeType | "email")[] }[];
7
9
  };
8
10
 
9
11
  export function getMetaInfKeycloakThemesJsonFilePath(params: {
10
- keycloakifyBuildDirPath: string;
12
+ resourcesDirPath: string;
11
13
  }) {
12
- const { keycloakifyBuildDirPath } = params;
14
+ const { resourcesDirPath } = params;
13
15
 
14
16
  return pathJoin(
15
- keycloakifyBuildDirPath === "." ? "" : keycloakifyBuildDirPath,
16
- "src",
17
- "main",
18
- "resources",
17
+ resourcesDirPath === "." ? "" : resourcesDirPath,
19
18
  "META-INF",
20
19
  "keycloak-themes.json"
21
20
  );
22
21
  }
23
22
 
24
- export function readMetaInfKeycloakThemes(params: {
25
- keycloakifyBuildDirPath: string;
26
- }): MetaInfKeycloakTheme {
27
- const { keycloakifyBuildDirPath } = params;
23
+ export function readMetaInfKeycloakThemes_fromResourcesDirPath(params: {
24
+ resourcesDirPath: string;
25
+ }) {
26
+ const { resourcesDirPath } = params;
28
27
 
29
28
  return JSON.parse(
30
29
  fs
31
30
  .readFileSync(
32
31
  getMetaInfKeycloakThemesJsonFilePath({
33
- keycloakifyBuildDirPath
32
+ resourcesDirPath
34
33
  })
35
34
  )
36
35
  .toString("utf8")
37
36
  ) as MetaInfKeycloakTheme;
38
37
  }
39
38
 
39
+ export async function readMetaInfKeycloakThemes_fromJar(params: {
40
+ jarFilePath: string;
41
+ }): Promise<MetaInfKeycloakTheme> {
42
+ const { jarFilePath } = params;
43
+ let metaInfKeycloakThemes: MetaInfKeycloakTheme | undefined = undefined;
44
+
45
+ await extractArchive({
46
+ archiveFilePath: jarFilePath,
47
+ onArchiveFile: async ({ relativeFilePathInArchive, readFile, earlyExit }) => {
48
+ if (
49
+ relativeFilePathInArchive ===
50
+ getMetaInfKeycloakThemesJsonFilePath({ resourcesDirPath: "." })
51
+ ) {
52
+ metaInfKeycloakThemes = JSON.parse((await readFile()).toString("utf8"));
53
+ earlyExit();
54
+ }
55
+ }
56
+ });
57
+
58
+ assert(metaInfKeycloakThemes !== undefined);
59
+
60
+ return metaInfKeycloakThemes;
61
+ }
62
+
40
63
  export function writeMetaInfKeycloakThemes(params: {
41
- keycloakifyBuildDirPath: string;
64
+ resourcesDirPath: string;
42
65
  metaInfKeycloakThemes: MetaInfKeycloakTheme;
43
66
  }) {
44
- const { keycloakifyBuildDirPath, metaInfKeycloakThemes } = params;
67
+ const { resourcesDirPath, metaInfKeycloakThemes } = params;
45
68
 
46
69
  const metaInfKeycloakThemesJsonPath = getMetaInfKeycloakThemesJsonFilePath({
47
- keycloakifyBuildDirPath
70
+ resourcesDirPath
48
71
  });
49
72
 
50
73
  {
@@ -1,4 +1,4 @@
1
- import { skipBuildJarsEnvName } from "../shared/constants";
1
+ import { onlyBuildJarFileBasenameEnvName } from "../shared/constants";
2
2
  import * as child_process from "child_process";
3
3
  import { Deferred } from "evt/tools/Deferred";
4
4
  import { assert } from "tsafe/assert";
@@ -14,10 +14,10 @@ export type BuildContextLike = {
14
14
  assert<BuildContext extends BuildContextLike ? true : false>();
15
15
 
16
16
  export async function keycloakifyBuild(params: {
17
- doSkipBuildJars: boolean;
17
+ onlyBuildJarFileBasename: string | undefined;
18
18
  buildContext: BuildContextLike;
19
19
  }): Promise<{ isKeycloakifyBuildSuccess: boolean }> {
20
- const { buildContext, doSkipBuildJars } = params;
20
+ const { buildContext, onlyBuildJarFileBasename } = params;
21
21
 
22
22
  const dResult = new Deferred<{ isSuccess: boolean }>();
23
23
 
@@ -25,7 +25,7 @@ export async function keycloakifyBuild(params: {
25
25
  cwd: buildContext.projectDirPath,
26
26
  env: {
27
27
  ...process.env,
28
- ...(doSkipBuildJars ? { [skipBuildJarsEnvName]: "true" } : {})
28
+ [onlyBuildJarFileBasenameEnvName]: onlyBuildJarFileBasename
29
29
  }
30
30
  });
31
31
 
@@ -2,7 +2,7 @@ import { getBuildContext } from "../shared/buildContext";
2
2
  import { exclude } from "tsafe/exclude";
3
3
  import type { CliCommandOptions as CliCommandOptions_common } from "../main";
4
4
  import { promptKeycloakVersion } from "../shared/promptKeycloakVersion";
5
- import { readMetaInfKeycloakThemes } from "../shared/metaInfKeycloakThemes";
5
+ import { readMetaInfKeycloakThemes_fromJar } from "../shared/metaInfKeycloakThemes";
6
6
  import { accountV1ThemeName, containerName } from "../shared/constants";
7
7
  import { SemVer } from "../tools/SemVer";
8
8
  import type { KeycloakVersionRange } from "../shared/KeycloakVersionRange";
@@ -21,6 +21,9 @@ import * as runExclusive from "run-exclusive";
21
21
  import { extractArchive } from "../tools/extractArchive";
22
22
  import { appBuild } from "./appBuild";
23
23
  import { keycloakifyBuild } from "./keycloakifyBuild";
24
+ import { isInside } from "../tools/isInside";
25
+ import { existsAsync } from "../tools/fs.existsAsync";
26
+ import { rm } from "../tools/fs.rm";
24
27
 
25
28
  export type CliCommandOptions = CliCommandOptions_common & {
26
29
  port: number;
@@ -98,7 +101,7 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
98
101
  }
99
102
 
100
103
  const { isKeycloakifyBuildSuccess } = await keycloakifyBuild({
101
- doSkipBuildJars: false,
104
+ onlyBuildJarFileBasename: undefined,
102
105
  buildContext
103
106
  });
104
107
 
@@ -112,13 +115,31 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
112
115
  }
113
116
  }
114
117
 
115
- const metaInfKeycloakThemes = readMetaInfKeycloakThemes({
116
- keycloakifyBuildDirPath: buildContext.keycloakifyBuildDirPath
117
- });
118
+ const { doesImplementAccountTheme } = await (async () => {
119
+ const latestJarFilePath = fs
120
+ .readdirSync(buildContext.keycloakifyBuildDirPath)
121
+ .filter(fileBasename => fileBasename.endsWith(".jar"))
122
+ .map(fileBasename =>
123
+ pathJoin(buildContext.keycloakifyBuildDirPath, fileBasename)
124
+ )
125
+ .sort((a, b) => fs.statSync(b).mtimeMs - fs.statSync(a).mtimeMs)[0];
126
+
127
+ assert(latestJarFilePath !== undefined);
128
+
129
+ const metaInfKeycloakThemes = await readMetaInfKeycloakThemes_fromJar({
130
+ jarFilePath: latestJarFilePath
131
+ });
132
+
133
+ const mainThemeEntry = metaInfKeycloakThemes.themes.find(
134
+ ({ name }) => name === buildContext.themeNames[0]
135
+ );
136
+
137
+ assert(mainThemeEntry !== undefined);
118
138
 
119
- const doesImplementAccountTheme = metaInfKeycloakThemes.themes.some(
120
- ({ name }) => name === accountV1ThemeName
121
- );
139
+ const doesImplementAccountTheme = mainThemeEntry.types.includes("account");
140
+
141
+ return { doesImplementAccountTheme };
142
+ })();
122
143
 
123
144
  const { keycloakVersion, keycloakMajorNumber: keycloakMajorVersionNumber } =
124
145
  await (async function getKeycloakMajor(): Promise<{
@@ -172,7 +193,11 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
172
193
  return "23" as const;
173
194
  }
174
195
 
175
- return "24-and-above" as const;
196
+ if (keycloakMajorVersionNumber === 24) {
197
+ return "24" as const;
198
+ }
199
+
200
+ return "25-and-above" as const;
176
201
  })();
177
202
 
178
203
  assert<
@@ -262,65 +287,30 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
262
287
 
263
288
  const jarFilePath = pathJoin(buildContext.keycloakifyBuildDirPath, jarFileBasename);
264
289
 
265
- const { doUseBuiltInAccountV1Theme } = await (async () => {
266
- let doUseBuiltInAccountV1Theme = false;
267
-
290
+ async function extractThemeResourcesFromJar() {
268
291
  await extractArchive({
269
292
  archiveFilePath: jarFilePath,
270
- onArchiveFile: async ({ relativeFilePathInArchive, readFile, earlyExit }) => {
271
- for (const themeName of buildContext.themeNames) {
272
- if (
273
- relativeFilePathInArchive ===
274
- pathJoin("theme", themeName, "account", "theme.properties")
275
- ) {
276
- if (
277
- (await readFile())
278
- .toString("utf8")
279
- .includes("parent=keycloak")
280
- ) {
281
- doUseBuiltInAccountV1Theme = true;
282
- }
283
-
284
- earlyExit();
285
- }
293
+ onArchiveFile: async ({ relativeFilePathInArchive, writeFile }) => {
294
+ if (isInside({ dirPath: "theme", filePath: relativeFilePathInArchive })) {
295
+ await writeFile({
296
+ filePath: pathJoin(
297
+ buildContext.keycloakifyBuildDirPath,
298
+ relativeFilePathInArchive
299
+ )
300
+ });
286
301
  }
287
302
  }
288
303
  });
304
+ }
289
305
 
290
- return { doUseBuiltInAccountV1Theme };
291
- })();
306
+ {
307
+ const destDirPath = pathJoin(buildContext.keycloakifyBuildDirPath, "theme");
308
+ if (await existsAsync(destDirPath)) {
309
+ await rm(destDirPath, { recursive: true });
310
+ }
311
+ }
292
312
 
293
- const accountThemePropertyPatch = !doUseBuiltInAccountV1Theme
294
- ? undefined
295
- : () => {
296
- for (const themeName of buildContext.themeNames) {
297
- const filePath = pathJoin(
298
- buildContext.keycloakifyBuildDirPath,
299
- "src",
300
- "main",
301
- "resources",
302
- "theme",
303
- themeName,
304
- "account",
305
- "theme.properties"
306
- );
307
-
308
- const sourceCode = fs.readFileSync(filePath);
309
-
310
- const modifiedSourceCode = Buffer.from(
311
- sourceCode
312
- .toString("utf8")
313
- .replace(`parent=${accountV1ThemeName}`, "parent=keycloak"),
314
- "utf8"
315
- );
316
-
317
- assert(Buffer.compare(modifiedSourceCode, sourceCode) !== 0);
318
-
319
- fs.writeFileSync(filePath, modifiedSourceCode);
320
- }
321
- };
322
-
323
- accountThemePropertyPatch?.();
313
+ await extractThemeResourcesFromJar();
324
314
 
325
315
  try {
326
316
  child_process.execSync(`docker rm --force ${containerName}`, {
@@ -348,14 +338,19 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
348
338
  : []),
349
339
  ...[
350
340
  ...buildContext.themeNames,
351
- ...(doUseBuiltInAccountV1Theme ? [] : [accountV1ThemeName])
341
+ ...(fs.existsSync(
342
+ pathJoin(
343
+ buildContext.keycloakifyBuildDirPath,
344
+ "theme",
345
+ accountV1ThemeName
346
+ )
347
+ )
348
+ ? [accountV1ThemeName]
349
+ : [])
352
350
  ]
353
351
  .map(themeName => ({
354
352
  localDirPath: pathJoin(
355
353
  buildContext.keycloakifyBuildDirPath,
356
- "src",
357
- "main",
358
- "resources",
359
354
  "theme",
360
355
  themeName
361
356
  ),
@@ -451,7 +446,7 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
451
446
  }
452
447
 
453
448
  const { isKeycloakifyBuildSuccess } = await keycloakifyBuild({
454
- doSkipBuildJars: true,
449
+ onlyBuildJarFileBasename: jarFileBasename,
455
450
  buildContext
456
451
  });
457
452
 
@@ -459,7 +454,7 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
459
454
  return;
460
455
  }
461
456
 
462
- accountThemePropertyPatch?.();
457
+ await extractThemeResourcesFromJar();
463
458
 
464
459
  console.log(chalk.green("Theme rebuilt and updated in Keycloak."));
465
460
  });
@@ -47,6 +47,10 @@ export function keycloakify(params?: Params) {
47
47
 
48
48
  const buildContext = JSON.parse(envValue) as BuildContext;
49
49
 
50
+ process.chdir(
51
+ pathJoin(buildContext.keycloakifyBuildDirPath, "resources")
52
+ );
53
+
50
54
  await postBuild?.(buildContext);
51
55
 
52
56
  process.exit(0);
@@ -4128,7 +4128,9 @@ function getBuildContext(params) {
4128
4128
  if (typeof buildOptions.themeName === "string") {
4129
4129
  return [buildOptions.themeName];
4130
4130
  }
4131
- return buildOptions.themeName;
4131
+ const [mainThemeName, ...themeVariantNames] = buildOptions.themeName;
4132
+ (0,tsafe.assert)(mainThemeName !== undefined);
4133
+ return [mainThemeName, ...themeVariantNames];
4132
4134
  })();
4133
4135
  const projectBuildDirPath = (() => {
4134
4136
  var _a;
@@ -4268,8 +4270,8 @@ __nccwpck_require__.r(__webpack_exports__);
4268
4270
  /* harmony export */ "loginThemePageIds": () => (/* binding */ loginThemePageIds),
4269
4271
  /* harmony export */ "nameOfTheGlobal": () => (/* binding */ nameOfTheGlobal),
4270
4272
  /* harmony export */ "nameOfTheLocalizationRealmOverridesUserProfileProperty": () => (/* binding */ nameOfTheLocalizationRealmOverridesUserProfileProperty),
4273
+ /* harmony export */ "onlyBuildJarFileBasenameEnvName": () => (/* binding */ onlyBuildJarFileBasenameEnvName),
4271
4274
  /* harmony export */ "resources_common": () => (/* binding */ resources_common),
4272
- /* harmony export */ "skipBuildJarsEnvName": () => (/* binding */ skipBuildJarsEnvName),
4273
4275
  /* harmony export */ "themeTypes": () => (/* binding */ themeTypes),
4274
4276
  /* harmony export */ "vitePluginSubScriptEnvNames": () => (/* binding */ vitePluginSubScriptEnvNames)
4275
4277
  /* harmony export */ });
@@ -4285,7 +4287,7 @@ const vitePluginSubScriptEnvNames = {
4285
4287
  runPostBuildScript: "KEYCLOAKIFY_RUN_POST_BUILD_SCRIPT",
4286
4288
  resolveViteConfig: "KEYCLOAKIFY_RESOLVE_VITE_CONFIG"
4287
4289
  };
4288
- const skipBuildJarsEnvName = "KEYCLOAKIFY_SKIP_BUILD_JAR";
4290
+ const onlyBuildJarFileBasenameEnvName = "KEYCLOAKIFY_ONLY_BUILD_JAR_FILE_BASENAME";
4289
4291
  const loginThemePageIds = [
4290
4292
  "login.ftl",
4291
4293
  "login-username.ftl",
@@ -5429,6 +5431,7 @@ function keycloakify(params) {
5429
5431
  break run_post_build_script_case;
5430
5432
  }
5431
5433
  const buildContext = JSON.parse(envValue);
5434
+ process.chdir((0, path_1.join)(buildContext.keycloakifyBuildDirPath, "resources"));
5432
5435
  await (postBuild === null || postBuild === void 0 ? void 0 : postBuild(buildContext));
5433
5436
  process.exit(0);
5434
5437
  }