sst 2.0.0-rc.3 → 2.0.0-rc.31

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 (135) hide show
  1. package/bootstrap.d.ts +2 -4
  2. package/bootstrap.js +101 -44
  3. package/bus.d.ts +3 -3
  4. package/cache.js +1 -1
  5. package/cdk/deploy-stack.d.ts +2 -2
  6. package/cdk/deploy-stack.js +24 -16
  7. package/cli/commands/bind.js +10 -7
  8. package/cli/commands/build.d.ts +2 -2
  9. package/cli/commands/build.js +9 -5
  10. package/cli/commands/console.d.ts +1 -1
  11. package/cli/commands/console.js +4 -4
  12. package/cli/commands/deploy.js +34 -19
  13. package/cli/commands/dev.js +40 -35
  14. package/cli/commands/diff.d.ts +11 -0
  15. package/cli/commands/diff.js +26 -0
  16. package/cli/commands/env.d.ts +1 -1
  17. package/cli/commands/env.js +14 -15
  18. package/cli/commands/plugins/kysely.js +13 -5
  19. package/cli/commands/remove.d.ts +2 -0
  20. package/cli/commands/remove.js +31 -12
  21. package/cli/commands/secrets/get.d.ts +3 -3
  22. package/cli/commands/secrets/get.js +8 -21
  23. package/cli/commands/secrets/list.d.ts +2 -2
  24. package/cli/commands/secrets/list.js +10 -7
  25. package/cli/commands/secrets/remove.d.ts +4 -4
  26. package/cli/commands/secrets/remove.js +8 -13
  27. package/cli/commands/secrets/secrets.js +4 -7
  28. package/cli/commands/secrets/set.d.ts +4 -6
  29. package/cli/commands/secrets/set.js +14 -31
  30. package/cli/commands/update.d.ts +2 -2
  31. package/cli/commands/update.js +35 -29
  32. package/cli/commands/version.d.ts +11 -0
  33. package/cli/commands/version.js +37 -0
  34. package/cli/local/router.d.ts +6 -6
  35. package/cli/local/router.js +3 -3
  36. package/cli/local/server.d.ts +1 -1
  37. package/cli/local/server.js +7 -12
  38. package/cli/program.d.ts +1 -1
  39. package/cli/program.js +18 -5
  40. package/cli/sst.js +16 -22
  41. package/cli/telemetry/environment.d.ts +1 -1
  42. package/cli/telemetry/environment.js +1 -1
  43. package/cli/ui/deploy.js +11 -6
  44. package/config.js +5 -5
  45. package/constructs/Api.d.ts +3 -3
  46. package/constructs/ApiGatewayV1Api.d.ts +2 -2
  47. package/constructs/App.d.ts +2 -2
  48. package/constructs/App.js +10 -2
  49. package/constructs/AppSyncApi.d.ts +1 -1
  50. package/constructs/AppSyncApi.js +1 -1
  51. package/constructs/Auth.js +2 -2
  52. package/constructs/EdgeFunction.js +2 -2
  53. package/constructs/Function.d.ts +4 -5
  54. package/constructs/Function.js +6 -14
  55. package/constructs/FunctionalStack.d.ts +2 -2
  56. package/constructs/Job.d.ts +2 -2
  57. package/constructs/Job.js +4 -3
  58. package/constructs/Metadata.d.ts +19 -19
  59. package/constructs/NextjsSite.d.ts +1 -1
  60. package/constructs/NextjsSite.js +21 -4
  61. package/constructs/RDS.d.ts +1 -1
  62. package/constructs/RDS.js +1 -1
  63. package/constructs/ReactStaticSite.d.ts +2 -2
  64. package/constructs/ReactStaticSite.js +2 -1
  65. package/constructs/Script.js +1 -1
  66. package/constructs/Secret.js +4 -2
  67. package/constructs/SsrSite.d.ts +11 -6
  68. package/constructs/SsrSite.js +57 -44
  69. package/constructs/Stack.d.ts +2 -4
  70. package/constructs/Stack.js +11 -32
  71. package/constructs/StaticSite.d.ts +3 -3
  72. package/constructs/StaticSite.js +10 -10
  73. package/constructs/Table.d.ts +1 -1
  74. package/constructs/ViteStaticSite.d.ts +1 -1
  75. package/constructs/ViteStaticSite.js +2 -1
  76. package/constructs/deferred_task.d.ts +1 -1
  77. package/constructs/deprecated/NextjsSite.js +4 -4
  78. package/constructs/util/apiGatewayV1AccessLog.d.ts +1 -1
  79. package/constructs/util/apiGatewayV2Domain.js +11 -2
  80. package/constructs/util/duration.d.ts +1 -1
  81. package/constructs/util/permission.d.ts +3 -3
  82. package/constructs/util/permission.js +8 -8
  83. package/constructs/util/size.d.ts +1 -1
  84. package/context/handler.d.ts +1 -1
  85. package/credentials.d.ts +1 -1
  86. package/credentials.js +8 -6
  87. package/index.d.ts +1 -0
  88. package/index.js +1 -0
  89. package/iot.js +2 -2
  90. package/logger.js +1 -1
  91. package/node/api/index.d.ts +2 -0
  92. package/node/api/index.js +8 -0
  93. package/node/auth/adapter/adapter.d.ts +1 -1
  94. package/node/auth/adapter/github.js +4 -4
  95. package/node/auth/adapter/google.d.ts +1 -1
  96. package/node/auth/session.d.ts +1 -1
  97. package/node/config/index.d.ts +2 -2
  98. package/node/job/index.d.ts +2 -2
  99. package/package.json +7 -12
  100. package/project.d.ts +44 -0
  101. package/{app.js → project.js} +52 -51
  102. package/runtime/handlers/dotnet.js +1 -1
  103. package/runtime/handlers/java.js +1 -1
  104. package/runtime/handlers/node.js +10 -6
  105. package/runtime/handlers.js +14 -11
  106. package/runtime/runtime.d.ts +4 -0
  107. package/runtime/workers.js +4 -0
  108. package/site-env.js +2 -1
  109. package/sst.mjs +5046 -20761
  110. package/stacks/build.d.ts +1 -1
  111. package/stacks/build.js +13 -21
  112. package/stacks/metadata.js +5 -5
  113. package/stacks/monitor.d.ts +1 -1
  114. package/stacks/monitor.js +2 -1
  115. package/stacks/synth.js +6 -6
  116. package/support/base-site-archiver.mjs +79 -0
  117. package/support/bootstrap-metadata-function/index.mjs +30820 -0
  118. package/support/bridge/bridge.mjs +83 -22
  119. package/support/custom-resources/index.mjs +75 -33497
  120. package/support/edge-function/edge-lambda.mjs +2 -2
  121. package/support/nodejs-runtime/index.mjs +79 -72
  122. package/support/rds-migrator/index.mjs +27 -23
  123. package/support/script-function/index.mjs +30485 -0
  124. package/support/ssr-site-function-archiver.mjs +96 -0
  125. package/watcher.js +1 -1
  126. package/app.d.ts +0 -36
  127. package/constructs/Script/cfn-response.d.ts +0 -19
  128. package/constructs/Script/cfn-response.js +0 -77
  129. package/constructs/Script/index.d.ts +0 -1
  130. package/constructs/Script/index.js +0 -78
  131. package/constructs/Script/outbound.d.ts +0 -10
  132. package/constructs/Script/outbound.js +0 -42
  133. package/constructs/Script/util.d.ts +0 -2
  134. package/constructs/Script/util.js +0 -11
  135. package/support/base-site-archiver.cjs +0 -116
