sst 2.1.16 → 2.1.18

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 (53) hide show
  1. package/bootstrap.js +1 -1
  2. package/cli/commands/bind.js +1 -1
  3. package/cli/commands/env.js +1 -1
  4. package/constructs/App.js +3 -3
  5. package/constructs/AstroSite.js +8 -41
  6. package/constructs/EdgeFunction.d.ts +19 -21
  7. package/constructs/EdgeFunction.js +235 -167
  8. package/constructs/Function.d.ts +1 -1
  9. package/constructs/Function.js +3 -3
  10. package/constructs/Job.js +2 -2
  11. package/constructs/NextjsSite.d.ts +6 -5
  12. package/constructs/NextjsSite.js +5 -3
  13. package/constructs/RDS.js +1 -1
  14. package/constructs/RemixSite.d.ts +2 -3
  15. package/constructs/RemixSite.js +42 -71
  16. package/constructs/SolidStartSite.js +8 -41
  17. package/constructs/SsrFunction.d.ts +9 -6
  18. package/constructs/SsrFunction.js +61 -50
  19. package/constructs/SsrSite.d.ts +6 -4
  20. package/constructs/SsrSite.js +0 -4
  21. package/constructs/Stack.js +2 -1
  22. package/constructs/future/Auth.d.ts +79 -0
  23. package/constructs/future/Auth.js +116 -0
  24. package/constructs/future/index.d.ts +1 -0
  25. package/constructs/future/index.js +1 -0
  26. package/context/handler.d.ts +1 -1
  27. package/node/api/index.d.ts +27 -4
  28. package/node/api/index.js +64 -2
  29. package/node/auth/index.d.ts +3 -0
  30. package/node/auth/index.js +3 -0
  31. package/node/future/auth/adapter/adapter.d.ts +10 -0
  32. package/node/future/auth/adapter/adapter.js +1 -0
  33. package/node/future/auth/adapter/github.d.ts +16 -0
  34. package/node/future/auth/adapter/github.js +22 -0
  35. package/node/future/auth/adapter/google.d.ts +20 -0
  36. package/node/future/auth/adapter/google.js +10 -0
  37. package/node/future/auth/adapter/link.d.ts +11 -0
  38. package/node/future/auth/adapter/link.js +48 -0
  39. package/node/future/auth/adapter/oauth.d.ts +34 -0
  40. package/node/future/auth/adapter/oauth.js +66 -0
  41. package/node/future/auth/adapter/oidc.d.ts +26 -0
  42. package/node/future/auth/adapter/oidc.js +62 -0
  43. package/node/future/auth/handler.d.ts +18 -0
  44. package/node/future/auth/handler.js +189 -0
  45. package/node/future/auth/index.d.ts +10 -0
  46. package/node/future/auth/index.js +10 -0
  47. package/node/future/auth/session.d.ts +46 -0
  48. package/node/future/auth/session.js +99 -0
  49. package/node/util/index.js +1 -1
  50. package/package.json +4 -1
  51. package/runtime/handlers/node.js +1 -1
  52. package/sst.mjs +4 -4
  53. 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 * as lambda from "aws-cdk-lib/aws-lambda";
