zudoku 0.1.1-dev.18 → 0.1.1-dev.19

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 (139) hide show
  1. package/dist/lib/plugins/openapi/worker/createSharedWorkerClient.js +2 -3
  2. package/dist/lib/plugins/openapi/worker/createSharedWorkerClient.js.map +1 -1
  3. package/dist/lib/plugins/openapi/worker/shared-worker.d.ts +1 -0
  4. package/dist/lib/plugins/openapi/worker/shared-worker.js +6 -0
  5. package/dist/lib/plugins/openapi/worker/shared-worker.js.map +1 -0
  6. package/dist/vite/config.d.ts +1 -1
  7. package/dist/vite/config.js +13 -13
  8. package/dist/vite/config.js.map +1 -1
  9. package/dist/vite/dev-server.js +1 -1
  10. package/dist/vite/dev-server.js.map +1 -1
  11. package/lib/zudoku.openapi-worker.js +12 -0
  12. package/lib/zudoku.plugins.js +1318 -1323
  13. package/package.json +5 -2
  14. package/src/cli/build/handler.ts +14 -0
  15. package/src/cli/cli.ts +77 -0
  16. package/src/cli/cmds/build.ts +24 -0
  17. package/src/cli/cmds/dev.ts +29 -0
  18. package/src/cli/common/analytics/lib.ts +89 -0
  19. package/src/cli/common/constants.ts +10 -0
  20. package/src/cli/common/logger.ts +5 -0
  21. package/src/cli/common/machine-id/lib.ts +85 -0
  22. package/src/cli/common/outdated.ts +102 -0
  23. package/src/cli/common/output.ts +86 -0
  24. package/src/cli/common/utils/box.license.txt +202 -0
  25. package/src/cli/common/utils/box.ts +116 -0
  26. package/src/cli/common/utils/ports.ts +21 -0
  27. package/src/cli/common/validators/lib.ts +43 -0
  28. package/src/cli/common/xdg/lib.ts +36 -0
  29. package/src/cli/dev/handler.ts +42 -0
  30. package/src/config/config.ts +56 -0
  31. package/src/index.ts +8 -0
  32. package/src/lib/DevPortal.tsx +93 -0
  33. package/src/lib/Heading.tsx +60 -0
  34. package/src/lib/Router.tsx +28 -0
  35. package/src/lib/auth.ts +1 -0
  36. package/src/lib/authentication/authentication.ts +18 -0
  37. package/src/lib/authentication/clerk.ts +45 -0
  38. package/src/lib/authentication/openid.ts +192 -0
  39. package/src/lib/components/AnchorLink.tsx +19 -0
  40. package/src/lib/components/CategoryHeading.tsx +16 -0
  41. package/src/lib/components/Dialog.tsx +119 -0
  42. package/src/lib/components/DynamicIcon.tsx +60 -0
  43. package/src/lib/components/Header.tsx +69 -0
  44. package/src/lib/components/Input.tsx +24 -0
  45. package/src/lib/components/Layout.tsx +56 -0
  46. package/src/lib/components/Markdown.tsx +37 -0
  47. package/src/lib/components/SyntaxHighlight.tsx +94 -0
  48. package/src/lib/components/TopNavigation.tsx +32 -0
  49. package/src/lib/components/context/ComponentsContext.tsx +24 -0
  50. package/src/lib/components/context/DevPortalProvider.ts +54 -0
  51. package/src/lib/components/context/PluginSystem.ts +0 -0
  52. package/src/lib/components/context/ThemeContext.tsx +46 -0
  53. package/src/lib/components/context/ViewportAnchorContext.tsx +139 -0
  54. package/src/lib/components/navigation/SideNavigation.tsx +18 -0
  55. package/src/lib/components/navigation/SideNavigationCategory.tsx +74 -0
  56. package/src/lib/components/navigation/SideNavigationItem.tsx +143 -0
  57. package/src/lib/components/navigation/SideNavigationWrapper.tsx +15 -0
  58. package/src/lib/components/navigation/useNavigationCollapsibleState.ts +27 -0
  59. package/src/lib/components/navigation/util.ts +38 -0
  60. package/src/lib/components.ts +3 -0
  61. package/src/lib/core/DevPortalContext.ts +164 -0
  62. package/src/lib/core/helmet.ts +5 -0
  63. package/src/lib/core/icons.tsx +1 -0
  64. package/src/lib/core/plugins.ts +43 -0
  65. package/src/lib/core/router.tsx +1 -0
  66. package/src/lib/core/types/combine.ts +16 -0
  67. package/src/lib/oas/graphql/index.ts +422 -0
  68. package/src/lib/oas/graphql/server.ts +10 -0
  69. package/src/lib/oas/parser/dereference/index.ts +59 -0
  70. package/src/lib/oas/parser/dereference/resolveRef.ts +32 -0
  71. package/src/lib/oas/parser/index.ts +94 -0
  72. package/src/lib/oas/parser/schemas/v3.0.json +1489 -0
  73. package/src/lib/oas/parser/schemas/v3.1.json +1298 -0
  74. package/src/lib/oas/parser/upgrade/index.ts +108 -0
  75. package/src/lib/plugins/api-key/SettingsApiKeys.tsx +22 -0
  76. package/src/lib/plugins/api-key/index.tsx +123 -0
  77. package/src/lib/plugins/markdown/MdxPage.tsx +128 -0
  78. package/src/lib/plugins/markdown/Toc.tsx +122 -0
  79. package/src/lib/plugins/markdown/generateRoutes.tsx +72 -0
  80. package/src/lib/plugins/markdown/index.tsx +31 -0
  81. package/src/lib/plugins/openapi/ColorizedParam.tsx +82 -0
  82. package/src/lib/plugins/openapi/MakeRequest.tsx +49 -0
  83. package/src/lib/plugins/openapi/MethodBadge.tsx +36 -0
  84. package/src/lib/plugins/openapi/OperationList.tsx +117 -0
  85. package/src/lib/plugins/openapi/OperationListItem.tsx +55 -0
  86. package/src/lib/plugins/openapi/ParameterList.tsx +32 -0
  87. package/src/lib/plugins/openapi/ParameterListItem.tsx +60 -0
  88. package/src/lib/plugins/openapi/RequestBodySidecarBox.tsx +51 -0
  89. package/src/lib/plugins/openapi/ResponsesSidecarBox.tsx +60 -0
  90. package/src/lib/plugins/openapi/Select.tsx +35 -0
  91. package/src/lib/plugins/openapi/Sidecar.tsx +160 -0
  92. package/src/lib/plugins/openapi/SidecarBox.tsx +36 -0
  93. package/src/lib/plugins/openapi/graphql/fragment-masking.ts +111 -0
  94. package/src/lib/plugins/openapi/graphql/gql.ts +70 -0
  95. package/src/lib/plugins/openapi/graphql/graphql.ts +795 -0
  96. package/src/lib/plugins/openapi/graphql/index.ts +2 -0
  97. package/src/lib/plugins/openapi/index.tsx +142 -0
  98. package/src/lib/plugins/openapi/playground/Playground.tsx +309 -0
  99. package/src/lib/plugins/openapi/queries.graphql +6 -0
  100. package/src/lib/plugins/openapi/util/generateSchemaExample.ts +59 -0
  101. package/src/lib/plugins/openapi/util/urql.ts +8 -0
  102. package/src/lib/plugins/openapi/worker/createSharedWorkerClient.ts +60 -0
  103. package/src/lib/plugins/openapi/worker/shared-worker.ts +5 -0
  104. package/src/lib/plugins/openapi/worker/worker.ts +30 -0
  105. package/src/lib/plugins/redirect/index.tsx +20 -0
  106. package/src/lib/plugins.ts +5 -0
  107. package/src/lib/ui/Button.tsx +56 -0
  108. package/src/lib/ui/Callout.tsx +87 -0
  109. package/src/lib/ui/Card.tsx +82 -0
  110. package/src/lib/ui/Note.tsx +58 -0
  111. package/src/lib/ui/Tabs.tsx +52 -0
  112. package/src/lib/util/MdxComponents.tsx +70 -0
  113. package/src/lib/util/cn.ts +6 -0
  114. package/src/lib/util/createVariantComponent.tsx +30 -0
  115. package/src/lib/util/createWaitForNotify.ts +18 -0
  116. package/src/lib/util/groupBy.ts +24 -0
  117. package/src/lib/util/joinPath.tsx +10 -0
  118. package/src/lib/util/pastellize.ts +25 -0
  119. package/src/lib/util/slugify.ts +3 -0
  120. package/src/lib/util/traverseNavigation.ts +55 -0
  121. package/src/lib/util/useScrollToAnchor.ts +38 -0
  122. package/src/lib/util/useScrollToTop.ts +13 -0
  123. package/src/ts.ts +94 -0
  124. package/src/types.d.ts +24 -0
  125. package/src/vite/build.ts +33 -0
  126. package/src/vite/config.test.ts +10 -0
  127. package/src/vite/config.ts +183 -0
  128. package/src/vite/dev-server.ts +64 -0
  129. package/src/vite/html.ts +37 -0
  130. package/src/vite/plugin-api.ts +57 -0
  131. package/src/vite/plugin-auth.ts +32 -0
  132. package/src/vite/plugin-component.ts +26 -0
  133. package/src/vite/plugin-config.ts +31 -0
  134. package/src/vite/plugin-docs.test.ts +32 -0
  135. package/src/vite/plugin-docs.ts +52 -0
  136. package/src/vite/plugin-html.ts +50 -0
  137. package/src/vite/plugin-mdx.ts +74 -0
  138. package/src/vite/plugin-metadata.ts +30 -0
  139. package/src/vite/plugin.ts +23 -0
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "zudoku",
3
- "version": "0.1.1-dev.18",
3
+ "version": "0.1.1-dev.19",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist",
7
7
  "lib",
