sst 2.0.0-rc.46 → 2.0.0-rc.48

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 (47) hide show
  1. package/cli/commands/build.js +5 -4
  2. package/cli/commands/console.js +3 -3
  3. package/cli/commands/deploy.js +9 -3
  4. package/cli/commands/dev.js +5 -7
  5. package/cli/commands/diff.js +10 -15
  6. package/cli/commands/plugins/pothos.js +2 -2
  7. package/cli/commands/remove.js +9 -6
  8. package/cli/commands/secrets/get.js +3 -3
  9. package/cli/commands/secrets/list.js +5 -0
  10. package/cli/commands/secrets/remove.js +11 -4
  11. package/cli/commands/secrets/set.js +3 -3
  12. package/cli/commands/{update-mod.d.ts → transform.d.ts} +2 -2
  13. package/cli/commands/{update-mod.js → transform.js} +6 -4
  14. package/cli/commands/update.js +6 -4
  15. package/cli/commands/version.d.ts +2 -4
  16. package/cli/commands/version.js +5 -35
  17. package/cli/sst.js +4 -2
  18. package/cli/ui/deploy.d.ts +1 -1
  19. package/cli/ui/deploy.js +32 -2
  20. package/cli/ui/header.d.ts +1 -0
  21. package/cli/ui/header.js +4 -3
  22. package/cli/ui/stack.d.ts +1 -0
  23. package/cli/ui/stack.js +6 -0
  24. package/constructs/Api.d.ts +1 -69
  25. package/constructs/Api.js +0 -36
  26. package/constructs/App.d.ts +0 -16
  27. package/constructs/App.js +0 -17
  28. package/constructs/Function.d.ts +0 -39
  29. package/constructs/Function.js +2 -34
  30. package/constructs/Job.d.ts +0 -39
  31. package/constructs/Job.js +1 -27
  32. package/constructs/Metadata.d.ts +1 -3
  33. package/constructs/Stack.d.ts +0 -16
  34. package/constructs/Stack.js +0 -17
  35. package/constructs/index.d.ts +0 -3
  36. package/constructs/index.js +0 -3
  37. package/package.json +1 -1
  38. package/project.d.ts +1 -0
  39. package/project.js +13 -11
  40. package/sst.mjs +247 -135
  41. package/stacks/diff.js +2 -2
  42. package/constructs/GraphQLApi.d.ts +0 -98
  43. package/constructs/GraphQLApi.js +0 -80
  44. package/constructs/ReactStaticSite.d.ts +0 -20
  45. package/constructs/ReactStaticSite.js +0 -55
  46. package/constructs/ViteStaticSite.d.ts +0 -32
  47. package/constructs/ViteStaticSite.js +0 -67
