sst 2.0.0-rc.52 → 2.0.0-rc.54

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bootstrap.d.ts CHANGED
@@ -1,3 +1,13 @@
1
+ export declare const BootstrapContext: {
2
+ use(): {
3
+ version: string;
4
+ bucket: string;
5
+ };
6
+ provide(value: {
7
+ version: string;
8
+ bucket: string;
9
+ }): void;
10
+ };
1
11
  export declare const useBootstrap: () => {
2
12
  version: string;
3
13
  bucket: string;
package/bootstrap.js CHANGED
@@ -23,7 +23,7 @@ const OUTPUT_VERSION = "Version";
23
23
  const OUTPUT_BUCKET = "BucketName";
24
24
  const LATEST_VERSION = "6";
25
25
  const __dirname = url.fileURLToPath(new URL(".", import.meta.url));
26
- const BootstrapContext = Context.create();
26
+ export const BootstrapContext = Context.create("Bootstrap");
27
27
  export const useBootstrap = BootstrapContext.use;
28
28
  export async function initBootstrap() {
29
29
  Logger.debug("Initializing bootstrap context");
@@ -459,6 +459,11 @@ async function canSkipDeploy(deployStackOptions, cloudFormationStack, parameterC
459
459
  debug(`${deployName}: no existing stack`);
460
460
  return false;
461
461
  }
462
+ // SST check: stack is not busy
463
+ if (cloudFormationStack.stackStatus.isInProgress) {
464
+ debug(`${deployName}: stack is busy`);
465
+ return false;
466
+ }
462
467
  // Template has changed (assets taken into account here)
463
468
  if (JSON.stringify(deployStackOptions.stack.template) !==
464
469
  JSON.stringify(await cloudFormationStack.template())) {
@@ -16,12 +16,21 @@ export const deploy = (program) => program.command("deploy [filter]", "Deploy yo
16
16
  const { createSpinner } = await import("../spinner.js");
17
17
  const { dim, blue, bold } = await import("colorette");
18
18
  const { useProject } = await import("../../project.js");
19
- const { loadAssembly, Stacks } = await import("../../stacks/index.js");
19
+ const { loadAssembly, useAppMetadata, saveAppMetadata, Stacks, } = await import("../../stacks/index.js");
20
20
  const { render } = await import("ink");
21
21
  const { DeploymentUI } = await import("../ui/deploy.js");
22
22
  const { mapValues } = await import("remeda");
23
23
  const project = useProject();
24
- const identity = await useSTSIdentity();
24
+ const [identity, appMetadata] = await Promise.all([
25
+ useSTSIdentity(),
26
+ useAppMetadata(),
27
+ ]);
28
+ // Check app mode changed
29
+ if (appMetadata && appMetadata.mode !== "deploy") {
30
+ if (!await promptChangeMode()) {
31
+ process.exit(0);
32
+ }
33
+ }
25
34
  Colors.line(`${Colors.primary.bold(`SST v${project.version}`)}`);
26
35
  Colors.gap();
27
36
  Colors.line(`${Colors.primary(`➜`)} ${Colors.bold("App:")} ${project.config.name}`);
@@ -61,5 +70,21 @@ export const deploy = (program) => program.command("deploy [filter]", "Deploy yo
61
70
  if (Object.values(results).some((stack) => Stacks.isFailed(stack.status)))
62
71
  process.exit(1);
63
72
  fs.writeFile(path.join(project.paths.out, "outputs.json"), JSON.stringify(mapValues(results, (val) => val.outputs), null, 2));
73
+ // Update app state
74
+ await saveAppMetadata({ mode: "deploy" });
64
75
  process.exit(0);
65
76
  });
77
+ async function promptChangeMode() {
78
+ const readline = await import("readline");
79
+ const rl = readline.createInterface({
80
+ input: process.stdin,
81
+ output: process.stdout,
82
+ });
83
+ return new Promise((resolve) => {
84
+ console.log("");
85
+ rl.question("You were previously running this stage in dev mode. It is recommended that you use a different stage for production. Read more here — https://docs.sst.dev/live-lambda-development\n\nAre you sure you want to deploy to this stage? (y/N) ", async (input) => {
86
+ rl.close();
87
+ resolve(input.trim() === "y");
88
+ });
89
+ });
90
+ }
@@ -10,21 +10,19 @@ export const dev = (program) => program.command(["dev", "start"], "Work on your
10
10
  const { useRuntimeServer } = await import("../../runtime/server.js");
11
11
  const { useBus } = await import("../../bus.js");
12
12
  const { useWatcher } = await import("../../watcher.js");
13
- const { Stacks } = await import("../../stacks/index.js");
13
+ const { useAppMetadata, saveAppMetadata, Stacks, } = await import("../../stacks/index.js");
14
14
  const { Logger } = await import("../../logger.js");
15
15
  const { createSpinner } = await import("../spinner.js");
16
- const { bold, magenta, green, blue, red } = await import("colorette");
16
+ const { bold, dim, yellow } = await import("colorette");
17
17
  const { render } = await import("ink");
18
18
  const React = await import("react");
19
19
  const { Context } = await import("../../context/context.js");
20
- const { DeploymentUI } = await import("../ui/deploy.js");
20
+ const { printDeploymentResults, DeploymentUI } = await import("../ui/deploy.js");
21
21
  const { useLocalServer } = await import("../local/server.js");
22
22
  const path = await import("path");
23
23
  const fs = await import("fs/promises");
24
24
  const crypto = await import("crypto");
25
- const { printDeploymentResults } = await import("../ui/deploy.js");
26
25
  const { useFunctions } = await import("../../constructs/Function.js");
27
- const { dim, gray, yellow } = await import("colorette");
28
26
  const { SiteEnv } = await import("../../site-env.js");
29
27
  const { usePothosBuilder } = await import("./plugins/pothos.js");
30
28
  const { useKyselyTypeGenerator } = await import("./plugins/kysely.js");
@@ -169,8 +167,13 @@ export const dev = (program) => program.command(["dev", "start"], "Work on your
169
167
  const results = await Stacks.deployMany(assembly.stacks);
170
168
  component.clear();
171
169
  component.unmount();
172
- lastDeployed = nextChecksum;
173
170
  printDeploymentResults(assembly, results);
171
+ // Update app state
172
+ if (!lastDeployed) {
173
+ await saveAppMetadata({ mode: "dev" });
174
+ }
175
+ lastDeployed = nextChecksum;
176
+ // Update site env
174
177
  const keys = await SiteEnv.keys();
175
178
  if (keys.length) {
176
179
  const result = {};
@@ -186,6 +189,7 @@ export const dev = (program) => program.command(["dev", "start"], "Work on your
186
189
  }
187
190
  await SiteEnv.writeValues(result);
188
191
  }
192
+ // Write outputs.json
189
193
  fs.writeFile(path.join(project.paths.out, "outputs.json"), JSON.stringify(pipe(results, omitBy((_, key) => key.includes("SstSiteEnv")), mapValues((val) => val.outputs)), null, 2));
190
194
  isWorking = false;
191
195
  if (isDirty)
@@ -217,11 +221,20 @@ export const dev = (program) => program.command(["dev", "start"], "Work on your
217
221
  });
218
222
  await build();
219
223
  });
220
- await useLocalServer({
221
- key: "",
222
- cert: "",
223
- live: true,
224
- });
224
+ const [appMetadata] = await Promise.all([
225
+ useAppMetadata(),
226
+ useLocalServer({
227
+ key: "",
228
+ cert: "",
229
+ live: true,
230
+ }),
231
+ ]);
232
+ // Check app mode changed
233
+ if (appMetadata && appMetadata.mode !== "dev") {
234
+ if (!await promptChangeMode()) {
235
+ process.exit(0);
236
+ }
237
+ }
225
238
  clear();
226
239
  await printHeader({ console: true, hint: "ready!" });
227
240
  await Promise.all([
@@ -236,3 +249,17 @@ export const dev = (program) => program.command(["dev", "start"], "Work on your
236
249
  useStackBuilder(),
237
250
  ]);
238
251
  });
252
+ async function promptChangeMode() {
253
+ const readline = await import("readline");
254
+ const rl = readline.createInterface({
255
+ input: process.stdin,
256
+ output: process.stdout,
257
+ });
258
+ return new Promise((resolve) => {
259
+ console.log("");
260
+ rl.question("You have previously deployed this stage in production. It is recommended that you use a different stage for development. Read more here — https://docs.sst.dev/live-lambda-development\n\nAre you sure you want to run this stage in dev mode? [y/N] ", async (input) => {
261
+ rl.close();
262
+ resolve(input.trim() === "y");
263
+ });
264
+ });
265
+ }
@@ -5,7 +5,7 @@ export const remove = (program) => program.command("remove [filter]", "Remove yo
5
5
  const React = await import("react");
6
6
  const { dim, blue, bold } = await import("colorette");
7
7
  const { useProject } = await import("../../project.js");
8
- const { loadAssembly, Stacks } = await import("../../stacks/index.js");
8
+ const { loadAssembly, clearAppMetadata, Stacks, } = await import("../../stacks/index.js");
9
9
  const { render } = await import("ink");
10
10
  const { DeploymentUI } = await import("../ui/deploy.js");
11
11
  const { printDeploymentResults } = await import("../ui/deploy.js");
@@ -42,5 +42,6 @@ export const remove = (program) => program.command("remove [filter]", "Remove yo
42
42
  printDeploymentResults(assembly, results, true);
43
43
  if (Object.values(results).some((stack) => Stacks.isFailed(stack.status)))
44
44
  process.exit(1);
45
+ await clearAppMetadata();
45
46
  process.exit(0);
46
47
  });
@@ -20,7 +20,7 @@ export const useLocalServerConfig = Context.memo(async () => {
20
20
  });
21
21
  return {
22
22
  port,
23
- url: `https://console.sst.dev/${project.config.name}/${project.config.stage}${port !== 13557 ? `?port=${port}` : ""}`,
23
+ url: `https://console.sst.dev/${project.config.name}/${project.config.stage}${port !== 13557 ? `?_port=${port}` : ""}`,
24
24
  };
25
25
  });
