@gogitcms/editor 0.23.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.
Files changed (143) hide show
  1. package/app/index.html +38 -0
  2. package/app/src/App.tsx +2665 -0
  3. package/app/src/apollo.ts +119 -0
  4. package/app/src/auth.ts +248 -0
  5. package/app/src/config.ts +88 -0
  6. package/app/src/main.tsx +18 -0
  7. package/app/src/media.ts +151 -0
  8. package/app/src/navigation.tsx +321 -0
  9. package/app/src/plugins.ts +11 -0
  10. package/app/src/previewHost.tsx +263 -0
  11. package/app/src/previewToken.ts +68 -0
  12. package/app/src/queries.ts +591 -0
  13. package/app/src/virtual-cms-plugins.d.ts +6 -0
  14. package/app/vendor/analytics/src/__tests__/config.test.ts +91 -0
  15. package/app/vendor/analytics/src/config.ts +94 -0
  16. package/app/vendor/analytics/src/events.ts +56 -0
  17. package/app/vendor/analytics/src/index.ts +23 -0
  18. package/app/vendor/analytics/src/provider.tsx +196 -0
  19. package/app/vendor/design-system/src/ThemeProvider.tsx +86 -0
  20. package/app/vendor/design-system/src/__tests__/ApplyChangesModal.test.tsx +148 -0
  21. package/app/vendor/design-system/src/__tests__/BranchImport.test.tsx +46 -0
  22. package/app/vendor/design-system/src/__tests__/Button.test.tsx +45 -0
  23. package/app/vendor/design-system/src/__tests__/ChangeRequestSummary.test.tsx +57 -0
  24. package/app/vendor/design-system/src/__tests__/ContentBrowser.changes.test.tsx +611 -0
  25. package/app/vendor/design-system/src/__tests__/ContentBrowser.collab.test.tsx +322 -0
  26. package/app/vendor/design-system/src/__tests__/ContentBrowser.collabsync.test.tsx +264 -0
  27. package/app/vendor/design-system/src/__tests__/ContentBrowser.contentslot.test.tsx +53 -0
  28. package/app/vendor/design-system/src/__tests__/ContentBrowser.discriminator.test.tsx +142 -0
  29. package/app/vendor/design-system/src/__tests__/ContentBrowser.drafts.test.tsx +271 -0
  30. package/app/vendor/design-system/src/__tests__/ContentBrowser.fields.test.tsx +117 -0
  31. package/app/vendor/design-system/src/__tests__/ContentBrowser.media.test.tsx +140 -0
  32. package/app/vendor/design-system/src/__tests__/ContentBrowser.mixedcollab.test.tsx +63 -0
  33. package/app/vendor/design-system/src/__tests__/ContentBrowser.mixedvalues.test.tsx +38 -0
  34. package/app/vendor/design-system/src/__tests__/ContentBrowser.pagination.test.tsx +62 -0
  35. package/app/vendor/design-system/src/__tests__/ContentBrowser.previewtab.test.tsx +212 -0
  36. package/app/vendor/design-system/src/__tests__/ContentBrowser.reorder.test.tsx +45 -0
  37. package/app/vendor/design-system/src/__tests__/ContentBrowser.search.test.tsx +135 -0
  38. package/app/vendor/design-system/src/__tests__/ContentBrowser.selectvalue.test.tsx +185 -0
  39. package/app/vendor/design-system/src/__tests__/ContentBrowser.staged.test.tsx +132 -0
  40. package/app/vendor/design-system/src/__tests__/ContentBrowser.usermenu.test.tsx +56 -0
  41. package/app/vendor/design-system/src/__tests__/MediaBrowser.test.tsx +353 -0
  42. package/app/vendor/design-system/src/__tests__/MediaField.test.tsx +185 -0
  43. package/app/vendor/design-system/src/__tests__/Notifications.test.tsx +69 -0
  44. package/app/vendor/design-system/src/__tests__/Onboarding.test.tsx +287 -0
  45. package/app/vendor/design-system/src/__tests__/cssTokens.test.ts +201 -0
  46. package/app/vendor/design-system/src/__tests__/fieldComponents.test.ts +43 -0
  47. package/app/vendor/design-system/src/__tests__/reorder.test.ts +58 -0
  48. package/app/vendor/design-system/src/components/ApplyChangesModal.tsx +348 -0
  49. package/app/vendor/design-system/src/components/BranchImport.tsx +157 -0
  50. package/app/vendor/design-system/src/components/BranchMenu.tsx +192 -0
  51. package/app/vendor/design-system/src/components/Button.tsx +131 -0
  52. package/app/vendor/design-system/src/components/ChangeDetail.tsx +472 -0
  53. package/app/vendor/design-system/src/components/ChangeRequestSummary.tsx +173 -0
  54. package/app/vendor/design-system/src/components/CollabField.tsx +388 -0
  55. package/app/vendor/design-system/src/components/ContentBrowser.tsx +5073 -0
  56. package/app/vendor/design-system/src/components/Icon.tsx +28 -0
  57. package/app/vendor/design-system/src/components/Icon.web.tsx +31 -0
  58. package/app/vendor/design-system/src/components/Input.tsx +106 -0
  59. package/app/vendor/design-system/src/components/MediaBrowser.tsx +766 -0
  60. package/app/vendor/design-system/src/components/MediaField.tsx +670 -0
  61. package/app/vendor/design-system/src/components/MediaPreview.tsx +91 -0
  62. package/app/vendor/design-system/src/components/MediaPreview.web.tsx +169 -0
  63. package/app/vendor/design-system/src/components/NavRow.tsx +105 -0
  64. package/app/vendor/design-system/src/components/Notifications.tsx +301 -0
  65. package/app/vendor/design-system/src/components/Onboarding.tsx +751 -0
  66. package/app/vendor/design-system/src/components/ProjectMenu.tsx +124 -0
  67. package/app/vendor/design-system/src/components/Segment.tsx +87 -0
  68. package/app/vendor/design-system/src/components/Skeleton.tsx +216 -0
  69. package/app/vendor/design-system/src/components/Spinner.tsx +44 -0
  70. package/app/vendor/design-system/src/components/Text.tsx +85 -0
  71. package/app/vendor/design-system/src/components/documentDrafts.ts +213 -0
  72. package/app/vendor/design-system/src/components/layout.tsx +284 -0
  73. package/app/vendor/design-system/src/components/primitives.tsx +143 -0
  74. package/app/vendor/design-system/src/components/reorder.ts +40 -0
  75. package/app/vendor/design-system/src/fieldComponents.ts +63 -0
  76. package/app/vendor/design-system/src/icons.ts +102 -0
  77. package/app/vendor/design-system/src/index.ts +198 -0
  78. package/app/vendor/design-system/src/media.ts +229 -0
  79. package/app/vendor/design-system/src/theme.ts +116 -0
  80. package/app/vendor/design-system/src/web/Button.tsx +110 -0
  81. package/app/vendor/design-system/src/web/Icon.tsx +52 -0
  82. package/app/vendor/design-system/src/web/Input.tsx +39 -0
  83. package/app/vendor/design-system/src/web/index.ts +43 -0
  84. package/app/vendor/design-system/src/web/primitives.tsx +119 -0
  85. package/app/vendor/markdown-editor/src/MarkdownEditor.tsx +248 -0
  86. package/app/vendor/markdown-editor/src/Toolbar.tsx +157 -0
  87. package/app/vendor/markdown-editor/src/field.tsx +67 -0
  88. package/app/vendor/markdown-editor/src/flavors/commonmark/index.ts +27 -0
  89. package/app/vendor/markdown-editor/src/flavors/gfm/index.ts +30 -0
  90. package/app/vendor/markdown-editor/src/flavors/gfm/parser.ts +73 -0
  91. package/app/vendor/markdown-editor/src/flavors/gfm/schema.ts +70 -0
  92. package/app/vendor/markdown-editor/src/flavors/gfm/serializer.ts +82 -0
  93. package/app/vendor/markdown-editor/src/flavors/gfm/taskList.ts +39 -0
  94. package/app/vendor/markdown-editor/src/flavors/registry.ts +13 -0
  95. package/app/vendor/markdown-editor/src/flavors/shared/inputrules.ts +79 -0
  96. package/app/vendor/markdown-editor/src/flavors/shared/keymap.ts +89 -0
  97. package/app/vendor/markdown-editor/src/flavors/shared/placeholder.ts +24 -0
  98. package/app/vendor/markdown-editor/src/flavors/shared/plugins.ts +58 -0
  99. package/app/vendor/markdown-editor/src/flavors/types.ts +22 -0
  100. package/app/vendor/markdown-editor/src/formats/registry.ts +45 -0
  101. package/app/vendor/markdown-editor/src/icons.tsx +120 -0
  102. package/app/vendor/markdown-editor/src/index.ts +23 -0
  103. package/app/vendor/markdown-editor/src/theme.ts +249 -0
  104. package/app/vendor/markdown-editor/src/types.ts +58 -0
  105. package/app/vendor/plugin-sdk/src/__tests__/documentActions.test.ts +108 -0
  106. package/app/vendor/plugin-sdk/src/__tests__/loader.test.ts +75 -0
  107. package/app/vendor/plugin-sdk/src/__tests__/registry.test.ts +148 -0
  108. package/app/vendor/plugin-sdk/src/index.ts +33 -0
  109. package/app/vendor/plugin-sdk/src/loader.ts +45 -0
  110. package/app/vendor/plugin-sdk/src/navkeys.ts +17 -0
  111. package/app/vendor/plugin-sdk/src/react.tsx +155 -0
  112. package/app/vendor/plugin-sdk/src/registry.ts +337 -0
  113. package/app/vendor/plugin-sdk/src/types.ts +230 -0
  114. package/app/vendor/realtime/src/__tests__/pure.test.ts +58 -0
  115. package/app/vendor/realtime/src/__tests__/seeded-room.base64 +1 -0
  116. package/app/vendor/realtime/src/__tests__/seeding.test.ts +70 -0
  117. package/app/vendor/realtime/src/collab.ts +308 -0
  118. package/app/vendor/realtime/src/hooks.ts +57 -0
  119. package/app/vendor/realtime/src/index.ts +12 -0
  120. package/app/vendor/realtime/src/pure.ts +43 -0
  121. package/app/vite.config.mjs +59 -0
  122. package/bin/gogitcms-editor.mjs +168 -0
  123. package/npm-shrinkwrap.json +5696 -0
  124. package/package.json +74 -0
  125. package/src/commands/build.mjs +100 -0
  126. package/src/commands/dev.mjs +186 -0
  127. package/src/commands/init.mjs +144 -0
  128. package/src/commands/login.mjs +116 -0
  129. package/src/commands/status.mjs +68 -0
  130. package/src/lib/api.mjs +122 -0
  131. package/src/lib/appRoot.mjs +51 -0
  132. package/src/lib/baked.json +3 -0
  133. package/src/lib/config.mjs +108 -0
  134. package/src/lib/credentials.mjs +91 -0
  135. package/src/lib/defaults.mjs +26 -0
  136. package/src/lib/graphql.mjs +28 -0
  137. package/src/lib/localServer.mjs +206 -0
  138. package/src/lib/open.mjs +23 -0
  139. package/src/lib/plugins.mjs +164 -0
  140. package/src/lib/tokenBroker.mjs +85 -0
  141. package/src/tui/prompts.mjs +92 -0
  142. package/src/tui/screen.mjs +282 -0
  143. package/src/tui/theme.mjs +40 -0