8
- import * as s3Assets from "aws-cdk-lib/aws-s3-assets";
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
- this.props = props;
27
- const { scopeOverride } = props;
28
- // Correct scope
29
- this.scope = scopeOverride || this;
30
- // Wrap function code
31
- this.wrapFunctionCode();
32
- // Create function asset
33
- const asset = this.createAsset();
34
- // Create function role
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
- // Create function
37
- const { functionArn, versionId } = this.createFunction(asset);
38
- this.functionArn = functionArn;
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 lambda.Version.fromVersionArn(this, `${this.node.id}FunctionVersion`, `${this.functionArn}:${this.versionId}`);
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
- wrapFunctionCode() {
48
- const { bundlePath, format } = this.props;
49
- // Parse handler
50
- const parts = this.props.handler.split(".");
51
- const handlerImportPath = parts.slice(0, -1).join(".");
52
- const handlerMethod = parts.slice(-1)[0];
53
- const handlerExt = [".js", ".jsx", ".mjs", ".cjs"].find((ext) => fs.existsSync(path.join(bundlePath, handlerImportPath + ext)));
54
- const imports = this.props.format === "esm"
55
- ? `import * as index from "./${handlerImportPath}${handlerExt}";`
56
- : `"use strict"; const index = require("./${handlerImportPath}");`;
57
- const exports = this.props.format === "esm"
58
- ? `export { handler };`
59
- : `exports.handler = handler;`;
60
- const content = `${imports}
61
- const handler = async (event) => {
62
- try {
63
- // We expose an environment variable token which is used by the code
64
- // replacer to inject the environment variables assigned to the
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
- // Attach permission
105
- if (permissions) {
106
- attachPermissionsToRole(role, permissions);
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 role;
116
+ return {
117
+ bundle: outputPath,
118
+ handler: outputHandler,
119
+ handlerFilename: outputFilename + outputFileExt,
120
+ };
109
121
  }
110
- createFunction(asset) {
111
- const { runtime, timeout, memorySize } = this.props;
112
- const name = this.node.id;
113
- // Create a S3 bucket in us-east-1 to store Lambda code. Create
114
- // 1 bucket for all Edge functions.
115
- const bucketCR = this.createSingletonBucketCR();
116
- const bucketName = bucketCR.getAttString("BucketName");
117
- // Create a Lambda function in us-east-1
118
- const functionCR = this.createFunctionCR(name, this.role, bucketName, {
119
- Description: `${name} handler`,
120
- Handler: "index-wrapper.handler",
121
- Code: {
122
- S3Bucket: asset.s3BucketName,
123
- S3Key: asset.s3ObjectKey,
124
- },
125
- Runtime: runtime === "nodejs14.x"
126
- ? lambda.Runtime.NODEJS_14_X.name
127
- : runtime === "nodejs16.x"
128
- ? lambda.Runtime.NODEJS_16_X.name
129
- : lambda.Runtime.NODEJS_18_X.name,
130
- MemorySize: typeof memorySize === "string"
131
- ? toCdkSize(memorySize).toMebibytes()
132
- : memorySize,
133
- Timeout: typeof timeout === "string"
134
- ? toCdkDuration(timeout).toSeconds()
135
- : timeout,
136
- Role: this.role.roleArn,
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
- createLambdaCodeReplacer(asset) {
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: this.getLambdaContentReplaceValues(),
214
+ replacements,
170
215
  },
171
216
  });
172
217
  resource.node.addDependency(policy);
173
218
  return resource;
174
219
  }
175
- createSingletonBucketCR() {
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 lambda.Function(stack, providerId, {
186
- code: lambda.Code.fromAsset(path.join(__dirname, "../support/edge-function")),
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: lambda.Runtime.NODEJS_16_X,
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
- createFunctionCR(name, role, bucketName, functionParams) {
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 = `${name}EdgeLambda`;
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 lambda.Function(stack, providerId, {
218
- code: lambda.Code.fromAsset(path.join(__dirname, "../support/edge-function")),
280
+ provider = new CdkFunction(stack, providerId, {
281
+ code: Code.fromAsset(path.join(__dirname, "../support/edge-function")),
219
282
  handler: "edge-lambda.handler",
220
- runtime: lambda.Runtime.NODEJS_16_X,
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 resource = new CustomResource(this.scope, resId, {
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: bucketName,
242
- FunctionParams: 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 resource;
327
+ return { fn, fnArn: fn.getAttString("FunctionArn") };
246
328
  }
247
- createVersionCR(name, functionArn) {
329
+ createVersionInUsEast1(fn, fnArn) {
248
330
  // Do not recreate if exist
249
331
  const providerId = "EdgeLambdaVersionProvider";
250
- const resId = `${name}EdgeLambdaVersion`;
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 lambda.Function(stack, providerId, {
256
- code: lambda.Code.fromAsset(path.join(__dirname, "../support/edge-function")),
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: lambda.Runtime.NODEJS_16_X,
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
- return new CustomResource(this.scope, resId, {
353
+ const version = new CustomResource(this.scope, resId, {
272
354
  serviceToken: provider.functionArn,
273
355
  resourceType: "Custom::SSTEdgeLambdaVersion",
274
356
  properties: {
275
- FunctionArn: 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 = versionCR.node.defaultChild;
313
- const originalLogicalId = Stack.of(versionCR).resolve(cfn.logicalId);
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(functionCR);
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);
@@ -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
- _disableBind?: boolean;
541
+ _doNotAllowOthersToBind?: boolean;
542
542
  private functionUrl?;
543
543
  private props;
544
544
  private allBindings;
@@ -64,7 +64,7 @@ export class Function extends lambda.Function {
64
64
  id;
65
65
  _isLiveDevEnabled;
66
66
  /** @internal */
67
- _disableBind;
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._disableBind = true;
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._disableBind = true;
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._disableBind = true;
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._disableBind = true;
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: Omit<NextjsSiteProps, "path"> & {
33
- path: string;
34
- timeout: number | Duration;
35
- memorySize: number | Size;
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(): {
@@ -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
- bundlePath: path.join(this.props.path, ".open-next", "server-function"),
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
- bundlePath: middlewarePath,
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
@@ -290,7 +290,7 @@ export class RDS extends Construct {
290
290
  format: "esm",
291
291
  },
292
292
  });
293
- fn._disableBind = true;
293
+ fn._doNotAllowOthersToBind = true;
294
294
  fn.attachPermissions([this.cdk.cluster]);
295
295
  this.migratorFunction = fn;
296
296
  }
@@ -1,4 +1,4 @@
1
- import * as lambda from "aws-cdk-lib/aws-lambda";
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(): lambda.Function;
24
+ protected createFunctionForRegional(): CdkFunction;
25
25
  protected createFunctionForEdge(): EdgeFunction;
26
- private normalizeRuntime;
27
26
  }