sst 2.24.19 → 2.24.21

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/cli/sst.js CHANGED
@@ -55,6 +55,7 @@ process.on("uncaughtException", (err) => {
55
55
  spinner.fail(spinner.text);
56
56
  }
57
57
  if (!(err instanceof SilentError)) {
58
+ console.log();
58
59
  console.log(red("Error:"), err.message);
59
60
  if (!(err instanceof VisibleError)) {
60
61
  console.log();
package/config.js CHANGED
@@ -5,7 +5,6 @@ import { useProject } from "./project.js";
5
5
  import { useAWSClient } from "./credentials.js";
6
6
  import { useIOT } from "./iot.js";
7
7
  import { Stacks } from "./stacks/index.js";
8
- const ssm = useAWSClient(SSMClient);
9
8
  const FALLBACK_STAGE = ".fallback";
10
9
  const SECRET_UPDATED_AT_ENV = "SST_ADMIN_SECRET_UPDATED_AT";
11
10
  const PREFIX = {
@@ -171,6 +170,7 @@ export var Config;
171
170
  Config.restart = restart;
172
171
  })(Config || (Config = {}));
173
172
  async function* scanParameters(prefix) {
173
+ const ssm = useAWSClient(SSMClient);
174
174
  let token;
175
175
  while (true) {
176
176
  const results = await ssm.send(new GetParametersByPathCommand({
@@ -186,12 +186,14 @@ async function* scanParameters(prefix) {
186
186
  }
187
187
  }
188
188
  function getParameter(name) {
189
+ const ssm = useAWSClient(SSMClient);
189
190
  return ssm.send(new GetParameterCommand({
190
191
  Name: name,
191
192
  WithDecryption: true,
192
193
  }));
193
194
  }
194
195
  function putParameter(name, value) {
196
+ const ssm = useAWSClient(SSMClient);
195
197
  return ssm.send(new PutParameterCommand({
196
198
  Name: name,
197
199
  Value: value,
@@ -201,6 +203,7 @@ function putParameter(name, value) {
201
203
  }));
202
204
  }
203
205
  function deleteParameter(name) {
206
+ const ssm = useAWSClient(SSMClient);
204
207
  return ssm.send(new DeleteParameterCommand({
205
208
  Name: name,
206
209
  }));
@@ -426,7 +426,6 @@ export declare class EventBus extends Construct implements SSTConstruct {
426
426
  getFunctionBinding(): FunctionBindingProps;
427
427
  private retrierQueue;
428
428
  private retrierFn;
429
- private retrierMap;
430
429
  private getRetrier;
431
430
  private createEventBus;
432
431
  private addRule;
@@ -437,6 +436,9 @@ export declare class EventBus extends Construct implements SSTConstruct {
437
436
  private subs;
438
437
  subscribe(type: string | string[], target: FunctionDefinition, props?: {
439
438
  retries?: number;
440
- }): this;
439
+ }): EventBus;
440
+ subscribe(scope: Construct, type: string | string[], target: FunctionDefinition, props?: {
441
+ retries?: number;
442
+ }): EventBus;
441
443
  private addFunctionTarget;
442
444
  }
@@ -10,6 +10,9 @@ import { SqsEventSource } from "aws-cdk-lib/aws-lambda-event-sources";
10
10
  import { SqsDestination } from "aws-cdk-lib/aws-lambda-destinations";
11
11
  import url from "url";
12
12
  import path from "path";
13
+ import { Effect, PolicyStatement } from "aws-cdk-lib/aws-iam";
14
+ import { Duration } from "aws-cdk-lib/core";
15
+ import { Stack } from "./Stack.js";
13
16
  const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
14
17
  /////////////////////
15
18
  // Construct
@@ -233,7 +236,6 @@ export class EventBus extends Construct {
233
236
  }
234
237
  retrierQueue;
235
238
  retrierFn;
236
- retrierMap = {};
237
239
  getRetrier() {
238
240
  const app = this.node.root;
239
241
  if (this.retrierFn && this.retrierQueue) {
@@ -245,12 +247,26 @@ export class EventBus extends Construct {
245
247
  this.retrierFn = new lambda.Function(this, `RetrierFunction`, {
246
248
  functionName: app.logicalPrefixedName(this.node.id + "Retrier"),
247
249
  runtime: lambda.Runtime.NODEJS_14_X,
250
+ timeout: Duration.seconds(30),
248
251
  handler: "index.handler",
249
252
  code: lambda.Code.fromAsset(path.join(__dirname, "../support/event-bus-retrier")),
250
253
  environment: {
251
254
  RETRIER_QUEUE_URL: this.retrierQueue.queueUrl,
252
255
  },
253
256
  });
257
+ this.retrierFn.addToRolePolicy(new PolicyStatement({
258
+ effect: Effect.ALLOW,
259
+ actions: ["lambda:InvokeFunction", "lambda:GetFunction"],
260
+ resources: [
261
+ `arn:${Stack.of(this).partition}:lambda:${app.region}:${app.account}:function:*`,
262
+ ],
263
+ conditions: {
264
+ StringEquals: {
265
+ "aws:ResourceTag/sst:app": app.name,
266
+ "aws:ResourceTag/sst:stage": app.stage,
267
+ },
268
+ },
269
+ }));
254
270
  this.retrierFn.addEventSource(new SqsEventSource(this.retrierQueue));
255
271
  this.retrierQueue.grantSendMessages(this.retrierFn);
256
272
  return { fn: this.retrierFn, queue: this.retrierQueue };
@@ -353,7 +369,18 @@ export class EventBus extends Construct {
353
369
  eventsRule.addTarget(new LambdaFunctionTarget(fn, targetProps));
354
370
  }
355
371
  subs = new Map();
356
- subscribe(type, target, props) {
372
+ subscribe(scope_or_type, type_or_target, target_or_props, maybe_props) {
373
+ let [scope, type, target, props] = (() => {
374
+ if (scope_or_type instanceof Construct) {
375
+ return [
376
+ scope_or_type,
377
+ type_or_target,
378
+ target_or_props,
379
+ maybe_props,
380
+ ];
381
+ }
382
+ return [this, scope_or_type, type_or_target, maybe_props];
383
+ })();
357
384
  type = Array.isArray(type) ? type : [type];
358
385
  const joined = type.length > 1 ? "multi" : type.join("_");
359
386
  const count = (this.subs.get(joined) || 0) + 1;
@@ -363,17 +390,15 @@ export class EventBus extends Construct {
363
390
  const fn = (() => {
364
391
  if (retries) {
365
392
  const retrier = this.getRetrier();
366
- const fn = Fn.fromDefinition(this, name, target, {
393
+ const fn = Fn.fromDefinition(scope, name, target, {
367
394
  onFailure: new SqsDestination(retrier.queue),
368
395
  });
369
- this.retrierMap[fn.functionArn] = retries;
370
- retrier.fn.addEnvironment(`RETRIES`, JSON.stringify(this.retrierMap));
371
- fn.grantInvoke(retrier.fn);
396
+ fn.addEnvironment("SST_RETRIES", retries.toString());
372
397
  return fn;
373
398
  }
374
- return Fn.fromDefinition(this, name, target);
399
+ return Fn.fromDefinition(scope, name, target);
375
400
  })();
376
- this.addRule(this, name + "_rule", {
401
+ this.addRule(scope, name + "_rule", {
377
402
  pattern: {
378
403
  detailType: type,
379
404
  },
@@ -342,24 +342,38 @@ export class Service extends Construct {
342
342
  /////////////////////
343
343
  validateServiceExists() {
344
344
  const { path: servicePath, file } = this.props;
345
+ // Validate path exists
345
346
  if (!fs.existsSync(servicePath)) {
346
- throw new Error(`No service found at "${path.resolve(servicePath)}"`);
347
+ throw new VisibleError(`In the "${this.node.id}" Service, path is not found at "${path.resolve(servicePath)}"`);
347
348
  }
349
+ // Validate path is a directory
350
+ if (fs.statSync(servicePath).isFile()) {
351
+ throw new VisibleError([
352
+ `In the "${this.node.id}" Service, the path "${path.resolve(servicePath)}" should be a directory.`,
353
+ `Did you mean:`,
354
+ ``,
355
+ ` {`,
356
+ ` path: "${path.dirname(servicePath)}",`,
357
+ ` file: "${path.basename(servicePath)}",`,
358
+ ` }`,
359
+ ].join("\n"));
360
+ }
361
+ // Validate path exists
348
362
  if (file) {
349
363
  const dockerfilePath = path.join(servicePath, file);
350
364
  if (!fs.existsSync(dockerfilePath)) {
351
- throw new Error(`No Dockerfile found at "${dockerfilePath}". Make sure to set the "file" property to the path of the Dockerfile relative to "${servicePath}".`);
365
+ throw new VisibleError(`In the "${this.node.id}" Service, no Dockerfile is found at "${dockerfilePath}". Make sure to set the "file" property to the path of the Dockerfile relative to "${servicePath}".`);
352
366
  }
353
367
  }
354
368
  }
355
369
  validateMemoryAndCpu() {
356
370
  const { memory, cpu } = this.props;
357
371
  if (!supportedCpus[cpu]) {
358
- throw new Error(`Only the following "cpu" settings are supported for the ${this.node.id} service: ${Object.keys(supportedCpus).join(", ")}`);
372
+ throw new VisibleError(`In the "${this.node.id}" Service, only the following "cpu" settings are supported: ${Object.keys(supportedCpus).join(", ")}`);
359
373
  }
360
374
  // @ts-ignore
361
375
  if (!supportedMemories[cpu][memory]) {
362
- throw new Error(`Only the following "memory" settings are supported with "${cpu}" for the ${this.node.id} service: ${Object.keys(supportedMemories[cpu]).join(", ")}`);
376
+ throw new VisibleError(`In the "${this.node.id}" Service, only the following "memory" settings are supported with "${cpu}": ${Object.keys(supportedMemories[cpu]).join(", ")}`);
363
377
  }
364
378
  }
365
379
  createVpc() {
@@ -12,6 +12,10 @@ interface OnSuccessResponder<T> {
12
12
  properties: typeof input;
13
13
  };
14
14
  }
15
+ export declare class UnknownProviderError {
16
+ provider?: string | undefined;
17
+ constructor(provider?: string | undefined);
18
+ }
15
19
  export declare function AuthHandler<Providers extends Record<string, Adapter<any>>, Sessions extends SessionBuilder, Result = {
16
20
  [key in keyof Providers]: {
17
21
  provider: key;
@@ -25,6 +29,6 @@ export declare function AuthHandler<Providers extends Record<string, Adapter<any
25
29
  onAuthorize?: (event: APIGatewayProxyEventV2) => Promise<void | keyof Providers>;
26
30
  onSuccess: (input: Result, response: OnSuccessResponder<SessionValue | Sessions["$type"]>) => Promise<ReturnType<OnSuccessResponder<SessionValue | Sessions["$type"]>[keyof OnSuccessResponder<any>]>>;
27
31
  onIndex?: (event: APIGatewayProxyEventV2) => Promise<APIGatewayProxyStructuredResultV2>;
28
- onError?: () => Promise<APIGatewayProxyStructuredResultV2>;
32
+ onError?: (error: UnknownProviderError) => Promise<APIGatewayProxyStructuredResultV2 | undefined>;
29
33
  }): (event: APIGatewayProxyEventV2, context: import("aws-lambda").Context) => Promise<APIGatewayProxyStructuredResultV2>;
30
34
  export {};
@@ -1,6 +1,12 @@
1
1
  import { createSigner, createVerifier } from "fast-jwt";
2
2
  import { ApiHandler, useCookie, useCookies, useFormValue, usePathParam, useQueryParam, useQueryParams, useResponse, } from "../../api/index.js";
3
3
  import { Config } from "../../config/index.js";
4
+ export class UnknownProviderError {
5
+ provider;
6
+ constructor(provider) {
7
+ this.provider = provider;
8
+ }
9
+ }
4
10
  export function AuthHandler(input) {
5
11
  return ApiHandler(async (evt) => {
6
12
  const step = usePathParam("step");
@@ -139,6 +145,9 @@ export function AuthHandler(input) {
139
145
  });
140
146
  }
141
147
  if (!provider || !input.providers[provider]) {
148
+ const response = input.onError?.(new UnknownProviderError(provider));
149
+ if (response)
150
+ return response;
142
151
  return {
143
152
  statusCode: 400,
144
153
  body: `Was not able to find provider "${String(provider)}"`,
@@ -246,8 +255,6 @@ export function AuthHandler(input) {
246
255
  }
247
256
  }
248
257
  if (result.type === "error") {
249
- if (input.onError)
250
- return input.onError();
251
258
  return {
252
259
  statusCode: 400,
253
260
  body: "an error has occured",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "sideEffects": false,
3
3
  "name": "sst",
4
- "version": "2.24.19",
4
+ "version": "2.24.21",
5
5
  "bin": {
6
6
  "sst": "cli/sst.js"
7
7
  },