@@ -0,0 +1,57 @@
1
+ import { useEffect, useRef, useState } from "react";
2
+ import { createDocCollab, type DocCollab, type CollabUser } from "./collab";
3
+
4
+ export type UseDocCollabOptions = {
5
+ // Base WebSocket URL, e.g. "wss://host/collab/ws". When empty/undefined the
6
+ // hook stays inert (returns null) — collaboration is simply off.
7
+ wsBase?: string;
8
+ room: string;
9
+ user: CollabUser | null | undefined;
10
+ getToken: () => string;
11
+ };
12
+
13
+ // useDocCollab owns one DocCollab for a document, recreating it when the room
14
+ // changes and destroying it on unmount. Returns null while inputs are missing.
15
+ export function useDocCollab(opts: UseDocCollabOptions): DocCollab | null {
16
+ const { wsBase, room, user } = opts;
17
+ // Keep the latest token getter without forcing recreation of the connection.
18
+ const getToken = useRef(opts.getToken);
19
+ getToken.current = opts.getToken;
20
+
21
+ const [collab, setCollab] = useState<DocCollab | null>(null);
22
+ const enabled = !!wsBase && !!room && !!user;
23
+
24
+ useEffect(() => {
25
+ // Visible diagnostic: shows whether a session is being created and why not.
26
+ console.log(`[collab] useDocCollab room=${room} enabled=${enabled}`, {
27
+ wsBase,
28
+ hasUser: !!user,
29
+ });
30
+ if (!enabled) {
31
+ setCollab(null);
32
+ return;
33
+ }
34
+ let instance: DocCollab;
35
+ try {
36
+ instance = createDocCollab({
37
+ wsBase: wsBase!,
38
+ room,
39
+ user: user!,
40
+ getToken: () => getToken.current(),
41
+ });
42
+ } catch (err) {
43
+ console.error(`[collab] failed to start session for room=${room}`, err);
44
+ setCollab(null);
45
+ return;
46
+ }
47
+ setCollab(instance);
48
+ return () => {
49
+ instance.destroy();
50
+ setCollab(null);
51
+ };
52
+ // user identity is stable per session; recreate only on connection identity.
53
+ // eslint-disable-next-line react-hooks/exhaustive-deps
54
+ }, [enabled, wsBase, room]);
55
+
56
+ return collab;
57
+ }
@@ -0,0 +1,12 @@
1
+ // Public API for @gogitcms/realtime — the collaborative-editing (Yjs) client.
2
+ export { createDocCollab, colorForId } from "./collab";
3
+ export type {
4
+ DocCollab,
5
+ CollabText,
6
+ CollabPeer,
7
+ CollabUser,
8
+ CollabStatus,
9
+ CreateDocCollabOptions,
10
+ } from "./collab";
11
+ export { useDocCollab } from "./hooks";
12
+ export type { UseDocCollabOptions } from "./hooks";
@@ -0,0 +1,43 @@
1
+ // Pure, dependency-free helpers (no yjs), so they can be unit-tested in isolation.
2
+
3
+ // colorForId derives a deterministic, pleasant color from a stable id (e.g. a
4
+ // user id), so a given user shows the same presence color everywhere without
5
+ // server coordination.
6
+ export function colorForId(id: string): string {
7
+ let h = 0;
8
+ for (let i = 0; i < id.length; i++) h = (h * 31 + id.charCodeAt(i)) >>> 0;
9
+ const hue = h % 360;
10
+ return `hsl(${hue} 70% 45%)`;
11
+ }
12
+
13
+ // computeSplice returns the minimal single edit (delete a middle run, insert a
14
+ // middle run) that turns `cur` into `next`, preserving the shared prefix and
15
+ // suffix. Applying only the changed middle to a Y.Text lets concurrent remote
16
+ // edits outside that range survive, instead of replacing the whole value.
17
+ export function computeSplice(
18
+ cur: string,
19
+ next: string,
20
+ ): { index: number; deleteCount: number; insert: string } {
21
+ const start = commonPrefix(cur, next);
22
+ const suffix = commonSuffix(cur, next, start);
23
+ const endCur = cur.length - suffix;
24
+ const endNext = next.length - suffix;
25
+ return { index: start, deleteCount: Math.max(0, endCur - start), insert: next.slice(start, endNext) };
26
+ }
27
+
28
+ // commonPrefix returns the length of the shared leading run of a and b.
29
+ function commonPrefix(a: string, b: string): number {
30
+ const n = Math.min(a.length, b.length);
31
+ let i = 0;
32
+ while (i < n && a[i] === b[i]) i++;
33
+ return i;
34
+ }
35
+
36
+ // commonSuffix returns the length of the shared trailing run of a and b, not
37
+ // overlapping the first `floor` characters already matched as a prefix.
38
+ function commonSuffix(a: string, b: string, floor: number): number {
39
+ const max = Math.min(a.length, b.length) - floor;
40
+ let i = 0;
41
+ while (i < max && a[a.length - 1 - i] === b[b.length - 1 - i]) i++;
42
+ return i;
43
+ }
@@ -0,0 +1,59 @@
1
+ // Generated by scripts/prepack.mjs — do not edit.
2
+ // Mirrors apps/frontend/vite.config.ts, with the workspace @gogitcms/* packages
3
+ // resolved to the copies vendored alongside this file.
4
+ import path from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ import { defineConfig } from "vite";
7
+ import react from "@vitejs/plugin-react";
8
+
9
+ const here = path.dirname(fileURLToPath(import.meta.url));
10
+ const vendor = (name) => path.join(here, "vendor", name, "src", "index.ts");
11
+
12
+ // The staged main.tsx boots plugins from virtual:cms-plugins. gogitcms-editor's dev
13
+ // and build commands provide the real module (from the project's cms.config.js
14
+ // `plugins`) as an `enforce: "pre"` inline plugin, which resolves the id
15
+ // before this one. This fallback only supplies an empty list for a bare
16
+ // `vite build` on the staged app (e.g. the smoke test), so that still compiles.
17
+ const emptyCmsPlugins = {
18
+ name: "cms-plugins-empty",
19
+ resolveId(id) {
20
+ return id === "virtual:cms-plugins" ? "\0virtual:cms-plugins" : undefined;
21
+ },
22
+ load(id) {
23
+ return id === "\0virtual:cms-plugins" ? "export const plugins = [];\n" : undefined;
24
+ },
25
+ };
26
+
27
+ export default defineConfig({
28
+ plugins: [react(), emptyCmsPlugins],
29
+ define: { global: "window", __DEV__: JSON.stringify(false) },
30
+ resolve: {
31
+ alias: {
32
+ "react-native": "react-native-web",
33
+ "@gogitcms/analytics": vendor("analytics"),
34
+ "@gogitcms/design-system": vendor("design-system"),
35
+ "@gogitcms/markdown-editor": vendor("markdown-editor"),
36
+ "@gogitcms/plugin-sdk": vendor("plugin-sdk"),
37
+ "@gogitcms/realtime": vendor("realtime"),
38
+ },
39
+ extensions: [".web.tsx", ".web.ts", ".tsx", ".ts", ".js"],
40
+ },
41
+ optimizeDeps: {
42
+ // Force these CJS-in-an-ESM-world packages to be pre-bundled, so Vite never
43
+ // serves them raw (a raw CJS module references `module`/`exports`, which
44
+ // don't exist in the browser — "module is not defined").
45
+ // * @react-navigation/native: type:commonjs with an ESM-syntax main; when
46
+ // served raw its CJS leaf use-latest-callback breaks default interop.
47
+ // * @apollo/client + rehackt: Apollo's React shim `rehackt` is CJS, and
48
+ // whether Vite folds it into the Apollo chunk or serves it standalone
49
+ // varies by install/discovery order; force both so it is always bundled.
50
+ include: ["@react-navigation/native", "@apollo/client", "@apollo/client > rehackt"],
51
+ // Web variants must lead here too: the esbuild pre-bundler resolves imports
52
+ // with this list (not resolve.extensions above), so without .web.tsx/.web.ts
53
+ // it would scan native-only files like Icon.tsx and try to pull in
54
+ // react-native-svg, which the web build never actually loads.
55
+ esbuildOptions: {
56
+ resolveExtensions: [".web.tsx", ".web.ts", ".web.js", ".js", ".ts", ".tsx"],
57
+ },
58
+ },
59
+ });
@@ -0,0 +1,168 @@
1
+ #!/usr/bin/env node
2
+ // gogitcms-editor — terminal UI for setting up and running the Go·Git CMS editor.
3
+ //
4
+ // Subcommands map one-to-one onto the steps of the onboarding design. Running it
5
+ // bare prints the help.
6
+
7
+ import { Screen } from "../src/tui/screen.mjs";
8
+ import { NotInteractiveError } from "../src/tui/prompts.mjs";
9
+ import { login } from "../src/commands/login.mjs";
10
+ import { init } from "../src/commands/init.mjs";
11
+ import { dev } from "../src/commands/dev.mjs";
12
+ import { build } from "../src/commands/build.mjs";
13
+ import { logout, nextHint, status } from "../src/commands/status.mjs";
14
+ import * as config from "../src/lib/config.mjs";
15
+ import * as credentials from "../src/lib/credentials.mjs";
16
+
17
+ // Flags shared by every command, plus the few that are command-specific. Values
18
+ // are strings unless listed as boolean.
19
+ const BOOLEAN_FLAGS = new Set(["open", "yes", "help", "version", "no-tty", "local"]);
20
+ const ALIASES = { p: "port", o: "out", h: "help", y: "yes", v: "version" };
21
+
22
+ function parseArgs(argv) {
23
+ const opts = {};
24
+ const rest = [];
25
+ for (let i = 0; i < argv.length; i++) {
26
+ const arg = argv[i];
27
+ if (!arg.startsWith("-")) {
28
+ rest.push(arg);
29
+ continue;
30
+ }
31
+ const [flag, inline] = arg.replace(/^--?/, "").split("=");
32
+ const name = ALIASES[flag] || flag;
33
+ if (BOOLEAN_FLAGS.has(name)) {
34
+ opts[camel(name)] = true;
35
+ continue;
36
+ }
37
+ opts[camel(name)] = inline ?? argv[++i];
38
+ }
39
+ return { opts, rest };
40
+ }
41
+
42
+ const camel = (s) => s.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
43
+
44
+ const COMMANDS = { login, init, dev, build, status, logout };
45
+
46
+ const HELP = `
47
+ gogitcms-editor — set up and run the Go·Git CMS editor
48
+
49
+ gogitcms-editor login authenticate this machine
50
+ gogitcms-editor init create cms.config.mjs for this project
51
+ gogitcms-editor dev start the local editor
52
+ gogitcms-editor build export the static editor to ./dist
53
+ gogitcms-editor status show setup progress
54
+ gogitcms-editor logout forget this machine's stored session
55
+
56
+ Options
57
+ --local edit the files in this directory, with no account
58
+ or server (dev only; runs the bundled gitcms-local
59
+ binary from the platform optional dependency)
60
+ --root <dir> working copy to serve in --local mode (default .)
61
+ --local-port <port> port for the local content server in --local mode
62
+ (default 4321)
63
+ --api-url <url> CMS server to talk to (env GITCMS_API_URL)
64
+ --workspace <id|slug> workspace for init, when not choosing interactively
65
+ --repository <id|owner/name> repository for init
66
+ -y, --yes don't ask before overwriting an existing config
67
+ -p, --port <port> dev server port (default 5173)
68
+ --host <host> dev server bind address (default localhost)
69
+ --open open the browser once dev is ready
70
+ -o, --out <dir> build output directory (default dist)
71
+ --base <path> path the editor is served from, overriding the
72
+ config's BASE_PATH (e.g. /cms/); applies to
73
+ init, dev and build
74
+ --no-tty plain output, no menus or animation
75
+ -h, --help show this help
76
+ `;
77
+
78
+ async function main() {
79
+ const { opts, rest } = parseArgs(process.argv.slice(2));
80
+ if (opts.noTty) process.env.GITCMS_NO_TTY = "1";
81
+
82
+ if (opts.version) {
83
+ const { default: pkg } = await import("../package.json", { with: { type: "json" } });
84
+ console.log(pkg.version);
85
+ return 0;
86
+ }
87
+ // No command is not an error, but there is nothing to guess at either: show
88
+ // what the CLI can do and let the user pick.
89
+ const name = rest[0];
90
+ if (!name || opts.help || name === "help") {
91
+ console.log(HELP.trim());
92
+ return 0;
93
+ }
94
+ if (!COMMANDS[name]) {
95
+ console.error(`Unknown command: ${name}\nRun gogitcms-editor help for the list.`);
96
+ return 1;
97
+ }
98
+
99
+ const screen = new Screen();
100
+ // Seed the status line from what is already on disk, so the very first frame
101
+ // reports the real state instead of a default that flips a moment later.
102
+ await primeStatus(screen, opts);
103
+
104
+ // Ctrl-C anywhere outside a prompt (the long device-code poll, a running dev
105
+ // server) lands here. Without it the process dies mid-frame, leaving the
106
+ // status bar drawn and the terminal's colours unreset. A command that owns
107
+ // something closable claims the signal via screen.onInterrupt and returns
108
+ // normally instead, so the finally below still restores the terminal.
109
+ const onSignal = () => {
110
+ if (screen.onInterrupt) {
111
+ const claimed = screen.onInterrupt;
112
+ screen.onInterrupt = null;
113
+ void claimed();
114
+ return;
115
+ }
116
+ screen.finish();
117
+ process.exit(130);
118
+ };
119
+ process.on("SIGINT", onSignal);
120
+ process.on("SIGTERM", onSignal);
121
+
122
+ try {
123
+ return await COMMANDS[name](screen, opts);
124
+ } catch (e) {
125
+ screen.stopSpinner();
126
+ if (e instanceof NotInteractiveError) {
127
+ screen.fail(e.message);
128
+ return 1;
129
+ }
130
+ screen.fail(e?.message || String(e));
131
+ if (process.env.GITCMS_DEBUG) console.error(e);
132
+ return 1;
133
+ } finally {
134
+ screen.finish();
135
+ // These outlive the command otherwise, and a listener on the process keeps
136
+ // nothing alive but does keep firing.
137
+ process.off("SIGINT", onSignal);
138
+ process.off("SIGTERM", onSignal);
139
+ }
140
+ }
141
+
142
+ async function primeStatus(screen, opts) {
143
+ try {
144
+ const cfg = await config.load();
145
+ // Local mode has no account, so the sign-in step is satisfied by definition —
146
+ // showing "not logged in" would be reporting a blocker that doesn't exist.
147
+ if (opts.local || cfg?.MODE === "local") {
148
+ screen.setDone(0, true);
149
+ screen.setDone(1, Boolean(cfg));
150
+ screen.setStatus({ auth: "local files", authed: true, hint: "run gogitcms-editor dev --local" });
151
+ return;
152
+ }
153
+ const apiUrl = opts.apiUrl || cfg?.API_URL;
154
+ const creds = apiUrl ? await credentials.load(apiUrl) : (await credentials.loadAny())?.[1];
155
+ const done = [Boolean(creds), Boolean(cfg), false];
156
+ done.forEach((d, i) => screen.setDone(i, d));
157
+ screen.setStatus({
158
+ auth: creds?.email || "not logged in",
159
+ authed: Boolean(creds),
160
+ hint: nextHint(done),
161
+ });
162
+ } catch {
163
+ // A malformed config must not stop the CLI from running — the command that
164
+ // needs it will report the problem in context.
165
+ }
166
+ }
167
+
168
+ process.exitCode = await main();