@@ -3,14 +3,15 @@ export const build = (program) => program.command("build", "Build your app", (ya
3
3
  describe: "Output directory, defaults to .sst/dist",
4
4
  }), async (args) => {
5
5
  const { useProject } = await import("../../project.js");
6
- const { createSpinner } = await import("../spinner.js");
7
6
  const { Stacks } = await import("../../stacks/index.js");
8
- const spinner = createSpinner("Building stacks").start();
9
- await Stacks.synth({
7
+ const { Colors } = await import("../colors.js");
8
+ const path = await import("path");
9
+ const result = await Stacks.synth({
10
10
  fn: useProject().stacks,
11
11
  buildDir: args.to,
12
12
  mode: "deploy",
13
13
  });
14
- spinner.succeed();
14
+ Colors.line("");
15
+ Colors.line(Colors.success(`✔`), Colors.bold(" Built:"), `${result.stacks.length} stack${result.stacks.length > 1 ? "s" : ""} to ${path.relative(process.cwd(), result.directory)}`);
15
16
  process.exit(0);
16
17
  });
@@ -1,8 +1,8 @@
1
- import { useLocalServerConfig } from "../local/server.js";
2
1
  export const consoleCommand = async (program) => program.command("console", "Start the SST Console", (yargs) => yargs, async () => {
3
2
  const { blue } = await import("colorette");
4
3
  const { useRuntimeServer } = await import("../../runtime/server.js");
5
4
  const { useLocalServer } = await import("../local/server.js");
5
+ const { printHeader } = await import("../ui/header.js");
6
6
  await Promise.all([
7
7
  useRuntimeServer(),
8
8
  useLocalServer({
@@ -11,6 +11,6 @@ export const consoleCommand = async (program) => program.command("console", "Sta
11
11
  live: false,
12
12
  }),
13
13
  ]);
14
- const local = await useLocalServerConfig();
15
- console.log(`Console started: ${local.url}`);
14
+ console.clear();
15
+ printHeader({ console: true, hint: "ready!" });
16
16
  });
@@ -1,5 +1,5 @@
1
+ import { useSTSIdentity } from "../../credentials.js";
1
2
  import { Colors } from "../colors.js";
2
- import { printHeader } from "../ui/header.js";
3
3
  export const deploy = (program) => program.command("deploy [filter]", "Deploy your app to AWS", (yargs) => yargs
4
4
  .option("from", {
5
5
  type: "string",
@@ -18,7 +18,14 @@ export const deploy = (program) => program.command("deploy [filter]", "Deploy yo
18
18
  const { render } = await import("ink");
19
19
  const { DeploymentUI } = await import("../ui/deploy.js");
20
20
  const project = useProject();
21
- printHeader({});
21
+ const identity = await useSTSIdentity();
22
+ Colors.line(`${Colors.primary.bold(`SST v${project.version}`)}`);
23
+ Colors.gap();
24
+ Colors.line(`${Colors.primary(`➜`)} ${Colors.bold("App:")} ${project.config.name}`);
25
+ Colors.line(` ${Colors.bold("Stage:")} ${project.config.stage}`);
26
+ Colors.line(` ${Colors.bold("Region:")} ${project.config.region}`);
27
+ Colors.line(` ${Colors.bold("Account:")} ${identity.Account}`);
28
+ Colors.gap();
22
29
  // Generate cloud assembly
23
30
  // - if --from is specified, we will use the existing cloud assembly
24
31
  // - if --from is not specified, we will call synth to generate
@@ -35,7 +42,6 @@ export const deploy = (program) => program.command("deploy [filter]", "Deploy yo
35
42
  mode: "deploy",
36
43
  });
37
44
  spinner.succeed();
38
- console.log();
39
45
  return result;
40
46
  })();
41
47
  const target = assembly.stacks.filter((s) => !args.filter ||
@@ -61,6 +61,7 @@ export const dev = (program) => program.command(["dev", "start"], "Work on your
61
61
  Colors.line(prefix(evt.properties.requestID), Colors.dim.bold("Invoked"), Colors.dim(useFunctions().fromID(evt.properties.functionID).handler));
62
62
  });
63
63
  bus.subscribe("worker.stdout", async (evt) => {
64
+ prefix(evt.properties.requestID);
64
65
  const { started } = pending.get(evt.properties.requestID);
65
66
  for (let line of evt.properties.message.split("\n")) {
66
67
  Colors.line(prefix(evt.properties.requestID), Colors.dim(("+" + (Date.now() - started) + "ms").padEnd(7)), Colors.dim(line));
@@ -106,9 +107,7 @@ export const dev = (program) => program.command(["dev", "start"], "Work on your
106
107
  Colors.gap();
107
108
  const spinner = createSpinner({
108
109
  color: "gray",
109
- text: lastDeployed
110
- ? ` Building stacks`
111
- : dim(` Checking for changes`),
110
+ text: lastDeployed ? ` Building...` : dim(` Checking for changes`),
112
111
  }).start();
113
112
  try {
114
113
  const [metafile, sstConfig] = await Stacks.load(project.paths.config);
@@ -124,8 +123,7 @@ export const dev = (program) => program.command(["dev", "start"], "Work on your
124
123
  const next = await checksum(assembly.directory);
125
124
  Logger.debug("Checksum", "next", next, "old", lastDeployed);
126
125
  if (next === lastDeployed) {
127
- spinner.succeed(" No changes");
128
- Colors.gap();
126
+ spinner.succeed(Colors.dim(" Built with no changes"));
129
127
  return;
130
128
  }
131
129
  if (!lastDeployed) {
@@ -134,7 +132,7 @@ export const dev = (program) => program.command(["dev", "start"], "Work on your
134
132
  Colors.mode("gap");
135
133
  }
136
134
  else {
137
- spinner.succeed(` Stacks built!`);
135
+ spinner.succeed(Colors.dim(` Built`));
138
136
  Colors.mode("gap");
139
137
  }
140
138
  pending = assembly;
@@ -221,7 +219,7 @@ export const dev = (program) => program.command(["dev", "start"], "Work on your
221
219
  live: true,
222
220
  });
223
221
  console.clear();
224
- await printHeader({ console: true });
222
+ await printHeader({ console: true, hint: "ready!" });
225
223
  /*
226
224
  console.log(` ${primary(`➜`)} ${bold(dim(`Outputs:`))}`);
227
225
  for (let i = 0; i < 3; i++) {
@@ -1,3 +1,4 @@
1
+ import { stackNameToId } from "../ui/stack.js";
1
2
  export const diff = (program) => program.command("diff", "Compare your app with what is deployed on AWS", (yargs) => yargs.option("dev", {
2
3
  type: "boolean",
3
4
  describe: "Compare in dev mode",
@@ -8,15 +9,13 @@ export const diff = (program) => program.command("diff", "Compare your app with
8
9
  const { CloudFormationClient, GetTemplateCommand } = await import("@aws-sdk/client-cloudformation");
9
10
  const { createSpinner } = await import("../spinner.js");
10
11
  const { green } = await import("colorette");
12
+ const { Colors } = await import("../colors.js");
11
13
  // Build app
12
- const spinner = createSpinner("Building stacks");
13
14
  const project = useProject();
14
15
  const assembly = await Stacks.synth({
15
16
  fn: project.stacks,
16
17
  mode: args.dev ? "dev" : "deploy",
17
18
  });
18
- spinner.succeed();
19
- console.log("");
20
19
  // Diff each stack
21
20
  let changesAcc = 0;
22
21
  let changedStacks = 0;
@@ -33,19 +32,19 @@ export const diff = (program) => program.command("diff", "Compare your app with
33
32
  spinner.clear();
34
33
  // print diff result
35
34
  if (count === 0) {
36
- console.log(`➜ ${stack.stackName}: No changes`);
37
- console.log("");
35
+ Colors.line(`➜ ${Colors.dim.bold(stackNameToId(stack.stackName) + ":")} No changes`);
36
+ Colors.gap();
38
37
  }
39
38
  else if (count === 1) {
40
- console.log(`➜ ${stack.stackName}: ${count} change`);
41
- console.log("");
39
+ Colors.line(`➜ ${Colors.dim.bold(stackNameToId(stack.stackName) + ":")} ${count} change`);
40
+ Colors.gap();
42
41
  console.log(diff);
43
42
  changesAcc += count;
44
43
  changedStacks++;
45
44
  }
46
45
  else {
47
- console.log(`➜ ${stack.stackName}: ${count} changes`);
48
- console.log("");
46
+ Colors.line(`➜ ${Colors.dim.bold(stackNameToId(stack.stackName) + ":")} ${count} changes`);
47
+ Colors.gap();
49
48
  console.log(diff);
50
49
  changesAcc += count;
51
50
  changedStacks++;
@@ -53,14 +52,10 @@ export const diff = (program) => program.command("diff", "Compare your app with
53
52
  }
54
53
  // Handle no changes
55
54
  if (changedStacks === 0) {
56
- console.log(green(""), " There were no changes");
55
+ Colors.line(Colors.success(`✔`), Colors.bold(" Diff:"), "No changes");
57
56
  }
58
57
  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`);
58
+ Colors.line(Colors.success(`✔`), Colors.bold(" Diff:"), changesAcc === 1 ? "1 change found in" : `${changesAcc} changes in`, changedStacks === 1 ? "1 stack" : `${changedStacks} stacks`);
64
59
  }
65
60
  process.exit(0);
66
61
  });
@@ -18,10 +18,10 @@ export const usePothosBuilder = Context.memo(() => {
18
18
  await fs.writeFile(route.output, schema);
19
19
  // bus.publish("pothos.extracted", { file: route.output });
20
20
  await Promise.all(route.commands.map((cmd) => execAsync(cmd)));
21
- Colors.line(Colors.success(`✔`), " Extracted pothos schema");
21
+ Colors.line(Colors.success(`✔`), " Pothos: Extracted pothos schema");
22
22
  }
23
23
  catch (ex) {
24
- Colors.line(Colors.danger(`✖`), " Failed to extract schema from pothos:");
24
+ Colors.line(Colors.danger(`✖`), " Pothos: Failed to extract schema:");
25
25
  for (let line of ex.message.split("\n")) {
26
26
  console.log(` `, line);
27
27
  }
@@ -10,12 +10,15 @@ export const remove = (program) => program.command("remove [filter]", "Remove yo
10
10
  const { DeploymentUI } = await import("../ui/deploy.js");
11
11
  const { printDeploymentResults } = await import("../ui/deploy.js");
12
12
  const { Colors } = await import("../colors.js");
13
+ const { useSTSIdentity } = await import("../../credentials.js");
13
14
  const project = useProject();
14
- console.log();
15
- console.log(` ${Colors.primary(`${bold(`SST`)} v${project.version}`)}`);
16
- console.log();
17
- console.log(` ${Colors.primary(`➜`)} ${bold(`Stage:`)} ${dim(project.config.stage)}`);
18
- console.log();
15
+ const identity = await useSTSIdentity();
16
+ Colors.line(`${Colors.primary.bold(`SST v${project.version}`)}`);
17
+ Colors.gap();
18
+ Colors.line(`${Colors.primary(`➜`)} ${Colors.bold("App:")} ${project.config.name}`);
19
+ Colors.line(` ${Colors.bold("Stage:")} ${project.config.stage}`);
20
+ Colors.line(` ${Colors.bold("Region:")} ${project.config.region}`);
21
+ Colors.line(` ${Colors.bold("Account:")} ${identity.Account}`);
19
22
  const assembly = await (async function () {
20
23
  if (args.from) {
21
24
  const result = await loadAssembly(args.from);
@@ -36,7 +39,7 @@ export const remove = (program) => program.command("remove [filter]", "Remove yo
36
39
  const results = await Stacks.removeMany(target);
37
40
  component.clear();
38
41
  component.unmount();
39
- printDeploymentResults(assembly, results);
42
+ printDeploymentResults(assembly, results, true);
40
43
  if (Object.values(results).some((stack) => Stacks.isFailed(stack.status)))
41
44
  process.exit(1);
42
45
  process.exit(0);
@@ -10,15 +10,15 @@ export const get = (program) => program.command("get <name>", "Get the value of
10
10
  }), async (args) => {
11
11
  const { red } = await import("colorette");
12
12
  const { Config } = await import("../../../config.js");
13
- const { bold } = await import("colorette");
13
+ const { Colors } = await import("../../colors.js");
14
14
  try {
15
15
  const result = await Config.getSecret({
16
16
  key: args.name,
17
17
  fallback: args.fallback === true,
18
18
  });
19
- console.log(bold(result));
19
+ console.log(result);
20
20
  }
21
21
  catch {
22
- console.log(red(`${bold(args.name)} is not set`));
22
+ Colors.line(Colors.danger(`✖ `), `"${args.name}" is not set`);
23
23
  }
24
24
  });
@@ -4,7 +4,12 @@ export const list = (program) => program.command("list [format]", "Fetch all the
4
4
  }), async (args) => {
5
5
  const { Config } = await import("../../../config.js");
6
6
  const { gray } = await import("colorette");
7
+ const { Colors } = await import("../../colors.js");
7
8
  const secrets = await Config.secrets();
9
+ if (Object.entries(secrets).length === 0) {
10
+ Colors.line("No secrets set");
11
+ return;
12
+ }
8
13
  switch (args.format || "table") {
9
14
  case "env":
10
15
  for (const [key, value] of Object.entries(secrets)) {
@@ -9,8 +9,15 @@ export const remove = (program) => program.command("remove <name>", "Remove a se
9
9
  describe: "Remove the fallback value",
10
10
  }), async (args) => {
11
11
  const { Config } = await import("../../../config.js");
12
- await Config.removeSecret({
13
- key: args.name,
14
- fallback: args.fallback === true,
15
- });
12
+ const { Colors } = await import("../../colors.js");
13
+ try {
14
+ await Config.removeSecret({
15
+ key: args.name,
16
+ fallback: args.fallback === true,
17
+ });
18
+ Colors.line(Colors.success(`✔ `), `Removed "${args.name}"`);
19
+ }
20
+ catch {
21
+ Colors.line(Colors.danger(`✖ `), `"${args.name}" is not set`);
22
+ }
16
23
  });
@@ -16,14 +16,14 @@ export const set = (program) => program.command("set <name> <value>", "Set the v
16
16
  const { Config } = await import("../../../config.js");
17
17
  const { blue } = await import("colorette");
18
18
  const { createSpinner } = await import("../../spinner.js");
19
- const setting = createSpinner(`Setting secret ${blue(args.name)}`).start();
19
+ const setting = createSpinner(` Setting "${args.name}"`).start();
20
20
  await Config.setSecret({
21
21
  key: args.name,
22
22
  value: args.value,
23
23
  fallback: args.fallback === true,
24
24
  });
25
25
  setting.succeed();
26
- const restarting = createSpinner(`Restarting all functions using ${blue(args.name)}...`).start();
26
+ const restarting = createSpinner(` Restarting all functions using ${blue(args.name)}...`).start();
27
27
  const count = await Config.restart(args.name);
28
- restarting.succeed(`Restarted ${blue(count)} functions`);
28
+ restarting.succeed(` Restarted ${count} functions`);
29
29
  });
@@ -1,6 +1,6 @@
1
1
  /// <reference types="yargs" />
2
2
  import type { Program } from "../program.js";
3
- export declare const updateMod: (program: Program) => import("yargs").Argv<{
3
+ export declare const transform: (program: Program) => import("yargs").Argv<{
4
4
  stage: string | undefined;
5
5
  } & {
6
6
  profile: string | undefined;
@@ -11,5 +11,5 @@ export declare const updateMod: (program: Program) => import("yargs").Argv<{
11
11
  } & {
12
12
  role: string | undefined;
13
13
  } & {
14
- transform: string;
14
+ mod: string;
15
15
  }>;
@@ -1,13 +1,15 @@
1
- export const updateMod = (program) => program.command("update-mod <transform>", "Transforms your SST app", (yargs) => yargs.positional("transform", {
1
+ export const transform = (program) => program.command("transform <mod>", "Apply a transform on your SST app", (yargs) => yargs.positional("mod", {
2
2
  type: "string",
3
3
  describe: "Name of the transform",
4
4
  demandOption: true,
5
5
  }), async (args) => {
6
- const { green } = await import("colorette");
7
- if (args.transform === "resource-binding-secrets") {
6
+ const { Colors } = await import("../colors.js");
7
+ if (args.mod === "resource-binding-secrets") {
8
8
  await handleSecretsMigration();
9
+ Colors.line(Colors.success(`✔ `), `Transform "${args.mod}" applied successfully!`);
10
+ return;
9
11
  }
10
- console.log(green(`Update transform "${args.transform}" has been applied successfully!`));
12
+ Colors.line(Colors.danger(`✖ `), `Transform "${args.mod}" not found`);
11
13
  });
12
14
  async function handleSecretsMigration() {
13
15
  const { useProject } = await import("../../project.js");
@@ -9,6 +9,7 @@ export const update = (program) => program.command("update [version]", "Update y
9
9
  const path = await import("path");
10
10
  const { fetch } = await import("undici");
11
11
  const { useProject } = await import("../../project.js");
12
+ const { Colors } = await import("../colors.js");
12
13
  async function find(dir) {
13
14
  const children = await fs.readdir(dir);
14
15
  const tasks = children.map(async (item) => {
@@ -67,14 +68,15 @@ export const update = (program) => program.command("update [version]", "Update y
67
68
  });
68
69
  await Promise.all(tasks);
69
70
  if (results.size === 0) {
70
- console.log(green(`All packages already match version ${metadata.version}`));
71
+ Colors.line(Colors.success(`✔ `), `Already using v${metadata.version}`);
71
72
  return;
72
73
  }
73
74
  for (const [file, pkgs] of results.entries()) {
74
- console.log(green(`✅ ${path.relative(project.paths.root, file)}`));
75
+ Colors.line(Colors.success(`✔ `), Colors.bold.dim(path.relative(project.paths.root, file)));
75
76
  for (const [pkg, version] of pkgs) {
76
- console.log(green(` ${pkg}@${version}`));
77
+ Colors.line(Colors.dim(` ${pkg}@${version}`));
77
78
  }
78
79
  }
79
- console.log(yellow("Don't forget to run your package manager to install the packages"));
80
+ Colors.gap();
81
+ Colors.line(`${Colors.primary(`➜`)} ${Colors.warning("Make sure to run: npm install (or pnpm install, or yarn)")}`);
80
82
  });
@@ -1,6 +1,6 @@
1
1
  /// <reference types="yargs" />
2
- import { Program } from "../program.js";
3
- export declare const env: (program: Program) => import("yargs").Argv<{
2
+ import type { Program } from "../program.js";
3
+ export declare const version: (program: Program) => import("yargs").Argv<{
4
4
  stage: string | undefined;
5
5
  } & {
6
6
  profile: string | undefined;
@@ -10,6 +10,4 @@ export declare const env: (program: Program) => import("yargs").Argv<{
10
10
  verbose: boolean | undefined;
11
11
  } & {
12
12
  role: string | undefined;
13
- } & {
14
- command: string;
15
13
  }>;
@@ -1,37 +1,7 @@
1
- import { useProject } from "../../project.js";
2
- import { createSpinner } from "../spinner.js";
3
- import fs from "fs/promises";
4
- import { SiteEnv } from "../../site-env.js";
5
- import { spawnSync } from "child_process";
6
- export const env = (program) => program.command("env <command>", "description", (yargs) => yargs.positional("command", {
7
- type: "string",
8
- describe: "Command to run with environment variabels loaded",
9
- demandOption: true,
10
- }), async (args) => {
1
+ export const version = (program) => program.command("version", "Print SST and CDK version", (yargs) => yargs, async (args) => {
2
+ const { Colors } = await import("../colors.js");
3
+ const { useProject } = await import("../../project.js");
11
4
  const project = useProject();
12
- let spinner;
13
- while (true) {
14
- const exists = await fs
15
- .access(SiteEnv.valuesFile())
16
- .then(() => true)
17
- .catch(() => false);
18
- if (!exists) {
19
- spinner = createSpinner("Waiting for SST to start").start();
20
- await new Promise((resolve) => setTimeout(resolve, 1000));
21
- continue;
22
- }
23
- spinner?.succeed();
24
- const sites = await SiteEnv.values();
25
- const env = sites[process.cwd()] || {};
26
- const result = spawnSync(args.command, {
27
- env: {
28
- ...process.env,
29
- ...env,
30
- },
31
- stdio: "inherit",
32
- shell: process.env.SHELL || true,
33
- });
34
- process.exitCode = result.status || undefined;
35
- break;
36
- }
5
+ Colors.line(Colors.bold(`SST:`), `v${project.version}`);
6
+ Colors.line(Colors.bold(`CDK:`), `v${project.cdkVersion}`);
37
7
  });
package/cli/sst.js CHANGED
@@ -17,8 +17,9 @@ import { remove } from "./commands/remove.js";
17
17
  import { consoleCommand } from "./commands/console.js";
18
18
  import { secrets } from "./commands/secrets/secrets.js";
19
19
  import { update } from "./commands/update.js";
20
- import { updateMod } from "./commands/update-mod.js";
20
+ import { transform } from "./commands/transform.js";
21
21
  import { diff } from "./commands/diff.js";
22
+ import { version } from "./commands/version.js";
22
23
  dev(program);
23
24
  deploy(program);
24
25
  build(program);
@@ -27,9 +28,10 @@ env(program);
27
28
  secrets(program);
28
29
  remove(program);
29
30
  update(program);
30
- updateMod(program);
31
+ transform(program);
31
32
  consoleCommand(program);
32
33
  diff(program);
34
+ version(program);
33
35
  // @ts-expect-error
34
36
  process.setSourceMapsEnabled(true);
35
37
  process.removeAllListeners("uncaughtException");
@@ -5,5 +5,5 @@ interface Props {
5
5
  assembly: CloudAssembly;
6
6
  }
7
7
  export declare const DeploymentUI: (props: Props) => JSX.Element;
8
- export declare function printDeploymentResults(assembly: CloudAssembly, results: Awaited<ReturnType<typeof Stacks.deployMany>>): void;
8
+ export declare function printDeploymentResults(assembly: CloudAssembly, results: Awaited<ReturnType<typeof Stacks.deployMany>>, remove?: boolean): void;
9
9
  export {};
package/cli/ui/deploy.js CHANGED
@@ -63,12 +63,12 @@ export const DeploymentUI = (props) => {
63
63
  " ",
64
64
  React.createElement(Text, { dimColor: true }, "Deploying..."))))));
65
65
  };
66
- export function printDeploymentResults(assembly, results) {
66
+ export function printDeploymentResults(assembly, results, remove) {
67
67
  // Print success stacks
68
68
  const success = Object.entries(results).filter(([_stack, result]) => Object.keys(result.errors).length === 0);
69
69
  if (success.length) {
70
70
  Colors.gap();
71
- Colors.line(Colors.success(`✔`), Colors.bold(` Deployed:`));
71
+ Colors.line(Colors.success(`✔`), Colors.bold(remove ? ` Removed:` : ` Deployed:`));
72
72
  for (const [stack, result] of success) {
73
73
  const outputs = Object.entries(result.outputs).filter(([key, _]) => {
74
74
  if (key.startsWith("Export"))
@@ -98,6 +98,10 @@ export function printDeploymentResults(assembly, results) {
98
98
  for (const [id, error] of Object.entries(result.errors)) {
99
99
  const readable = logicalIdToCdkPath(assembly, stack, id) || id;
100
100
  Colors.line(` ${Colors.danger.bold(readable + ":")} ${error}`);
101
+ const helper = getHelper(error);
102
+ if (helper) {
103
+ Colors.line(` ${Colors.warning.bold("⮑ Hint:")} ${helper}`);
104
+ }
101
105
  }
102
106
  }
103
107
  Colors.gap();
@@ -115,3 +119,29 @@ function logicalIdToCdkPath(assembly, stack, logicalId) {
115
119
  }
116
120
  return found.split("/").filter(Boolean).slice(1, -1).join("/");
117
121
  }
122
+ function getHelper(error) {
123
+ return `This is a common deploy error. Check out this GitHub issue for more details - https://github.com/serverless-stack/sst/issues/125`;
124
+ return getApiAccessLogPermissionsHelper(error)
125
+ || getAppSyncMultiResolverHelper(error)
126
+ || getApiLogRoleHelper(error);
127
+ }
128
+ function getApiAccessLogPermissionsHelper(error) {
129
+ // Can run into this issue when enabling access logs for API Gateway
130
+ // note: this should be handled in SST as access log group names are now
131
+ // hardcoded with /aws/vendedlogs/apis prefix.
132
+ if (error.indexOf("Insufficient permissions to enable logging") > -1) {
133
+ return `This is a common deploy error. Check out this GitHub issue for more details - https://github.com/serverless-stack/sst/issues/125`;
134
+ }
135
+ }
136
+ function getAppSyncMultiResolverHelper(error) {
137
+ // Can run into this issue when updating an AppSyncApi resolver
138
+ if (error.indexOf("Only one resolver is allowed per field. (Service: AWSAppSync") > -1) {
139
+ return `This is a common error for deploying AppSync APIs. Check out this GitHub issue for more details - https://github.com/aws/aws-cdk/issues/13269`;
140
+ }
141
+ }
142
+ function getApiLogRoleHelper(error) {
143
+ // Can run into this issue when enabling access logs for WebSocketApi
144
+ if (error.indexOf("CloudWatch Logs role ARN must be set in account settings to enable logging (Service: AmazonApiGatewayV2") > -1) {
145
+ return `This is a common error when configuring Access Log for WebSocket APIs. The AWS API Gateway service in your AWS account does not have permissions to the CloudWatch logs service. Follow this article to create an IAM role for logging to CloudWatch - https://aws.amazon.com/premiumsupport/knowledge-center/api-gateway-cloudwatch-logs/`;
146
+ }
147
+ }
@@ -1,4 +1,5 @@
1
1
  export declare function printHeader(input: {
2
2
  console?: boolean;
3
+ hint?: string;
3
4
  }): Promise<void>;
4
5
  export declare function printConsole(): void;
package/cli/ui/header.js CHANGED
@@ -3,12 +3,13 @@ import { Colors } from "../colors.js";
3
3
  import { useLocalServerConfig } from "../local/server.js";
4
4
  export async function printHeader(input) {
5
5
  const project = useProject();
6
- Colors.line(`${Colors.primary.bold("SST v2.0.4")} ${Colors.dim(`ready!`)}`);
6
+ Colors.line(`${Colors.primary.bold(`SST v${project.version}`)} ${input.hint ? Colors.dim(`ready!`) : ""}`);
7
7
  Colors.gap();
8
- Colors.line(`${Colors.primary(`➜`)} ${Colors.bold("Stage:")} ${project.config.stage}`);
8
+ Colors.line(`${Colors.primary(`➜`)} ${Colors.bold("App:")} ${project.config.name}`);
9
+ Colors.line(`${Colors.primary(` `)} ${Colors.bold("Stage:")} ${project.config.stage}`);
9
10
  if (input.console) {
10
11
  const local = await useLocalServerConfig();
11
- Colors.line(`${Colors.primary(`➜`)} ${Colors.bold("Console:")} ${Colors.link(local.url)}`);
12
+ Colors.line(`${Colors.primary(` `)} ${Colors.bold("Console:")} ${Colors.link(local.url)}`);
12
13
  }
13
14
  Colors.gap();
14
15
  }
@@ -0,0 +1 @@
1
+ export declare function stackNameToId(stack: string): string;
@@ -0,0 +1,6 @@
1
+ import { useProject } from "../../project.js";
2
+ export function stackNameToId(stack) {
3
+ const project = useProject();
4
+ const prefix = `${project.config.stage}-${project.config.name}-`;
5
+ return stack.startsWith(prefix) ? stack.substring(prefix.length) : stack;
6
+ }