sst 2.5.5 → 2.5.6

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/config.js CHANGED
@@ -10,7 +10,7 @@ export var Config;
10
10
  async function parameters() {
11
11
  const result = [];
12
12
  for await (const p of scan(PREFIX.FALLBACK)) {
13
- const parsed = parse(p.Name);
13
+ const parsed = parse(p.Name, PREFIX.FALLBACK);
14
14
  if (parsed.type === "secrets")
15
15
  continue;
16
16
  result.push({
@@ -19,7 +19,7 @@ export var Config;
19
19
  });
20
20
  }
21
21
  for await (const p of scan(PREFIX.STAGE)) {
22
- const parsed = parse(p.Name);
22
+ const parsed = parse(p.Name, PREFIX.STAGE);
23
23
  if (parsed.type === "secrets")
24
24
  continue;
25
25
  result.push({
@@ -45,13 +45,13 @@ export var Config;
45
45
  async function secrets() {
46
46
  const result = {};
47
47
  for await (const p of scan(PREFIX.STAGE + "Secret")) {
48
- const parsed = parse(p.Name);
48
+ const parsed = parse(p.Name, PREFIX.STAGE);
49
49
  if (!result[parsed.id])
50
50
  result[parsed.id] = {};
51
51
  result[parsed.id].value = p.Value;
52
52
  }
53
53
  for await (const p of scan(PREFIX.FALLBACK + "Secret")) {
54
- const parsed = parse(p.Name);
54
+ const parsed = parse(p.Name, PREFIX.FALLBACK);
55
55
  if (!result[parsed.id])
56
56
  result[parsed.id] = {};
57
57
  result[parsed.id].fallback = p.Value;
@@ -178,19 +178,19 @@ const SECRET_UPDATED_AT_ENV = "SST_ADMIN_SECRET_UPDATED_AT";
178
178
  const PREFIX = {
179
179
  get STAGE() {
180
180
  const project = useProject();
181
- return `/sst/${project.config.name}/${project.config.stage}/`;
181
+ return project.config.ssmPrefix;
182
182
  },
183
183
  get FALLBACK() {
184
184
  const project = useProject();
185
185
  return `/sst/${project.config.name}/${FALLBACK_STAGE}/`;
186
186
  },
187
187
  };
188
- function parse(ssmName) {
189
- const parts = ssmName.split("/");
188
+ function parse(ssmName, prefix) {
189
+ const parts = ssmName.substring(prefix.length).split("/");
190
190
  return {
191
- type: parts[4],
192
- id: parts[5],
193
- prop: parts.slice(6).join("/"),
191
+ type: parts[0],
192
+ id: parts[1],
193
+ prop: parts.slice(2).join("/"),
194
194
  };
195
195
  }
196
196
  async function restartFunction(arn) {
@@ -165,10 +165,11 @@ export class SsrSite extends Construct {
165
165
  * ```
166
166
  */
167
167
  attachPermissions(permissions) {
168
+ this.serverLambdaForDev?.attachPermissions(permissions);
169
+ this.serverLambdaForEdge?.attachPermissions(permissions);
168
170
  if (this.serverLambdaForRegional) {
169
171
  attachPermissionsToRole(this.serverLambdaForRegional.role, permissions);
170
172
  }
171
- this.serverLambdaForEdge?.attachPermissions(permissions);
172
173
  }
173
174
  /** @internal */
174
175
  getConstructMetadata() {
@@ -432,6 +433,9 @@ export class SsrSite extends Construct {
432
433
  environment,
433
434
  permissions,
434
435
  role,
436
+ // Force enable live dev to prevent the function handler to be built
437
+ // in the case user set "enableLiveDev: false" on the app or stack.
438
+ enableLiveDev: true,
435
439
  });
436
440
  fn._doNotAllowOthersToBind = true;
437
441
  return fn;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sst",
3
- "version": "2.5.5",
3
+ "version": "2.5.6",
4
4
  "bin": {
5
5
  "sst": "cli/sst.js"
6
6
  },
package/project.d.ts CHANGED
@@ -56,4 +56,10 @@ interface GlobalOptions {
56
56
  region?: string;
57
57
  }
58
58
  export declare function initProject(globals: GlobalOptions): Promise<void>;
59
+ declare function sanitizeStageName(stage: string): string;
60
+ declare function isValidStageName(stage: string): boolean;
61
+ export declare const exportedForTesting: {
62
+ sanitizeStageName: typeof sanitizeStageName;
63
+ isValidStageName: typeof isValidStageName;
64
+ };
59
65
  export {};
package/project.js CHANGED
@@ -117,21 +117,29 @@ async function usePersonalStage(out) {
117
117
  return;
118
118
  }
119
119
  }
120
- async function promptPersonalStage(out) {
120
+ async function promptPersonalStage(out, isRetry) {
121
121
  const readline = await import("readline");
122
122
  const rl = readline.createInterface({
123
123
  input: process.stdin,
124
124
  output: process.stdout,
125
125
  });
126
- return new Promise((resolve) => {
127
- const suggested = os.userInfo().username;
128
- rl.question(`Please enter a name you’d like to use for your personal stage. Or hit enter to use ${blue(suggested)}: `, async (input) => {
126
+ const stage = await new Promise((resolve) => {
127
+ const suggested = sanitizeStageName(os.userInfo().username) || "local";
128
+ const instruction = !isRetry
129
+ ? `Please enter a name you’d like to use for your personal stage.`
130
+ : `Please enter a name that starts with a letter, followed by letters, numbers, or hyphens.`;
131
+ rl.question(`${instruction} Or hit enter to use ${blue(suggested)}: `, async (input) => {
129
132
  rl.close();
130
- const result = input || suggested;
131
- await fs.writeFile(path.join(out, "stage"), result);
133
+ const result = input === "" ? suggested : input;
132
134
  resolve(result);
133
135
  });
134
136
  });
137
+ // Validate stage name
138
+ if (isValidStageName(stage)) {
139
+ await fs.writeFile(path.join(out, "stage"), stage);
140
+ return stage;
141
+ }
142
+ return await promptPersonalStage(out, true);
135
143
  }
136
144
  async function findRoot() {
137
145
  async function find(dir) {
@@ -148,3 +156,17 @@ async function findRoot() {
148
156
  const result = await find(process.cwd());
149
157
  return result;
150
158
  }
159
+ function sanitizeStageName(stage) {
160
+ return (stage
161
+ .replace(/[^A-Za-z0-9-]/g, "-")
162
+ .replace(/--+/g, "-")
163
+ .replace(/^[^A-Za-z]/, "")
164
+ .replace(/-$/, "") || "local");
165
+ }
166
+ function isValidStageName(stage) {
167
+ return Boolean(stage.match(/^[A-Za-z][A-Za-z0-9-]*$/));
168
+ }
169
+ export const exportedForTesting = {
170
+ sanitizeStageName,
171
+ isValidStageName,
172
+ };
package/sst.mjs CHANGED
@@ -317,6 +317,7 @@ var init_build = __esm({
317
317
  var project_exports = {};
318
318
  __export(project_exports, {
319
319
  ProjectContext: () => ProjectContext,
320
+ exportedForTesting: () => exportedForTesting,
320
321
  initProject: () => initProject,
321
322
  useProject: () => useProject
322
323
  });
@@ -420,26 +421,29 @@ async function usePersonalStage(out) {
420
421
  return;
421
422
  }
422
423
  }
423
- async function promptPersonalStage(out) {
424
+ async function promptPersonalStage(out, isRetry) {
424
425
  const readline = await import("readline");
425
426
  const rl = readline.createInterface({
426
427
  input: process.stdin,
427
428
  output: process.stdout
428
429
  });
429
- return new Promise((resolve) => {
430
- const suggested = os.userInfo().username;
430
+ const stage = await new Promise((resolve) => {
431
+ const suggested = sanitizeStageName(os.userInfo().username) || "local";
432
+ const instruction = !isRetry ? `Please enter a name you\u2019d like to use for your personal stage.` : `Please enter a name that starts with a letter, followed by letters, numbers, or hyphens.`;
431
433
  rl.question(
432
- `Please enter a name you\u2019d like to use for your personal stage. Or hit enter to use ${blue(
433
- suggested
434
- )}: `,
434
+ `${instruction} Or hit enter to use ${blue(suggested)}: `,
435
435
  async (input) => {
436
436
  rl.close();
437
- const result = input || suggested;
438
- await fs4.writeFile(path4.join(out, "stage"), result);
437
+ const result = input === "" ? suggested : input;
439
438
  resolve(result);
440
439
  }
441
440
  );
442
441
  });
442
+ if (isValidStageName(stage)) {
443
+ await fs4.writeFile(path4.join(out, "stage"), stage);
444
+ return stage;
445
+ }
446
+ return await promptPersonalStage(out, true);
443
447
  }
444
448
  async function findRoot() {
445
449
  async function find2(dir) {
@@ -460,7 +464,13 @@ async function findRoot() {
460
464
  const result = await find2(process.cwd());
461
465
  return result;
462
466
  }
463
- var ProjectContext, CONFIG_EXTENSIONS;
467
+ function sanitizeStageName(stage) {
468
+ return stage.replace(/[^A-Za-z0-9-]/g, "-").replace(/--+/g, "-").replace(/^[^A-Za-z]/, "").replace(/-$/, "") || "local";
469
+ }
470
+ function isValidStageName(stage) {
471
+ return Boolean(stage.match(/^[A-Za-z][A-Za-z0-9-]*$/));
472
+ }
473
+ var ProjectContext, CONFIG_EXTENSIONS, exportedForTesting;
464
474
  var init_project = __esm({
465
475
  "src/project.ts"() {
466
476
  "use strict";
@@ -477,6 +487,10 @@ var init_project = __esm({
477
487
  ".config.mjs",
478
488
  ".config.js"
479
489
  ];
490
+ exportedForTesting = {
491
+ sanitizeStageName,
492
+ isValidStageName
493
+ };
480
494
  }
481
495
  });
482
496
 
@@ -6523,12 +6537,12 @@ async function* scan(prefix) {
6523
6537
  token = results.NextToken;
6524
6538
  }
6525
6539
  }
6526
- function parse(ssmName) {
6527
- const parts = ssmName.split("/");
6540
+ function parse(ssmName, prefix) {
6541
+ const parts = ssmName.substring(prefix.length).split("/");
6528
6542
  return {
6529
- type: parts[4],
6530
- id: parts[5],
6531
- prop: parts.slice(6).join("/")
6543
+ type: parts[0],
6544
+ id: parts[1],
6545
+ prop: parts.slice(2).join("/")
6532
6546
  };
6533
6547
  }
6534
6548
  async function restartFunction(arn) {
@@ -6569,7 +6583,7 @@ var init_config = __esm({
6569
6583
  async function parameters() {
6570
6584
  const result = [];
6571
6585
  for await (const p of scan(PREFIX.FALLBACK)) {
6572
- const parsed = parse(p.Name);
6586
+ const parsed = parse(p.Name, PREFIX.FALLBACK);
6573
6587
  if (parsed.type === "secrets")
6574
6588
  continue;
6575
6589
  result.push({
@@ -6578,7 +6592,7 @@ var init_config = __esm({
6578
6592
  });
6579
6593
  }
6580
6594
  for await (const p of scan(PREFIX.STAGE)) {
6581
- const parsed = parse(p.Name);
6595
+ const parsed = parse(p.Name, PREFIX.STAGE);
6582
6596
  if (parsed.type === "secrets")
6583
6597
  continue;
6584
6598
  result.push({
@@ -6604,13 +6618,13 @@ var init_config = __esm({
6604
6618
  async function secrets2() {
6605
6619
  const result = {};
6606
6620
  for await (const p of scan(PREFIX.STAGE + "Secret")) {
6607
- const parsed = parse(p.Name);
6621
+ const parsed = parse(p.Name, PREFIX.STAGE);
6608
6622
  if (!result[parsed.id])
6609
6623
  result[parsed.id] = {};
6610
6624
  result[parsed.id].value = p.Value;
6611
6625
  }
6612
6626
  for await (const p of scan(PREFIX.FALLBACK + "Secret")) {
6613
- const parsed = parse(p.Name);
6627
+ const parsed = parse(p.Name, PREFIX.FALLBACK);
6614
6628
  if (!result[parsed.id])
6615
6629
  result[parsed.id] = {};
6616
6630
  result[parsed.id].fallback = p.Value;
@@ -6721,7 +6735,7 @@ var init_config = __esm({
6721
6735
  PREFIX = {
6722
6736
  get STAGE() {
6723
6737
  const project = useProject();
6724
- return `/sst/${project.config.name}/${project.config.stage}/`;
6738
+ return project.config.ssmPrefix;
6725
6739
  },
6726
6740
  get FALLBACK() {
6727
6741
  const project = useProject();