8
8
  "cli.js",
9
- "src/app"
9
+ "src"
10
10
  ],
11
11
  "module": "./dist/index.js",
12
12
  "types": "./dist/index.d.ts",
@@ -40,6 +40,9 @@
40
40
  },
41
41
  "./app/*": {
42
42
  "import": "./src/app/*"
43
+ },
44
+ "./open-api-worker": {
45
+ "import": "./lib/zudoku.openapi-worker.js"
43
46
  }
44
47
  },
45
48
  "dependencies": {
@@ -0,0 +1,14 @@
1
+ import { runBuild } from "../../vite/build.js";
2
+ import { printDiagnosticsToConsole } from "../common/output.js";
3
+
4
+ export interface Arguments {
5
+ dir: string;
6
+ }
7
+
8
+ export async function build(argv: Arguments) {
9
+ printDiagnosticsToConsole("Starting build");
10
+ printDiagnosticsToConsole("");
11
+ printDiagnosticsToConsole("");
12
+
13
+ await runBuild({ dir: argv.dir });
14
+ }
package/src/cli/cli.ts ADDED
@@ -0,0 +1,77 @@
1
+ // import * as dotenv from "dotenv";
2
+ // dotenv.config();
3
+
4
+ import * as Sentry from "@sentry/node";
5
+ import { readFileSync } from "node:fs";
6
+ import { fileURLToPath } from "node:url";
7
+
8
+ import { gte } from "semver";
9
+ import { hideBin } from "yargs/helpers";
10
+ import yargs from "yargs/yargs";
11
+ import build from "./cmds/build.js";
12
+ import dev from "./cmds/dev.js";
13
+ import { shutdownAnalytics } from "./common/analytics/lib.js";
14
+ import { MAX_WAIT_PENDING_TIME_MS, SENTRY_DSN } from "./common/constants.js";
15
+ import { logger } from "./common/logger.js";
16
+ import { warnIfOutdatedVersion } from "./common/outdated.js";
17
+ import { printCriticalFailureToConsoleAndExit } from "./common/output.js";
18
+
19
+ const MIN_NODE_VERSION = "18.0.0";
20
+
21
+ if (gte(process.versions.node, MIN_NODE_VERSION)) {
22
+ let packageJson;
23
+ try {
24
+ packageJson = JSON.parse(
25
+ readFileSync(
26
+ fileURLToPath(new URL("../../package.json", import.meta.url)),
27
+ "utf-8",
28
+ ),
29
+ );
30
+ } catch (e) {
31
+ logger.error(e);
32
+ await printCriticalFailureToConsoleAndExit(
33
+ `Unable to load zudoku cli. The package.json is missing or malformed.`,
34
+ );
35
+ }
36
+
37
+ if (SENTRY_DSN) {
38
+ Sentry.init({
39
+ dsn: SENTRY_DSN,
40
+ release: packageJson?.version,
41
+ });
42
+ }
43
+
44
+ const cli = yargs(hideBin(process.argv))
45
+ .command(build)
46
+ .command(dev)
47
+ .demandCommand()
48
+ .strictCommands()
49
+ .version(packageJson?.version)
50
+ .fail(false)
51
+ .help();
52
+
53
+ try {
54
+ await cli.argv;
55
+
56
+ // Don't block
57
+ void warnIfOutdatedVersion(packageJson?.version);
58
+
59
+ void Sentry.close(MAX_WAIT_PENDING_TIME_MS).then(() => {
60
+ process.exit(0);
61
+ });
62
+ } catch (err) {
63
+ if (err instanceof Error) {
64
+ Sentry.captureException(err);
65
+ }
66
+ await printCriticalFailureToConsoleAndExit(err.message ?? err);
67
+ cli.showHelp();
68
+ } finally {
69
+ await shutdownAnalytics();
70
+ }
71
+ } else {
72
+ await printCriticalFailureToConsoleAndExit(
73
+ `The zup CLI requires at least node.js v${MIN_NODE_VERSION}. You are using v${process.versions.node}. Please update your version of node.js.
74
+
75
+ Consider using a Node.js version manager such as https://github.com/nvm-sh/nvm.`,
76
+ );
77
+ }
@@ -0,0 +1,24 @@
1
+ import { Argv } from "yargs";
2
+ import { Arguments, build } from "../build/handler.js";
3
+ import { captureEvent } from "../common/analytics/lib.js";
4
+
5
+ export default {
6
+ desc: "Build",
7
+ command: "build",
8
+ builder: (yargs: Argv): Argv<unknown> => {
9
+ return yargs.option("dir", {
10
+ type: "string",
11
+ describe: "The directory containing your project",
12
+ default: ".",
13
+ normalize: true,
14
+ hidden: true,
15
+ });
16
+ },
17
+ handler: async (argv: unknown) => {
18
+ await captureEvent({
19
+ argv,
20
+ event: "zudoku build",
21
+ });
22
+ await build(argv as Arguments);
23
+ },
24
+ };
@@ -0,0 +1,29 @@
1
+ import { Argv } from "yargs";
2
+ import { captureEvent } from "../common/analytics/lib.js";
3
+ import { Arguments, dev } from "../dev/handler.js";
4
+
5
+ export default {
6
+ desc: "Runs locally",
7
+ command: "dev",
8
+ builder: (yargs: Argv): Argv<unknown> => {
9
+ return yargs
10
+ .option("dir", {
11
+ type: "string",
12
+ describe: "The directory containing your project",
13
+ default: ".",
14
+ normalize: true,
15
+ hidden: true,
16
+ })
17
+ .option("port", {
18
+ type: "number",
19
+ describe: "The port to run the local server on",
20
+ });
21
+ },
22
+ handler: async (argv: unknown) => {
23
+ await captureEvent({
24
+ argv,
25
+ event: "zudoku dev",
26
+ });
27
+ await dev(argv as Arguments);
28
+ },
29
+ };
@@ -0,0 +1,89 @@
1
+ // import { Context } from "@sentry/node/types/integrations/context.js";
2
+ import { PostHog } from "posthog-node";
3
+ import { POST_HOG_CAPTURE_KEY } from "../constants.js";
4
+ import { machineId } from "../machine-id/lib.js";
5
+
6
+ interface IdentifyMessageV1 {
7
+ distinctId: string;
8
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
9
+ properties?: Record<string | number, any>;
10
+ disableGeoip?: boolean;
11
+ }
12
+
13
+ interface EventMessageV1 extends IdentifyMessageV1 {
14
+ event: string;
15
+ groups?: Record<string, string | number>;
16
+ sendFeatureFlags?: boolean;
17
+ timestamp?: Date;
18
+ }
19
+
20
+ interface ZuploEventMessage extends Omit<EventMessageV1, "distinctId"> {
21
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
22
+ argv: any;
23
+ }
24
+
25
+ let _postHog: PostHog | undefined;
26
+ // let _context: Context | undefined;
27
+
28
+ function init(): PostHog | undefined {
29
+ if (process.env.ZUPLO_DO_NOT_TRACK || !POST_HOG_CAPTURE_KEY) {
30
+ return undefined;
31
+ }
32
+
33
+ _postHog = new PostHog(POST_HOG_CAPTURE_KEY, {
34
+ host: "https://app.posthog.com",
35
+ flushAt: 1,
36
+ flushInterval: 1,
37
+ });
38
+
39
+ // // Use the additional information from sentry for the OS, CPU, etc
40
+ // // This is a bit of a hack since this is not an official API from sentry
41
+ // _context = (
42
+ // getDefaultIntegrations({}).filter(
43
+ // (integration) => integration.name == "Context",
44
+ // ) as Context[]
45
+ // )[0];
46
+
47
+ return _postHog;
48
+ }
49
+
50
+ // A convenience wrapper so that callers do not have to inject the distincId for each call to capture
51
+ export async function captureEvent({
52
+ event,
53
+ properties,
54
+ groups,
55
+ sendFeatureFlags,
56
+ timestamp,
57
+ disableGeoip,
58
+ }: ZuploEventMessage): Promise<void> {
59
+ if (_postHog === undefined) {
60
+ init();
61
+ }
62
+
63
+ if (_postHog) {
64
+ properties = properties ?? {};
65
+ properties.$set_once = properties.$set_once ?? {};
66
+
67
+ // Nice to have: set information if this is in CI
68
+ if (process.env.CI) {
69
+ properties["ci"] = process.env.CI;
70
+ }
71
+
72
+ // // Nice to have: set information about the machine
73
+ // await _context?.addContext(properties);
74
+
75
+ _postHog?.capture({
76
+ distinctId: machineId(),
77
+ event,
78
+ properties,
79
+ groups,
80
+ sendFeatureFlags,
81
+ timestamp,
82
+ disableGeoip,
83
+ });
84
+ }
85
+ }
86
+
87
+ export async function shutdownAnalytics(): Promise<void> {
88
+ return _postHog?.shutdown();
89
+ }
@@ -0,0 +1,10 @@
1
+ // Configuration
2
+ export const CLI_XDG_FOLDER_NAME = "zudoku";
3
+ export const VERSION_CHECK_FILE = "version.json";
4
+
5
+ // Sentry
6
+ export const SENTRY_DSN = undefined;
7
+ export const MAX_WAIT_PENDING_TIME_MS = 1000;
8
+
9
+ // PostHog
10
+ export const POST_HOG_CAPTURE_KEY = undefined;
@@ -0,0 +1,5 @@
1
+ import { LogLevel, createLogger } from "vite";
2
+
3
+ export const logger = createLogger(
4
+ (process.env.LOG_LEVEL || "info") as LogLevel,
5
+ );
@@ -0,0 +1,85 @@
1
+ /**
2
+ * node-machine-id
3
+ * Copyright (c) 2016 Aleksandr Komlev
4
+ * MIT Licensed
5
+ *
6
+ * From https://github.com/automation-stack/node-machine-id
7
+ */
8
+ import { execSync } from "node:child_process";
9
+ import { createHash } from "node:crypto";
10
+
11
+ const win32RegBinPath = {
12
+ skipped: "",
13
+ native: "%windir%\\System32",
14
+ mixed: "%windir%\\sysnative\\cmd.exe /c %windir%\\System32",
15
+ };
16
+ const guid = {
17
+ darwin: "ioreg -rd1 -c IOPlatformExpertDevice",
18
+ win32:
19
+ `${
20
+ win32RegBinPath[isWindowsProcessMixedOrNativeArchitecture()]
21
+ }\\REG.exe ` +
22
+ "QUERY HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Cryptography " +
23
+ "/v MachineGuid",
24
+ linux:
25
+ "( cat /var/lib/dbus/machine-id /etc/machine-id 2> /dev/null || hostname ) | head -n 1 || :",
26
+ };
27
+
28
+ function isWindowsProcessMixedOrNativeArchitecture():
29
+ | "skipped"
30
+ | "mixed"
31
+ | "native" {
32
+ // detect if the node binary is the same arch as the Windows OS.
33
+ // or if this is 32 bit node on 64 bit windows.
34
+ if (process.platform !== "win32") {
35
+ return "skipped";
36
+ }
37
+ if (process.arch === "ia32" && process.env["PROCESSOR_ARCHITEW6432"]) {
38
+ return "mixed";
39
+ }
40
+ return "native";
41
+ }
42
+
43
+ function hash(guid: string): string {
44
+ return createHash("sha256").update(guid).digest("hex");
45
+ }
46
+
47
+ function expose(result: string): string {
48
+ switch (process.platform) {
49
+ case "darwin":
50
+ return result
51
+ .split("IOPlatformUUID")[1]
52
+ .split("\n")[0]
53
+ .replace(/=|\s+]"/gi, "")
54
+ .toLowerCase();
55
+ case "win32":
56
+ return result
57
+ .toString()
58
+ .split("REG_SZ")[1]
59
+ .replace(/\r+|\n+|\s+/gi, "")
60
+ .toLowerCase();
61
+ case "linux":
62
+ return result
63
+ .toString()
64
+ .replace(/\r+|\n+|\s+/gi, "")
65
+ .toLowerCase();
66
+ case "freebsd":
67
+ return result
68
+ .toString()
69
+ .replace(/\r+|\n+|\s+/gi, "")
70
+ .toLowerCase();
71
+ default:
72
+ throw new Error(`Unsupported platform: ${process.platform}`);
73
+ }
74
+ }
75
+
76
+ export function machineId(): string {
77
+ switch (process.platform) {
78
+ case "darwin":
79
+ case "win32":
80
+ case "linux":
81
+ return hash(expose(execSync(guid[process.platform]).toString()));
82
+ default:
83
+ return "e16fc483-5593-4d71-b485-a6533693da9b";
84
+ }
85
+ }
@@ -0,0 +1,102 @@
1
+ import chalk from "chalk";
2
+ import { existsSync, mkdirSync } from "node:fs";
3
+ import { readFile, writeFile } from "node:fs/promises";
4
+ import { join } from "node:path";
5
+ import { gt } from "semver";
6
+ import { VERSION_CHECK_FILE } from "./constants.js";
7
+ import { printWarningToConsole } from "./output.js";
8
+ import box from "./utils/box.js";
9
+ import { ZUPLO_XDG_STATE_HOME } from "./xdg/lib.js";
10
+
11
+ interface VersionCheckInfo {
12
+ lastCheck: number;
13
+ latestVersion: string;
14
+ }
15
+ export async function warnIfOutdatedVersion(currentVersion: string) {
16
+ // Print update information, if available
17
+ if (
18
+ !process.env.ZUPLO_OVERRIDE_CI_TO_TEST &&
19
+ (process.env.CI || process.env.ZUPLO_DISABLE_UPDATE_CHECK)
20
+ ) {
21
+ return false;
22
+ }
23
+ const versionCheckInfo = await getVersionCheckInfo();
24
+ const shouldWarn = gt(versionCheckInfo.latestVersion, currentVersion);
25
+
26
+ if (shouldWarn) {
27
+ printWarningToConsole(
28
+ box(
29
+ `Update available! ${chalk.gray(`v${currentVersion}`)} ≫ ${chalk.green(
30
+ `v${versionCheckInfo.latestVersion}`,
31
+ )}
32
+ Run ${chalk.cyan("npm install zuplo@latest")} to update.
33
+
34
+ ${chalk.gray("Older versions are unsupported and may not work as expected.")}`,
35
+ ),
36
+ );
37
+ }
38
+
39
+ return shouldWarn;
40
+ }
41
+
42
+ async function getLatestVersion(): Promise<string> {
43
+ const response = await fetch(
44
+ "https://raw.githubusercontent.com/zuplo/zudoku/main/packages/zudoku/package.json",
45
+ );
46
+ const result = await response.json();
47
+
48
+ return result.dependencies["zudoku"];
49
+ }
50
+
51
+ async function getVersionCheckInfo(): Promise<VersionCheckInfo> {
52
+ if (!existsSync(ZUPLO_XDG_STATE_HOME)) {
53
+ mkdirSync(ZUPLO_XDG_STATE_HOME, { recursive: true });
54
+ }
55
+ const versionCheckPath = join(ZUPLO_XDG_STATE_HOME, VERSION_CHECK_FILE);
56
+
57
+ let versionCheckInfo: VersionCheckInfo | undefined;
58
+ if (existsSync(versionCheckPath)) {
59
+ try {
60
+ versionCheckInfo = await readFile(versionCheckPath, "utf-8").then(
61
+ JSON.parse,
62
+ );
63
+ } catch (err) {
64
+ // Error reading or parsing file, ignore it
65
+ }
66
+
67
+ // Check if the file is valid, if not ignore it
68
+ if (versionCheckInfo) {
69
+ if (
70
+ typeof versionCheckInfo.lastCheck !== "number" ||
71
+ typeof versionCheckInfo.latestVersion !== "string"
72
+ ) {
73
+ versionCheckInfo = undefined;
74
+ }
75
+ }
76
+ }
77
+
78
+ let shouldCheck = true;
79
+ if (versionCheckInfo) {
80
+ const now = Date.now();
81
+ const lastCheck = versionCheckInfo.lastCheck;
82
+ if (now - lastCheck < 1000 * 60 * 60 * 24) {
83
+ shouldCheck = false;
84
+ }
85
+ }
86
+
87
+ if (!versionCheckInfo || shouldCheck) {
88
+ const latestVersion = await getLatestVersion();
89
+
90
+ versionCheckInfo = {
91
+ lastCheck: Date.now(),
92
+ latestVersion,
93
+ };
94
+ await writeFile(
95
+ versionCheckPath,
96
+ JSON.stringify(versionCheckInfo),
97
+ "utf-8",
98
+ );
99
+ }
100
+
101
+ return versionCheckInfo;
102
+ }
@@ -0,0 +1,86 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ /* eslint-disable no-console */
3
+
4
+ import * as Sentry from "@sentry/node";
5
+ import chalk from "chalk";
6
+ import { MAX_WAIT_PENDING_TIME_MS } from "./constants.js";
7
+
8
+ // We standardize printing to the terminal with this module
9
+
10
+ // According to https://unix.stackexchange.com/questions/331611/do-progress-reports-logging-information-belong-on-stderr-or-stdout
11
+ // any diagnostic information should go to stderr, and only the actual output goes to stdout
12
+ export function printDiagnosticsToConsole(message?: any) {
13
+ console.error(chalk.bold.blue(message));
14
+ }
15
+
16
+ export function printWarningToConsole(message?: any) {
17
+ console.error(message);
18
+ }
19
+
20
+ // This information is displayed to the user, so it should be actionable.
21
+ export async function printCriticalFailureToConsoleAndExit(message?: any) {
22
+ console.error(chalk.bold.red(message));
23
+ await Sentry.close(MAX_WAIT_PENDING_TIME_MS).then(() => {
24
+ process.exit(1);
25
+ });
26
+ }
27
+
28
+ // Only use this to output the actual result of a command
29
+ // This outputs to STDOUT, which is reserved for the actual result of a command
30
+ export function printResultToConsole(message?: any) {
31
+ console.log(chalk.bold.green(message));
32
+ }
33
+
34
+ // Only use this to output the actual result of a command
35
+ // This outputs to STDOUT, which is reserved for the actual result of a command
36
+ export function printTableToConsole(table: any) {
37
+ console.table(table);
38
+ }
39
+
40
+ export async function printResultToConsoleAndExitGracefully(message?: any) {
41
+ printResultToConsole(message);
42
+ await Sentry.close(MAX_WAIT_PENDING_TIME_MS).then(() => {
43
+ process.exit(0);
44
+ });
45
+ }
46
+
47
+ export async function printTableToConsoleAndExitGracefully(table: any) {
48
+ printTableToConsole(table);
49
+ await Sentry.close(MAX_WAIT_PENDING_TIME_MS).then(() => {
50
+ process.exit(0);
51
+ });
52
+ }
53
+
54
+ // See https://nodejs.org/docs/latest-v18.x/api/process.html#a-note-on-process-io
55
+ // We want to deliberately have STDOUT flush synchronously, so we can pipe the output to another command
56
+
57
+ interface WriteStreamWithHandle {
58
+ _handle: {
59
+ // eslint-disable-next-line @typescript-eslint/ban-types
60
+ setBlocking: Function;
61
+ };
62
+ isTTY: boolean;
63
+ }
64
+
65
+ export default function setBlocking() {
66
+ // Deno and browser have no process object:
67
+ if (typeof process === "undefined") return;
68
+ [process.stdout, process.stderr].forEach((_stream) => {
69
+ const stream = _stream as any as WriteStreamWithHandle;
70
+ if (
71
+ stream._handle &&
72
+ stream.isTTY &&
73
+ typeof stream._handle.setBlocking === "function"
74
+ ) {
75
+ stream._handle.setBlocking(true);
76
+ }
77
+ });
78
+ }
79
+
80
+ export function textOrJson(text: string) {
81
+ try {
82
+ return JSON.parse(text);
83
+ } catch (e) {
84
+ return text;
85
+ }
86
+ }