git-stack-cli 2.6.0 → 2.6.1

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "git-stack-cli",
3
- "version": "2.6.0",
3
+ "version": "2.6.1",
4
4
  "description": "",
5
5
  "author": "magus",
6
6
  "license": "MIT",
@@ -111,6 +111,11 @@ if (!WATCH) {
111
111
  continue;
112
112
  }
113
113
 
114
+ // ignore bun build files
115
+ if (filename.includes(".bun-build")) {
116
+ continue;
117
+ }
118
+
114
119
  log(`⚠️ Change ${filename}`);
115
120
 
116
121
  if (VERBOSE) {
package/src/app/App.tsx CHANGED
@@ -14,6 +14,7 @@ import { Main } from "~/app/Main";
14
14
  import { Output } from "~/app/Output";
15
15
  import { Providers } from "~/app/Providers";
16
16
  import { RebaseCheck } from "~/app/RebaseCheck";
17
+ import { RequireBranch } from "~/app/RequireBranch";
17
18
  import { Store } from "~/app/Store";
18
19
  import { VerboseDebugInfo } from "~/app/VerboseDebugInfo";
19
20
  import { Fixup } from "~/commands/Fixup";
@@ -112,11 +113,13 @@ function MaybeMain() {
112
113
  <DependencyCheck>
113
114
  <DirtyCheck>
114
115
  <GatherMetadata>
115
- <LocalCommitStatus>
116
- <DetectInitialPR>
117
- <Main />
118
- </DetectInitialPR>
119
- </LocalCommitStatus>
116
+ <RequireBranch>
117
+ <LocalCommitStatus>
118
+ <DetectInitialPR>
119
+ <Main />
120
+ </DetectInitialPR>
121
+ </LocalCommitStatus>
122
+ </RequireBranch>
120
123
  </GatherMetadata>
121
124
  </DirtyCheck>
122
125
  </DependencyCheck>
@@ -6,6 +6,7 @@ import { Brackets } from "~/app/Brackets";
6
6
  import { Command } from "~/app/Command";
7
7
  import { FormatText } from "~/app/FormatText";
8
8
  import { YesNoPrompt } from "~/app/YesNoPrompt";
9
+ import { assertNever } from "~/core/assertNever";
9
10
  import { cli } from "~/core/cli";
10
11
  import { colors } from "~/core/colors";
11
12
  import { fetch_json } from "~/core/fetch_json";
@@ -28,7 +29,7 @@ type State = {
28
29
  error: null | Error;
29
30
  local_version: null | string;
30
31
  latest_version: null | string;
31
- status: "init" | "prompt" | "install" | "done" | "exit";
32
+ status: "init" | "prompt" | "install" | "done";
32
33
  is_brew_bun_standalone: boolean;
33
34
  };
34
35
 
@@ -61,7 +62,24 @@ export function AutoUpdate(props: Props) {
61
62
  }
62
63
 
63
64
  React.useEffect(() => {
64
- let status: State["status"] = "done";
65
+ switch (state.status) {
66
+ case "init":
67
+ case "prompt":
68
+ case "install":
69
+ break;
70
+
71
+ case "done": {
72
+ props.onDone?.();
73
+ break;
74
+ }
75
+
76
+ default:
77
+ assertNever(state.status);
78
+ }
79
+ }, [state.status]);
80
+
81
+ React.useEffect(() => {
82
+ let status: State["status"] = "init";
65
83
  let latest_version: string | null = null;
66
84
  let is_brew_bun_standalone = false;
67
85
 
@@ -73,17 +91,13 @@ export function AutoUpdate(props: Props) {
73
91
  throw new Error("Auto update requires process.env.CLI_VERSION to be set");
74
92
  }
75
93
 
76
- if (is_output) {
77
- handle_output(<Ink.Text key="init">Checking for latest version...</Ink.Text>);
78
- }
79
-
80
94
  const timeout_ms = is_finite_value(props.timeoutMs) ? props.timeoutMs : 2 * 1000;
81
95
 
82
96
  const npm_json = await Promise.race([
83
97
  fetch_json(`https://registry.npmjs.org/${props.name}`),
84
98
 
85
99
  sleep(timeout_ms).then(() => {
86
- throw new Error("Timeout");
100
+ throw new Error("AutoUpdate timeout");
87
101
  }),
88
102
  ]);
89
103
 
@@ -126,8 +140,13 @@ export function AutoUpdate(props: Props) {
126
140
  }
127
141
 
128
142
  const semver_result = semver_compare(latest_version, local_version);
143
+ if (props_ref.current.verbose) {
144
+ handle_output(<Ink.Text dimColor>{JSON.stringify({ semver_result })}</Ink.Text>);
145
+ }
129
146
 
130
147
  if (semver_result === 0) {
148
+ status = "done";
149
+
131
150
  if (is_output) {
132
151
  handle_output(
133
152
  <Ink.Text>
@@ -138,15 +157,12 @@ export function AutoUpdate(props: Props) {
138
157
  return;
139
158
  }
140
159
 
141
- if (semver_result === -1) {
142
- // latest version is less than or equal to local version, skip auto update
143
- throw new Error(
144
- `latest version < local_version, skipping auto update [${latest_version} < ${local_version}]`,
145
- );
160
+ if (semver_result === 1) {
161
+ // trigger yes no prompt
162
+ status = "prompt";
146
163
  }
147
164
 
148
- // trigger yes no prompt
149
- status = "prompt";
165
+ throw new Error("AutoUpdate failed");
150
166
  }
151
167
 
152
168
  const onError = props_ref.current.onError || (() => {});
@@ -156,9 +172,6 @@ export function AutoUpdate(props: Props) {
156
172
  patch({ status, local_version, latest_version, is_brew_bun_standalone });
157
173
  })
158
174
  .catch((error) => {
159
- patch({ status, error, local_version, latest_version, is_brew_bun_standalone });
160
- onError(error);
161
-
162
175
  if (props_ref.current.verbose) {
163
176
  handle_output(
164
177
  <Ink.Text key="error" color={colors.red}>
@@ -166,9 +179,11 @@ export function AutoUpdate(props: Props) {
166
179
  </Ink.Text>,
167
180
  );
168
181
  }
169
- })
170
- .finally(() => {
171
- props.onDone?.();
182
+
183
+ // ensure we always exit
184
+ status = "done";
185
+ patch({ status, error, local_version, latest_version, is_brew_bun_standalone });
186
+ onError(error);
172
187
  });
173
188
  }, []);
174
189
 
@@ -180,54 +195,60 @@ export function AutoUpdate(props: Props) {
180
195
  case "prompt": {
181
196
  let install_command = "";
182
197
  if (state.is_brew_bun_standalone) {
183
- install_command = `npm install -g ${props.name}@latest`;
198
+ install_command = "brew install magus/git-stack/git-stack";
184
199
  } else {
185
- install_command = "brew upgrade magus/git-stack/git-stack";
200
+ install_command = `npm install -g ${props.name}@latest`;
186
201
  }
187
202
 
188
203
  return (
189
204
  <YesNoPrompt
190
205
  message={
191
206
  <Ink.Box flexDirection="column">
192
- <Ink.Text color={colors.yellow}>
207
+ <Ink.Box flexDirection="column">
208
+ <Ink.Text color={colors.yellow}>
209
+ <FormatText
210
+ wrapper={<Ink.Text />}
211
+ message="New version available {latest_version}"
212
+ values={{
213
+ latest_version: <Brackets>{state.latest_version}</Brackets>,
214
+ }}
215
+ />
216
+ ,
217
+ </Ink.Text>
218
+ <Ink.Text> </Ink.Text>
219
+ <Command>{install_command}</Command>
220
+ <Ink.Text> </Ink.Text>
221
+ </Ink.Box>
222
+ <Ink.Box>
193
223
  <FormatText
194
- wrapper={<Ink.Text />}
195
- message="New version available {latest_version}, would you like to update?"
196
- values={{
197
- latest_version: <Brackets>{state.latest_version}</Brackets>,
198
- }}
224
+ wrapper={<Ink.Text color={colors.yellow} />}
225
+ message="Would you like to run the above command to update?"
199
226
  />
200
- ,
201
- </Ink.Text>
202
- <Ink.Text> </Ink.Text>
203
- <Command>{install_command}</Command>
204
- <Ink.Text> </Ink.Text>
205
- <FormatText
206
- wrapper={<Ink.Text color={colors.yellow} />}
207
- message="Would you like to run the above command to update?"
208
- />
227
+ </Ink.Box>
209
228
  </Ink.Box>
210
229
  }
211
230
  onYes={async () => {
212
- handle_output(
213
- <FormatText
214
- key="install"
215
- wrapper={<Ink.Text />}
216
- message="Installing {name}@{version}..."
217
- values={{
218
- name: <Ink.Text color={colors.yellow}>{props.name}</Ink.Text>,
219
- version: <Ink.Text color={colors.blue}>{state.latest_version}</Ink.Text>,
220
- }}
221
- />,
222
- );
231
+ handle_output(<Command>{install_command}</Command>);
223
232
 
224
233
  patch({ status: "install" });
225
234
 
226
- await cli(install_command);
235
+ await cli(install_command, {
236
+ env: {
237
+ ...process.env,
238
+ HOMEBREW_COLOR: "1",
239
+ },
240
+ onOutput: (data: string) => {
241
+ handle_output(<Ink.Text>{data}</Ink.Text>);
242
+ },
243
+ });
227
244
 
228
- patch({ status: "exit" });
245
+ handle_output(
246
+ <Ink.Text key="done">
247
+ ✅ Installed <Brackets>{state.latest_version}</Brackets>
248
+ </Ink.Text>,
249
+ );
229
250
 
230
- handle_output(<Ink.Text key="done">Auto update done.</Ink.Text>);
251
+ patch({ status: "done" });
231
252
  }}
232
253
  onNo={() => {
233
254
  patch({ status: "done" });
@@ -239,9 +260,6 @@ export function AutoUpdate(props: Props) {
239
260
  case "install":
240
261
  return null;
241
262
 
242
- case "exit":
243
- return null;
244
-
245
263
  case "done":
246
264
  return props.children;
247
265
  }
@@ -67,33 +67,10 @@ async function run() {
67
67
 
68
68
  actions.debug(`master_branch = ${master_branch}`);
69
69
 
70
- const branch_name = (await cli("git rev-parse --abbrev-ref HEAD")).stdout;
71
-
72
- // handle detahed head state
73
- if (branch_name === "HEAD") {
74
- actions.error("Must run within a branch.");
75
- actions.exit(0);
76
- return;
77
- }
78
-
79
- // handle when there are no detected changes
80
- if (`origin/${branch_name}` === master_branch) {
81
- actions.error("Must run within a branch.");
82
- actions.exit(0);
83
- return;
84
- }
85
-
86
70
  const head = (await cli("git rev-parse HEAD")).stdout;
71
+ const branch_name = (await cli("git rev-parse --abbrev-ref HEAD")).stdout;
87
72
  const merge_base = (await cli(`git merge-base HEAD ${master_branch}`)).stdout;
88
73
 
89
- // handle when there are no detected changes
90
- if (head === merge_base) {
91
- actions.newline();
92
- actions.output(<Ink.Text color={colors.gray}>No changes detected.</Ink.Text>);
93
- actions.exit(0);
94
- return;
95
- }
96
-
97
74
  // git@github.com:magus/git-multi-diff-playground.git
98
75
  // https://github.com/magus/git-multi-diff-playground.git
99
76
  const origin_url = (await cli(`git config --get remote.origin.url`)).stdout;
@@ -107,6 +84,7 @@ async function run() {
107
84
  state.master_branch = master_branch;
108
85
  state.head = head;
109
86
  state.branch_name = branch_name;
87
+ state.merge_base = merge_base;
110
88
  });
111
89
  } catch (err) {
112
90
  actions.error("Unable to gather git metadata.");
package/src/app/Main.tsx CHANGED
@@ -6,7 +6,6 @@ import { ManualRebase } from "~/app/ManualRebase";
6
6
  import { PostRebaseStatus } from "~/app/PostRebaseStatus";
7
7
  import { PreLocalMergeRebase } from "~/app/PreLocalMergeRebase";
8
8
  import { PreManualRebase } from "~/app/PreManualRebase";
9
- import { PreSelectCommitRanges } from "~/app/PreSelectCommitRanges";
10
9
  import { SelectCommitRanges } from "~/app/SelectCommitRanges";
11
10
  import { Status } from "~/app/Status";
12
11
  import { Store } from "~/app/Store";
@@ -32,9 +31,6 @@ export function Main() {
32
31
  case "pre-local-merge-rebase":
33
32
  return <PreLocalMergeRebase />;
34
33
 
35
- case "pre-select-commit-ranges":
36
- return <PreSelectCommitRanges />;
37
-
38
34
  case "select-commit-ranges":
39
35
  return <SelectCommitRanges />;
40
36
 
@@ -191,7 +191,6 @@ type ItemRowProps = {
191
191
 
192
192
  function ItemRow(props: ItemRowProps) {
193
193
  let color;
194
- let bold;
195
194
  let underline;
196
195
  let dimColor;
197
196
 
@@ -200,14 +199,8 @@ function ItemRow(props: ItemRowProps) {
200
199
  underline = true;
201
200
  }
202
201
 
203
- if (props.selected) {
204
- // color = "";
205
- bold = true;
206
- }
207
-
208
202
  if (props.disabled) {
209
203
  color = "";
210
- bold = false;
211
204
  underline = false;
212
205
  dimColor = true;
213
206
  }
@@ -218,7 +211,7 @@ function ItemRow(props: ItemRowProps) {
218
211
 
219
212
  <Ink.Box width={props.maxWidth}>
220
213
  <Ink.Text
221
- bold={bold}
214
+ // force line break
222
215
  underline={underline}
223
216
  color={color}
224
217
  dimColor={dimColor}
@@ -0,0 +1,67 @@
1
+ import * as React from "react";
2
+
3
+ import * as Ink from "ink-cjs";
4
+
5
+ import { Await } from "~/app/Await";
6
+ import { Store } from "~/app/Store";
7
+ import { colors } from "~/core/colors";
8
+ import { invariant } from "~/core/invariant";
9
+
10
+ type Props = {
11
+ children: React.ReactNode;
12
+ };
13
+
14
+ export function RequireBranch(props: Props) {
15
+ const fallback = <Ink.Text color={colors.yellow}>Gathering local git information…</Ink.Text>;
16
+
17
+ return (
18
+ <Await fallback={fallback} function={run}>
19
+ {props.children}
20
+ </Await>
21
+ );
22
+ }
23
+
24
+ async function run() {
25
+ const state = Store.getState();
26
+ const actions = Store.getState().actions;
27
+ const master_branch = state.master_branch;
28
+ const head = state.head;
29
+ const branch_name = state.branch_name;
30
+ const merge_base = state.merge_base;
31
+
32
+ invariant(head, "head must exist");
33
+ invariant(branch_name, "branch_name must exist");
34
+ invariant(merge_base, "merge_base must exist");
35
+
36
+ try {
37
+ // handle detahed head state
38
+ if (branch_name === "HEAD") {
39
+ actions.error("Must run within a branch.");
40
+ actions.exit(0);
41
+ return;
42
+ }
43
+
44
+ // handle when there are no detected changes
45
+ if (`origin/${branch_name}` === master_branch) {
46
+ actions.error("Must run within a branch.");
47
+ actions.exit(0);
48
+ return;
49
+ }
50
+
51
+ // handle when there are no detected changes
52
+ if (head === merge_base) {
53
+ actions.newline();
54
+ actions.output(<Ink.Text color={colors.gray}>No changes detected.</Ink.Text>);
55
+ actions.exit(0);
56
+ return;
57
+ }
58
+ } catch (err) {
59
+ actions.error("Unable to detect branch changes.");
60
+
61
+ if (err instanceof Error) {
62
+ actions.error(err.message);
63
+ }
64
+
65
+ actions.exit(17);
66
+ }
67
+ }
@@ -232,7 +232,7 @@ function SelectCommitRangesInternal(props: Props) {
232
232
  </Ink.Text>
233
233
  <Ink.Text color={colors.blue}>
234
234
  <FormatText
235
- message="Press {c} to {create} a new PR group"
235
+ message="Press {c} to {create} a new PR"
236
236
  values={{
237
237
  c: (
238
238
  <Ink.Text bold color={colors.green}>
@@ -271,7 +271,7 @@ function SelectCommitRangesInternal(props: Props) {
271
271
  </Ink.Box>
272
272
 
273
273
  <Ink.Box width={max_width}>
274
- <Ink.Text wrap="truncate-end" bold color={colors.green}>
274
+ <Ink.Text wrap="truncate-end" bold color={colors.white}>
275
275
  {group.title}
276
276
  </Ink.Text>
277
277
  </Ink.Box>
@@ -359,7 +359,7 @@ function SelectCommitRangesInternal(props: Props) {
359
359
  {group_input ? null : (
360
360
  <FormatText
361
361
  wrapper={<Ink.Text color={colors.gray} />}
362
- message="Press {c} to {create} a new PR group"
362
+ message="Press {c} to {create} a new PR"
363
363
  values={{
364
364
  c: (
365
365
  <Ink.Text bold color={colors.green}>
@@ -411,7 +411,7 @@ function SelectCommitRangesInternal(props: Props) {
411
411
  <Ink.Box>
412
412
  <FormatText
413
413
  wrapper={<Ink.Text color={colors.gray} />}
414
- message="Press {left} and {right} to view PR groups"
414
+ message="Press {left} and {right} to view PRs"
415
415
  values={{
416
416
  left: (
417
417
  <Ink.Text bold color={colors.green}>
@@ -463,7 +463,7 @@ function SelectCommitRangesInternal(props: Props) {
463
463
  actions.output(
464
464
  <FormatText
465
465
  wrapper={<Ink.Text dimColor />}
466
- message="Created new PR group {group} {note}"
466
+ message="Created new PR {group} {note}"
467
467
  values={{
468
468
  group: <Brackets>{title}</Brackets>,
469
469
  note: <Parens>{id}</Parens>,
@@ -42,11 +42,7 @@ async function run() {
42
42
  Store.setState((state) => {
43
43
  state.step = "pre-local-merge-rebase";
44
44
  });
45
- } else if (needs_update) {
46
- Store.setState((state) => {
47
- state.step = "pre-select-commit-ranges";
48
- });
49
- } else if (argv.force) {
45
+ } else if (needs_update || argv.force) {
50
46
  Store.setState((state) => {
51
47
  state.step = "select-commit-ranges";
52
48
  });
package/src/app/Store.tsx CHANGED
@@ -51,6 +51,7 @@ export type State = {
51
51
  master_branch: string;
52
52
  head: null | string;
53
53
  branch_name: null | string;
54
+ merge_base: null | string;
54
55
  commit_range: null | CommitMetadata.CommitRange;
55
56
  commit_map: null | CommitMap;
56
57
  pr_templates: Array<string>;
@@ -66,7 +67,6 @@ export type State = {
66
67
  | "status"
67
68
  | "pre-local-merge-rebase"
68
69
  | "local-merge-rebase"
69
- | "pre-select-commit-ranges"
70
70
  | "select-commit-ranges"
71
71
  | "pre-manual-rebase"
72
72
  | "manual-rebase"
@@ -122,6 +122,7 @@ const BaseStore = createStore<State>()(
122
122
  master_branch: "origin/master",
123
123
  head: null,
124
124
  branch_name: null,
125
+ merge_base: null,
125
126
  commit_range: null,
126
127
  commit_map: null,
127
128
  pr_templates: [],
@@ -22,7 +22,7 @@ type Props = {
22
22
  export function Rebase(props: Props) {
23
23
  return (
24
24
  <Await
25
- fallback={<Ink.Text color={colors.yellow}>Rebasing commits…</Ink.Text>}
25
+ fallback={<Ink.Text color={colors.yellow}>Rebasing…</Ink.Text>}
26
26
  function={() => Rebase.run(props)}
27
27
  />
28
28
  );
@@ -48,6 +48,7 @@ Rebase.run = async function run(props: Props) {
48
48
  return 19;
49
49
  });
50
50
 
51
+ const master_branch_name = master_branch.replace(/^origin\//, "");
51
52
  const temp_branch_name = `${branch_name}_${short_id()}`;
52
53
 
53
54
  try {
@@ -58,9 +59,56 @@ Rebase.run = async function run(props: Props) {
58
59
  await cli(`pwd`);
59
60
 
60
61
  // fetch origin master branch for latest sha
61
- const master_branch_name = master_branch.replace(/^origin\//, "");
62
62
  await cli(`git fetch --no-tags -v origin ${master_branch_name}`);
63
63
 
64
+ if (branch_name === master_branch_name) {
65
+ await rebase_master();
66
+ } else {
67
+ await rebase_branch();
68
+ }
69
+
70
+ actions.unregister_abort_handler();
71
+ } catch (err) {
72
+ actions.error("Unable to rebase.");
73
+
74
+ if (err instanceof Error) {
75
+ actions.error(err.message);
76
+ }
77
+
78
+ actions.exit(20);
79
+ }
80
+
81
+ const next_commit_range = await CommitMetadata.range();
82
+
83
+ actions.output(
84
+ <FormatText
85
+ wrapper={<Ink.Text color={colors.green} />}
86
+ message="✅ {branch_name} in sync with {origin_branch}"
87
+ values={{
88
+ branch_name: <Brackets>{branch_name}</Brackets>,
89
+ origin_branch: <Brackets>{master_branch}</Brackets>,
90
+ }}
91
+ />,
92
+ );
93
+
94
+ actions.set((state) => {
95
+ state.commit_range = next_commit_range;
96
+ });
97
+
98
+ if (props.onComplete) {
99
+ props.onComplete();
100
+ } else {
101
+ actions.output(<Status />);
102
+ actions.exit(0);
103
+ }
104
+
105
+ async function rebase_master() {
106
+ await cli(`git switch -C "${master_branch_name}" "${master_branch}"`);
107
+ }
108
+
109
+ async function rebase_branch() {
110
+ invariant(commit_range, "commit_range must exist");
111
+
64
112
  const master_sha = (await cli(`git rev-parse ${master_branch}`)).stdout;
65
113
  const rebase_merge_base = master_sha;
66
114
 
@@ -120,40 +168,6 @@ Rebase.run = async function run(props: Props) {
120
168
  await cli(`git branch -f ${branch_name} ${temp_branch_name}`);
121
169
 
122
170
  restore_git();
123
-
124
- actions.unregister_abort_handler();
125
- } catch (err) {
126
- actions.error("Unable to rebase.");
127
-
128
- if (err instanceof Error) {
129
- actions.error(err.message);
130
- }
131
-
132
- actions.exit(20);
133
- }
134
-
135
- const next_commit_range = await CommitMetadata.range();
136
-
137
- actions.output(
138
- <FormatText
139
- wrapper={<Ink.Text color={colors.green} />}
140
- message="✅ {branch_name} in sync with {origin_branch}"
141
- values={{
142
- branch_name: <Brackets>{branch_name}</Brackets>,
143
- origin_branch: <Brackets>{master_branch}</Brackets>,
144
- }}
145
- />,
146
- );
147
-
148
- actions.set((state) => {
149
- state.commit_range = next_commit_range;
150
- });
151
-
152
- if (props.onComplete) {
153
- props.onComplete();
154
- } else {
155
- actions.output(<Status />);
156
- actions.exit(0);
157
171
  }
158
172
 
159
173
  // cleanup git operations if cancelled during manual rebase
@@ -0,0 +1,49 @@
1
+ import * as React from "react";
2
+
3
+ import { colors } from "~/core/colors";
4
+
5
+ type RenderOptions = {
6
+ color: string;
7
+ name: string;
8
+ };
9
+
10
+ type Props = {
11
+ children: (render_options: RenderOptions) => React.ReactNode;
12
+ };
13
+
14
+ export function ColorTest(props: Props) {
15
+ return (
16
+ <React.Fragment>
17
+ {Object.entries(colors).map(([key, color]) => {
18
+ const name = `colors:${key}`;
19
+ return props.children({ color, name });
20
+ })}
21
+
22
+ {INK_COLORS.map((color) => {
23
+ const name = `ink:${color}`;
24
+ return props.children({ color, name });
25
+ })}
26
+ </React.Fragment>
27
+ );
28
+ }
29
+
30
+ // ForegroundColor
31
+ // https://github.com/magus/git-stack-cli/blob/master/node_modules/.pnpm/chalk@5.3.0/node_modules/chalk/source/vendor/ansi-styles/index.d.ts#L75
32
+ const INK_COLORS = [
33
+ "black",
34
+ "red",
35
+ "green",
36
+ "yellow",
37
+ "blue",
38
+ "cyan",
39
+ "magenta",
40
+ "white",
41
+ "blackBright",
42
+ "redBright",
43
+ "greenBright",
44
+ "yellowBright",
45
+ "blueBright",
46
+ "cyanBright",
47
+ "magentaBright",
48
+ "whiteBright",
49
+ ];