sst 2.5.5 → 2.5.7
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/commands/bind.js +17 -5
- package/config.js +10 -10
- package/constructs/SsrSite.js +5 -1
- package/credentials.js +1 -0
- package/package.json +2 -1
- package/project.d.ts +6 -0
- package/project.js +28 -6
- package/sst.mjs +48 -24
package/cli/commands/bind.js
CHANGED
|
@@ -8,6 +8,7 @@ export const bind = (program) => program
|
|
|
8
8
|
.example(`sst bind "vitest run"`, "Bind your resources to your tests")
|
|
9
9
|
.example(`sst bind "tsx scripts/myscript.ts"`, "Bind your resources to a script"), async (args) => {
|
|
10
10
|
const { spawn } = await import("child_process");
|
|
11
|
+
const kill = await import("tree-kill");
|
|
11
12
|
const { useProject } = await import("../../project.js");
|
|
12
13
|
const { useBus } = await import("../../bus.js");
|
|
13
14
|
const { useIOT } = await import("../../iot.js");
|
|
@@ -112,7 +113,7 @@ export const bind = (program) => program
|
|
|
112
113
|
Colors.line(`\n`, `Your AWS session is about to expire. Creating a new session and restarting \`${command}\`...`);
|
|
113
114
|
bindSite("iam_expired");
|
|
114
115
|
}, expireAt - Date.now());
|
|
115
|
-
runCommand({
|
|
116
|
+
await runCommand({
|
|
116
117
|
...siteConfig.envs,
|
|
117
118
|
AWS_ACCESS_KEY_ID: credentials.AccessKeyId,
|
|
118
119
|
AWS_SECRET_ACCESS_KEY: credentials.SecretAccessKey,
|
|
@@ -122,14 +123,14 @@ export const bind = (program) => program
|
|
|
122
123
|
}
|
|
123
124
|
}
|
|
124
125
|
// Fallback to use local IAM credentials
|
|
125
|
-
runCommand({
|
|
126
|
+
await runCommand({
|
|
126
127
|
...siteConfig.envs,
|
|
127
128
|
...(await localIamCredentials()),
|
|
128
129
|
});
|
|
129
130
|
}
|
|
130
131
|
async function bindScript() {
|
|
131
132
|
const { Config } = await import("../../config.js");
|
|
132
|
-
runCommand({
|
|
133
|
+
await runCommand({
|
|
133
134
|
...(await Config.env()),
|
|
134
135
|
...(await localIamCredentials()),
|
|
135
136
|
});
|
|
@@ -234,10 +235,21 @@ export const bind = (program) => program
|
|
|
234
235
|
AWS_SESSION_TOKEN: credentials.sessionToken,
|
|
235
236
|
};
|
|
236
237
|
}
|
|
237
|
-
function runCommand(envs) {
|
|
238
|
+
async function runCommand(envs) {
|
|
238
239
|
Colors.gap();
|
|
239
240
|
if (p) {
|
|
240
|
-
p.
|
|
241
|
+
p.removeAllListeners("exit");
|
|
242
|
+
// Note: calling p.kill() does not kill child processes. And in the
|
|
243
|
+
// cases of Next.js and CRA, servers are child processes. Need to
|
|
244
|
+
// kill the entire process tree to free up port ie. 3000.
|
|
245
|
+
await new Promise((resolve, reject) => {
|
|
246
|
+
kill.default(p?.pid, (error) => {
|
|
247
|
+
if (error) {
|
|
248
|
+
return reject(error);
|
|
249
|
+
}
|
|
250
|
+
resolve(true);
|
|
251
|
+
});
|
|
252
|
+
});
|
|
241
253
|
}
|
|
242
254
|
p = spawn(command, {
|
|
243
255
|
env: {
|
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
|
|
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[
|
|
192
|
-
id: parts[
|
|
193
|
-
prop: parts.slice(
|
|
191
|
+
type: parts[0],
|
|
192
|
+
id: parts[1],
|
|
193
|
+
prop: parts.slice(2).join("/"),
|
|
194
194
|
};
|
|
195
195
|
}
|
|
196
196
|
async function restartFunction(arn) {
|
package/constructs/SsrSite.js
CHANGED
|
@@ -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/credentials.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sst",
|
|
3
|
-
"version": "2.5.
|
|
3
|
+
"version": "2.5.7",
|
|
4
4
|
"bin": {
|
|
5
5
|
"sst": "cli/sst.js"
|
|
6
6
|
},
|
|
@@ -80,6 +80,7 @@
|
|
|
80
80
|
"react": "18.2.0",
|
|
81
81
|
"remeda": "^1.3.0",
|
|
82
82
|
"sst-aws-cdk": "2.62.2-3",
|
|
83
|
+
"tree-kill": "^1.2.2",
|
|
83
84
|
"undici": "^5.12.0",
|
|
84
85
|
"uuid": "^9.0.0",
|
|
85
86
|
"ws": "^8.11.0",
|
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
|
-
|
|
127
|
-
const suggested = os.userInfo().username;
|
|
128
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
433
|
-
suggested
|
|
434
|
-
)}: `,
|
|
434
|
+
`${instruction} Or hit enter to use ${blue(suggested)}: `,
|
|
435
435
|
async (input) => {
|
|
436
436
|
rl.close();
|
|
437
|
-
const result = input
|
|
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
|
-
|
|
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
|
|
|
@@ -1587,6 +1601,7 @@ function useAWSClient(client, force = false) {
|
|
|
1587
1601
|
retryDecider: (e) => {
|
|
1588
1602
|
if (e.code === "ENOTFOUND") {
|
|
1589
1603
|
printNoInternet();
|
|
1604
|
+
return true;
|
|
1590
1605
|
}
|
|
1591
1606
|
if ([
|
|
1592
1607
|
"ThrottlingException",
|
|
@@ -6523,12 +6538,12 @@ async function* scan(prefix) {
|
|
|
6523
6538
|
token = results.NextToken;
|
|
6524
6539
|
}
|
|
6525
6540
|
}
|
|
6526
|
-
function parse(ssmName) {
|
|
6527
|
-
const parts = ssmName.split("/");
|
|
6541
|
+
function parse(ssmName, prefix) {
|
|
6542
|
+
const parts = ssmName.substring(prefix.length).split("/");
|
|
6528
6543
|
return {
|
|
6529
|
-
type: parts[
|
|
6530
|
-
id: parts[
|
|
6531
|
-
prop: parts.slice(
|
|
6544
|
+
type: parts[0],
|
|
6545
|
+
id: parts[1],
|
|
6546
|
+
prop: parts.slice(2).join("/")
|
|
6532
6547
|
};
|
|
6533
6548
|
}
|
|
6534
6549
|
async function restartFunction(arn) {
|
|
@@ -6569,7 +6584,7 @@ var init_config = __esm({
|
|
|
6569
6584
|
async function parameters() {
|
|
6570
6585
|
const result = [];
|
|
6571
6586
|
for await (const p of scan(PREFIX.FALLBACK)) {
|
|
6572
|
-
const parsed = parse(p.Name);
|
|
6587
|
+
const parsed = parse(p.Name, PREFIX.FALLBACK);
|
|
6573
6588
|
if (parsed.type === "secrets")
|
|
6574
6589
|
continue;
|
|
6575
6590
|
result.push({
|
|
@@ -6578,7 +6593,7 @@ var init_config = __esm({
|
|
|
6578
6593
|
});
|
|
6579
6594
|
}
|
|
6580
6595
|
for await (const p of scan(PREFIX.STAGE)) {
|
|
6581
|
-
const parsed = parse(p.Name);
|
|
6596
|
+
const parsed = parse(p.Name, PREFIX.STAGE);
|
|
6582
6597
|
if (parsed.type === "secrets")
|
|
6583
6598
|
continue;
|
|
6584
6599
|
result.push({
|
|
@@ -6604,13 +6619,13 @@ var init_config = __esm({
|
|
|
6604
6619
|
async function secrets2() {
|
|
6605
6620
|
const result = {};
|
|
6606
6621
|
for await (const p of scan(PREFIX.STAGE + "Secret")) {
|
|
6607
|
-
const parsed = parse(p.Name);
|
|
6622
|
+
const parsed = parse(p.Name, PREFIX.STAGE);
|
|
6608
6623
|
if (!result[parsed.id])
|
|
6609
6624
|
result[parsed.id] = {};
|
|
6610
6625
|
result[parsed.id].value = p.Value;
|
|
6611
6626
|
}
|
|
6612
6627
|
for await (const p of scan(PREFIX.FALLBACK + "Secret")) {
|
|
6613
|
-
const parsed = parse(p.Name);
|
|
6628
|
+
const parsed = parse(p.Name, PREFIX.FALLBACK);
|
|
6614
6629
|
if (!result[parsed.id])
|
|
6615
6630
|
result[parsed.id] = {};
|
|
6616
6631
|
result[parsed.id].fallback = p.Value;
|
|
@@ -6721,7 +6736,7 @@ var init_config = __esm({
|
|
|
6721
6736
|
PREFIX = {
|
|
6722
6737
|
get STAGE() {
|
|
6723
6738
|
const project = useProject();
|
|
6724
|
-
return
|
|
6739
|
+
return project.config.ssmPrefix;
|
|
6725
6740
|
},
|
|
6726
6741
|
get FALLBACK() {
|
|
6727
6742
|
const project = useProject();
|
|
@@ -7129,6 +7144,7 @@ var bind = (program2) => program2.command(
|
|
|
7129
7144
|
),
|
|
7130
7145
|
async (args) => {
|
|
7131
7146
|
const { spawn: spawn7 } = await import("child_process");
|
|
7147
|
+
const kill = await import("tree-kill");
|
|
7132
7148
|
const { useProject: useProject2 } = await Promise.resolve().then(() => (init_project(), project_exports));
|
|
7133
7149
|
const { useBus: useBus2 } = await Promise.resolve().then(() => (init_bus(), bus_exports));
|
|
7134
7150
|
const { useIOT: useIOT2 } = await Promise.resolve().then(() => (init_iot(), iot_exports));
|
|
@@ -7256,7 +7272,7 @@ var bind = (program2) => program2.command(
|
|
|
7256
7272
|
);
|
|
7257
7273
|
bindSite("iam_expired");
|
|
7258
7274
|
}, expireAt - Date.now());
|
|
7259
|
-
runCommand({
|
|
7275
|
+
await runCommand({
|
|
7260
7276
|
...siteConfig.envs,
|
|
7261
7277
|
AWS_ACCESS_KEY_ID: credentials.AccessKeyId,
|
|
7262
7278
|
AWS_SECRET_ACCESS_KEY: credentials.SecretAccessKey,
|
|
@@ -7265,14 +7281,14 @@ var bind = (program2) => program2.command(
|
|
|
7265
7281
|
return;
|
|
7266
7282
|
}
|
|
7267
7283
|
}
|
|
7268
|
-
runCommand({
|
|
7284
|
+
await runCommand({
|
|
7269
7285
|
...siteConfig.envs,
|
|
7270
7286
|
...await localIamCredentials()
|
|
7271
7287
|
});
|
|
7272
7288
|
}
|
|
7273
7289
|
async function bindScript() {
|
|
7274
7290
|
const { Config: Config2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
7275
|
-
runCommand({
|
|
7291
|
+
await runCommand({
|
|
7276
7292
|
...await Config2.env(),
|
|
7277
7293
|
...await localIamCredentials()
|
|
7278
7294
|
});
|
|
@@ -7371,10 +7387,18 @@ var bind = (program2) => program2.command(
|
|
|
7371
7387
|
AWS_SESSION_TOKEN: credentials.sessionToken
|
|
7372
7388
|
};
|
|
7373
7389
|
}
|
|
7374
|
-
function runCommand(envs) {
|
|
7390
|
+
async function runCommand(envs) {
|
|
7375
7391
|
Colors2.gap();
|
|
7376
7392
|
if (p) {
|
|
7377
|
-
p.
|
|
7393
|
+
p.removeAllListeners("exit");
|
|
7394
|
+
await new Promise((resolve, reject) => {
|
|
7395
|
+
kill.default(p?.pid, (error2) => {
|
|
7396
|
+
if (error2) {
|
|
7397
|
+
return reject(error2);
|
|
7398
|
+
}
|
|
7399
|
+
resolve(true);
|
|
7400
|
+
});
|
|
7401
|
+
});
|
|
7378
7402
|
}
|
|
7379
7403
|
p = spawn7(command, {
|
|
7380
7404
|
env: {
|