package/bootstrap.d.ts CHANGED
@@ -1,6 +1,4 @@
1
- export declare const LATEST_VERSION = "3";
2
1
  export declare const useBootstrap: () => Promise<{
3
- version: string | undefined;
4
- bucket: string | undefined;
5
- stack: string | undefined;
2
+ version: never;
3
+ bucket: never;
6
4
  }>;
package/bootstrap.js CHANGED
@@ -1,79 +1,136 @@
1
- import { GetParametersCommand, SSMClient } from "@aws-sdk/client-ssm";
2
- import { Tags, Stack, RemovalPolicy, App } from "aws-cdk-lib";
1
+ import url from "url";
2
+ import path from "path";
3
+ import { DescribeStacksCommand, CloudFormationClient, } from "@aws-sdk/client-cloudformation";
4
+ import { Tags, Stack, RemovalPolicy, App, CfnOutput } from "aws-cdk-lib";
5
+ import { Function, Runtime, Code } from "aws-cdk-lib/aws-lambda";
6
+ import { SqsEventSource } from "aws-cdk-lib/aws-lambda-event-sources";
7
+ import { PolicyStatement } from "aws-cdk-lib/aws-iam";
8
+ import { Queue } from "aws-cdk-lib/aws-sqs";
9
+ import { Rule } from "aws-cdk-lib/aws-events";
10
+ import { SqsQueue } from "aws-cdk-lib/aws-events-targets";
3
11
  import { BlockPublicAccess, Bucket, BucketEncryption, } from "aws-cdk-lib/aws-s3";
4
- import { ParameterTier, StringParameter } from "aws-cdk-lib/aws-ssm";
5
- import { useProject } from "./app.js";
12
+ import { useProject } from "./project.js";
6
13
  import { createSpinner } from "./cli/spinner.js";
7
14
  import { Context } from "./context/context.js";
8
15
  import { useAWSClient } from "./credentials.js";
9
16
  import { VisibleError } from "./error.js";
10
17
  import { Logger } from "./logger.js";
11
18
  import { Stacks } from "./stacks/index.js";
