sst 2.0.0-rc.40 → 2.0.0-rc.41

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/cli/colors.d.ts CHANGED
@@ -1,7 +1,25 @@
1
1
  import chalk from "chalk";
2
2
  export declare const Colors: {
3
+ line: (message?: any, ...optionalParams: any[]) => void;
4
+ gap(): void;
5
+ hex: (color: string) => chalk.Chalk;
3
6
  primary: chalk.Chalk;
4
7
  link: chalk.Chalk;
5
8
  success: chalk.Chalk;
6
9
  danger: chalk.Chalk;
10
+ warning: chalk.Chalk;
11
+ dim: chalk.Chalk;
12
+ bold: chalk.Chalk;
13
+ all: chalk.Chalk & chalk.ChalkFunction & {
14
+ supportsColor: false | chalk.ColorSupport;
15
+ Level: chalk.Level;
16
+ Color: ("red" | "green" | "yellow" | "black" | "blue" | "magenta" | "cyan" | "white" | "gray" | "grey" | "blackBright" | "redBright" | "greenBright" | "yellowBright" | "blueBright" | "magentaBright" | "cyanBright" | "whiteBright") | ("bgBlack" | "bgRed" | "bgGreen" | "bgYellow" | "bgBlue" | "bgMagenta" | "bgCyan" | "bgWhite" | "bgGray" | "bgGrey" | "bgBlackBright" | "bgRedBright" | "bgGreenBright" | "bgYellowBright" | "bgBlueBright" | "bgMagentaBright" | "bgCyanBright" | "bgWhiteBright");
17
+ ForegroundColor: "red" | "green" | "yellow" | "black" | "blue" | "magenta" | "cyan" | "white" | "gray" | "grey" | "blackBright" | "redBright" | "greenBright" | "yellowBright" | "blueBright" | "magentaBright" | "cyanBright" | "whiteBright";
18
+ BackgroundColor: "bgBlack" | "bgRed" | "bgGreen" | "bgYellow" | "bgBlue" | "bgMagenta" | "bgCyan" | "bgWhite" | "bgGray" | "bgGrey" | "bgBlackBright" | "bgRedBright" | "bgGreenBright" | "bgYellowBright" | "bgBlueBright" | "bgMagentaBright" | "bgCyanBright" | "bgWhiteBright";
19
+ Modifiers: "bold" | "dim" | "reset" | "italic" | "underline" | "inverse" | "hidden" | "strikethrough" | "visible";
20
+ stderr: chalk.Chalk & {
21
+ supportsColor: false | chalk.ColorSupport;
22
+ };
23
+ };
24
+ prefix: string;
7
25
  };
package/cli/colors.js CHANGED
@@ -1,7 +1,24 @@
1
1
  import chalk from "chalk";
2
+ let last = "gap";
2
3
  export const Colors = {
4
+ line: (message, ...optionalParams) => {
5
+ last = "line";
6
+ console.log(message, ...optionalParams);
7
+ },
8
+ gap() {
9
+ if (last === "line") {
10
+ last = "gap";
11
+ console.log();
12
+ }
13
+ },
14
+ hex: chalk.hex,
3
15
  primary: chalk.hex("#E27152"),
4
16
  link: chalk.cyan,
5
17
  success: chalk.green,
6
18
  danger: chalk.red,
19
+ warning: chalk.yellow,
20
+ dim: chalk.dim,
21
+ bold: chalk.bold,
22
+ all: chalk,
23
+ prefix: chalk.bold("| "),
7
24
  };
