sst 2.0.0-rc.47 → 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 +4 -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 +245 -132
  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 ||
@@ -107,9 +107,7 @@ export const dev = (program) => program.command(["dev", "start"], "Work on your
107
107
  Colors.gap();
108
108
  const spinner = createSpinner({
109
109
  color: "gray",
110
- text: lastDeployed
111
- ? ` Building stacks`
112
- : dim(` Checking for changes`),
110
+ text: lastDeployed ? ` Building...` : dim(` Checking for changes`),
113
111
  }).start();
114
112
  try {
115
113
  const [metafile, sstConfig] = await Stacks.load(project.paths.config);
@@ -125,8 +123,7 @@ export const dev = (program) => program.command(["dev", "start"], "Work on your
125
123
  const next = await checksum(assembly.directory);
126
124
  Logger.debug("Checksum", "next", next, "old", lastDeployed);
127
125
  if (next === lastDeployed) {
128
- spinner.succeed(" No changes");
129
- Colors.gap();
126
+ spinner.succeed(Colors.dim(" Built with no changes"));
130
127
  return;
131
128
  }
132
129
  if (!lastDeployed) {
@@ -135,7 +132,7 @@ export const dev = (program) => program.command(["dev", "start"], "Work on your
135
132
  Colors.mode("gap");
136
133
  }
137
134
  else {
138
- spinner.succeed(` Stacks built!`);
135
+ spinner.succeed(Colors.dim(` Built`));
139
136
  Colors.mode("gap");
140
137
  }
141
138
  pending = assembly;
@@ -222,7 +219,7 @@ export const dev = (program) => program.command(["dev", "start"], "Work on your
222
219
  live: true,
223
220
  });
224
221
  console.clear();
225
- await printHeader({ console: true });
222
+ await printHeader({ console: true, hint: "ready!" });
226
223
  /*
227
224
  console.log(` ${primary(`➜`)} ${bold(dim(`Outputs:`))}`);
228
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
+ }
@@ -393,7 +393,7 @@ export interface ApiProps<Authorizers extends Record<string, ApiAuthorizer> = Re
393
393
  httpStages?: Omit<apig.HttpStageProps, "httpApi">[];
394
394
  };
395
395
  }
396
- export type ApiRouteProps<AuthorizerKeys> = FunctionInlineDefinition | ApiFunctionRouteProps<AuthorizerKeys> | ApiHttpRouteProps<AuthorizerKeys> | ApiAlbRouteProps<AuthorizerKeys> | ApiGraphQLRouteProps<AuthorizerKeys> | ApiPothosRouteProps<AuthorizerKeys>;
396
+ export type ApiRouteProps<AuthorizerKeys> = FunctionInlineDefinition | ApiFunctionRouteProps<AuthorizerKeys> | ApiHttpRouteProps<AuthorizerKeys> | ApiAlbRouteProps<AuthorizerKeys> | ApiGraphQLRouteProps<AuthorizerKeys>;
397
397
  interface ApiBaseRouteProps<AuthorizerKeys = string> {
398
398
  authorizer?: "none" | "iam" | (string extends AuthorizerKeys ? Omit<AuthorizerKeys, "none" | "iam"> : AuthorizerKeys);
399
399
  authorizationScopes?: string[];
@@ -485,63 +485,6 @@ export interface ApiAlbRouteProps<AuthorizersKeys> extends ApiBaseRouteProps<Aut
485
485
  integration?: apigIntegrations.HttpAlbIntegrationProps;
486
486
  };
487
487
  }
488
- /**
489
- * Specify a route handler that handles GraphQL queries using Pothos
490
- * @deprecated "pothos" routes are deprecated for the Api construct, and will be removed in SST v2. Use "graphql" routes instead. Read more about how to upgrade here — https://docs.sst.dev/upgrade-guide#upgrade-to-v118
491
- * @example
492
- * ```js
493
- * // Change
494
- * api.addRoutes(stack, {
495
- * "POST /graphql": {
496
- * type: "pothos",
497
- * function: {
498
- * handler: "functions/graphql/graphql.ts",
499
- * },
500
- * schema: "backend/functions/graphql/schema.ts",
501
- * output: "graphql/schema.graphql",
502
- * commands: [
503
- * "./genql graphql/graphql.schema graphql/
504
- * ]
505
- * }
506
- * })
507
- *
508
- * // To
509
- * api.addRoutes(stack, {
510
- * "POST /graphql": {
511
- * type: "graphql",
512
- * function: {
513
- * handler: "functions/graphql/graphql.ts",
514
- * },
515
- * pothos: {
516
- * schema: "backend/functions/graphql/schema.ts",
517
- * output: "graphql/schema.graphql",
518
- * commands: [
519
- * "./genql graphql/graphql.schema graphql/
520
- * ]
521
- * }
522
- * }
523
- * })
524
- * ```
525
- */
526
- export interface ApiPothosRouteProps<AuthorizerKeys> extends ApiBaseRouteProps<AuthorizerKeys> {
527
- type: "pothos";
528
- /**
529
- * The function definition used to create the function for this route. Must be a graphql handler
530
- */
531
- function: FunctionDefinition;
532
- /**
533
- * Path to pothos schema
534
- */
535
- schema?: string;
536
- /**
537
- * File to write graphql schema to
538
- */
539
- output?: string;
540
- /**
541
- * Commands to run after generating schema. Useful for code generation steps
542
- */
543
- commands?: string[];
544
- }
545
488
  /**
546
489
  * Specify a route handler that handles GraphQL queries using Pothos
547
490
  *
@@ -752,16 +695,6 @@ export declare class Api<Authorizers extends Record<string, ApiAuthorizer> = Rec
752
695
  schema?: undefined;
753
696
  output?: undefined;
754
697
  commands?: undefined;
755
- } | {
756
- type: "pothos";
757
- route: string;
758
- fn: {
759
- node: string;
760
- stack: string;
761
- } | undefined;
762
- schema: string | undefined;
763
- output: string | undefined;
764
- commands: string[] | undefined;
765
698
  } | {
766
699
  type: "graphql";
767
700
  route: string;
@@ -798,7 +731,6 @@ export declare class Api<Authorizers extends Record<string, ApiAuthorizer> = Rec
798
731
  private addRoute;
799
732
  private createHttpIntegration;
800
733
  private createAlbIntegration;
801
- protected createPothosIntegration(scope: Construct, routeKey: string, routeProps: ApiPothosRouteProps<keyof Authorizers>, postfixName: string): apig.HttpRouteIntegration;
802
734
  protected createGraphQLIntegration(scope: Construct, routeKey: string, routeProps: ApiGraphQLRouteProps<keyof Authorizers>, postfixName: string): apig.HttpRouteIntegration;
803
735
  protected createCdkFunctionIntegration(scope: Construct, routeKey: string, routeProps: ApiFunctionRouteProps<keyof Authorizers>, postfixName: string): apig.HttpRouteIntegration;
804
736
  protected createFunctionIntegration(scope: Construct, routeKey: string, routeProps: ApiFunctionRouteProps<keyof Authorizers>, postfixName: string): apig.HttpRouteIntegration;