12
- const SSM_NAME_VERSION = `/sst/bootstrap/version`;
13
- const SSM_NAME_STACK_NAME = `/sst/bootstrap/stack-name`;
14
- const SSM_NAME_BUCKET_NAME = `/sst/bootstrap/bucket-name`;
15
- export const LATEST_VERSION = "3";
19
+ const STACK_NAME = "SSTBootstrap";
20
+ const OUTPUT_VERSION = "Version";
21
+ const OUTPUT_BUCKET = "BucketName";
22
+ const LATEST_VERSION = "4";
23
+ const __dirname = url.fileURLToPath(new URL(".", import.meta.url));
16
24
  export const useBootstrap = Context.memo(async () => {
17
25
  Logger.debug("Initializing bootstrap context");
18
- const ret = await ssm();
19
- if (!ret.version ||
20
- !ret.bucket ||
21
- !ret.stack ||
22
- ret.version !== LATEST_VERSION) {
26
+ let status = await loadBootstrapStatus();
27
+ if (!status || status.version !== LATEST_VERSION) {
23
28
  const project = useProject();
24
29
  const spinner = createSpinner("Deploying bootstrap stack, this only needs to happen once").start();
30
+ // Create bootstrap stack
25
31
  const app = new App();
26
- const stack = new Stack(app, "SSTBootstrap", {
32
+ const stack = new Stack(app, STACK_NAME, {
27
33
  env: {
28
- region: project.region,
34
+ region: project.config.region,
29
35
  },
30
36
  });
37
+ // Add tags to stack
31
38
  const tags = {};
32
39
  for (const [key, value] of Object.entries(tags)) {
33
40
  Tags.of(app).add(key, value);
34
41
  }
35
- const bucket = new Bucket(stack, project.region, {
42
+ // Create S3 bucket to store stacks metadata
43
+ const bucket = new Bucket(stack, project.config.region, {
36
44
  encryption: BucketEncryption.S3_MANAGED,
37
45
  removalPolicy: RemovalPolicy.DESTROY,
38
46
  autoDeleteObjects: true,
39
47
  blockPublicAccess: BlockPublicAccess.BLOCK_ALL,
40
48
  });
41
- new StringParameter(stack, SSM_NAME_VERSION, {
42
- parameterName: SSM_NAME_VERSION,
43
- stringValue: LATEST_VERSION,
44
- tier: ParameterTier.STANDARD,
49
+ // Create Function and subscribe to CloudFormation events
50
+ const fn = new Function(stack, "MetadataHandler", {
51
+ code: Code.fromAsset(path.resolve(__dirname, "support/bootstrap-metadata-function")),
52
+ handler: "index.handler",
53
+ runtime: Runtime.NODEJS_16_X,
54
+ initialPolicy: [
55
+ new PolicyStatement({
56
+ actions: ["cloudformation:DescribeStacks"],
57
+ resources: ["*"],
58
+ }),
59
+ new PolicyStatement({
60
+ actions: ["s3:PutObject", "s3:DeleteObject"],
61
+ resources: [bucket.bucketArn + "/*"],
62
+ }),
63
+ ],
45
64
  });
46
- new StringParameter(stack, SSM_NAME_STACK_NAME, {
47
- parameterName: SSM_NAME_STACK_NAME,
48
- stringValue: stack.stackName,
49
- tier: ParameterTier.STANDARD,
50
- });
51
- new StringParameter(stack, SSM_NAME_BUCKET_NAME, {
52
- parameterName: SSM_NAME_BUCKET_NAME,
53
- stringValue: bucket.bucketName,
54
- tier: ParameterTier.STANDARD,
65
+ const queue = new Queue(stack, "MetadataQueue");
66
+ fn.addEventSource(new SqsEventSource(queue));
67
+ const rule = new Rule(stack, "MetadataRule", {
68
+ eventPattern: {
69
+ source: ["aws.cloudformation"],
70
+ detailType: ["CloudFormation Stack Status Change"],
71
+ detail: {
72
+ "status-details": {
73
+ status: [
74
+ "CREATE_COMPLETE",
75
+ "UPDATE_COMPLETE",
76
+ "UPDATE_ROLLBACK_COMPLETE",
77
+ "ROLLBACK_COMPLETE",
78
+ "DELETE_COMPLETE",
79
+ ],
80
+ }
81
+ }
82
+ }
55
83
  });
84
+ rule.addTarget(new SqsQueue(queue, {
85
+ retryAttempts: 10,
86
+ }));
87
+ // Create stack outputs to store bootstrap stack info
88
+ new CfnOutput(stack, OUTPUT_VERSION, { value: LATEST_VERSION });
89
+ new CfnOutput(stack, OUTPUT_BUCKET, { value: bucket.bucketName });
90
+ // Deploy bootstrap stack
56
91
  const asm = app.synth();
57
92
  const result = await Stacks.deploy(asm.stacks[0]);
58
93
  if (Stacks.isFailed(result.status)) {
59
94
  throw new VisibleError(`Failed to deploy bootstrap stack:\n${JSON.stringify(result.errors, null, 4)}`);
60
95
  }
61
96
  spinner.succeed();
62
- return ssm();
97
+ // Fetch bootstrap status
98
+ status = await loadBootstrapStatus();
99
+ if (!status) {
100
+ throw new VisibleError("Failed to deploy bootstrap stack");
101
+ }
63
102
  }
64
- Logger.debug("Loaded bootstrap info: ", JSON.stringify(ret));
65
- return ret;
103
+ Logger.debug("Loaded bootstrap info: ", JSON.stringify(status));
104
+ return status;
66
105
  });
67
- async function ssm() {
68
- const ssm = useAWSClient(SSMClient);
69
- const result = await ssm.send(new GetParametersCommand({
70
- Names: [SSM_NAME_VERSION, SSM_NAME_STACK_NAME, SSM_NAME_BUCKET_NAME],
71
- }));
72
- return {
73
- version: result.Parameters.find((p) => p.Name === SSM_NAME_VERSION)?.Value,
74
- bucket: result.Parameters.find((p) => p.Name === SSM_NAME_BUCKET_NAME)
75
- ?.Value,
76
- stack: result.Parameters.find((p) => p.Name === SSM_NAME_STACK_NAME)
77
- ?.Value,
78
- };
106
+ async function loadBootstrapStatus() {
107
+ // Get bootstrap CloudFormation stack
108
+ const cf = useAWSClient(CloudFormationClient);
109
+ let result;
110
+ try {
111
+ result = await cf.send(new DescribeStacksCommand({
112
+ StackName: STACK_NAME,
113
+ }));
114
+ }
115
+ catch (e) {
116
+ if (e.Code === "ValidationError"
117
+ && e.message === `Stack with id ${STACK_NAME} does not exist`) {
118
+ return null;
119
+ }
120
+ throw e;
121
+ }
122
+ // Parse stack outputs
123
+ let version, bucket;
124
+ (result.Stacks[0].Outputs || []).forEach((o) => {
125
+ if (o.OutputKey === OUTPUT_VERSION) {
126
+ version = o.OutputValue;
127
+ }
128
+ else if (o.OutputKey === OUTPUT_BUCKET) {
129
+ bucket = o.OutputValue;
130
+ }
131
+ });
132
+ if (!version || !bucket) {
133
+ return null;
134
+ }
135
+ return { version, bucket };
79
136
  }
package/bus.d.ts CHANGED
@@ -1,12 +1,12 @@
1
1
  export interface Events {
2
2
  }
3
- export declare type EventTypes = keyof Events;
4
- export declare type EventPayload<Type extends EventTypes = EventTypes> = {
3
+ export type EventTypes = keyof Events;
4
+ export type EventPayload<Type extends EventTypes = EventTypes> = {
5
5
  type: Type;
6
6
  sourceID: string;
7
7
  properties: Events[Type];
8
8
  };
9
- declare type Subscription = {
9
+ type Subscription = {
10
10
  type: EventTypes;
11
11
  cb: (payload: any) => void;
12
12
  };
package/cache.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import path from "path";
2
2
  import fs from "fs/promises";
3
- import { useProject } from "./app.js";
3
+ import { useProject } from "./project.js";
4
4
  import { Logger } from "./logger.js";
5
5
  import { Context } from "./context/context.js";
6
6
  export const useCache = Context.memo(async () => {
@@ -4,7 +4,7 @@ import { ISDK, SdkProvider } from "aws-cdk/lib/api/aws-auth/index.js";
4
4
  import { ToolkitInfo } from "aws-cdk/lib/api/toolkit-info.js";
5
5
  import { ResourcesToImport } from "aws-cdk/lib/api/util/cloudformation.js";
6
6
  import { StackActivityProgress } from "aws-cdk/lib/api/util/cloudformation/stack-activity-monitor.js";
7
- declare type TemplateBodyParameter = {
7
+ type TemplateBodyParameter = {
8
8
  TemplateBody?: string;
9
9
  TemplateURL?: string;
10
10
  };
@@ -166,7 +166,7 @@ export interface DeployStackOptions {
166
166
  */
167
167
  readonly assetParallelism?: boolean;
168
168
  }
169
- export declare type DeploymentMethod = DirectDeploymentMethod | ChangeSetDeploymentMethod;
169
+ export type DeploymentMethod = DirectDeploymentMethod | ChangeSetDeploymentMethod;
170
170
  export interface DirectDeploymentMethod {
171
171
  readonly method: "direct";
172
172
  }
@@ -10,7 +10,6 @@ import { contentHash } from "aws-cdk/lib/util/content-hash.js";
10
10
  import { CfnEvaluationException } from "aws-cdk/lib/api/evaluate-cloudformation-template.js";
11
11
  import { tryHotswapDeployment } from "aws-cdk/lib/api/hotswap-deployments.js";
12
12
  import { changeSetHasNoChanges, CloudFormationStack, TemplateParameters, waitForChangeSet, waitForStackDeploy, waitForStackDelete, } from "aws-cdk/lib/api/util/cloudformation.js";
13
- import { StackActivityMonitor, } from "aws-cdk/lib/api/util/cloudformation/stack-activity-monitor.js";
14
13
  import { blue } from "colorette";
15
14
  const LARGE_TEMPLATE_SIZE_KB = 50;
16
15
  export async function deployStack(options) {
@@ -261,14 +260,19 @@ class FullCloudFormationDeployment {
261
260
  }
262
261
  }
263
262
  async monitorDeployment(startTime, expectedChanges) {
264
- const monitor = this.options.quiet
265
- ? undefined
266
- : StackActivityMonitor.withDefaultPrinter(this.cfn, this.stackName, this.stackArtifact, {
267
- resourcesTotal: expectedChanges,
268
- progress: this.options.progress,
269
- changeSetCreationTime: startTime,
270
- ci: this.options.ci,
271
- }).start();
263
+ // const monitor = this.options.quiet
264
+ // ? undefined
265
+ // : StackActivityMonitor.withDefaultPrinter(
266
+ // this.cfn,
267
+ // this.stackName,
268
+ // this.stackArtifact,
269
+ // {
270
+ // resourcesTotal: expectedChanges,
271
+ // progress: this.options.progress,
272
+ // changeSetCreationTime: startTime,
273
+ // ci: this.options.ci,
274
+ // }
275
+ // ).start();
272
276
  let finalState = this.cloudFormationStack;
273
277
  try {
274
278
  const successStack = await waitForStackDeploy(this.cfn, this.stackName);
@@ -279,10 +283,10 @@ class FullCloudFormationDeployment {
279
283
  finalState = successStack;
280
284
  }
281
285
  catch (e) {
282
- throw new Error(suffixWithErrors(e.message, monitor?.errors));
286
+ throw new Error(suffixWithErrors(e.message /*, monitor?.errors*/));
283
287
  }
284
288
  finally {
285
- await monitor?.stop();
289
+ // await monitor?.stop();
286
290
  }
287
291
  debug("Stack %s has completed updating", this.stackName);
288
292
  return {
@@ -399,11 +403,13 @@ export async function destroyStack(options) {
399
403
  if (!currentStack.exists) {
400
404
  return;
401
405
  }
406
+ /*
402
407
  const monitor = options.quiet
403
- ? undefined
404
- : StackActivityMonitor.withDefaultPrinter(cfn, deployName, options.stack, {
405
- ci: options.ci,
408
+ ? undefined
409
+ : StackActivityMonitor.withDefaultPrinter(cfn, deployName, options.stack, {
410
+ ci: options.ci,
406
411
  }).start();
412
+ */
407
413
  try {
408
414
  await cfn
409
415
  .deleteStack({ StackName: deployName, RoleARN: options.roleArn })
@@ -415,12 +421,14 @@ export async function destroyStack(options) {
415
421
  }
416
422
  }
417
423
  catch (e) {
418
- throw new Error(suffixWithErrors(e.message, monitor?.errors));
424
+ throw new Error(suffixWithErrors(e.message /* , monitor?.errors */));
419
425
  }
420
426
  finally {
427
+ /*
421
428
  if (monitor) {
422
- await monitor.stop();
429
+ await monitor.stop();
423
430
  }
431
+ */
424
432
  }
425
433
  }
426
434
  /**
@@ -1,23 +1,26 @@
1
- import { useProject } from "../../app.js";
2
- export const bind = (program) => program.command("bind <command>", "Binds blah", (yargs) => yargs.positional("command", {
1
+ export const bind = (program) => program.command("bind <command>", "Bind your app's resources to a command", (yargs) => yargs
2
+ .positional("command", {
3
3
  type: "string",
4
- describe: "Command to bind to",
4
+ describe: "The command to bind to",
5
5
  demandOption: true,
6
- }), async (args) => {
6
+ })
7
+ .example(`sst bind "vitest run"`, "Bind your resources to your tests")
8
+ .example(`sst bind "tsx scripts/myscript.ts"`, "Bind your resources to a script"), async (args) => {
7
9
  const { Config } = await import("../../config.js");
8
- const { spawnSync, spawn } = await import("child_process");
10
+ const { spawnSync } = await import("child_process");
9
11
  const { useAWSCredentials } = await import("../../credentials.js");
12
+ const { useProject } = await import("../../project.js");
10
13
  const env = await Config.env();
11
14
  const project = useProject();
12
15
  const credentials = await useAWSCredentials();
13
16
  const result = spawnSync(args.command, {
14
17
  env: {
18
+ ...process.env,
15
19
  ...env,
16
20
  AWS_ACCESS_KEY_ID: credentials.accessKeyId,
17
21
  AWS_SECRET_ACCESS_KEY: credentials.secretAccessKey,
18
22
  AWS_SESSION_TOKEN: credentials.sessionToken,
19
- AWS_REGION: project.region,
20
- PATH: process.env.PATH,
23
+ AWS_REGION: project.config.region,
21
24
  },
22
25
  stdio: "inherit",
23
26
  shell: process.env.SHELL || true,
@@ -1,5 +1,5 @@
1
1
  /// <reference types="yargs" />
2
- import { Program } from "../program.js";
2
+ import type { Program } from "../program.js";
3
3
  export declare const build: (program: Program) => import("yargs").Argv<{
4
4
  stage: string | undefined;
5
5
  } & {
@@ -7,5 +7,5 @@ export declare const build: (program: Program) => import("yargs").Argv<{
7
7
  } & {
8
8
  region: string | undefined;
9
9
  } & {
10
- from: string | undefined;
10
+ to: string | undefined;
11
11
  }>;
@@ -1,11 +1,15 @@
1
- import { createSpinner } from "../spinner.js";
2
- export const build = (program) => program.command("build", "Build stacks code", yargs => yargs.option("from", { type: "string" }), async () => {
1
+ export const build = (program) => program.command("build", "Build your app", (yargs) => yargs.option("to", {
2
+ type: "string",
3
+ describe: "Output directory, defaults to .sst/dist",
4
+ }), async (args) => {
5
+ const { useProject } = await import("../../project.js");
6
+ const { createSpinner } = await import("../spinner.js");
3
7
  const { Stacks } = await import("../../stacks/index.js");
4
8
  const spinner = createSpinner("Building stacks").start();
5
- const fn = await Stacks.build();
6
9
  await Stacks.synth({
7
- fn,
8
- mode: "deploy"
10
+ fn: useProject().stacks,
11
+ buildDir: args.to,
12
+ mode: "deploy",
9
13
  });
10
14
  spinner.succeed();
11
15
  process.exit(0);
@@ -1,5 +1,5 @@
1
1
  /// <reference types="yargs" />
2
- import { Program } from "../program.js";
2
+ import type { Program } from "../program.js";
3
3
  export declare const consoleCommand: (program: Program) => Promise<import("yargs").Argv<{
4
4
  stage: string | undefined;
5
5
  } & {
@@ -1,7 +1,7 @@
1
- import { blue } from "colorette";
2
- import { useRuntimeServer } from "../../runtime/server.js";
3
- import { useLocalServer } from "../local/server.js";
4
- export const consoleCommand = async (program) => program.command("console", "Start the sst console", (yargs) => yargs, async () => {
1
+ export const consoleCommand = async (program) => program.command("console", "Start the SST Console", (yargs) => yargs, async () => {
2
+ const { blue } = await import("colorette");
3
+ const { useRuntimeServer } = await import("../../runtime/server.js");
4
+ const { useLocalServer } = await import("../local/server.js");
5
5
  await Promise.all([
6
6
  useRuntimeServer(),
7
7
  useLocalServer({
@@ -1,48 +1,63 @@
1
- import { printDeploymentResults } from "../ui/deploy.js";
2
- export const deploy = (program) => program.command("deploy [filter]", "Work on your SST app locally", (yargs) => yargs
3
- .option("from", { type: "string" })
1
+ export const deploy = (program) => program.command("deploy [filter]", "Deploy your app to AWS", (yargs) => yargs
2
+ .option("from", {
3
+ type: "string",
4
+ describe: "Deploy using previously built output",
5
+ })
4
6
  .option("fullscreen", {
5
7
  type: "boolean",
6
8
  describe: "Disable full screen UI",
7
9
  default: true,
8
10
  })
9
- .positional("filter", { type: "string" }), async (args) => {
11
+ .positional("filter", {
12
+ type: "string",
13
+ describe: "Optionally filter stacks to deploy",
14
+ }), async (args) => {
10
15
  const React = await import("react");
16
+ const { printDeploymentResults } = await import("../ui/deploy.js");
17
+ const { createSpinner } = await import("../spinner.js");
11
18
  const { CloudAssembly } = await import("aws-cdk-lib/cx-api");
12
19
  const { blue, bold } = await import("colorette");
13
- const { useProject } = await import("../../app.js");
20
+ const { useProject } = await import("../../project.js");
14
21
  const { Stacks } = await import("../../stacks/index.js");
15
22
  const { render } = await import("ink");
16
23
  const { DeploymentUI } = await import("../ui/deploy.js");
24
+ const project = useProject();
17
25
  const assembly = await (async function () {
18
26
  if (args.from) {
19
27
  const result = new CloudAssembly(args.from);
20
28
  return result;
21
29
  }
22
- const fn = await Stacks.build();
23
- return await Stacks.synth({
24
- fn,
30
+ const spinner = createSpinner("Building stacks");
31
+ const result = await Stacks.synth({
32
+ fn: project.stacks,
25
33
  mode: "deploy",
26
34
  });
35
+ spinner.succeed();
36
+ return result;
27
37
  })();
28
- const project = useProject();
29
38
  const target = assembly.stacks.filter((s) => !args.filter ||
30
39
  s.stackName.toLowerCase().includes(args.filter.toLowerCase()));
31
40
  if (!target.length) {
32
41
  console.log(`No stacks found matching ${blue(args.filter)}`);
33
42
  process.exit(1);
34
- return;
35
- }
36
- console.log(`Deploying ${bold(target.length + " stacks")} for stage ${blue(project.stage)}...`);
37
- let component = undefined;
38
- if (args.fullscreen) {
39
- process.stdout.write("\x1b[?1049h");
40
- component = render(React.createElement(DeploymentUI, { stacks: assembly.stacks.map((s) => s.stackName) }));
41
43
  }
44
+ console.log(`Deploying ${bold(target.length + " stacks")} for stage ${blue(project.config.stage)}...`);
45
+ const cleanup = (() => {
46
+ if (args.fullscreen) {
47
+ process.stdout.write("\x1b[?1049h");
48
+ const component = render(React.createElement(DeploymentUI, { stacks: assembly.stacks.map((s) => s.stackName) }));
49
+ return () => {
50
+ component.unmount();
51
+ process.stdout.write("\x1b[?1049l");
52
+ };
53
+ }
54
+ const spinner = createSpinner("Deploying stacks");
55
+ return () => spinner.succeed();
56
+ })();
42
57
  const results = await Stacks.deployMany(target);
43
- if (component)
44
- component.unmount();
45
- process.stdout.write("\x1b[?1049l");
58
+ cleanup();
46
59
  printDeploymentResults(results);
60
+ if (Object.values(results).some((stack) => Stacks.isFailed(stack.status)))
61
+ process.exit(1);
47
62
  process.exit(0);
48
63
  });
@@ -1,15 +1,4 @@
1
- import path from "path";
2
- import fs from "fs/promises";
3
- import crypto from "crypto";
4
- import { printDeploymentResults } from "../ui/deploy.js";
5
- import { useFunctions } from "../../constructs/Function.js";
6
- import { dim, gray, yellow } from "colorette";
7
- import { useProject } from "../../app.js";
8
- import { SiteEnv } from "../../site-env.js";
9
- import { usePothosBuilder } from "./plugins/pothos.js";
10
- import { useKyselyTypeGenerator } from "./plugins/kysely.js";
11
- import { useRDSWarmer } from "./plugins/warmer.js";
12
- export const dev = (program) => program.command(["start", "dev"], "Work on your SST app locally", (yargs) => yargs.option("fullscreen", {
1
+ export const dev = (program) => program.command(["dev", "start"], "Work on your app locally", (yargs) => yargs.option("fullscreen", {
13
2
  type: "boolean",
14
3
  describe: "Disable full screen UI",
15
4
  default: true,
@@ -28,19 +17,31 @@ export const dev = (program) => program.command(["start", "dev"], "Work on your
28
17
  const { Context } = await import("../../context/context.js");
29
18
  const { DeploymentUI } = await import("../ui/deploy.js");
30
19
  const { useLocalServer } = await import("../local/server.js");
20
+ const path = await import("path");
21
+ const fs = await import("fs/promises");
22
+ const crypto = await import("crypto");
23
+ const { printDeploymentResults } = await import("../ui/deploy.js");
24
+ const { useFunctions } = await import("../../constructs/Function.js");
25
+ const { dim, gray, yellow } = await import("colorette");
26
+ const { SiteEnv } = await import("../../site-env.js");
27
+ const { usePothosBuilder } = await import("./plugins/pothos.js");
28
+ const { useKyselyTypeGenerator } = await import("./plugins/kysely.js");
29
+ const { useRDSWarmer } = await import("./plugins/warmer.js");
30
+ const { useProject } = await import("../../project.js");
31
+ const { useMetadata } = await import("../../stacks/metadata.js");
31
32
  if (args._[0] === "start") {
32
33
  console.log(yellow(`Warning: ${bold(`sst start`)} has been renamed to ${bold(`sst dev`)}`));
33
34
  }
34
35
  const useFunctionLogger = Context.memo(async () => {
35
36
  const bus = useBus();
36
37
  bus.subscribe("function.invoked", async (evt) => {
37
- console.log(bold(magenta(`Invoked `)), bold(useFunctions().fromID(evt.properties.functionID).handler));
38
+ console.log(bold(magenta(`Invoked `)), useFunctions().fromID(evt.properties.functionID).handler);
38
39
  });
39
40
  bus.subscribe("function.build.success", async (evt) => {
40
- console.log(bold(gray(`Built `)), bold(useFunctions().fromID(evt.properties.functionID).handler));
41
+ console.log(bold(gray(`Built `)), useFunctions().fromID(evt.properties.functionID).handler);
41
42
  });
42
43
  bus.subscribe("function.build.failed", async (evt) => {
43
- console.log(bold(red(`Build failed `)), bold(useFunctions().fromID(evt.properties.functionID).handler));
44
+ console.log(bold(red(`Build failed `)), useFunctions().fromID(evt.properties.functionID).handler);
44
45
  console.log(dim(evt.properties.errors.join("\n")));
45
46
  });
46
47
  bus.subscribe("worker.stdout", async (evt) => {
@@ -50,14 +51,14 @@ export const dev = (program) => program.command(["start", "dev"], "Work on your
50
51
  const line = lines[i];
51
52
  lines[i] = " " + line;
52
53
  }
53
- console.log(bold(blue(`Log `)), bold(useFunctions().fromID(evt.properties.functionID).handler));
54
+ console.log(bold(blue(`Log `)), useFunctions().fromID(evt.properties.functionID).handler);
54
55
  console.log(dim(lines.join("\n")));
55
56
  });
56
57
  bus.subscribe("function.success", async (evt) => {
57
- console.log(bold(green(`Success `)), bold(useFunctions().fromID(evt.properties.functionID).handler));
58
+ console.log(bold(green(`Success `)), useFunctions().fromID(evt.properties.functionID).handler);
58
59
  });
59
60
  bus.subscribe("function.error", async (evt) => {
60
- console.log(bold(red(`Error `)), bold(useFunctions().fromID(evt.properties.functionID).handler), evt.properties.errorMessage);
61
+ console.log(bold(red(`Error `)), useFunctions().fromID(evt.properties.functionID).handler, evt.properties.errorMessage);
61
62
  for (const line of evt.properties.trace || []) {
62
63
  console.log(` ${dim(line)}`);
63
64
  }
@@ -65,17 +66,19 @@ export const dev = (program) => program.command(["start", "dev"], "Work on your
65
66
  });
66
67
  const useStackBuilder = Context.memo(async () => {
67
68
  const watcher = useWatcher();
68
- const bus = useBus();
69
69
  const project = useProject();
70
+ const bus = useBus();
70
71
  let lastDeployed;
71
72
  let pending;
72
73
  let isDeploying = false;
73
74
  async function build() {
74
75
  const spinner = createSpinner("Building stacks").start();
75
76
  try {
76
- const fn = await Stacks.build();
77
+ const [metafile, sstConfig] = await Stacks.load(project.paths.config);
78
+ project.metafile = metafile;
79
+ project.stacks = sstConfig.stacks;
77
80
  const assembly = await Stacks.synth({
78
- fn,
81
+ fn: project.stacks,
79
82
  outDir: `.sst/cdk.out`,
80
83
  mode: "dev",
81
84
  });
@@ -105,15 +108,20 @@ export const dev = (program) => program.command(["start", "dev"], "Work on your
105
108
  const assembly = pending;
106
109
  const nextChecksum = await checksum(assembly.directory);
107
110
  pending = undefined;
108
- let component = undefined;
109
- if (args.fullscreen) {
110
- process.stdout.write("\x1b[?1049h");
111
- component = render(React.createElement(DeploymentUI, { stacks: assembly.stacks.map((s) => s.stackName) }));
112
- }
111
+ const cleanup = (() => {
112
+ if (args.fullscreen) {
113
+ process.stdout.write("\x1b[?1049h");
114
+ const component = render(React.createElement(DeploymentUI, { stacks: assembly.stacks.map((s) => s.stackName) }));
115
+ return () => {
116
+ component.unmount();
117
+ process.stdout.write("\x1b[?1049l");
118
+ };
119
+ }
120
+ const spinner = createSpinner("Deploying stacks");
121
+ return () => spinner.succeed();
122
+ })();
113
123
  const results = await Stacks.deployMany(assembly.stacks);
114
- if (component)
115
- component.unmount();
116
- process.stdout.write("\x1b[?1049l");
124
+ cleanup();
117
125
  lastDeployed = nextChecksum;
118
126
  printDeploymentResults(results);
119
127
  const keys = await SiteEnv.keys();
@@ -151,14 +159,10 @@ export const dev = (program) => program.command(["start", "dev"], "Work on your
151
159
  .digest("hex");
152
160
  return hash;
153
161
  }
154
- let metafile;
155
- bus.subscribe("stack.built", async (evt) => {
156
- metafile = evt.properties.metafile;
157
- });
158
162
  watcher.subscribe("file.changed", async (evt) => {
159
- if (!metafile)
163
+ if (!project.metafile)
160
164
  return;
161
- if (!metafile.inputs[evt.properties.relative])
165
+ if (!project.metafile.inputs[evt.properties.relative])
162
166
  return;
163
167
  build();
164
168
  });
@@ -180,6 +184,7 @@ export const dev = (program) => program.command(["start", "dev"], "Work on your
180
184
  useIOTBridge(),
181
185
  useRuntimeServer(),
182
186
  usePothosBuilder(),
187
+ useMetadata(),
183
188
  useKyselyTypeGenerator(),
184
189
  useRDSWarmer(),
185
190
  useFunctionLogger(),