26
26
  export async function useLocalServer(opts) {
package/cli/sst.js CHANGED
@@ -8,6 +8,10 @@ import dotenv from "dotenv";
8
8
  dotenv.config({
9
9
  override: true,
10
10
  });
11
+ dotenv.config({
12
+ path: ".env.local",
13
+ override: true,
14
+ });
11
15
  import { env } from "./commands/env.js";
12
16
  import { dev } from "./commands/dev.js";
13
17
  import { bind } from "./commands/bind.js";
package/cli/ui/deploy.js CHANGED
@@ -45,9 +45,11 @@ export const DeploymentUI = (props) => {
45
45
  return "yellow";
46
46
  }
47
47
  return (React.createElement(Box, { flexDirection: "column" },
48
- Object.entries(resources).map(([_, evt]) => {
48
+ Object.entries(resources)
49
+ .slice(0, process.stdout.rows - 2)
50
+ .map(([_, evt], index) => {
49
51
  const readable = logicalIdToCdkPath(props.assembly, evt.StackName, evt.LogicalResourceId);
50
- return (React.createElement(Box, { key: evt.LogicalResourceId },
52
+ return (React.createElement(Box, { key: index },
51
53
  React.createElement(Text, null,
52
54
  React.createElement(Spinner, null),
53
55
  " ",
@@ -113,7 +115,6 @@ function logicalIdToCdkPath(assembly, stack, logicalId) {
113
115
  return found.split("/").filter(Boolean).slice(1, -1).join("/");
114
116
  }
115
117
  function getHelper(error) {
116
- return `This is a common deploy error. Check out this GitHub issue for more details - https://github.com/serverless-stack/sst/issues/125`;
117
118
  return (getApiAccessLogPermissionsHelper(error) ||
118
119
  getAppSyncMultiResolverHelper(error) ||
119
120
  getApiLogRoleHelper(error));
package/constructs/App.js CHANGED
@@ -207,7 +207,6 @@ export class App extends cdk.App {
207
207
  Auth.injectConfig();
208
208
  this.ensureUniqueConstructIds();
209
209
  this.codegenTypes();
210
- this.buildConstructsMetadata();
211
210
  this.createBindingSsmParameters();
212
211
  this.removeGovCloudUnsupportedResourceProperties();
213
212
  for (const child of this.node.children) {
@@ -231,6 +230,7 @@ export class App extends cdk.App {
231
230
  }
232
231
  async finish() {
233
232
  await useDeferredTasks().run();
233
+ this.buildConstructsMetadata();
234
234
  }
235
235
  isRunningSSTTest() {
236
236
  // Check the env var set inside test/setup-tests.js
@@ -179,21 +179,19 @@ export interface StaticSiteProps {
179
179
  * ```
180
180
  */
181
181
  purgeFiles?: boolean;
182
- dev?: {
183
- /**
184
- * When running `sst dev, site is not deployed. This is to ensure `sst dev` can start up quickly.
185
- * @default false
186
- * @example
187
- * ```js
188
- * new StaticSite(stack, "frontend", {
189
- * dev: {
190
- * deploy: true
191
- * }
192
- * });
193
- * ```
194
- */
195
- deploy?: boolean;
196
- };
182
+ /**
183
+ * When running `sst start`, a placeholder site is deployed. This is to ensure that the site content remains unchanged, and subsequent `sst start` can start up quickly.
184
+ *
185
+ * @default false
186
+ *
187
+ * @example
188
+ * ```js
189
+ * new StaticSite(stack, "frontend", {
190
+ * disablePlaceholder: true
191
+ * });
192
+ * ```
193
+ */
194
+ disablePlaceholder?: boolean;
197
195
  vite?: {
198
196
  /**
199
197
  * The path where code-gen should place the type definition for environment variables
@@ -211,11 +209,11 @@ export interface StaticSiteProps {
211
209
  };
212
210
  /**
213
211
  * While deploying, SST waits for the CloudFront cache invalidation process to finish. This ensures that the new content will be served once the deploy command finishes. However, this process can sometimes take more than 5 mins. For non-prod environments it might make sense to pass in `false`. That'll skip waiting for the cache to invalidate and speed up the deploy process.
214
- * @default false
212
+ * @default true
215
213
  * @example
216
214
  * ```js
217
215
  * new StaticSite(stack, "frontend", {
218
- * waitForInvalidation: true
216
+ * waitForInvalidation: false
219
217
  * });
220
218
  * ```
221
219
  */
@@ -282,33 +280,55 @@ export interface StaticSiteCdkDistributionProps extends BaseSiteCdkDistributionP
282
280
  */
283
281
  export declare class StaticSite extends Construct implements SSTConstruct {
284
282
  readonly id: string;
283
+ readonly cdk: {
284
+ /**
285
+ * The internally created CDK `Bucket` instance.
286
+ */
287
+ bucket: s3.Bucket;
288
+ /**
289
+ * The internally created CDK `Distribution` instance.
290
+ */
291
+ distribution: cloudfront.Distribution;
292
+ /**
293
+ * The Route 53 hosted zone for the custom domain.
294
+ */
295
+ hostedZone?: route53.IHostedZone;
296
+ certificate?: acm.ICertificate;
297
+ };
285
298
  private props;
286
- private doNotDeploy;
287
- private bucket;
288
- private distribution;
289
- private hostedZone?;
290
- private certificate?;
299
+ private isPlaceholder;
300
+ private assets;
301
+ private filenamesAsset?;
302
+ private awsCliLayer;
291
303
  constructor(scope: Construct, id: string, props?: StaticSiteProps);
292
304
  /**
293
305
  * The CloudFront URL of the website.
294
306
  */
295
- get url(): string | undefined;
307
+ get url(): string;
296
308
  /**
297
309
  * If the custom domain is enabled, this is the URL of the website with the custom domain.
298
310
  */
299
311
  get customDomainUrl(): string | undefined;
300
312
  /**
301
- * The internally created CDK resources.
313
+ * The ARN of the internally created S3 Bucket.
302
314
  */
303
- get cdk(): {
304
- bucket: s3.Bucket;
305
- distribution: cloudfront.Distribution;
306
- hostedZone: route53.IHostedZone | undefined;
307
- certificate: acm.ICertificate | undefined;
308
- };
315
+ get bucketArn(): string;
316
+ /**
317
+ * The name of the internally created S3 Bucket.
318
+ */
319
+ get bucketName(): string;
320
+ /**
321
+ * The ID of the internally created CloudFront Distribution.
322
+ */
323
+ get distributionId(): string;
324
+ /**
325
+ * The domain name of the internally created CloudFront Distribution.
326
+ */
327
+ get distributionDomain(): string;
309
328
  getConstructMetadata(): {
310
329
  type: "StaticSite";
311
330
  data: {
331
+ distributionId: string;
312
332
  customDomainUrl: string | undefined;
313
333
  };
314
334
  };