@@ -1,8 +1,9 @@
1
+ import { useLocalServerConfig } from "../local/server.js";
1
2
  export const consoleCommand = async (program) => program.command("console", "Start the SST Console", (yargs) => yargs, async () => {
2
3
  const { blue } = await import("colorette");
3
4
  const { useRuntimeServer } = await import("../../runtime/server.js");
4
5
  const { useLocalServer } = await import("../local/server.js");
5
- const [_, local] = await Promise.all([
6
+ await Promise.all([
6
7
  useRuntimeServer(),
7
8
  useLocalServer({
8
9
  key: "",
@@ -10,5 +11,6 @@ export const consoleCommand = async (program) => program.command("console", "Sta
10
11
  live: false,
11
12
  }),
12
13
  ]);
13
- console.log(`Console started: ${blue(local.url)}`);
14
+ const local = await useLocalServerConfig();
15
+ console.log(`Console started: ${local.url}`);
14
16
  });
@@ -1,3 +1,5 @@
1
+ import { Colors } from "../colors.js";
2
+ import { printHeader } from "../ui/header.js";
1
3
  export const deploy = (program) => program.command("deploy [filter]", "Deploy your app to AWS", (yargs) => yargs
2
4
  .option("from", {
3
5
  type: "string",
@@ -15,13 +17,8 @@ export const deploy = (program) => program.command("deploy [filter]", "Deploy yo
15
17
  const { loadAssembly, Stacks } = await import("../../stacks/index.js");
16
18
  const { render } = await import("ink");
17
19
  const { DeploymentUI } = await import("../ui/deploy.js");
18
- const { Colors } = await import("../colors.js");
19
20
  const project = useProject();
20
- console.log();
21
- console.log(` ${Colors.primary(`${bold(`SST`)} v${project.version}`)}`);
22
- console.log();
23
- console.log(` ${Colors.primary(`➜`)} ${bold(`Stage:`)} ${dim(project.config.stage)}`);
24
- console.log();
21
+ printHeader({});
25
22
  // Generate cloud assembly
26
23
  // - if --from is specified, we will use the existing cloud assembly
27
24
  // - if --from is not specified, we will call synth to generate
@@ -32,7 +29,6 @@ export const deploy = (program) => program.command("deploy [filter]", "Deploy yo
32
29
  }
33
30
  const spinner = createSpinner({
34
31
  text: " Building stacks",
35
- indent: 2,
36
32
  });
37
33
  const result = await Stacks.synth({
38
34
  fn: project.stacks,
@@ -45,7 +41,7 @@ export const deploy = (program) => program.command("deploy [filter]", "Deploy yo
45
41
  const target = assembly.stacks.filter((s) => !args.filter ||
46
42
  s.stackName.toLowerCase().includes(args.filter.toLowerCase()));
47
43
  if (!target.length) {
48
- console.log(`No stacks found matching ${blue(args.filter)}`);
44
+ Colors.line(`No stacks found matching ${blue(args.filter)}`);
49
45
  process.exit(1);
50
46
  }
51
47
  const component = render(React.createElement(DeploymentUI, { stacks: assembly.stacks.map((s) => s.stackName) }));
@@ -1,5 +1,6 @@
1
1
  import chalk from "chalk";
2
2
  import { Colors } from "../colors.js";
3
+ import { printHeader } from "../ui/header.js";
3
4
  export const dev = (program) => program.command(["dev", "start"], "Work on your app locally", (yargs) => yargs, async (args) => {
4
5
  const { useRuntimeWorkers } = await import("../../runtime/workers.js");
5
6
  const { useIOTBridge } = await import("../../runtime/iot.js");
@@ -27,70 +28,71 @@ export const dev = (program) => program.command(["dev", "start"], "Work on your
27
28
  const { useRDSWarmer } = await import("./plugins/warmer.js");
28
29
  const { useProject } = await import("../../project.js");
29
30
  const { useMetadata } = await import("../../stacks/metadata.js");
30
- const { Functions } = await import("../ui/functions.js");
31
31
  if (args._[0] === "start") {
32
32
  console.log(yellow(`Warning: ${bold(`sst start`)} has been renamed to ${bold(`sst dev`)}`));
33
33
  }
34
34
  const useFunctionLogger = Context.memo(async () => {
35
- const component = render(React.createElement(Functions, null));
36
35
  const bus = useBus();
37
- /*
36
+ const colors = ["#01cdfe", "#ff71ce", "#05ffa1", "#b967ff"];
37
+ let index = 0;
38
+ const pending = new Map();
39
+ function start(requestID) {
40
+ pending.set(requestID, {
41
+ requestID,
42
+ started: Date.now(),
43
+ color: colors[index % colors.length],
44
+ });
45
+ index++;
46
+ }
47
+ function prefix(requestID) {
48
+ return Colors.hex(pending.get(requestID).color)(Colors.prefix);
49
+ }
50
+ function end(requestID) {
51
+ // index--;
52
+ // if (index < 0) index = colors.length - 1;
53
+ pending.delete(requestID);
54
+ }
38
55
  bus.subscribe("function.invoked", async (evt) => {
39
- console.log(
40
- bold(magenta(` ➜ `)),
41
- useFunctions().fromID(evt.properties.functionID).handler!
42
- );
56
+ start(evt.properties.requestID);
57
+ Colors.line(prefix(evt.properties.requestID), Colors.dim.bold("Invoked"), Colors.dim(useFunctions().fromID(evt.properties.functionID).handler));
43
58
  });
44
-
59
+ bus.subscribe("worker.stdout", async (evt) => {
60
+ const { started } = pending.get(evt.properties.requestID);
61
+ for (let line of evt.properties.message.split("\n")) {
62
+ Colors.line(prefix(evt.properties.requestID), Colors.dim(("+" + (Date.now() - started) + "ms").padEnd(7)), Colors.dim(line));
63
+ }
64
+ });
65
+ /*
45
66
  bus.subscribe("function.build.success", async (evt) => {
46
67
  console.log(
47
68
  bold(gray(`Built `)),
48
69
  useFunctions().fromID(evt.properties.functionID).handler!
49
70
  );
50
71
  });
72
+ */
51
73
  bus.subscribe("function.build.failed", async (evt) => {
52
- console.log(
53
- bold(red(`Build failed `)),
54
- useFunctions().fromID(evt.properties.functionID).handler!
55
- );
56
- console.log(dim(evt.properties.errors.join("\n")));
57
- });
58
-
59
- bus.subscribe("worker.stdout", async (evt) => {
60
- const { message } = evt.properties;
61
- const lines = message.split("\n");
62
- for (let i = 0; i < lines.length; i++) {
63
- const line = lines[i];
64
- lines[i] = " " + line;
65
- }
66
- console.log(
67
- bold(blue(` ➜ `)),
68
- useFunctions().fromID(evt.properties.functionID).handler!
69
- );
70
- console.log(dim(lines.join("\n")));
74
+ Colors.line(Colors.danger(Colors.prefix), useFunctions().fromID(evt.properties.functionID).handler);
75
+ for (const line of evt.properties.errors) {
76
+ Colors.line(Colors.danger(Colors.prefix), line);
77
+ }
71
78
  });
72
- */
73
- /*
74
79
  bus.subscribe("function.success", async (evt) => {
75
- console.log(
76
- bold(green(` ✔ `)),
77
- useFunctions().fromID(evt.properties.functionID).handler!
78
- );
79
- console.log();
80
+ // stdout logs sometimes come in after
81
+ const req = pending.get(evt.properties.requestID);
82
+ setTimeout(() => {
83
+ Colors.line(prefix(evt.properties.requestID), Colors.dim(`Done in ${Date.now() - req.started - 100}ms`));
84
+ end(evt.properties.requestID);
85
+ }, 100);
80
86
  });
81
- */
82
- /*
83
87
  bus.subscribe("function.error", async (evt) => {
84
- console.log(
85
- bold(red(` ✖ `)),
86
- useFunctions().fromID(evt.properties.functionID).handler!
87
- );
88
- console.log(` ${Colors.danger(evt.properties.errorMessage)}`);
89
- for (const line of evt.properties.trace || []) {
90
- console.log(` ${dim(line)}`);
91
- }
88
+ setTimeout(() => {
89
+ Colors.line(prefix(evt.properties.requestID), Colors.danger.bold("Error:"), Colors.danger.bold(evt.properties.errorMessage));
90
+ for (const line of evt.properties.trace || []) {
91
+ Colors.line(prefix(evt.properties.requestID), `${dim(line)}`);
92
+ }
93
+ end(evt.properties.requestID);
94
+ }, 100);
92
95
  });
93
- */
94
96
  });
95
97
  const useStackBuilder = Context.memo(async () => {
96
98
  const watcher = useWatcher();
@@ -100,8 +102,8 @@ export const dev = (program) => program.command(["dev", "start"], "Work on your
100
102
  let pending;
101
103
  let isDeploying = false;
102
104
  async function build() {
105
+ Colors.gap();
103
106
  const spinner = createSpinner({
104
- indent: 2,
105
107
  color: "gray",
106
108
  text: lastDeployed
107
109
  ? ` Building stacks`
@@ -121,7 +123,7 @@ export const dev = (program) => program.command(["dev", "start"], "Work on your
121
123
  Logger.debug("Checksum", "next", next, "old", lastDeployed);
122
124
  if (next === lastDeployed) {
123
125
  spinner.succeed(" No changes");
124
- console.log();
126
+ Colors.gap();
125
127
  return;
126
128
  }
127
129
  if (!lastDeployed) {
@@ -137,10 +139,11 @@ export const dev = (program) => program.command(["dev", "start"], "Work on your
137
139
  }
138
140
  catch (ex) {
139
141
  spinner.fail();
140
- console.log(ex.stack
142
+ Colors.line(ex.stack
141
143
  .split("\n")
142
- .map((line) => " " + line)
144
+ .map((line) => " " + line)
143
145
  .join("\n"));
146
+ Colors.gap();
144
147
  }
145
148
  }
146
149
  async function deploy() {
@@ -152,12 +155,12 @@ export const dev = (program) => program.command(["dev", "start"], "Work on your
152
155
  const assembly = pending;
153
156
  const nextChecksum = await checksum(assembly.directory);
154
157
  pending = undefined;
155
- console.log();
158
+ if (lastDeployed)
159
+ console.log();
156
160
  const component = render(React.createElement(DeploymentUI, { stacks: assembly.stacks.map((s) => s.stackName) }));
157
161
  const results = await Stacks.deployMany(assembly.stacks);
158
162
  component.clear();
159
163
  component.unmount();
160
- render(React.createElement(Functions, null));
161
164
  lastDeployed = nextChecksum;
162
165
  printDeploymentResults(assembly, results);
163
166
  const keys = await SiteEnv.keys();
@@ -208,17 +211,13 @@ export const dev = (program) => program.command(["dev", "start"], "Work on your
208
211
  const project = useProject();
209
212
  const primary = chalk.hex("#E27152");
210
213
  const link = chalk.cyan;
211
- const local = await useLocalServer({
214
+ await useLocalServer({
212
215
  key: "",
213
216
  cert: "",
214
217
  live: true,
215
218
  });
216
219
  console.clear();
217
- console.log();
218
- console.log(` ${Colors.primary(`${bold(`SST`)} v${project.version}`)} ${dim(`ready!`)}`);
219
- console.log();
220
- console.log(` ${primary(`➜`)} ${bold(`Stage:`)} ${dim(project.config.stage)}`);
221
- console.log(` ${primary(`➜`)} ${bold(`Console:`)} ${link(local.url)}`);
220
+ await printHeader({ console: true });
222
221
  /*
223
222
  console.log(` ${primary(`➜`)} ${bold(dim(`Outputs:`))}`);
224
223
  for (let i = 0; i < 3; i++) {
@@ -2,12 +2,13 @@ export const diff = (program) => program.command("diff", "Compare your app with
2
2
  type: "boolean",
3
3
  describe: "Compare in dev mode",
4
4
  }), async (args) => {
5
- const { printStackDiff } = await import("aws-cdk/lib/diff.js");
6
5
  const { useProject } = await import("../../project.js");
7
6
  const { Stacks } = await import("../../stacks/index.js");
8
7
  const { useAWSClient } = await import("../../credentials.js");
9
8
  const { CloudFormationClient, GetTemplateCommand } = await import("@aws-sdk/client-cloudformation");
10
9
  const { createSpinner } = await import("../spinner.js");
10
+ const { green } = await import("colorette");
11
+ // Build app
11
12
  const spinner = createSpinner("Building stacks");
12
13
  const project = useProject();
13
14
  const assembly = await Stacks.synth({
@@ -15,12 +16,51 @@ export const diff = (program) => program.command("diff", "Compare your app with
15
16
  mode: args.dev ? "dev" : "deploy",
16
17
  });
17
18
  spinner.succeed();
19
+ console.log("");
20
+ // Diff each stack
21
+ let changesAcc = 0;
22
+ let changedStacks = 0;
18
23
  const cfn = useAWSClient(CloudFormationClient);
19
24
  for (const stack of assembly.stacks) {
25
+ const spinner = createSpinner(`${stack.stackName}: Checking for changes...`);
26
+ // get old template
20
27
  const response = await cfn.send(new GetTemplateCommand({
21
28
  StackName: stack.stackName,
22
29
  }));
23
- printStackDiff(JSON.parse(response.TemplateBody), stack, true, 3);
30
+ const oldTemplate = JSON.parse(response.TemplateBody);
31
+ // generate diff
32
+ const { count, diff } = await Stacks.diff(stack, oldTemplate);
33
+ spinner.clear();
34
+ // print diff result
35
+ if (count === 0) {
36
+ console.log(`➜ ${stack.stackName}: No changes`);
37
+ console.log("");
38
+ }
39
+ else if (count === 1) {
40
+ console.log(`➜ ${stack.stackName}: ${count} change`);
41
+ console.log("");
42
+ console.log(diff);
43
+ changesAcc += count;
44
+ changedStacks++;
45
+ }
46
+ else {
47
+ console.log(`➜ ${stack.stackName}: ${count} changes`);
48
+ console.log("");
49
+ console.log(diff);
50
+ changesAcc += count;
51
+ changedStacks++;
52
+ }
53
+ }
54
+ // Handle no changes
55
+ if (changedStacks === 0) {
56
+ console.log(green("✔"), " There were no changes");
57
+ }
58
+ else {
59
+ console.log(green("✔"), changesAcc === 1
60
+ ? "1 change found in"
61
+ : `${changesAcc} changes found in`, changedStacks === 1
62
+ ? "1 stack"
63
+ : `${changedStacks} stacks`);
24
64
  }
25
65
  process.exit(0);
26
66
  });
@@ -17,7 +17,7 @@ export const env = (program) => program.command("env <command>", "Load environme
17
17
  .then(() => true)
18
18
  .catch(() => false);
19
19
  if (!exists) {
20
- spinner = createSpinner("Waiting for SST to start").start();
20
+ spinner = createSpinner("Cannot find SST environment variables. Waiting for SST to start...").start();
21
21
  await new Promise((resolve) => setTimeout(resolve, 1000));
22
22
  continue;
23
23
  }
@@ -11,9 +11,11 @@ declare module "../../bus.js" {
11
11
  "local.patches": DendriformPatch[];
12
12
  }
13
13
  }
14
- export declare function useLocalServer(opts: Opts): Promise<{
14
+ export declare const useLocalServerConfig: () => Promise<{
15
15
  port: number;
16
16
  url: string;
17
+ }>;
18
+ export declare function useLocalServer(opts: Opts): Promise<{
17
19
  updateState: (cb: (draft: WritableDraft<State>) => void) => void;
18
20
  updateFunction: (id: string, cb: (draft: WritableDraft<FunctionState>) => void) => void;
19
21
  }>;
@@ -12,11 +12,20 @@ import { sync } from "cross-spawn";
12
12
  import { useProject } from "../../project.js";
13
13
  import { useBus } from "../../bus.js";
14
14
  import getPort from "get-port";
15
- export async function useLocalServer(opts) {
15
+ import { Context } from "../../context/context.js";
16
+ export const useLocalServerConfig = Context.memo(async () => {
16
17
  const project = useProject();
17
18
  const port = await getPort({
18
19
  port: 13557,
19
20
  });
21
+ return {
22
+ port,
23
+ url: `https://console.sst.dev/${project.config.name}/${project.config.stage}${port !== 13557 ? `?port=${port}` : ""}`,
24
+ };
25
+ });
26
+ export async function useLocalServer(opts) {
27
+ const cfg = await useLocalServerConfig();
28
+ const project = useProject();
20
29
  let state = {
21
30
  app: project.config.name,
22
31
  stage: project.config.stage,
@@ -106,7 +115,7 @@ export async function useLocalServer(opts) {
106
115
  console.log("Rejecting unauthorized connection from " + req.headers.origin);
107
116
  socket.terminate();
108
117
  });
109
- server.listen(port);
118
+ server.listen(cfg.port);
110
119
  const handler = applyWSSHandler({
111
120
  wss,
112
121
  router,
@@ -203,8 +212,6 @@ export async function useLocalServer(opts) {
203
212
  });
204
213
  });
205
214
  const result = {
206
- port,
207
- url: `https://console.sst.dev/${project.config.name}/${project.config.stage}${port !== 13557 ? `?port=${port}` : ""}`,
208
215
  updateState,
209
216
  updateFunction,
210
217
  };
package/cli/ui/deploy.js CHANGED
@@ -3,7 +3,6 @@ import { Box, Text } from "ink";
3
3
  import { useBus } from "../../bus.js";
4
4
  import { Stacks } from "../../stacks/index.js";
5
5
  import inkSpinner from "ink-spinner";
6
- import { bold, dim, green, red } from "colorette";
7
6
  import { Colors } from "../colors.js";
8
7
  // @ts-ignore
9
8
  const { default: Spinner } = inkSpinner;
@@ -12,7 +11,9 @@ export const DeploymentUI = (props) => {
12
11
  const [resources, setResources] = useState({});
13
12
  const [resources2, setResources2] = useState({});
14
13
  useEffect(() => {
15
- console.log(` ${Colors.primary(`➜`)} ${bold(dim(`Deploying...`))}`);
14
+ Colors.gap();
15
+ Colors.line(`${Colors.primary(`➜`)} ${Colors.bold(`Deploying...`)}`);
16
+ Colors.gap();
16
17
  const bus = useBus();
17
18
  const update = bus.subscribe("stack.updated", (payload) => {
18
19
  setStacks((prev) => ({
@@ -32,9 +33,9 @@ export const DeploymentUI = (props) => {
32
33
  return;
33
34
  setResources2((previous) => {
34
35
  if (Stacks.isFinal(event.ResourceStatus)) {
35
- console.log(dim(` ${event.StackName} ${event.ResourceType} ${event.LogicalResourceId}`), Stacks.isFailed(event.ResourceStatus)
36
+ Colors.line(Colors.warning(Colors.prefix), Colors.dim(`${event.StackName} ${event.ResourceType} ${event.LogicalResourceId}`), Stacks.isFailed(event.ResourceStatus)
36
37
  ? Colors.danger(event.ResourceStatus)
37
- : dim(event.ResourceStatus));
38
+ : Colors.dim(event.ResourceStatus));
38
39
  const { [event.LogicalResourceId]: _, ...next } = previous;
39
40
  return next;
40
41
  }
@@ -61,7 +62,6 @@ export const DeploymentUI = (props) => {
61
62
  Object.entries(resources2).map(([_, evt]) => {
62
63
  return (React.createElement(Box, { key: evt.LogicalResourceId },
63
64
  React.createElement(Text, null,
64
- " ",
65
65
  React.createElement(Spinner, null),
66
66
  " ",
67
67
  evt.StackName,
@@ -75,9 +75,11 @@ export const DeploymentUI = (props) => {
75
75
  React.createElement(Box, null)));
76
76
  };
77
77
  export function printDeploymentResults(assembly, results) {
78
- console.log();
79
- console.log(` ${green(`✔`)} ${bold(dim(`Deployed:`))}`);
78
+ Colors.gap();
79
+ Colors.line(Colors.success(`✔`), Colors.bold(` Deployed`));
80
80
  for (const [stack, result] of Object.entries(results)) {
81
+ if (Object.values(result.errors).length)
82
+ continue;
81
83
  const outputs = Object.entries(result.outputs).filter(([key, _]) => {
82
84
  if (key.startsWith("Export"))
83
85
  return false;
@@ -87,22 +89,22 @@ export function printDeploymentResults(assembly, results) {
87
89
  return false;
88
90
  return true;
89
91
  });
90
- console.log(` ${dim(stack)}`);
92
+ Colors.line(` ${Colors.dim(stack)}`);
91
93
  if (outputs.length > 0) {
92
94
  for (const key of Object.keys(Object.fromEntries(outputs)).sort()) {
93
95
  const value = result.outputs[key];
94
- console.log(` ${bold(dim(key))}: ${value}`);
96
+ Colors.line(` ${Colors.bold.dim(key)}: ${value}`);
95
97
  }
96
98
  }
97
99
  }
98
- console.log();
100
+ Colors.gap();
99
101
  if (Object.values(results).flatMap((s) => Object.keys(s.errors)).length) {
100
- console.log(` ${red(`✖`)} ${bold(dim(`Errors:`))}`);
102
+ Colors.line(`${Colors.danger(`✖`)} ${Colors.bold.dim(`Errors`)}`);
101
103
  for (const [stack, result] of Object.entries(results)) {
102
104
  const hasErrors = Object.entries(result.errors).length > 0;
103
105
  if (!hasErrors)
104
106
  continue;
105
- console.log(` ${dim(stack)}`);
107
+ Colors.line(` ${Colors.dim(stack)}`);
106
108
  for (const [id, error] of Object.entries(result.errors)) {
107
109
  const found = Object.entries(assembly.manifest.artifacts?.[stack].metadata || {}).find(([_key, value]) => value[0]?.type === "aws:cdk:logicalId" && value[0]?.data === id)?.[0] || "";
108
110
  const readable = found
@@ -110,9 +112,9 @@ export function printDeploymentResults(assembly, results) {
110
112
  .filter(Boolean)
111
113
  .slice(1, -1)
112
114
  .join("/");
113
- console.log(` ${bold(red(readable))}: ${error}`);
115
+ Colors.line(` ${Colors.danger.bold(readable)}: ${error}`);
114
116
  }
115
- console.log();
116
117
  }
118
+ Colors.gap();
117
119
  }
118
120
  }
@@ -0,0 +1,4 @@
1
+ export declare function printHeader(input: {
2
+ console?: boolean;
3
+ }): Promise<void>;
4
+ export declare function printConsole(): void;
@@ -0,0 +1,12 @@
1
+ import { Colors } from "../colors.js";
2
+ import { useLocalServerConfig } from "../local/server.js";
3
+ export async function printHeader(input) {
4
+ const local = await useLocalServerConfig();
5
+ Colors.line(`${Colors.primary.bold("SST v2.0.4")} ${Colors.dim(`ready!`)}`);
6
+ Colors.gap();
7
+ Colors.line(`${Colors.primary(`➜`)} ${Colors.bold("Stage:")} dev`);
8
+ if (input.console)
9
+ Colors.line(`${Colors.primary(`➜`)} ${Colors.bold("Console:")} ${Colors.link(local.url)}`);
10
+ Colors.gap();
11
+ }
12
+ export function printConsole() { }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sst",
3
- "version": "2.0.0-rc.40",
3
+ "version": "2.0.0-rc.41",
4
4
  "bin": {
5
5
  "sst": "cli/sst.js"
6
6
  },