sst 2.1.17 → 2.1.19
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.js +1 -1
- package/cli/commands/bind.js +1 -1
- package/cli/commands/env.js +1 -1
- package/constructs/App.js +3 -3
- package/constructs/AstroSite.js +8 -41
- package/constructs/EdgeFunction.d.ts +19 -21
- package/constructs/EdgeFunction.js +235 -167
- package/constructs/Function.d.ts +1 -1
- package/constructs/Function.js +3 -3
- package/constructs/Job.js +2 -2
- package/constructs/NextjsSite.d.ts +6 -5
- package/constructs/NextjsSite.js +5 -3
- package/constructs/RDS.js +1 -1
- package/constructs/RemixSite.d.ts +2 -3
- package/constructs/RemixSite.js +42 -71
- package/constructs/SolidStartSite.js +8 -41
- package/constructs/SsrFunction.d.ts +9 -6
- package/constructs/SsrFunction.js +61 -50
- package/constructs/SsrSite.d.ts +6 -4
- package/constructs/SsrSite.js +0 -4
- package/constructs/Stack.js +2 -1
- package/node/util/index.js +1 -1
- package/package.json +1 -1
- package/runtime/handlers/node.js +1 -1
- package/runtime/handlers.js +2 -0
- package/sst.mjs +6 -4
- package/support/nodejs-runtime/index.mjs +62 -14629
|
@@ -2,12 +2,15 @@ import fs from "fs";
|
|
|
2
2
|
import url from "url";
|
|
3
3
|
import path from "path";
|
|
4
4
|
import crypto from "crypto";
|
|
5
|
+
import { buildSync } from "esbuild";
|
|
5
6
|
import { Construct } from "constructs";
|
|
6
7
|
import { Effect, Role, Policy, PolicyStatement, CompositePrincipal, ServicePrincipal, ManagedPolicy, } from "aws-cdk-lib/aws-iam";
|
|
7
|
-
import
|
|
8
|
-
import
|
|
8
|
+
import { Version, Code, Runtime, Function as CdkFunction, } from "aws-cdk-lib/aws-lambda";
|
|
9
|
+
import { Asset } from "aws-cdk-lib/aws-s3-assets";
|
|
9
10
|
import { Lazy, Duration as CdkDuration, CustomResource, } from "aws-cdk-lib";
|
|
11
|
+
import { useProject } from "../project.js";
|
|
10
12
|
import { Stack } from "./Stack.js";
|
|
13
|
+
import { bindEnvironment, bindPermissions, getReferencedSecrets, } from "./util/functionBinding.js";
|
|
11
14
|
import { toCdkSize } from "./util/size.js";
|
|
12
15
|
import { toCdkDuration } from "./util/duration.js";
|
|
13
16
|
import { attachPermissionsToRole } from "./util/permission.js";
|
|
@@ -20,132 +23,174 @@ export class EdgeFunction extends Construct {
|
|
|
20
23
|
functionArn;
|
|
21
24
|
scope;
|
|
22
25
|
versionId;
|
|
26
|
+
bindingEnvs;
|
|
23
27
|
props;
|
|
24
28
|
constructor(scope, id, props) {
|
|
25
29
|
super(scope, id);
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
//
|
|
29
|
-
|
|
30
|
-
//
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
30
|
+
// Override scope
|
|
31
|
+
// note: this is intended to be used internally by SST to make constructs
|
|
32
|
+
// backwards compatible when the hirechical structure of the constructs
|
|
33
|
+
// changes. When the hirerchical structure changes, the child AWS
|
|
34
|
+
// resources' logical ID will change. And CloudFormation will recreate
|
|
35
|
+
// them.
|
|
36
|
+
this.scope = props.scopeOverride || this;
|
|
37
|
+
this.props = {
|
|
38
|
+
...props,
|
|
39
|
+
bundle: props.bundle || "placeholder",
|
|
40
|
+
environment: props.environment || {},
|
|
41
|
+
permissions: props.permissions || [],
|
|
42
|
+
};
|
|
43
|
+
// Build bundle if not prebuilt
|
|
44
|
+
const { bundle, handler, handlerFilename } = props.bundle
|
|
45
|
+
? this.updateBundleWithEnvWrapper()
|
|
46
|
+
: this.buildBundle();
|
|
47
|
+
this.props.bundle = bundle;
|
|
48
|
+
this.props.handler = handler;
|
|
49
|
+
// Bind first b/e function's environment variables cannot be added after
|
|
50
|
+
this.bindingEnvs = {};
|
|
51
|
+
this.bind(props.bind || []);
|
|
52
|
+
const asset = this.createCodeAsset();
|
|
53
|
+
const assetReplacer = this.createCodeReplacer(asset, handlerFilename);
|
|
35
54
|
this.role = this.createRole();
|
|
36
|
-
|
|
37
|
-
const {
|
|
38
|
-
this.
|
|
55
|
+
const bucket = this.createSingletonBucketInUsEast1();
|
|
56
|
+
const { fn, fnArn } = this.createFunctionInUsEast1(asset, bucket);
|
|
57
|
+
const { versionId } = this.createVersionInUsEast1(fn, fnArn);
|
|
58
|
+
// Deploy after the code is updated
|
|
59
|
+
fn.node.addDependency(assetReplacer);
|
|
60
|
+
this.functionArn = fnArn;
|
|
39
61
|
this.versionId = versionId;
|
|
40
62
|
}
|
|
41
63
|
get currentVersion() {
|
|
42
|
-
return
|
|
64
|
+
return Version.fromVersionArn(this, `${this.node.id}FunctionVersion`, `${this.functionArn}:${this.versionId}`);
|
|
43
65
|
}
|
|
44
66
|
attachPermissions(permissions) {
|
|
45
67
|
attachPermissionsToRole(this.role, permissions);
|
|
46
68
|
}
|
|
47
|
-
|
|
48
|
-
const {
|
|
49
|
-
|
|
50
|
-
const
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
// EdgeFunction construct.
|
|
66
|
-
//
|
|
67
|
-
// "{{ _SST_FUNCTION_ENVIRONMENT_ }}" will get replaced during
|
|
68
|
-
// deployment with an object of environment key-value pairs, ie.
|
|
69
|
-
// const environment = {"API_URL": "https://api.example.com"};
|
|
70
|
-
//
|
|
71
|
-
// This inlining strategy is required as Lambda@Edge doesn't natively
|
|
72
|
-
// support runtime environment variables. A downside of this approach
|
|
73
|
-
// is that environment variables cannot be toggled after deployment,
|
|
74
|
-
// each change to one requires a redeployment.
|
|
75
|
-
const environment = "{{ _SST_FUNCTION_ENVIRONMENT_ }}";
|
|
76
|
-
process.env = { ...process.env, ...environment };
|
|
77
|
-
} catch (e) {
|
|
78
|
-
console.log("Failed to set SST Lambda@Edge environment.");
|
|
79
|
-
console.log(e);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
return await index.${handlerMethod}(event);
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
${exports}
|
|
86
|
-
`;
|
|
87
|
-
fs.writeFileSync(path.join(bundlePath, `index-wrapper.${format === "esm" ? "mjs" : "cjs"}`), content);
|
|
88
|
-
}
|
|
89
|
-
createAsset() {
|
|
90
|
-
const { bundlePath } = this.props;
|
|
91
|
-
return new s3Assets.Asset(this.scope, `FunctionAsset`, {
|
|
92
|
-
path: bundlePath,
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
createRole() {
|
|
96
|
-
const { permissions } = this.props;
|
|
97
|
-
// Create function role
|
|
98
|
-
const role = new Role(this.scope, `ServerLambdaRole`, {
|
|
99
|
-
assumedBy: new CompositePrincipal(new ServicePrincipal("lambda.amazonaws.com"), new ServicePrincipal("edgelambda.amazonaws.com")),
|
|
100
|
-
managedPolicies: [
|
|
101
|
-
ManagedPolicy.fromManagedPolicyArn(this, "EdgeLambdaPolicy", `arn:${Stack.of(this).partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole`),
|
|
69
|
+
buildBundle() {
|
|
70
|
+
const { handler, format, esbuild, runtime } = this.props;
|
|
71
|
+
const isESM = format === "esm";
|
|
72
|
+
const { dir: inputPath, base: inputHandler, name: inputFilename, ext: inputHandlerFunction, } = path.parse(handler);
|
|
73
|
+
const inputFileExt = this.getHandlerExtension(path.join(inputPath, inputFilename));
|
|
74
|
+
// Create a directory that we will use to create the bundled version
|
|
75
|
+
// of the "core server build" along with our custom Lamba server handler.
|
|
76
|
+
const outputPath = path.resolve(path.join(useProject().paths.artifacts, `EdgeFunction-${this.node.id}-${this.node.addr}`));
|
|
77
|
+
const outputHandler = inputHandler;
|
|
78
|
+
const outputFilename = inputFilename;
|
|
79
|
+
const outputFileExt = isESM ? ".mjs" : ".cjs";
|
|
80
|
+
const { external, ...override } = esbuild || {};
|
|
81
|
+
const result = buildSync({
|
|
82
|
+
entryPoints: [handler.replace(inputHandlerFunction, inputFileExt)],
|
|
83
|
+
platform: "node",
|
|
84
|
+
external: [
|
|
85
|
+
...(isESM || runtime === "nodejs18.x" ? [] : ["aws-sdk"]),
|
|
86
|
+
...(external || []),
|
|
102
87
|
],
|
|
88
|
+
metafile: true,
|
|
89
|
+
bundle: true,
|
|
90
|
+
...(isESM
|
|
91
|
+
? {
|
|
92
|
+
format: "esm",
|
|
93
|
+
target: "esnext",
|
|
94
|
+
mainFields: ["module", "main"],
|
|
95
|
+
banner: {
|
|
96
|
+
js: [
|
|
97
|
+
`import { createRequire as topLevelCreateRequire } from 'module';`,
|
|
98
|
+
`const require = topLevelCreateRequire(import.meta.url);`,
|
|
99
|
+
`import { fileURLToPath as topLevelFileUrlToPath } from "url"`,
|
|
100
|
+
`const __dirname = topLevelFileUrlToPath(new URL(".", import.meta.url))`,
|
|
101
|
+
`process.env = { ...process.env, ..."{{ _SST_FUNCTION_ENVIRONMENT_ }}" };`,
|
|
102
|
+
].join("\n"),
|
|
103
|
+
},
|
|
104
|
+
}
|
|
105
|
+
: {
|
|
106
|
+
format: "cjs",
|
|
107
|
+
target: "node14",
|
|
108
|
+
}),
|
|
109
|
+
outfile: path.join(outputPath, outputFilename + outputFileExt),
|
|
110
|
+
...override,
|
|
103
111
|
});
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
112
|
+
if (result.errors.length > 0) {
|
|
113
|
+
result.errors.forEach((error) => console.error(error));
|
|
114
|
+
throw new Error(`There was a problem bundling the SSR function for the "${this.scope.node.id}" Site.`);
|
|
107
115
|
}
|
|
108
|
-
return
|
|
116
|
+
return {
|
|
117
|
+
bundle: outputPath,
|
|
118
|
+
handler: outputHandler,
|
|
119
|
+
handlerFilename: outputFilename + outputFileExt,
|
|
120
|
+
};
|
|
109
121
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
//
|
|
114
|
-
//
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
//
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
122
|
+
updateBundleWithEnvWrapper() {
|
|
123
|
+
// We expose an environment variable token which is used by the code
|
|
124
|
+
// replacer to inject the environment variables assigned to the
|
|
125
|
+
// EdgeFunction construct.
|
|
126
|
+
//
|
|
127
|
+
// "{{ _SST_FUNCTION_ENVIRONMENT_ }}" will get replaced during
|
|
128
|
+
// deployment with an object of environment key-value pairs, ie.
|
|
129
|
+
// const environment = {"API_URL": "https://api.example.com"};
|
|
130
|
+
//
|
|
131
|
+
// This inlining strategy is required as Lambda@Edge doesn't natively
|
|
132
|
+
// support runtime environment variables. A downside of this approach
|
|
133
|
+
// is that environment variables cannot be toggled after deployment,
|
|
134
|
+
// each change to one requires a redeployment.
|
|
135
|
+
const { bundle, handler } = this.props;
|
|
136
|
+
const { dir: inputPath, name: inputFilename, ext: inputHandlerFunction, } = path.parse(handler);
|
|
137
|
+
const inputFileExt = this.getHandlerExtension(path.join(bundle, inputPath, inputFilename));
|
|
138
|
+
const handlerFilename = handler.replace(inputHandlerFunction, inputFileExt);
|
|
139
|
+
const filePath = path.join(bundle, handlerFilename);
|
|
140
|
+
const fileData = fs.readFileSync(filePath, "utf8");
|
|
141
|
+
fs.writeFileSync(filePath, `process.env = { ...process.env, ..."{{ _SST_FUNCTION_ENVIRONMENT_ }}" };\n${fileData}`);
|
|
142
|
+
return { bundle, handler, handlerFilename };
|
|
143
|
+
}
|
|
144
|
+
bind(constructs) {
|
|
145
|
+
const app = this.node.root;
|
|
146
|
+
this.bindingEnvs = {
|
|
147
|
+
SST_APP: app.name,
|
|
148
|
+
SST_STAGE: app.stage,
|
|
149
|
+
SST_REGION: app.region,
|
|
150
|
+
SST_SSM_PREFIX: useProject().config.ssmPrefix,
|
|
151
|
+
};
|
|
152
|
+
// Get referenced secrets
|
|
153
|
+
const referencedSecrets = [];
|
|
154
|
+
constructs.forEach((c) => referencedSecrets.push(...getReferencedSecrets(c)));
|
|
155
|
+
[...constructs, ...referencedSecrets].forEach((c) => {
|
|
156
|
+
// Bind environment
|
|
157
|
+
this.bindingEnvs = {
|
|
158
|
+
...this.bindingEnvs,
|
|
159
|
+
...bindEnvironment(c),
|
|
160
|
+
};
|
|
161
|
+
// Bind permissions
|
|
162
|
+
if (this.props.permissions !== "*") {
|
|
163
|
+
this.props.permissions.push(...Object.entries(bindPermissions(c)).map(([action, resources]) => new PolicyStatement({
|
|
164
|
+
actions: [action],
|
|
165
|
+
effect: Effect.ALLOW,
|
|
166
|
+
resources,
|
|
167
|
+
})));
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
createCodeAsset() {
|
|
172
|
+
const { bundle } = this.props;
|
|
173
|
+
return new Asset(this.scope, `FunctionAsset`, {
|
|
174
|
+
path: bundle,
|
|
137
175
|
});
|
|
138
|
-
const functionArn = functionCR.getAttString("FunctionArn");
|
|
139
|
-
// Create a Lambda function version in us-east-1
|
|
140
|
-
const versionCR = this.createVersionCR(name, functionArn);
|
|
141
|
-
const versionId = versionCR.getAttString("Version");
|
|
142
|
-
this.updateVersionLogicalId(functionCR, versionCR);
|
|
143
|
-
// Deploy after the code is updated
|
|
144
|
-
const updaterCR = this.createLambdaCodeReplacer(asset);
|
|
145
|
-
functionCR.node.addDependency(updaterCR);
|
|
146
|
-
return { functionArn, versionId };
|
|
147
176
|
}
|
|
148
|
-
|
|
177
|
+
createCodeReplacer(asset, handlerFilename) {
|
|
178
|
+
const { environment } = this.props;
|
|
179
|
+
const replacements = [
|
|
180
|
+
{
|
|
181
|
+
files: handlerFilename,
|
|
182
|
+
search: '"{{ _SST_FUNCTION_ENVIRONMENT_ }}"',
|
|
183
|
+
replace: JSON.stringify({
|
|
184
|
+
...environment,
|
|
185
|
+
...this.bindingEnvs,
|
|
186
|
+
}),
|
|
187
|
+
},
|
|
188
|
+
...Object.entries(environment).map(([key, value]) => ({
|
|
189
|
+
files: "**/*.*js",
|
|
190
|
+
search: `{{ ${key} }}`,
|
|
191
|
+
replace: value,
|
|
192
|
+
})),
|
|
193
|
+
];
|
|
149
194
|
// Note: Source code for the Lambda functions have "{{ ENV_KEY }}" in them.
|
|
150
195
|
// They need to be replaced with real values before the Lambda
|
|
151
196
|
// functions get deployed.
|
|
@@ -166,13 +211,30 @@ ${exports}
|
|
|
166
211
|
properties: {
|
|
167
212
|
bucket: asset.s3BucketName,
|
|
168
213
|
key: asset.s3ObjectKey,
|
|
169
|
-
replacements
|
|
214
|
+
replacements,
|
|
170
215
|
},
|
|
171
216
|
});
|
|
172
217
|
resource.node.addDependency(policy);
|
|
173
218
|
return resource;
|
|
174
219
|
}
|
|
175
|
-
|
|
220
|
+
createRole() {
|
|
221
|
+
const { permissions } = this.props;
|
|
222
|
+
// Create function role
|
|
223
|
+
const role = new Role(this.scope, `ServerLambdaRole`, {
|
|
224
|
+
assumedBy: new CompositePrincipal(new ServicePrincipal("lambda.amazonaws.com"), new ServicePrincipal("edgelambda.amazonaws.com")),
|
|
225
|
+
managedPolicies: [
|
|
226
|
+
ManagedPolicy.fromManagedPolicyArn(this, "EdgeLambdaPolicy", `arn:${Stack.of(this).partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole`),
|
|
227
|
+
],
|
|
228
|
+
});
|
|
229
|
+
// Attach permission
|
|
230
|
+
if (permissions) {
|
|
231
|
+
attachPermissionsToRole(role, permissions);
|
|
232
|
+
}
|
|
233
|
+
return role;
|
|
234
|
+
}
|
|
235
|
+
createSingletonBucketInUsEast1() {
|
|
236
|
+
// Create a S3 bucket in us-east-1 to store Lambda code. Create
|
|
237
|
+
// 1 bucket for all Edge functions.
|
|
176
238
|
// Do not recreate if exist
|
|
177
239
|
const providerId = "EdgeLambdaBucketProvider";
|
|
178
240
|
const resId = "EdgeLambdaBucket";
|
|
@@ -182,10 +244,10 @@ ${exports}
|
|
|
182
244
|
return existingResource;
|
|
183
245
|
}
|
|
184
246
|
// Create provider
|
|
185
|
-
const provider = new
|
|
186
|
-
code:
|
|
247
|
+
const provider = new CdkFunction(stack, providerId, {
|
|
248
|
+
code: Code.fromAsset(path.join(__dirname, "../support/edge-function")),
|
|
187
249
|
handler: "s3-bucket.handler",
|
|
188
|
-
runtime:
|
|
250
|
+
runtime: Runtime.NODEJS_16_X,
|
|
189
251
|
timeout: CdkDuration.minutes(15),
|
|
190
252
|
memorySize: 1024,
|
|
191
253
|
initialPolicy: [
|
|
@@ -206,18 +268,19 @@ ${exports}
|
|
|
206
268
|
});
|
|
207
269
|
return resource;
|
|
208
270
|
}
|
|
209
|
-
|
|
271
|
+
createFunctionInUsEast1(asset, bucket) {
|
|
272
|
+
const { handler, runtime, timeout, memorySize } = this.props;
|
|
210
273
|
// Do not recreate if exist
|
|
211
274
|
const providerId = "EdgeLambdaProvider";
|
|
212
|
-
const resId = `${
|
|
275
|
+
const resId = `${this.node.id}EdgeLambda`;
|
|
213
276
|
const stack = Stack.of(this);
|
|
214
277
|
let provider = stack.node.tryFindChild(providerId);
|
|
215
278
|
// Create provider if not already created
|
|
216
279
|
if (!provider) {
|
|
217
|
-
provider = new
|
|
218
|
-
code:
|
|
280
|
+
provider = new CdkFunction(stack, providerId, {
|
|
281
|
+
code: Code.fromAsset(path.join(__dirname, "../support/edge-function")),
|
|
219
282
|
handler: "edge-lambda.handler",
|
|
220
|
-
runtime:
|
|
283
|
+
runtime: Runtime.NODEJS_16_X,
|
|
221
284
|
timeout: CdkDuration.minutes(15),
|
|
222
285
|
memorySize: 1024,
|
|
223
286
|
initialPolicy: [
|
|
@@ -229,33 +292,52 @@ ${exports}
|
|
|
229
292
|
],
|
|
230
293
|
});
|
|
231
294
|
if (provider.role) {
|
|
232
|
-
role.grantPassRole(provider.role);
|
|
295
|
+
this.role.grantPassRole(provider.role);
|
|
233
296
|
}
|
|
234
297
|
}
|
|
235
298
|
// Create custom resource
|
|
236
|
-
const
|
|
299
|
+
const fn = new CustomResource(this.scope, resId, {
|
|
237
300
|
serviceToken: provider.functionArn,
|
|
238
301
|
resourceType: "Custom::SSTEdgeLambda",
|
|
239
302
|
properties: {
|
|
240
303
|
FunctionNamePrefix: `${Stack.of(this).stackName}-${resId}`,
|
|
241
|
-
FunctionBucket:
|
|
242
|
-
FunctionParams:
|
|
304
|
+
FunctionBucket: bucket.getAttString("BucketName"),
|
|
305
|
+
FunctionParams: {
|
|
306
|
+
Description: `${this.node.id} handler`,
|
|
307
|
+
Handler: handler,
|
|
308
|
+
Code: {
|
|
309
|
+
S3Bucket: asset.s3BucketName,
|
|
310
|
+
S3Key: asset.s3ObjectKey,
|
|
311
|
+
},
|
|
312
|
+
Runtime: runtime === "nodejs14.x"
|
|
313
|
+
? Runtime.NODEJS_14_X.name
|
|
314
|
+
: runtime === "nodejs16.x"
|
|
315
|
+
? Runtime.NODEJS_16_X.name
|
|
316
|
+
: Runtime.NODEJS_18_X.name,
|
|
317
|
+
MemorySize: typeof memorySize === "string"
|
|
318
|
+
? toCdkSize(memorySize).toMebibytes()
|
|
319
|
+
: memorySize,
|
|
320
|
+
Timeout: typeof timeout === "string"
|
|
321
|
+
? toCdkDuration(timeout).toSeconds()
|
|
322
|
+
: timeout,
|
|
323
|
+
Role: this.role.roleArn,
|
|
324
|
+
},
|
|
243
325
|
},
|
|
244
326
|
});
|
|
245
|
-
return
|
|
327
|
+
return { fn, fnArn: fn.getAttString("FunctionArn") };
|
|
246
328
|
}
|
|
247
|
-
|
|
329
|
+
createVersionInUsEast1(fn, fnArn) {
|
|
248
330
|
// Do not recreate if exist
|
|
249
331
|
const providerId = "EdgeLambdaVersionProvider";
|
|
250
|
-
const resId = `${
|
|
332
|
+
const resId = `${this.node.id}EdgeLambdaVersion`;
|
|
251
333
|
const stack = Stack.of(this);
|
|
252
334
|
let provider = stack.node.tryFindChild(providerId);
|
|
253
335
|
// Create provider if not already created
|
|
254
336
|
if (!provider) {
|
|
255
|
-
provider = new
|
|
256
|
-
code:
|
|
337
|
+
provider = new CdkFunction(stack, providerId, {
|
|
338
|
+
code: Code.fromAsset(path.join(__dirname, "../support/edge-function")),
|
|
257
339
|
handler: "edge-lambda-version.handler",
|
|
258
|
-
runtime:
|
|
340
|
+
runtime: Runtime.NODEJS_16_X,
|
|
259
341
|
timeout: CdkDuration.minutes(15),
|
|
260
342
|
memorySize: 1024,
|
|
261
343
|
initialPolicy: [
|
|
@@ -268,56 +350,42 @@ ${exports}
|
|
|
268
350
|
});
|
|
269
351
|
}
|
|
270
352
|
// Create custom resource
|
|
271
|
-
|
|
353
|
+
const version = new CustomResource(this.scope, resId, {
|
|
272
354
|
serviceToken: provider.functionArn,
|
|
273
355
|
resourceType: "Custom::SSTEdgeLambdaVersion",
|
|
274
356
|
properties: {
|
|
275
|
-
FunctionArn:
|
|
357
|
+
FunctionArn: fnArn,
|
|
276
358
|
},
|
|
277
359
|
});
|
|
278
|
-
}
|
|
279
|
-
/////////////////////
|
|
280
|
-
// Internal Functions
|
|
281
|
-
/////////////////////
|
|
282
|
-
getLambdaContentReplaceValues() {
|
|
283
|
-
const { format } = this.props;
|
|
284
|
-
const replaceValues = [];
|
|
285
|
-
Object.entries(this.props.environment || {}).forEach(([key, value]) => {
|
|
286
|
-
const token = `{{ ${key} }}`;
|
|
287
|
-
replaceValues.push({
|
|
288
|
-
files: "**/*.js",
|
|
289
|
-
search: token,
|
|
290
|
-
replace: value,
|
|
291
|
-
}, {
|
|
292
|
-
files: "**/*.cjs",
|
|
293
|
-
search: token,
|
|
294
|
-
replace: value,
|
|
295
|
-
}, {
|
|
296
|
-
files: "**/*.mjs",
|
|
297
|
-
search: token,
|
|
298
|
-
replace: value,
|
|
299
|
-
});
|
|
300
|
-
});
|
|
301
|
-
replaceValues.push({
|
|
302
|
-
files: `index-wrapper.${format === "esm" ? "mjs" : "cjs"}`,
|
|
303
|
-
search: '"{{ _SST_FUNCTION_ENVIRONMENT_ }}"',
|
|
304
|
-
replace: JSON.stringify(this.props.environment || {}),
|
|
305
|
-
});
|
|
306
|
-
return replaceValues;
|
|
307
|
-
}
|
|
308
|
-
updateVersionLogicalId(functionCR, versionCR) {
|
|
309
360
|
// Override the version's logical ID with a lazy string which includes the
|
|
310
361
|
// hash of the function itself, so a new version resource is created when
|
|
311
362
|
// the function configuration changes.
|
|
312
|
-
const cfn =
|
|
313
|
-
const originalLogicalId = Stack.of(
|
|
363
|
+
const cfn = version.node.defaultChild;
|
|
364
|
+
const originalLogicalId = Stack.of(version).resolve(cfn.logicalId);
|
|
314
365
|
cfn.overrideLogicalId(Lazy.uncachedString({
|
|
315
366
|
produce: () => {
|
|
316
|
-
const hash = this.calculateHash(
|
|
367
|
+
const hash = this.calculateHash(fn);
|
|
317
368
|
const logicalId = this.trimFromStart(originalLogicalId, 255 - 32);
|
|
318
369
|
return `${logicalId}${hash}`;
|
|
319
370
|
},
|
|
320
371
|
}));
|
|
372
|
+
return { version, versionId: version.getAttString("Version") };
|
|
373
|
+
}
|
|
374
|
+
getHandlerExtension(pathWithoutExtension) {
|
|
375
|
+
const ext = [
|
|
376
|
+
".ts",
|
|
377
|
+
".tsx",
|
|
378
|
+
".mts",
|
|
379
|
+
".cts",
|
|
380
|
+
".js",
|
|
381
|
+
".jsx",
|
|
382
|
+
".mjs",
|
|
383
|
+
".cjs",
|
|
384
|
+
].find((ext) => fs.existsSync(pathWithoutExtension + ext));
|
|
385
|
+
if (!ext) {
|
|
386
|
+
throw new Error(`Cannot find the SSR function handler file for the "${this.scope.node.id}" Site.`);
|
|
387
|
+
}
|
|
388
|
+
return ext;
|
|
321
389
|
}
|
|
322
390
|
trimFromStart(s, maxLength) {
|
|
323
391
|
const desiredLength = Math.min(maxLength, s.length);
|
package/constructs/Function.d.ts
CHANGED
|
@@ -538,7 +538,7 @@ export declare class Function extends lambda.Function implements SSTConstruct {
|
|
|
538
538
|
readonly id: string;
|
|
539
539
|
readonly _isLiveDevEnabled: boolean;
|
|
540
540
|
/** @internal */
|
|
541
|
-
|
|
541
|
+
_doNotAllowOthersToBind?: boolean;
|
|
542
542
|
private functionUrl?;
|
|
543
543
|
private props;
|
|
544
544
|
private allBindings;
|
package/constructs/Function.js
CHANGED
|
@@ -64,7 +64,7 @@ export class Function extends lambda.Function {
|
|
|
64
64
|
id;
|
|
65
65
|
_isLiveDevEnabled;
|
|
66
66
|
/** @internal */
|
|
67
|
-
|
|
67
|
+
_doNotAllowOthersToBind;
|
|
68
68
|
functionUrl;
|
|
69
69
|
props;
|
|
70
70
|
allBindings = [];
|
|
@@ -429,7 +429,7 @@ export class Function extends lambda.Function {
|
|
|
429
429
|
...(inheritedProps || {}),
|
|
430
430
|
handler: definition,
|
|
431
431
|
});
|
|
432
|
-
fn.
|
|
432
|
+
fn._doNotAllowOthersToBind = true;
|
|
433
433
|
return fn;
|
|
434
434
|
}
|
|
435
435
|
else if (definition instanceof Function) {
|
|
@@ -444,7 +444,7 @@ export class Function extends lambda.Function {
|
|
|
444
444
|
}
|
|
445
445
|
else if (definition.handler !== undefined) {
|
|
446
446
|
const fn = new Function(scope, id, Function.mergeProps(inheritedProps, definition));
|
|
447
|
-
fn.
|
|
447
|
+
fn._doNotAllowOthersToBind = true;
|
|
448
448
|
return fn;
|
|
449
449
|
}
|
|
450
450
|
throw new Error(`Invalid function definition for the "${id}" Function`);
|
package/constructs/Job.js
CHANGED
|
@@ -238,7 +238,7 @@ export class Job extends Construct {
|
|
|
238
238
|
},
|
|
239
239
|
permissions,
|
|
240
240
|
});
|
|
241
|
-
fn.
|
|
241
|
+
fn._doNotAllowOthersToBind = true;
|
|
242
242
|
return fn;
|
|
243
243
|
}
|
|
244
244
|
createCodeBuildInvoker() {
|
|
@@ -262,7 +262,7 @@ export class Job extends Construct {
|
|
|
262
262
|
},
|
|
263
263
|
enableLiveDev: false,
|
|
264
264
|
});
|
|
265
|
-
fn.
|
|
265
|
+
fn._doNotAllowOthersToBind = true;
|
|
266
266
|
return fn;
|
|
267
267
|
}
|
|
268
268
|
bindForCodeBuild(constructs) {
|
|
@@ -3,7 +3,6 @@ import * as lambda from "aws-cdk-lib/aws-lambda";
|
|
|
3
3
|
import * as cloudfront from "aws-cdk-lib/aws-cloudfront";
|
|
4
4
|
import { SsrSite, SsrSiteProps } from "./SsrSite.js";
|
|
5
5
|
import { Size } from "./util/size.js";
|
|
6
|
-
import { Duration } from "./util/duration.js";
|
|
7
6
|
export interface NextjsSiteProps extends Omit<SsrSiteProps, "edge"> {
|
|
8
7
|
imageOptimization?: {
|
|
9
8
|
/**
|
|
@@ -29,10 +28,12 @@ export interface NextjsSiteProps extends Omit<SsrSiteProps, "edge"> {
|
|
|
29
28
|
* ```
|
|
30
29
|
*/
|
|
31
30
|
export declare class NextjsSite extends SsrSite {
|
|
32
|
-
protected props:
|
|
33
|
-
path:
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
protected props: NextjsSiteProps & {
|
|
32
|
+
path: Exclude<NextjsSiteProps["path"], undefined>;
|
|
33
|
+
runtime: Exclude<NextjsSiteProps["runtime"], undefined>;
|
|
34
|
+
timeout: Exclude<NextjsSiteProps["timeout"], undefined>;
|
|
35
|
+
memorySize: Exclude<NextjsSiteProps["memorySize"], undefined>;
|
|
36
|
+
waitForInvalidation: Exclude<NextjsSiteProps["waitForInvalidation"], undefined>;
|
|
36
37
|
};
|
|
37
38
|
constructor(scope: Construct, id: string, props?: NextjsSiteProps);
|
|
38
39
|
protected initBuildConfig(): {
|
package/constructs/NextjsSite.js
CHANGED
|
@@ -38,14 +38,15 @@ export class NextjsSite extends SsrSite {
|
|
|
38
38
|
};
|
|
39
39
|
}
|
|
40
40
|
createFunctionForRegional() {
|
|
41
|
-
const { runtime, timeout, memorySize, permissions, environment, cdk } = this.props;
|
|
41
|
+
const { runtime, timeout, memorySize, bind, permissions, environment, cdk, } = this.props;
|
|
42
42
|
const ssrFn = new SsrFunction(this, `ServerFunction`, {
|
|
43
43
|
description: "Server handler for Next.js",
|
|
44
|
-
|
|
44
|
+
bundle: path.join(this.props.path, ".open-next", "server-function"),
|
|
45
45
|
handler: "index.handler",
|
|
46
46
|
runtime,
|
|
47
47
|
timeout,
|
|
48
48
|
memorySize,
|
|
49
|
+
bind,
|
|
49
50
|
permissions,
|
|
50
51
|
environment,
|
|
51
52
|
...cdk?.server,
|
|
@@ -86,8 +87,9 @@ export class NextjsSite extends SsrSite {
|
|
|
86
87
|
const middlewarePath = path.resolve(sitePath, ".open-next/middleware-function");
|
|
87
88
|
if (fs.existsSync(middlewarePath)) {
|
|
88
89
|
return new EdgeFunction(this, "Middleware", {
|
|
89
|
-
|
|
90
|
+
bundle: middlewarePath,
|
|
90
91
|
handler: "index.handler",
|
|
92
|
+
runtime: "nodejs18.x",
|
|
91
93
|
timeout: 5,
|
|
92
94
|
memorySize: 128,
|
|
93
95
|
permissions,
|
package/constructs/RDS.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Function as CdkFunction } from "aws-cdk-lib/aws-lambda";
|
|
2
2
|
import { SsrSite } from "./SsrSite.js";
|
|
3
3
|
import { EdgeFunction } from "./EdgeFunction.js";
|
|
4
4
|
/**
|
|
@@ -21,7 +21,6 @@ export declare class RemixSite extends SsrSite {
|
|
|
21
21
|
clientBuildVersionedSubDir: string;
|
|
22
22
|
};
|
|
23
23
|
private createServerLambdaBundle;
|
|
24
|
-
protected createFunctionForRegional():
|
|
24
|
+
protected createFunctionForRegional(): CdkFunction;
|
|
25
25
|
protected createFunctionForEdge(): EdgeFunction;
|
|
26
|
-
private normalizeRuntime;
|
|
27
26
|
}
|