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 +10 -0
- package/bootstrap.js +1 -1
- package/cdk/deploy-stack.js +5 -0
- package/cli/commands/deploy.js +27 -2
- package/cli/commands/dev.js +38 -11
- package/cli/commands/remove.js +2 -1
- package/cli/local/server.js +1 -1
- package/cli/sst.js +4 -0
- package/cli/ui/deploy.js +4 -3
- package/constructs/App.js +1 -1
- package/constructs/StaticSite.d.ts +50 -30
- package/constructs/StaticSite.js +105 -80
- package/context/context.d.ts +2 -2
- package/context/context.js +4 -4
- package/package.json +1 -1
- package/project.js +2 -2
- package/sst.mjs +230 -59
- package/stacks/app-metadata.d.ts +7 -0
- package/stacks/app-metadata.js +78 -0
- package/stacks/index.d.ts +1 -0
- package/stacks/index.js +1 -0
- package/stacks/monitor.js +1 -3
package/bootstrap.d.ts
CHANGED
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");
|
package/cdk/deploy-stack.js
CHANGED
|
@@ -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())) {
|
package/cli/commands/deploy.js
CHANGED
|
@@ -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
|
|
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
|
+
}
|
package/cli/commands/dev.js
CHANGED
|
@@ -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,
|
|
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
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
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
|
+
}
|
package/cli/commands/remove.js
CHANGED
|
@@ -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
|
});
|
package/cli/local/server.js
CHANGED
|
@@ -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 ? `?
|
|
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)
|
|
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:
|
|
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
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
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
|
|
212
|
+
* @default true
|
|
215
213
|
* @example
|
|
216
214
|
* ```js
|
|
217
215
|
* new StaticSite(stack, "frontend", {
|
|
218
|
-
* waitForInvalidation:
|
|
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
|
|
287
|
-
private
|
|
288
|
-
private
|
|
289
|
-
private
|
|
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
|
|
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
|
|
313
|
+
* The ARN of the internally created S3 Bucket.
|
|
302
314
|
*/
|
|
303
|
-
get
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
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
|
};
|