@awsless/awsless 0.0.1
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/dist/bin.cjs +2411 -0
- package/dist/bin.d.ts +1 -0
- package/dist/bin.js +2389 -0
- package/dist/chunk-6KILQ5DR.js +15 -0
- package/dist/index.cjs +32 -0
- package/dist/index.d.ts +989 -0
- package/dist/index.js +6 -0
- package/package.json +57 -0
package/dist/bin.js
ADDED
|
@@ -0,0 +1,2389 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
__require,
|
|
4
|
+
definePlugin
|
|
5
|
+
} from "./chunk-6KILQ5DR.js";
|
|
6
|
+
|
|
7
|
+
// src/cli/program.ts
|
|
8
|
+
import { Command } from "commander";
|
|
9
|
+
|
|
10
|
+
// src/app.ts
|
|
11
|
+
import { App as App4, DefaultStackSynthesizer } from "aws-cdk-lib";
|
|
12
|
+
|
|
13
|
+
// src/stack.ts
|
|
14
|
+
import { Arn, Stack } from "aws-cdk-lib";
|
|
15
|
+
import { PolicyStatement } from "aws-cdk-lib/aws-iam";
|
|
16
|
+
|
|
17
|
+
// src/cli/style.ts
|
|
18
|
+
import chalk from "chalk";
|
|
19
|
+
var symbol = {
|
|
20
|
+
info: "\u2139",
|
|
21
|
+
success: "\u2714",
|
|
22
|
+
warning: "\u26A0",
|
|
23
|
+
question: "?",
|
|
24
|
+
error: "\u2716",
|
|
25
|
+
ellipsis: "\u2026",
|
|
26
|
+
pointerSmall: "\u203A",
|
|
27
|
+
// line: '─',
|
|
28
|
+
pointer: "\u276F"
|
|
29
|
+
};
|
|
30
|
+
var style = {
|
|
31
|
+
primary: chalk.bold.hex("#FF9000"),
|
|
32
|
+
// title: chalk.white,
|
|
33
|
+
normal: chalk.white,
|
|
34
|
+
label: chalk.white.bold,
|
|
35
|
+
placeholder: chalk.dim,
|
|
36
|
+
link: chalk.cyan,
|
|
37
|
+
info: chalk.blue,
|
|
38
|
+
success: chalk.green,
|
|
39
|
+
warning: chalk.yellow,
|
|
40
|
+
error: chalk.red,
|
|
41
|
+
attr: chalk.yellow,
|
|
42
|
+
cursor: chalk.bgWhite.blackBright
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// src/cli/logger.ts
|
|
46
|
+
var queue = [];
|
|
47
|
+
var debugError = (error) => {
|
|
48
|
+
queue.push({
|
|
49
|
+
date: /* @__PURE__ */ new Date(),
|
|
50
|
+
type: style.error.dim("error"),
|
|
51
|
+
// color: 'red',
|
|
52
|
+
// type: 'error',
|
|
53
|
+
message: typeof error === "string" ? error : error instanceof Error ? style.error(error.message || "") : JSON.stringify(error)
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
var debug = (...parts) => {
|
|
57
|
+
queue.push({
|
|
58
|
+
date: /* @__PURE__ */ new Date(),
|
|
59
|
+
type: style.warning.dim("debug"),
|
|
60
|
+
// color: 'yellow',
|
|
61
|
+
// type: 'debug',
|
|
62
|
+
message: parts.map((part) => typeof part === "string" ? part : JSON.stringify(part)).join(" ")
|
|
63
|
+
});
|
|
64
|
+
};
|
|
65
|
+
var flushDebug = () => {
|
|
66
|
+
return queue.splice(0, queue.length);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// src/util/param.ts
|
|
70
|
+
import { DeleteParameterCommand, GetParameterCommand, GetParametersByPathCommand, ParameterType, PutParameterCommand, SSMClient } from "@aws-sdk/client-ssm";
|
|
71
|
+
var configParameterPrefix = (config2) => {
|
|
72
|
+
return `/${config2.stage}/awsless/${config2.name}`;
|
|
73
|
+
};
|
|
74
|
+
var Params = class {
|
|
75
|
+
constructor(config2) {
|
|
76
|
+
this.config = config2;
|
|
77
|
+
this.client = new SSMClient({
|
|
78
|
+
credentials: config2.credentials,
|
|
79
|
+
region: config2.region
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
client;
|
|
83
|
+
getName(name) {
|
|
84
|
+
return `${configParameterPrefix(this.config)}/${name}`;
|
|
85
|
+
}
|
|
86
|
+
async get(name) {
|
|
87
|
+
debug("Get remote config value");
|
|
88
|
+
debug("Name:", style.info(name));
|
|
89
|
+
let result;
|
|
90
|
+
try {
|
|
91
|
+
result = await this.client.send(new GetParameterCommand({
|
|
92
|
+
Name: this.getName(name),
|
|
93
|
+
WithDecryption: true
|
|
94
|
+
}));
|
|
95
|
+
} catch (error) {
|
|
96
|
+
if (error instanceof Error && error.name === "ParameterNotFound") {
|
|
97
|
+
debug("Parameter not found");
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
throw error;
|
|
101
|
+
}
|
|
102
|
+
const value = result.Parameter?.Value;
|
|
103
|
+
debug("Value:", style.info(value));
|
|
104
|
+
debug("Done getting remote config value");
|
|
105
|
+
return value;
|
|
106
|
+
}
|
|
107
|
+
async set(name, value) {
|
|
108
|
+
debug("Save remote config value");
|
|
109
|
+
debug("Name:", style.info(name));
|
|
110
|
+
debug("Value:", style.info(value));
|
|
111
|
+
await this.client.send(new PutParameterCommand({
|
|
112
|
+
Type: ParameterType.STRING,
|
|
113
|
+
Name: this.getName(name),
|
|
114
|
+
Value: value,
|
|
115
|
+
Overwrite: true
|
|
116
|
+
}));
|
|
117
|
+
debug("Done saving remote config value");
|
|
118
|
+
}
|
|
119
|
+
async delete(name) {
|
|
120
|
+
debug("Delete remote config value");
|
|
121
|
+
debug("Name:", style.info(name));
|
|
122
|
+
try {
|
|
123
|
+
await this.client.send(new DeleteParameterCommand({
|
|
124
|
+
Name: this.getName(name)
|
|
125
|
+
}));
|
|
126
|
+
} catch (error) {
|
|
127
|
+
if (error instanceof Error && error.name === "ParameterNotFound") {
|
|
128
|
+
debug("Remote config value was already deleted");
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
throw error;
|
|
132
|
+
}
|
|
133
|
+
debug("Done deleting remote config value");
|
|
134
|
+
}
|
|
135
|
+
async list() {
|
|
136
|
+
debug("Load remote config values");
|
|
137
|
+
const result = await this.client.send(new GetParametersByPathCommand({
|
|
138
|
+
Path: configParameterPrefix(this.config),
|
|
139
|
+
WithDecryption: true,
|
|
140
|
+
MaxResults: 10,
|
|
141
|
+
Recursive: true
|
|
142
|
+
}));
|
|
143
|
+
debug("Done loading remote config values");
|
|
144
|
+
const values = {};
|
|
145
|
+
result.Parameters?.forEach((param) => {
|
|
146
|
+
const name = param.Name.substring(configParameterPrefix(this.config).length).substring(1);
|
|
147
|
+
values[name] = param.Value || "";
|
|
148
|
+
});
|
|
149
|
+
return values;
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
// src/stack.ts
|
|
154
|
+
var toStack = ({ config: config2, assets, app, stackConfig, plugins }) => {
|
|
155
|
+
const stackName = `${config2.name}-${stackConfig.name}`;
|
|
156
|
+
const stack = new Stack(app, stackConfig.name, {
|
|
157
|
+
stackName,
|
|
158
|
+
tags: {
|
|
159
|
+
APP: config2.name,
|
|
160
|
+
STAGE: config2.stage,
|
|
161
|
+
STACK: stackConfig.name
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
debug("Define stack:", style.info(stackConfig.name));
|
|
165
|
+
const bindings = [];
|
|
166
|
+
const bind = (cb) => {
|
|
167
|
+
bindings.push(cb);
|
|
168
|
+
};
|
|
169
|
+
debug("Run plugin onStack listeners");
|
|
170
|
+
const functions = plugins.map((plugin) => plugin.onStack?.({
|
|
171
|
+
config: config2,
|
|
172
|
+
assets,
|
|
173
|
+
app,
|
|
174
|
+
stack,
|
|
175
|
+
stackConfig,
|
|
176
|
+
bind
|
|
177
|
+
})).filter(Boolean).flat().filter(Boolean);
|
|
178
|
+
if (stack.node.children.length === 0) {
|
|
179
|
+
throw new Error(`Stack ${style.info(stackConfig.name)} has no resources defined`);
|
|
180
|
+
}
|
|
181
|
+
bindings.forEach((cb) => functions.forEach(cb));
|
|
182
|
+
const allowConfigParameters = new PolicyStatement({
|
|
183
|
+
actions: [
|
|
184
|
+
"ssm:GetParameter",
|
|
185
|
+
"ssm:GetParameters",
|
|
186
|
+
"ssm:GetParametersByPath"
|
|
187
|
+
],
|
|
188
|
+
resources: [
|
|
189
|
+
Arn.format({
|
|
190
|
+
service: "ssm",
|
|
191
|
+
resource: "parameter",
|
|
192
|
+
resourceName: configParameterPrefix(config2)
|
|
193
|
+
})
|
|
194
|
+
// Fn.sub('arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter' + configParameterPrefix(config)),
|
|
195
|
+
]
|
|
196
|
+
});
|
|
197
|
+
functions.forEach((lambda) => lambda.addToRolePolicy(allowConfigParameters));
|
|
198
|
+
return {
|
|
199
|
+
stack,
|
|
200
|
+
depends: stackConfig.depends
|
|
201
|
+
};
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
// src/util/path.ts
|
|
205
|
+
import { join } from "path";
|
|
206
|
+
var rootDir = process.cwd();
|
|
207
|
+
var outDir = join(rootDir, ".awsless");
|
|
208
|
+
var assemblyDir = join(outDir, "assembly");
|
|
209
|
+
var functionDir = join(outDir, "function");
|
|
210
|
+
|
|
211
|
+
// src/stack/app-bootstrap.ts
|
|
212
|
+
import { Stack as Stack3 } from "aws-cdk-lib";
|
|
213
|
+
|
|
214
|
+
// src/plugins/cron/index.ts
|
|
215
|
+
import { z as z10 } from "zod";
|
|
216
|
+
|
|
217
|
+
// src/plugins/cron/schema/schedule.ts
|
|
218
|
+
import { Schedule } from "aws-cdk-lib/aws-events";
|
|
219
|
+
import { z } from "zod";
|
|
220
|
+
import { awsCronExpressionValidator } from "aws-cron-expression-validator";
|
|
221
|
+
var RateExpressionSchema = z.custom((value) => {
|
|
222
|
+
return z.string().regex(/rate\([0-9]+ (seconds?|minutes?|hours?|days?)\)/).refine((rate) => {
|
|
223
|
+
const [str] = rate.substring(5).split(" ");
|
|
224
|
+
const number = parseInt(str);
|
|
225
|
+
return number > 0;
|
|
226
|
+
}).safeParse(value).success;
|
|
227
|
+
}, "Invalid rate expression").transform(Schedule.expression);
|
|
228
|
+
var CronExpressionSchema = z.custom((value) => {
|
|
229
|
+
return z.string().startsWith("cron(").endsWith(")").safeParse(value).success;
|
|
230
|
+
}, "Invalid cron expression").superRefine((value, ctx) => {
|
|
231
|
+
const cron = value.substring(5, value.length - 1);
|
|
232
|
+
try {
|
|
233
|
+
awsCronExpressionValidator(cron);
|
|
234
|
+
} catch (error) {
|
|
235
|
+
if (error instanceof Error) {
|
|
236
|
+
ctx.addIssue({
|
|
237
|
+
code: z.ZodIssueCode.custom,
|
|
238
|
+
message: error.message
|
|
239
|
+
});
|
|
240
|
+
} else {
|
|
241
|
+
ctx.addIssue({
|
|
242
|
+
code: z.ZodIssueCode.custom,
|
|
243
|
+
message: "Invalid cron expression"
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}).transform(Schedule.expression);
|
|
248
|
+
var ScheduleExpressionSchema = RateExpressionSchema.or(CronExpressionSchema);
|
|
249
|
+
|
|
250
|
+
// src/plugins/cron/index.ts
|
|
251
|
+
import { Rule } from "aws-cdk-lib/aws-events";
|
|
252
|
+
|
|
253
|
+
// src/util/resource.ts
|
|
254
|
+
import { constantCase, paramCase, pascalCase } from "change-case";
|
|
255
|
+
var toId = (resource, id) => {
|
|
256
|
+
return pascalCase(`${resource}-${id}`);
|
|
257
|
+
};
|
|
258
|
+
var toName = (stack, id) => {
|
|
259
|
+
return paramCase(`${stack.stackName}-${id}`);
|
|
260
|
+
};
|
|
261
|
+
var toEnvKey = (resource, id) => {
|
|
262
|
+
return constantCase(`RESOURCE_${resource}_${id}`);
|
|
263
|
+
};
|
|
264
|
+
var addResourceEnvironment = (stack, resource, id, lambda) => {
|
|
265
|
+
const key = toEnvKey(resource, id);
|
|
266
|
+
const value = toName(stack, id);
|
|
267
|
+
lambda.addEnvironment(key, value, {
|
|
268
|
+
removeInEdge: true
|
|
269
|
+
});
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
// src/plugins/function/index.ts
|
|
273
|
+
import { z as z9 } from "zod";
|
|
274
|
+
|
|
275
|
+
// src/schema/duration.ts
|
|
276
|
+
import { z as z2 } from "zod";
|
|
277
|
+
import { Duration as CDKDuration } from "aws-cdk-lib/core";
|
|
278
|
+
function toDuration(duration) {
|
|
279
|
+
const [count, unit] = duration.split(" ");
|
|
280
|
+
const countNum = parseInt(count);
|
|
281
|
+
const unitLower = unit.toLowerCase();
|
|
282
|
+
if (unitLower.startsWith("second")) {
|
|
283
|
+
return CDKDuration.seconds(countNum);
|
|
284
|
+
} else if (unitLower.startsWith("minute")) {
|
|
285
|
+
return CDKDuration.minutes(countNum);
|
|
286
|
+
} else if (unitLower.startsWith("hour")) {
|
|
287
|
+
return CDKDuration.hours(countNum);
|
|
288
|
+
} else if (unitLower.startsWith("day")) {
|
|
289
|
+
return CDKDuration.days(countNum);
|
|
290
|
+
}
|
|
291
|
+
return CDKDuration.days(0);
|
|
292
|
+
}
|
|
293
|
+
var DurationSchema = z2.custom((value) => {
|
|
294
|
+
return z2.string().regex(/[0-9]+ (seconds?|minutes?|hours?|days?)/).safeParse(value).success;
|
|
295
|
+
}, "Invalid duration").transform(toDuration);
|
|
296
|
+
|
|
297
|
+
// src/schema/local-file.ts
|
|
298
|
+
import { access, constants } from "fs/promises";
|
|
299
|
+
import { z as z3 } from "zod";
|
|
300
|
+
var LocalFileSchema = z3.string().refine(async (path) => {
|
|
301
|
+
try {
|
|
302
|
+
await access(path, constants.R_OK);
|
|
303
|
+
} catch (error) {
|
|
304
|
+
return false;
|
|
305
|
+
}
|
|
306
|
+
return true;
|
|
307
|
+
}, `File doesn't exist`);
|
|
308
|
+
|
|
309
|
+
// src/plugins/function/index.ts
|
|
310
|
+
import { Code, Function } from "aws-cdk-lib/aws-lambda";
|
|
311
|
+
|
|
312
|
+
// src/plugins/function/schema/runtime.ts
|
|
313
|
+
import { Runtime as CdkRuntime } from "aws-cdk-lib/aws-lambda";
|
|
314
|
+
import { z as z4 } from "zod";
|
|
315
|
+
var runtimes = {
|
|
316
|
+
"container": CdkRuntime.FROM_IMAGE,
|
|
317
|
+
"rust": CdkRuntime.PROVIDED_AL2,
|
|
318
|
+
"nodejs16.x": CdkRuntime.NODEJS_16_X,
|
|
319
|
+
"nodejs18.x": CdkRuntime.NODEJS_18_X,
|
|
320
|
+
"python3.9": CdkRuntime.PYTHON_3_9,
|
|
321
|
+
"python3.10": CdkRuntime.PYTHON_3_10,
|
|
322
|
+
"go1.x": CdkRuntime.PROVIDED_AL2,
|
|
323
|
+
"go": CdkRuntime.PROVIDED_AL2
|
|
324
|
+
};
|
|
325
|
+
var toRuntime = (runtime) => {
|
|
326
|
+
return runtimes[runtime];
|
|
327
|
+
};
|
|
328
|
+
var RuntimeSchema = z4.enum(Object.keys(runtimes)).transform(toRuntime);
|
|
329
|
+
|
|
330
|
+
// src/plugins/function/schema/architecture.ts
|
|
331
|
+
import { Architecture as CdkArchitecture } from "aws-cdk-lib/aws-lambda";
|
|
332
|
+
import { z as z5 } from "zod";
|
|
333
|
+
var toArchitecture = (architecture) => {
|
|
334
|
+
return architecture === "x86_64" ? CdkArchitecture.X86_64 : CdkArchitecture.ARM_64;
|
|
335
|
+
};
|
|
336
|
+
var ArchitectureSchema = z5.enum(["x86_64", "arm_64"]).transform(toArchitecture);
|
|
337
|
+
|
|
338
|
+
// src/schema/resource-id.ts
|
|
339
|
+
import { z as z6 } from "zod";
|
|
340
|
+
var ResourceIdSchema = z6.string().min(3).max(24).regex(/[a-z\-]+/, "Invalid resource ID");
|
|
341
|
+
|
|
342
|
+
// src/schema/size.ts
|
|
343
|
+
import { Size as CDKSize } from "aws-cdk-lib/core";
|
|
344
|
+
import { z as z7 } from "zod";
|
|
345
|
+
function toSize(size) {
|
|
346
|
+
const [count, unit] = size.split(" ");
|
|
347
|
+
const countNum = parseInt(count);
|
|
348
|
+
if (unit === "KB") {
|
|
349
|
+
return CDKSize.kibibytes(countNum);
|
|
350
|
+
} else if (unit === "MB") {
|
|
351
|
+
return CDKSize.mebibytes(countNum);
|
|
352
|
+
} else if (unit === "GB") {
|
|
353
|
+
return CDKSize.gibibytes(countNum);
|
|
354
|
+
}
|
|
355
|
+
throw new TypeError(`Invalid size ${size}`);
|
|
356
|
+
}
|
|
357
|
+
var SizeSchema = z7.custom((value) => {
|
|
358
|
+
return z7.string().regex(/[0-9]+ (KB|MB|GB)/).safeParse(value).success;
|
|
359
|
+
}, "Invalid size").transform(toSize);
|
|
360
|
+
|
|
361
|
+
// src/plugins/function/util/build-worker.ts
|
|
362
|
+
import { Worker } from "worker_threads";
|
|
363
|
+
var cjs = typeof __require !== "undefined";
|
|
364
|
+
var importESM = `
|
|
365
|
+
import { bundle } from "@awsless/code";
|
|
366
|
+
import { createHash } from "crypto";
|
|
367
|
+
import { parentPort, workerData } from "worker_threads";
|
|
368
|
+
`;
|
|
369
|
+
var importCJS = `
|
|
370
|
+
const { bundle } = require("@awsless/code");
|
|
371
|
+
const { createHash } = require("crypto");
|
|
372
|
+
const { parentPort, workerData } = require("worker_threads");
|
|
373
|
+
`;
|
|
374
|
+
var workerCode = `
|
|
375
|
+
${cjs ? importCJS : importESM}
|
|
376
|
+
|
|
377
|
+
const build = async (file) => {
|
|
378
|
+
const { code, map } = await bundle(file, {
|
|
379
|
+
format: 'esm',
|
|
380
|
+
sourceMap: true,
|
|
381
|
+
minimize: true,
|
|
382
|
+
onwarn: () => {},
|
|
383
|
+
moduleSideEffects: (id) => file === id,
|
|
384
|
+
external: (importee) => (
|
|
385
|
+
importee.startsWith('aws-sdk') ||
|
|
386
|
+
importee.startsWith('@aws-sdk')
|
|
387
|
+
),
|
|
388
|
+
})
|
|
389
|
+
|
|
390
|
+
const hash = createHash('sha1').update(code).digest('hex')
|
|
391
|
+
|
|
392
|
+
parentPort.postMessage(JSON.stringify({
|
|
393
|
+
handler: 'index.default',
|
|
394
|
+
hash,
|
|
395
|
+
files: [
|
|
396
|
+
{ name: 'index.js', code, map: map?.toString() }
|
|
397
|
+
]
|
|
398
|
+
}))
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
build(workerData)
|
|
402
|
+
`;
|
|
403
|
+
var defaultBuild = async (file) => {
|
|
404
|
+
return new Promise((resolve, reject) => {
|
|
405
|
+
const worker = new Worker(workerCode, { workerData: file, eval: true });
|
|
406
|
+
const cleanUp2 = () => {
|
|
407
|
+
worker.removeAllListeners();
|
|
408
|
+
worker.terminate();
|
|
409
|
+
};
|
|
410
|
+
worker.on("message", (data) => {
|
|
411
|
+
resolve(JSON.parse(data.toString("utf8")));
|
|
412
|
+
cleanUp2();
|
|
413
|
+
});
|
|
414
|
+
worker.on("error", (data) => {
|
|
415
|
+
reject(data);
|
|
416
|
+
cleanUp2();
|
|
417
|
+
});
|
|
418
|
+
worker.on("exit", (code) => {
|
|
419
|
+
if (code !== 0) {
|
|
420
|
+
reject(new Error(`Worker exited with code ${code}`));
|
|
421
|
+
cleanUp2();
|
|
422
|
+
}
|
|
423
|
+
});
|
|
424
|
+
});
|
|
425
|
+
};
|
|
426
|
+
|
|
427
|
+
// src/plugins/function/util/build.ts
|
|
428
|
+
import JSZip from "jszip";
|
|
429
|
+
import { basename, join as join2 } from "path";
|
|
430
|
+
import { mkdir, writeFile } from "fs/promises";
|
|
431
|
+
import { filesize } from "filesize";
|
|
432
|
+
var zipFiles = (files) => {
|
|
433
|
+
const zip = new JSZip();
|
|
434
|
+
for (const file of files) {
|
|
435
|
+
zip.file(file.name, file.code);
|
|
436
|
+
}
|
|
437
|
+
return zip.generateAsync({
|
|
438
|
+
type: "nodebuffer",
|
|
439
|
+
compression: "DEFLATE",
|
|
440
|
+
compressionOptions: {
|
|
441
|
+
level: 9
|
|
442
|
+
}
|
|
443
|
+
});
|
|
444
|
+
};
|
|
445
|
+
var writeBuildHash = async (config2, stack, id, hash) => {
|
|
446
|
+
const funcPath = join2(functionDir, config2.name, stack.artifactId, id);
|
|
447
|
+
const versionFile = join2(funcPath, "HASH");
|
|
448
|
+
await writeFile(versionFile, hash);
|
|
449
|
+
};
|
|
450
|
+
var writeBuildFiles = async (config2, stack, id, files) => {
|
|
451
|
+
const bundle = await zipFiles(files);
|
|
452
|
+
const funcPath = join2(functionDir, config2.name, stack.artifactId, id);
|
|
453
|
+
const filesPath = join2(funcPath, "files");
|
|
454
|
+
const bundleFile = join2(funcPath, "bundle.zip");
|
|
455
|
+
debug("Bundle size of", style.info(join2(config2.name, stack.artifactId, id)), "is", style.attr(filesize(bundle.byteLength)));
|
|
456
|
+
await mkdir(filesPath, { recursive: true });
|
|
457
|
+
await writeFile(bundleFile, bundle);
|
|
458
|
+
await Promise.all(files.map(async (file) => {
|
|
459
|
+
const fileName = join2(filesPath, file.name);
|
|
460
|
+
await mkdir(basename(fileName), { recursive: true });
|
|
461
|
+
await writeFile(fileName, file.code);
|
|
462
|
+
if (file.map) {
|
|
463
|
+
const mapName = join2(filesPath, `${file.name}.map`);
|
|
464
|
+
await writeFile(mapName, file.map);
|
|
465
|
+
}
|
|
466
|
+
}));
|
|
467
|
+
return {
|
|
468
|
+
file: bundleFile,
|
|
469
|
+
size: bundle.byteLength
|
|
470
|
+
};
|
|
471
|
+
};
|
|
472
|
+
|
|
473
|
+
// src/plugins/function/util/publish.ts
|
|
474
|
+
import { join as join3 } from "path";
|
|
475
|
+
import { readFile } from "fs/promises";
|
|
476
|
+
import { GetObjectCommand, ObjectCannedACL, PutObjectCommand, S3Client, StorageClass } from "@aws-sdk/client-s3";
|
|
477
|
+
|
|
478
|
+
// src/stack/bootstrap.ts
|
|
479
|
+
import { CfnOutput, RemovalPolicy, Stack as Stack2 } from "aws-cdk-lib";
|
|
480
|
+
import { Bucket, BucketAccessControl } from "aws-cdk-lib/aws-s3";
|
|
481
|
+
var assetBucketName = (config2) => {
|
|
482
|
+
return `awsless-bootstrap-${config2.account}-${config2.region}`;
|
|
483
|
+
};
|
|
484
|
+
var assetBucketUrl = (config2, stackName) => {
|
|
485
|
+
const bucket = assetBucketName(config2);
|
|
486
|
+
return `https://s3-${config2.region}.amazonaws.com/${bucket}/${stackName}/cloudformation.json`;
|
|
487
|
+
};
|
|
488
|
+
var version = "2";
|
|
489
|
+
var bootstrapStack = (config2, app) => {
|
|
490
|
+
const stack = new Stack2(app, "bootstrap", {
|
|
491
|
+
stackName: `awsless-bootstrap`
|
|
492
|
+
});
|
|
493
|
+
new Bucket(stack, "assets", {
|
|
494
|
+
bucketName: assetBucketName(config2),
|
|
495
|
+
versioned: true,
|
|
496
|
+
accessControl: BucketAccessControl.PRIVATE,
|
|
497
|
+
removalPolicy: RemovalPolicy.DESTROY
|
|
498
|
+
});
|
|
499
|
+
new CfnOutput(stack, "version", {
|
|
500
|
+
exportName: "version",
|
|
501
|
+
value: version
|
|
502
|
+
});
|
|
503
|
+
return stack;
|
|
504
|
+
};
|
|
505
|
+
var shouldDeployBootstrap = async (client, name) => {
|
|
506
|
+
debug("Check bootstrap status");
|
|
507
|
+
const info = await client.get(name);
|
|
508
|
+
return !info || info.outputs.version !== version || !["CREATE_COMPLETE", "UPDATE_COMPLETE"].includes(info.status);
|
|
509
|
+
};
|
|
510
|
+
|
|
511
|
+
// src/plugins/function/util/publish.ts
|
|
512
|
+
var publishFunctionAsset = async (config2, stack, id) => {
|
|
513
|
+
const bucket = assetBucketName(config2);
|
|
514
|
+
const key = `${config2.name}/${stack.artifactId}/function/${id}.zip`;
|
|
515
|
+
const funcPath = join3(functionDir, config2.name, stack.artifactId, id);
|
|
516
|
+
const bundleFile = join3(funcPath, "bundle.zip");
|
|
517
|
+
const hashFile = join3(funcPath, "HASH");
|
|
518
|
+
const hash = await readFile(hashFile, "utf8");
|
|
519
|
+
const file = await readFile(bundleFile);
|
|
520
|
+
const client = new S3Client({
|
|
521
|
+
credentials: config2.credentials,
|
|
522
|
+
region: config2.region
|
|
523
|
+
});
|
|
524
|
+
let getResult;
|
|
525
|
+
try {
|
|
526
|
+
getResult = await client.send(new GetObjectCommand({
|
|
527
|
+
Bucket: bucket,
|
|
528
|
+
Key: key
|
|
529
|
+
}));
|
|
530
|
+
} catch (error) {
|
|
531
|
+
if (error instanceof Error && error.name === "NoSuchKey") {
|
|
532
|
+
} else {
|
|
533
|
+
throw error;
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
if (getResult?.Metadata?.hash === hash) {
|
|
537
|
+
return getResult.VersionId;
|
|
538
|
+
}
|
|
539
|
+
const putResult = await client.send(new PutObjectCommand({
|
|
540
|
+
Bucket: bucket,
|
|
541
|
+
Key: key,
|
|
542
|
+
Body: file,
|
|
543
|
+
ACL: ObjectCannedACL.private,
|
|
544
|
+
StorageClass: StorageClass.STANDARD,
|
|
545
|
+
Metadata: {
|
|
546
|
+
hash
|
|
547
|
+
}
|
|
548
|
+
}));
|
|
549
|
+
return putResult.VersionId;
|
|
550
|
+
};
|
|
551
|
+
|
|
552
|
+
// src/plugins/function/schema/retry-attempts.ts
|
|
553
|
+
import { z as z8 } from "zod";
|
|
554
|
+
var RetryAttempts = z8.number().int().min(0).max(2);
|
|
555
|
+
|
|
556
|
+
// src/util/byte-size.ts
|
|
557
|
+
import { filesize as filesize2 } from "filesize";
|
|
558
|
+
var formatByteSize = (size) => {
|
|
559
|
+
const [number, unit] = filesize2(size).toString().split(" ");
|
|
560
|
+
return style.attr(number) + style.attr.dim(unit);
|
|
561
|
+
};
|
|
562
|
+
|
|
563
|
+
// src/plugins/function/index.ts
|
|
564
|
+
var FunctionSchema = z9.union([
|
|
565
|
+
LocalFileSchema,
|
|
566
|
+
z9.object({
|
|
567
|
+
file: LocalFileSchema,
|
|
568
|
+
timeout: DurationSchema.optional(),
|
|
569
|
+
runtime: RuntimeSchema.optional(),
|
|
570
|
+
memorySize: SizeSchema.optional(),
|
|
571
|
+
architecture: ArchitectureSchema.optional(),
|
|
572
|
+
ephemeralStorageSize: SizeSchema.optional(),
|
|
573
|
+
retryAttempts: RetryAttempts,
|
|
574
|
+
environment: z9.record(z9.string(), z9.string()).optional()
|
|
575
|
+
})
|
|
576
|
+
]);
|
|
577
|
+
var schema = z9.object({
|
|
578
|
+
defaults: z9.object({
|
|
579
|
+
function: z9.object({
|
|
580
|
+
timeout: DurationSchema.default("10 seconds"),
|
|
581
|
+
runtime: RuntimeSchema.default("nodejs18.x"),
|
|
582
|
+
memorySize: SizeSchema.default("128 MB"),
|
|
583
|
+
architecture: ArchitectureSchema.default("arm_64"),
|
|
584
|
+
ephemeralStorageSize: SizeSchema.default("512 MB"),
|
|
585
|
+
retryAttempts: RetryAttempts.default(2),
|
|
586
|
+
environment: z9.record(z9.string(), z9.string()).optional()
|
|
587
|
+
}).default({})
|
|
588
|
+
}).default({}),
|
|
589
|
+
stacks: z9.object({
|
|
590
|
+
functions: z9.record(
|
|
591
|
+
ResourceIdSchema,
|
|
592
|
+
FunctionSchema
|
|
593
|
+
).optional()
|
|
594
|
+
}).array()
|
|
595
|
+
});
|
|
596
|
+
var functionPlugin = definePlugin({
|
|
597
|
+
name: "function",
|
|
598
|
+
schema,
|
|
599
|
+
onStack(context) {
|
|
600
|
+
return Object.entries(context.stackConfig.functions || {}).map(([id, fileOrProps]) => {
|
|
601
|
+
return toFunction(context, id, fileOrProps);
|
|
602
|
+
});
|
|
603
|
+
}
|
|
604
|
+
});
|
|
605
|
+
var toFunction = ({ config: config2, stack, stackConfig, assets }, id, fileOrProps) => {
|
|
606
|
+
const props = typeof fileOrProps === "string" ? { ...config2.defaults?.function, file: fileOrProps } : { ...config2.defaults?.function, ...fileOrProps };
|
|
607
|
+
const lambda = new Function(stack, toId("function", id), {
|
|
608
|
+
functionName: toName(stack, id),
|
|
609
|
+
handler: "index.default",
|
|
610
|
+
code: Code.fromInline("export default () => {}"),
|
|
611
|
+
...props,
|
|
612
|
+
memorySize: props.memorySize.toMebibytes()
|
|
613
|
+
});
|
|
614
|
+
lambda.addEnvironment("APP", config2.name, { removeInEdge: true });
|
|
615
|
+
lambda.addEnvironment("STAGE", config2.stage, { removeInEdge: true });
|
|
616
|
+
lambda.addEnvironment("STACK", stackConfig.name, { removeInEdge: true });
|
|
617
|
+
if (lambda.runtime.toString().startsWith("nodejs")) {
|
|
618
|
+
lambda.addEnvironment("AWS_NODEJS_CONNECTION_REUSE_ENABLED", "1", {
|
|
619
|
+
removeInEdge: true
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
assets.add({
|
|
623
|
+
stack: stackConfig,
|
|
624
|
+
resource: "function",
|
|
625
|
+
resourceName: id,
|
|
626
|
+
async build() {
|
|
627
|
+
const result = await defaultBuild(props.file);
|
|
628
|
+
const bundle = await writeBuildFiles(config2, stack, id, result.files);
|
|
629
|
+
await writeBuildHash(config2, stack, id, result.hash);
|
|
630
|
+
const func = lambda.node.defaultChild;
|
|
631
|
+
func.handler = result.handler;
|
|
632
|
+
return {
|
|
633
|
+
size: formatByteSize(bundle.size)
|
|
634
|
+
};
|
|
635
|
+
},
|
|
636
|
+
async publish() {
|
|
637
|
+
const version2 = await publishFunctionAsset(config2, stack, id);
|
|
638
|
+
const func = lambda.node.defaultChild;
|
|
639
|
+
func.code = {
|
|
640
|
+
s3Bucket: assetBucketName(config2),
|
|
641
|
+
s3Key: `${config2.name}/${stack.artifactId}/function/${id}.zip`,
|
|
642
|
+
s3ObjectVersion: version2
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
});
|
|
646
|
+
return lambda;
|
|
647
|
+
};
|
|
648
|
+
|
|
649
|
+
// src/plugins/cron/index.ts
|
|
650
|
+
import { LambdaFunction } from "aws-cdk-lib/aws-events-targets";
|
|
651
|
+
var cronPlugin = definePlugin({
|
|
652
|
+
name: "cron",
|
|
653
|
+
schema: z10.object({
|
|
654
|
+
stacks: z10.object({
|
|
655
|
+
crons: z10.record(ResourceIdSchema, z10.object({
|
|
656
|
+
consumer: FunctionSchema,
|
|
657
|
+
schedule: ScheduleExpressionSchema,
|
|
658
|
+
description: z10.string().max(512).optional()
|
|
659
|
+
})).optional()
|
|
660
|
+
}).array()
|
|
661
|
+
}),
|
|
662
|
+
onStack(context) {
|
|
663
|
+
return Object.entries(context.stackConfig.crons || {}).map(([id, props]) => {
|
|
664
|
+
const lambda = toFunction(context, id, props.consumer);
|
|
665
|
+
const target = new LambdaFunction(lambda);
|
|
666
|
+
new Rule(context.stack, toId("cron", id), {
|
|
667
|
+
ruleName: toName(context.stack, id),
|
|
668
|
+
schedule: props.schedule,
|
|
669
|
+
description: props.description,
|
|
670
|
+
targets: [target]
|
|
671
|
+
});
|
|
672
|
+
return lambda;
|
|
673
|
+
});
|
|
674
|
+
}
|
|
675
|
+
});
|
|
676
|
+
|
|
677
|
+
// src/plugins/queue.ts
|
|
678
|
+
import { z as z11 } from "zod";
|
|
679
|
+
import { Queue } from "aws-cdk-lib/aws-sqs";
|
|
680
|
+
import { SqsEventSource } from "aws-cdk-lib/aws-lambda-event-sources";
|
|
681
|
+
var queuePlugin = definePlugin({
|
|
682
|
+
name: "queue",
|
|
683
|
+
schema: z11.object({
|
|
684
|
+
defaults: z11.object({
|
|
685
|
+
queue: z11.object({
|
|
686
|
+
// fifo: z.boolean().default(false),
|
|
687
|
+
retentionPeriod: DurationSchema.default("7 days"),
|
|
688
|
+
visibilityTimeout: DurationSchema.default("30 seconds"),
|
|
689
|
+
deliveryDelay: DurationSchema.default("0 seconds"),
|
|
690
|
+
receiveMessageWaitTime: DurationSchema.default("0 seconds"),
|
|
691
|
+
maxMessageSize: SizeSchema.default("256 KB")
|
|
692
|
+
}).default({})
|
|
693
|
+
}).default({}),
|
|
694
|
+
stacks: z11.object({
|
|
695
|
+
queues: z11.record(ResourceIdSchema, z11.union([
|
|
696
|
+
LocalFileSchema,
|
|
697
|
+
z11.object({
|
|
698
|
+
consumer: FunctionSchema,
|
|
699
|
+
// fifo: z.boolean().optional(),
|
|
700
|
+
retentionPeriod: DurationSchema.optional(),
|
|
701
|
+
visibilityTimeout: DurationSchema.optional(),
|
|
702
|
+
deliveryDelay: DurationSchema.optional(),
|
|
703
|
+
receiveMessageWaitTime: DurationSchema.optional(),
|
|
704
|
+
maxMessageSize: SizeSchema.optional()
|
|
705
|
+
})
|
|
706
|
+
])).optional()
|
|
707
|
+
}).array()
|
|
708
|
+
}),
|
|
709
|
+
onStack(ctx) {
|
|
710
|
+
const { stack, config: config2, stackConfig, bind } = ctx;
|
|
711
|
+
return Object.entries(stackConfig.queues || {}).map(([id, functionOrProps]) => {
|
|
712
|
+
const props = typeof functionOrProps === "string" ? { ...config2.defaults.queue, consumer: functionOrProps } : { ...config2.defaults.queue, ...functionOrProps };
|
|
713
|
+
const queue2 = new Queue(stack, toId("queue", id), {
|
|
714
|
+
queueName: toName(stack, id),
|
|
715
|
+
...props,
|
|
716
|
+
maxMessageSizeBytes: props.maxMessageSize.toBytes()
|
|
717
|
+
});
|
|
718
|
+
const lambda = toFunction(ctx, id, props.consumer);
|
|
719
|
+
lambda.addEventSource(new SqsEventSource(queue2));
|
|
720
|
+
bind((lambda2) => {
|
|
721
|
+
queue2.grantSendMessages(lambda2);
|
|
722
|
+
addResourceEnvironment(stack, "queue", id, lambda2);
|
|
723
|
+
});
|
|
724
|
+
return lambda;
|
|
725
|
+
});
|
|
726
|
+
}
|
|
727
|
+
});
|
|
728
|
+
|
|
729
|
+
// src/plugins/table/index.ts
|
|
730
|
+
import { z as z16 } from "zod";
|
|
731
|
+
import { BillingMode, Table } from "aws-cdk-lib/aws-dynamodb";
|
|
732
|
+
|
|
733
|
+
// src/plugins/table/schema/class-type.ts
|
|
734
|
+
import { TableClass } from "aws-cdk-lib/aws-dynamodb";
|
|
735
|
+
import { z as z12 } from "zod";
|
|
736
|
+
var types = {
|
|
737
|
+
"standard": TableClass.STANDARD,
|
|
738
|
+
"standard-infrequent-access": TableClass.STANDARD_INFREQUENT_ACCESS
|
|
739
|
+
};
|
|
740
|
+
var TableClassSchema = z12.enum(Object.keys(types)).transform((value) => {
|
|
741
|
+
return types[value];
|
|
742
|
+
});
|
|
743
|
+
|
|
744
|
+
// src/plugins/table/schema/attribute.ts
|
|
745
|
+
import { AttributeType } from "aws-cdk-lib/aws-dynamodb";
|
|
746
|
+
import { z as z13 } from "zod";
|
|
747
|
+
var types2 = {
|
|
748
|
+
string: AttributeType.STRING,
|
|
749
|
+
number: AttributeType.NUMBER,
|
|
750
|
+
binary: AttributeType.BINARY
|
|
751
|
+
};
|
|
752
|
+
var AttributeSchema = z13.enum(Object.keys(types2)).transform((value) => types2[value]);
|
|
753
|
+
|
|
754
|
+
// src/plugins/table/schema/key.ts
|
|
755
|
+
import { z as z14 } from "zod";
|
|
756
|
+
var KeySchema = z14.string().min(1).max(255);
|
|
757
|
+
|
|
758
|
+
// src/plugins/table/schema/projection-type.ts
|
|
759
|
+
import { ProjectionType } from "aws-cdk-lib/aws-dynamodb";
|
|
760
|
+
import { z as z15 } from "zod";
|
|
761
|
+
var types3 = {
|
|
762
|
+
"all": ProjectionType.ALL,
|
|
763
|
+
"keys-only": ProjectionType.KEYS_ONLY
|
|
764
|
+
};
|
|
765
|
+
var ProjectionTypeSchema = z15.union([
|
|
766
|
+
z15.enum(Object.keys(types3)).transform((value) => ({
|
|
767
|
+
ProjectionType: types3[value]
|
|
768
|
+
})),
|
|
769
|
+
z15.array(KeySchema).min(0).max(20).transform((keys) => ({
|
|
770
|
+
ProjectionType: ProjectionType.INCLUDE,
|
|
771
|
+
NonKeyAttributes: keys
|
|
772
|
+
}))
|
|
773
|
+
]);
|
|
774
|
+
|
|
775
|
+
// src/plugins/table/index.ts
|
|
776
|
+
var tablePlugin = definePlugin({
|
|
777
|
+
name: "table",
|
|
778
|
+
schema: z16.object({
|
|
779
|
+
stacks: z16.object({
|
|
780
|
+
tables: z16.record(
|
|
781
|
+
ResourceIdSchema,
|
|
782
|
+
z16.object({
|
|
783
|
+
hash: KeySchema,
|
|
784
|
+
sort: KeySchema.optional(),
|
|
785
|
+
fields: z16.record(z16.string(), AttributeSchema),
|
|
786
|
+
class: TableClassSchema.default("standard"),
|
|
787
|
+
pointInTimeRecovery: z16.boolean().default(false),
|
|
788
|
+
timeToLiveAttribute: z16.string().optional(),
|
|
789
|
+
indexes: z16.record(z16.string(), z16.object({
|
|
790
|
+
hash: KeySchema,
|
|
791
|
+
sort: KeySchema.optional(),
|
|
792
|
+
projection: ProjectionTypeSchema.default("all")
|
|
793
|
+
})).optional()
|
|
794
|
+
}).refine((props) => {
|
|
795
|
+
return (
|
|
796
|
+
// Check the hash key
|
|
797
|
+
props.fields.hasOwnProperty(props.hash) && // Check the sort key
|
|
798
|
+
(!props.sort || props.fields.hasOwnProperty(props.sort)) && // Check all indexes
|
|
799
|
+
!Object.values(props.indexes || {}).map((index) => (
|
|
800
|
+
// Check the index hash key
|
|
801
|
+
props.fields.hasOwnProperty(index.hash) && // Check the index sort key
|
|
802
|
+
(!index.sort || props.fields.hasOwnProperty(index.sort))
|
|
803
|
+
)).includes(false)
|
|
804
|
+
);
|
|
805
|
+
}, "Hash & Sort keys must be defined inside the table fields")
|
|
806
|
+
).optional()
|
|
807
|
+
}).array()
|
|
808
|
+
}),
|
|
809
|
+
onStack({ stack, stackConfig, bind }) {
|
|
810
|
+
Object.entries(stackConfig.tables || {}).map(([id, props]) => {
|
|
811
|
+
const buildKey = (attr) => {
|
|
812
|
+
return { name: attr, type: props.fields[attr] };
|
|
813
|
+
};
|
|
814
|
+
const table = new Table(stack, toId("table", id), {
|
|
815
|
+
tableName: toName(stack, id),
|
|
816
|
+
partitionKey: buildKey(props.hash),
|
|
817
|
+
sortKey: props.sort ? buildKey(props.sort) : void 0,
|
|
818
|
+
billingMode: BillingMode.PAY_PER_REQUEST,
|
|
819
|
+
pointInTimeRecovery: props.pointInTimeRecovery,
|
|
820
|
+
timeToLiveAttribute: props.timeToLiveAttribute,
|
|
821
|
+
tableClass: props.class
|
|
822
|
+
});
|
|
823
|
+
Object.entries(props.indexes || {}).forEach(([indexName, entry]) => {
|
|
824
|
+
table.addGlobalSecondaryIndex({
|
|
825
|
+
indexName,
|
|
826
|
+
partitionKey: buildKey(entry.hash),
|
|
827
|
+
sortKey: entry.sort ? buildKey(entry.sort) : void 0,
|
|
828
|
+
...entry.projection
|
|
829
|
+
});
|
|
830
|
+
});
|
|
831
|
+
bind((lambda) => {
|
|
832
|
+
table.grantReadWriteData(lambda);
|
|
833
|
+
addResourceEnvironment(stack, "table", id, lambda);
|
|
834
|
+
});
|
|
835
|
+
});
|
|
836
|
+
}
|
|
837
|
+
});
|
|
838
|
+
|
|
839
|
+
// src/plugins/store.ts
|
|
840
|
+
import { z as z17 } from "zod";
|
|
841
|
+
import { Bucket as Bucket2, BucketAccessControl as BucketAccessControl2 } from "aws-cdk-lib/aws-s3";
|
|
842
|
+
import { RemovalPolicy as RemovalPolicy2 } from "aws-cdk-lib";
|
|
843
|
+
var storePlugin = definePlugin({
|
|
844
|
+
name: "store",
|
|
845
|
+
schema: z17.object({
|
|
846
|
+
stacks: z17.object({
|
|
847
|
+
stores: z17.array(ResourceIdSchema).optional()
|
|
848
|
+
}).array()
|
|
849
|
+
}),
|
|
850
|
+
onStack({ stack, stackConfig, bind }) {
|
|
851
|
+
(stackConfig.stores || []).forEach((id) => {
|
|
852
|
+
const bucket = new Bucket2(stack, toId("store", id), {
|
|
853
|
+
bucketName: toName(stack, id),
|
|
854
|
+
accessControl: BucketAccessControl2.PRIVATE,
|
|
855
|
+
removalPolicy: RemovalPolicy2.DESTROY
|
|
856
|
+
});
|
|
857
|
+
bind((lambda) => {
|
|
858
|
+
bucket.grantReadWrite(lambda), addResourceEnvironment(stack, "store", id, lambda);
|
|
859
|
+
});
|
|
860
|
+
});
|
|
861
|
+
}
|
|
862
|
+
});
|
|
863
|
+
|
|
864
|
+
// src/plugins/topic.ts
|
|
865
|
+
import { z as z18 } from "zod";
|
|
866
|
+
import { Topic } from "aws-cdk-lib/aws-sns";
|
|
867
|
+
import { SnsEventSource } from "aws-cdk-lib/aws-lambda-event-sources";
|
|
868
|
+
import { Arn as Arn2, ArnFormat } from "aws-cdk-lib";
|
|
869
|
+
import { PolicyStatement as PolicyStatement2 } from "aws-cdk-lib/aws-iam";
|
|
870
|
+
var topicPlugin = definePlugin({
|
|
871
|
+
name: "topic",
|
|
872
|
+
schema: z18.object({
|
|
873
|
+
stacks: z18.object({
|
|
874
|
+
topics: z18.record(ResourceIdSchema, FunctionSchema).optional()
|
|
875
|
+
}).array()
|
|
876
|
+
}),
|
|
877
|
+
onBootstrap({ config: config2, stack }) {
|
|
878
|
+
const allTopicNames = config2.stacks.map((stack2) => {
|
|
879
|
+
return Object.keys(stack2.topics || {});
|
|
880
|
+
}).flat();
|
|
881
|
+
const uniqueTopicNames = [...new Set(allTopicNames)];
|
|
882
|
+
uniqueTopicNames.forEach((id) => {
|
|
883
|
+
new Topic(stack, toId("topic", id), {
|
|
884
|
+
topicName: `${config2.name}-${id}`,
|
|
885
|
+
displayName: id
|
|
886
|
+
});
|
|
887
|
+
});
|
|
888
|
+
},
|
|
889
|
+
onStack(ctx) {
|
|
890
|
+
const { config: config2, stack, stackConfig, bind } = ctx;
|
|
891
|
+
bind((lambda) => {
|
|
892
|
+
lambda.addToRolePolicy(new PolicyStatement2({
|
|
893
|
+
actions: ["sns:publish"],
|
|
894
|
+
resources: ["*"]
|
|
895
|
+
}));
|
|
896
|
+
});
|
|
897
|
+
return Object.entries(stackConfig.topics || {}).map(([id, props]) => {
|
|
898
|
+
const lambda = toFunction(ctx, id, props);
|
|
899
|
+
const topic = Topic.fromTopicArn(
|
|
900
|
+
stack,
|
|
901
|
+
toId("topic", id),
|
|
902
|
+
Arn2.format({
|
|
903
|
+
arnFormat: ArnFormat.NO_RESOURCE_NAME,
|
|
904
|
+
service: "sns",
|
|
905
|
+
resource: `${config2.name}-${id}`
|
|
906
|
+
}, stack)
|
|
907
|
+
);
|
|
908
|
+
lambda.addEventSource(new SnsEventSource(topic));
|
|
909
|
+
return lambda;
|
|
910
|
+
});
|
|
911
|
+
}
|
|
912
|
+
});
|
|
913
|
+
|
|
914
|
+
// src/plugins/search.ts
|
|
915
|
+
import { z as z19 } from "zod";
|
|
916
|
+
import { CfnCollection } from "aws-cdk-lib/aws-opensearchserverless";
|
|
917
|
+
import { PolicyStatement as PolicyStatement3 } from "aws-cdk-lib/aws-iam";
|
|
918
|
+
var searchPlugin = definePlugin({
|
|
919
|
+
name: "search",
|
|
920
|
+
schema: z19.object({
|
|
921
|
+
stacks: z19.object({
|
|
922
|
+
searchs: z19.array(ResourceIdSchema).optional()
|
|
923
|
+
}).array()
|
|
924
|
+
}),
|
|
925
|
+
onStack({ stack, stackConfig, bind }) {
|
|
926
|
+
(stackConfig.searchs || []).forEach((id) => {
|
|
927
|
+
const collection = new CfnCollection(stack, toId("search", id), {
|
|
928
|
+
name: toName(stack, id),
|
|
929
|
+
type: "SEARCH"
|
|
930
|
+
});
|
|
931
|
+
bind((lambda) => {
|
|
932
|
+
lambda.addToRolePolicy(new PolicyStatement3({
|
|
933
|
+
actions: ["aoss:APIAccessAll"],
|
|
934
|
+
resources: [collection.attrArn]
|
|
935
|
+
}));
|
|
936
|
+
});
|
|
937
|
+
});
|
|
938
|
+
}
|
|
939
|
+
});
|
|
940
|
+
|
|
941
|
+
// src/plugins/index.ts
|
|
942
|
+
var defaultPlugins = [
|
|
943
|
+
functionPlugin,
|
|
944
|
+
cronPlugin,
|
|
945
|
+
queuePlugin,
|
|
946
|
+
tablePlugin,
|
|
947
|
+
storePlugin,
|
|
948
|
+
topicPlugin,
|
|
949
|
+
searchPlugin
|
|
950
|
+
];
|
|
951
|
+
|
|
952
|
+
// src/stack/app-bootstrap.ts
|
|
953
|
+
var appBootstrapStack = ({ config: config2, app, assets }) => {
|
|
954
|
+
const stack = new Stack3(app, "bootstrap", {
|
|
955
|
+
stackName: `${config2.name}-bootstrap`
|
|
956
|
+
});
|
|
957
|
+
const plugins = [
|
|
958
|
+
...defaultPlugins,
|
|
959
|
+
...config2.plugins || []
|
|
960
|
+
];
|
|
961
|
+
debug("Run plugin onBootstrap listeners");
|
|
962
|
+
plugins.forEach((plugin) => plugin.onBootstrap?.({ config: config2, stack, app, assets }));
|
|
963
|
+
return stack;
|
|
964
|
+
};
|
|
965
|
+
|
|
966
|
+
// src/util/deployment.ts
|
|
967
|
+
var flattenDependencyTree = (stacks) => {
|
|
968
|
+
const list3 = [];
|
|
969
|
+
const walk = (stacks2) => {
|
|
970
|
+
stacks2.forEach((node) => {
|
|
971
|
+
list3.push(node);
|
|
972
|
+
walk(node.children);
|
|
973
|
+
});
|
|
974
|
+
};
|
|
975
|
+
walk(stacks);
|
|
976
|
+
return list3;
|
|
977
|
+
};
|
|
978
|
+
var createDependencyTree = (stacks, startingLevel) => {
|
|
979
|
+
const list3 = stacks.map(({ stack, config: config2 }) => ({
|
|
980
|
+
stack,
|
|
981
|
+
depends: config2?.depends?.map((dep) => dep.name) || []
|
|
982
|
+
}));
|
|
983
|
+
const findChildren = (list4, parents, level) => {
|
|
984
|
+
const children = [];
|
|
985
|
+
const rests = [];
|
|
986
|
+
for (const item of list4) {
|
|
987
|
+
const isChild = item.depends.filter((dep) => !parents.includes(dep)).length === 0;
|
|
988
|
+
if (isChild) {
|
|
989
|
+
children.push(item);
|
|
990
|
+
} else {
|
|
991
|
+
rests.push(item);
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
if (!rests.length) {
|
|
995
|
+
return children.map(({ stack }) => ({
|
|
996
|
+
stack,
|
|
997
|
+
level,
|
|
998
|
+
children: []
|
|
999
|
+
}));
|
|
1000
|
+
}
|
|
1001
|
+
return children.map(({ stack }) => {
|
|
1002
|
+
return {
|
|
1003
|
+
stack,
|
|
1004
|
+
level,
|
|
1005
|
+
children: findChildren(rests, [...parents, stack.artifactId], level + 1)
|
|
1006
|
+
};
|
|
1007
|
+
});
|
|
1008
|
+
};
|
|
1009
|
+
return findChildren(list3, [], startingLevel);
|
|
1010
|
+
};
|
|
1011
|
+
var createDeploymentLine = (stacks) => {
|
|
1012
|
+
const flat = flattenDependencyTree(stacks);
|
|
1013
|
+
const line = [];
|
|
1014
|
+
flat.forEach((node) => {
|
|
1015
|
+
const level = node.level;
|
|
1016
|
+
if (!line[level]) {
|
|
1017
|
+
line[level] = [];
|
|
1018
|
+
}
|
|
1019
|
+
line[level].push(node.stack);
|
|
1020
|
+
});
|
|
1021
|
+
return line;
|
|
1022
|
+
};
|
|
1023
|
+
|
|
1024
|
+
// src/util/assets.ts
|
|
1025
|
+
var Assets = class {
|
|
1026
|
+
assets = {};
|
|
1027
|
+
id = 0;
|
|
1028
|
+
add(opts) {
|
|
1029
|
+
if (!this.assets[opts.stack.name]) {
|
|
1030
|
+
this.assets[opts.stack.name] = [];
|
|
1031
|
+
}
|
|
1032
|
+
this.assets[opts.stack.name].push({
|
|
1033
|
+
...opts,
|
|
1034
|
+
id: this.id++
|
|
1035
|
+
});
|
|
1036
|
+
}
|
|
1037
|
+
list() {
|
|
1038
|
+
return this.assets;
|
|
1039
|
+
}
|
|
1040
|
+
forEach(cb) {
|
|
1041
|
+
Object.values(this.assets).forEach((assets) => {
|
|
1042
|
+
cb(assets[0].stack, assets);
|
|
1043
|
+
});
|
|
1044
|
+
}
|
|
1045
|
+
map(cb) {
|
|
1046
|
+
return Object.values(this.assets).map((assets) => {
|
|
1047
|
+
return cb(assets[0].stack, assets);
|
|
1048
|
+
});
|
|
1049
|
+
}
|
|
1050
|
+
};
|
|
1051
|
+
|
|
1052
|
+
// src/app.ts
|
|
1053
|
+
var makeApp = (config2) => {
|
|
1054
|
+
return new App4({
|
|
1055
|
+
outdir: assemblyDir,
|
|
1056
|
+
defaultStackSynthesizer: new DefaultStackSynthesizer({
|
|
1057
|
+
fileAssetsBucketName: assetBucketName(config2),
|
|
1058
|
+
fileAssetPublishingRoleArn: "",
|
|
1059
|
+
generateBootstrapVersionRule: false
|
|
1060
|
+
})
|
|
1061
|
+
});
|
|
1062
|
+
};
|
|
1063
|
+
var getAllDepends = (filters) => {
|
|
1064
|
+
const list3 = [];
|
|
1065
|
+
const walk = (deps) => {
|
|
1066
|
+
deps.forEach((dep) => {
|
|
1067
|
+
!list3.includes(dep) && list3.push(dep);
|
|
1068
|
+
dep.depends && walk(dep.depends);
|
|
1069
|
+
});
|
|
1070
|
+
};
|
|
1071
|
+
walk(filters);
|
|
1072
|
+
return list3;
|
|
1073
|
+
};
|
|
1074
|
+
var toApp = async (config2, filters) => {
|
|
1075
|
+
const assets = new Assets();
|
|
1076
|
+
const app = makeApp(config2);
|
|
1077
|
+
const stacks = [];
|
|
1078
|
+
const plugins = [
|
|
1079
|
+
...defaultPlugins,
|
|
1080
|
+
...config2.plugins || []
|
|
1081
|
+
];
|
|
1082
|
+
debug("Plugins detected:", plugins.map((plugin) => style.info(plugin.name)).join(", "));
|
|
1083
|
+
debug("Run plugin onApp listeners");
|
|
1084
|
+
plugins.forEach((plugin) => plugin.onApp?.({ config: config2, app, assets }));
|
|
1085
|
+
debug("Stack filters:", filters.map((filter) => style.info(filter)).join(", "));
|
|
1086
|
+
const filterdStacks = filters.length === 0 ? config2.stacks : getAllDepends(
|
|
1087
|
+
// config.stacks,
|
|
1088
|
+
config2.stacks.filter((stack) => filters.includes(stack.name))
|
|
1089
|
+
);
|
|
1090
|
+
for (const stackConfig of filterdStacks) {
|
|
1091
|
+
const { stack } = toStack({
|
|
1092
|
+
config: config2,
|
|
1093
|
+
stackConfig,
|
|
1094
|
+
assets,
|
|
1095
|
+
plugins,
|
|
1096
|
+
app
|
|
1097
|
+
});
|
|
1098
|
+
stacks.push({ stack, config: stackConfig });
|
|
1099
|
+
}
|
|
1100
|
+
let dependencyTree;
|
|
1101
|
+
const bootstrap2 = appBootstrapStack({ config: config2, app, assets });
|
|
1102
|
+
if (bootstrap2.node.children.length === 0) {
|
|
1103
|
+
dependencyTree = createDependencyTree(stacks, 0);
|
|
1104
|
+
} else {
|
|
1105
|
+
dependencyTree = [{
|
|
1106
|
+
stack: bootstrap2,
|
|
1107
|
+
level: 0,
|
|
1108
|
+
children: createDependencyTree(stacks, 1)
|
|
1109
|
+
}];
|
|
1110
|
+
}
|
|
1111
|
+
return {
|
|
1112
|
+
app,
|
|
1113
|
+
assets,
|
|
1114
|
+
plugins,
|
|
1115
|
+
stackNames: filterdStacks.map((stack) => stack.name),
|
|
1116
|
+
dependencyTree
|
|
1117
|
+
};
|
|
1118
|
+
};
|
|
1119
|
+
|
|
1120
|
+
// src/cli/ui/layout/basic.ts
|
|
1121
|
+
var br = () => {
|
|
1122
|
+
return "\n";
|
|
1123
|
+
};
|
|
1124
|
+
var hr = () => {
|
|
1125
|
+
return (term) => {
|
|
1126
|
+
term.out.write([
|
|
1127
|
+
style.placeholder("\u2500".repeat(term.out.width())),
|
|
1128
|
+
br()
|
|
1129
|
+
]);
|
|
1130
|
+
};
|
|
1131
|
+
};
|
|
1132
|
+
|
|
1133
|
+
// src/cli/ui/layout/logs.ts
|
|
1134
|
+
var previous = /* @__PURE__ */ new Date();
|
|
1135
|
+
var logs = () => {
|
|
1136
|
+
if (!process.env.VERBOSE) {
|
|
1137
|
+
return [];
|
|
1138
|
+
}
|
|
1139
|
+
const logs2 = flushDebug();
|
|
1140
|
+
return [
|
|
1141
|
+
hr(),
|
|
1142
|
+
br(),
|
|
1143
|
+
" ".repeat(3),
|
|
1144
|
+
style.label("Debug Logs:"),
|
|
1145
|
+
br(),
|
|
1146
|
+
br(),
|
|
1147
|
+
logs2.map((log) => {
|
|
1148
|
+
const diff = log.date.getTime() - previous.getTime();
|
|
1149
|
+
const time = `+${diff}`.padStart(7);
|
|
1150
|
+
previous = log.date;
|
|
1151
|
+
return [
|
|
1152
|
+
style.attr(`${time}${style.attr.dim("ms")}`),
|
|
1153
|
+
" [ ",
|
|
1154
|
+
log.type,
|
|
1155
|
+
" ] ",
|
|
1156
|
+
log.message,
|
|
1157
|
+
br(),
|
|
1158
|
+
log.type === "error" ? br() : ""
|
|
1159
|
+
];
|
|
1160
|
+
}),
|
|
1161
|
+
br(),
|
|
1162
|
+
hr()
|
|
1163
|
+
];
|
|
1164
|
+
};
|
|
1165
|
+
|
|
1166
|
+
// src/cli/ui/layout/footer.ts
|
|
1167
|
+
var footer = () => {
|
|
1168
|
+
return [
|
|
1169
|
+
br(),
|
|
1170
|
+
logs()
|
|
1171
|
+
];
|
|
1172
|
+
};
|
|
1173
|
+
|
|
1174
|
+
// src/config.ts
|
|
1175
|
+
import { join as join4 } from "path";
|
|
1176
|
+
|
|
1177
|
+
// src/util/account.ts
|
|
1178
|
+
import { STSClient, GetCallerIdentityCommand } from "@aws-sdk/client-sts";
|
|
1179
|
+
var getAccountId = async (credentials, region) => {
|
|
1180
|
+
const client = new STSClient({ credentials, region });
|
|
1181
|
+
const result = await client.send(new GetCallerIdentityCommand({}));
|
|
1182
|
+
return result.Account;
|
|
1183
|
+
};
|
|
1184
|
+
|
|
1185
|
+
// src/util/credentials.ts
|
|
1186
|
+
import { fromIni } from "@aws-sdk/credential-providers";
|
|
1187
|
+
var getCredentials = (profile) => {
|
|
1188
|
+
return fromIni({
|
|
1189
|
+
profile
|
|
1190
|
+
});
|
|
1191
|
+
};
|
|
1192
|
+
|
|
1193
|
+
// src/config.ts
|
|
1194
|
+
import { load } from "ts-import";
|
|
1195
|
+
|
|
1196
|
+
// src/schema/app.ts
|
|
1197
|
+
import { z as z23 } from "zod";
|
|
1198
|
+
|
|
1199
|
+
// src/schema/stack.ts
|
|
1200
|
+
import { z as z20 } from "zod";
|
|
1201
|
+
var StackSchema = z20.object({
|
|
1202
|
+
name: ResourceIdSchema,
|
|
1203
|
+
depends: z20.array(z20.lazy(() => StackSchema)).optional()
|
|
1204
|
+
});
|
|
1205
|
+
|
|
1206
|
+
// src/schema/region.ts
|
|
1207
|
+
import { z as z21 } from "zod";
|
|
1208
|
+
var US = ["us-east-2", "us-east-1", "us-west-1", "us-west-2"];
|
|
1209
|
+
var AF = ["af-south-1"];
|
|
1210
|
+
var AP = ["ap-east-1", "ap-south-2", "ap-southeast-3", "ap-southeast-4", "ap-south-1", "ap-northeast-3", "ap-northeast-2", "ap-southeast-1", "ap-southeast-2", "ap-northeast-1"];
|
|
1211
|
+
var CA = ["ca-central-1"];
|
|
1212
|
+
var EU = ["eu-central-1", "eu-west-1", "eu-west-2", "eu-south-1", "eu-west-3", "eu-south-2", "eu-north-1", "eu-central-2"];
|
|
1213
|
+
var ME = ["me-south-1", "me-central-1"];
|
|
1214
|
+
var SA = ["sa-east-1"];
|
|
1215
|
+
var regions = [
|
|
1216
|
+
...US,
|
|
1217
|
+
...AF,
|
|
1218
|
+
...AP,
|
|
1219
|
+
...CA,
|
|
1220
|
+
...EU,
|
|
1221
|
+
...ME,
|
|
1222
|
+
...SA
|
|
1223
|
+
];
|
|
1224
|
+
var RegionSchema = z21.enum(regions);
|
|
1225
|
+
|
|
1226
|
+
// src/schema/plugin.ts
|
|
1227
|
+
import { z as z22 } from "zod";
|
|
1228
|
+
var PluginSchema = z22.object({
|
|
1229
|
+
name: z22.string(),
|
|
1230
|
+
schema: z22.custom().optional(),
|
|
1231
|
+
depends: z22.array(z22.lazy(() => PluginSchema)).optional(),
|
|
1232
|
+
onBootstrap: z22.function().optional(),
|
|
1233
|
+
onStack: z22.function().returns(z22.any()).optional(),
|
|
1234
|
+
onApp: z22.function().optional()
|
|
1235
|
+
// bind: z.function().optional(),
|
|
1236
|
+
});
|
|
1237
|
+
|
|
1238
|
+
// src/schema/app.ts
|
|
1239
|
+
var AppSchema = z23.object({
|
|
1240
|
+
name: ResourceIdSchema,
|
|
1241
|
+
region: RegionSchema,
|
|
1242
|
+
profile: z23.string(),
|
|
1243
|
+
stage: z23.string().regex(/[a-z]+/).default("prod"),
|
|
1244
|
+
defaults: z23.object({}).default({}),
|
|
1245
|
+
stacks: z23.array(StackSchema).min(1),
|
|
1246
|
+
plugins: z23.array(PluginSchema).optional()
|
|
1247
|
+
});
|
|
1248
|
+
|
|
1249
|
+
// src/config.ts
|
|
1250
|
+
var importConfig = async (options) => {
|
|
1251
|
+
debug("Import config file");
|
|
1252
|
+
const fileName = join4(process.cwd(), options.configFile || "awsless.config.ts");
|
|
1253
|
+
const module = await load(fileName, {
|
|
1254
|
+
transpileOptions: {
|
|
1255
|
+
cache: {
|
|
1256
|
+
dir: join4(outDir, "config")
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
});
|
|
1260
|
+
const appConfig = typeof module.default === "function" ? await module.default({
|
|
1261
|
+
profile: options.profile,
|
|
1262
|
+
region: options.region,
|
|
1263
|
+
stage: options.stage
|
|
1264
|
+
}) : module.default;
|
|
1265
|
+
debug("Validate config file");
|
|
1266
|
+
const plugins = [
|
|
1267
|
+
...defaultPlugins,
|
|
1268
|
+
...appConfig.plugins || []
|
|
1269
|
+
];
|
|
1270
|
+
let schema2 = AppSchema;
|
|
1271
|
+
for (const plugin of plugins) {
|
|
1272
|
+
if (plugin.schema) {
|
|
1273
|
+
schema2 = schema2.and(plugin.schema);
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
const config2 = await schema2.parseAsync(appConfig);
|
|
1277
|
+
debug("Final config:", config2.stacks);
|
|
1278
|
+
debug("Load credentials", style.info(config2.profile));
|
|
1279
|
+
const credentials = getCredentials(config2.profile);
|
|
1280
|
+
debug("Load AWS account ID");
|
|
1281
|
+
const account = await getAccountId(credentials, config2.region);
|
|
1282
|
+
debug("Account ID:", style.info(account));
|
|
1283
|
+
return {
|
|
1284
|
+
...config2,
|
|
1285
|
+
account,
|
|
1286
|
+
credentials
|
|
1287
|
+
};
|
|
1288
|
+
};
|
|
1289
|
+
|
|
1290
|
+
// src/cli/ui/layout/list.ts
|
|
1291
|
+
var list = (data) => {
|
|
1292
|
+
const padding = 3;
|
|
1293
|
+
const gap = 1;
|
|
1294
|
+
const size = Object.keys(data).reduce((total, name) => {
|
|
1295
|
+
return name.length > total ? name.length : total;
|
|
1296
|
+
}, 0);
|
|
1297
|
+
return Object.entries(data).map(([name, value]) => [
|
|
1298
|
+
" ".repeat(padding),
|
|
1299
|
+
style.label((name + ":").padEnd(size + gap + 1)),
|
|
1300
|
+
value,
|
|
1301
|
+
br()
|
|
1302
|
+
]);
|
|
1303
|
+
};
|
|
1304
|
+
|
|
1305
|
+
// src/cli/ui/layout/header.ts
|
|
1306
|
+
var header = (config2) => {
|
|
1307
|
+
return [
|
|
1308
|
+
br(),
|
|
1309
|
+
list({
|
|
1310
|
+
App: config2.name,
|
|
1311
|
+
Stage: config2.stage,
|
|
1312
|
+
Region: config2.region,
|
|
1313
|
+
Profile: config2.profile
|
|
1314
|
+
}),
|
|
1315
|
+
br()
|
|
1316
|
+
];
|
|
1317
|
+
};
|
|
1318
|
+
|
|
1319
|
+
// src/cli/lib/signal.ts
|
|
1320
|
+
var Signal = class {
|
|
1321
|
+
constructor(value) {
|
|
1322
|
+
this.value = value;
|
|
1323
|
+
}
|
|
1324
|
+
subs = /* @__PURE__ */ new Set();
|
|
1325
|
+
get() {
|
|
1326
|
+
return this.value;
|
|
1327
|
+
}
|
|
1328
|
+
set(value) {
|
|
1329
|
+
this.value = value;
|
|
1330
|
+
this.subs.forEach((sub) => sub(value));
|
|
1331
|
+
}
|
|
1332
|
+
update(cb) {
|
|
1333
|
+
this.set(cb(this.value));
|
|
1334
|
+
}
|
|
1335
|
+
subscribe(cb) {
|
|
1336
|
+
this.subs.add(cb);
|
|
1337
|
+
return () => {
|
|
1338
|
+
this.subs.delete(cb);
|
|
1339
|
+
};
|
|
1340
|
+
}
|
|
1341
|
+
};
|
|
1342
|
+
var derive = (deps, factory) => {
|
|
1343
|
+
const values = deps.map((dep) => dep.get());
|
|
1344
|
+
const signal = new Signal(factory(...values));
|
|
1345
|
+
deps.forEach((dep) => {
|
|
1346
|
+
dep.subscribe(() => {
|
|
1347
|
+
const values2 = deps.map((dep2) => dep2.get());
|
|
1348
|
+
signal.set(factory(...values2));
|
|
1349
|
+
});
|
|
1350
|
+
});
|
|
1351
|
+
return signal;
|
|
1352
|
+
};
|
|
1353
|
+
|
|
1354
|
+
// src/cli/ui/layout/spinner.ts
|
|
1355
|
+
var frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
1356
|
+
var length = frames.length;
|
|
1357
|
+
var createSpinner = () => {
|
|
1358
|
+
const index = new Signal(0);
|
|
1359
|
+
const frame = derive([index], (index2) => style.info(frames[index2 % length]));
|
|
1360
|
+
const interval = setInterval(() => {
|
|
1361
|
+
index.update((i) => i + 1);
|
|
1362
|
+
}, 80);
|
|
1363
|
+
return [
|
|
1364
|
+
frame,
|
|
1365
|
+
() => {
|
|
1366
|
+
clearInterval(interval);
|
|
1367
|
+
}
|
|
1368
|
+
];
|
|
1369
|
+
};
|
|
1370
|
+
|
|
1371
|
+
// src/cli/ui/layout/dialog.ts
|
|
1372
|
+
var dialog = (type, lines) => {
|
|
1373
|
+
const padding = 3;
|
|
1374
|
+
const icon = style[type](symbol[type].padEnd(padding));
|
|
1375
|
+
return lines.map((line, i) => {
|
|
1376
|
+
if (i === 0) {
|
|
1377
|
+
return icon + line;
|
|
1378
|
+
} else {
|
|
1379
|
+
return " ".repeat(padding) + line;
|
|
1380
|
+
}
|
|
1381
|
+
}).join(br()) + br();
|
|
1382
|
+
};
|
|
1383
|
+
var loadingDialog = (message) => {
|
|
1384
|
+
const [icon, stop] = createSpinner();
|
|
1385
|
+
const description = new Signal(message);
|
|
1386
|
+
const time = new Signal("");
|
|
1387
|
+
const start = /* @__PURE__ */ new Date();
|
|
1388
|
+
return (term) => {
|
|
1389
|
+
term.out.write([
|
|
1390
|
+
icon,
|
|
1391
|
+
" ",
|
|
1392
|
+
description,
|
|
1393
|
+
" ",
|
|
1394
|
+
time,
|
|
1395
|
+
br()
|
|
1396
|
+
]);
|
|
1397
|
+
return (message2) => {
|
|
1398
|
+
const end = /* @__PURE__ */ new Date();
|
|
1399
|
+
const diff = end.getTime() - start.getTime();
|
|
1400
|
+
description.set(message2);
|
|
1401
|
+
time.set(style.attr(diff) + style.attr.dim("ms"));
|
|
1402
|
+
stop();
|
|
1403
|
+
icon.set(style.success(symbol.success));
|
|
1404
|
+
};
|
|
1405
|
+
};
|
|
1406
|
+
};
|
|
1407
|
+
|
|
1408
|
+
// src/cli/lib/interface.ts
|
|
1409
|
+
import { createInterface, emitKeypressEvents } from "readline";
|
|
1410
|
+
import { exec } from "child_process";
|
|
1411
|
+
var parseAction = (key) => {
|
|
1412
|
+
if (key.meta && key.name !== "escape") {
|
|
1413
|
+
return;
|
|
1414
|
+
}
|
|
1415
|
+
if (key.ctrl) {
|
|
1416
|
+
if (key.name === "a")
|
|
1417
|
+
return "first";
|
|
1418
|
+
if (key.name === "c")
|
|
1419
|
+
return "abort";
|
|
1420
|
+
if (key.name === "d")
|
|
1421
|
+
return "abort";
|
|
1422
|
+
if (key.name === "e")
|
|
1423
|
+
return "last";
|
|
1424
|
+
if (key.name === "g")
|
|
1425
|
+
return "reset";
|
|
1426
|
+
}
|
|
1427
|
+
if (key.name === "return")
|
|
1428
|
+
return "submit";
|
|
1429
|
+
if (key.name === "enter")
|
|
1430
|
+
return "submit";
|
|
1431
|
+
if (key.name === "backspace")
|
|
1432
|
+
return "delete";
|
|
1433
|
+
if (key.name === "delete")
|
|
1434
|
+
return "deleteForward";
|
|
1435
|
+
if (key.name === "abort")
|
|
1436
|
+
return "abort";
|
|
1437
|
+
if (key.name === "escape")
|
|
1438
|
+
return "exit";
|
|
1439
|
+
if (key.name === "tab" && key.shift)
|
|
1440
|
+
return "previous";
|
|
1441
|
+
if (key.name === "tab")
|
|
1442
|
+
return "next";
|
|
1443
|
+
if (key.name === "up")
|
|
1444
|
+
return "up";
|
|
1445
|
+
if (key.name === "down")
|
|
1446
|
+
return "down";
|
|
1447
|
+
if (key.name === "right")
|
|
1448
|
+
return "right";
|
|
1449
|
+
if (key.name === "left")
|
|
1450
|
+
return "left";
|
|
1451
|
+
return "input";
|
|
1452
|
+
};
|
|
1453
|
+
var Interface = class {
|
|
1454
|
+
constructor(input) {
|
|
1455
|
+
this.input = input;
|
|
1456
|
+
this.readline = createInterface({ input: this.input, escapeCodeTimeout: 50 });
|
|
1457
|
+
emitKeypressEvents(this.input, this.readline);
|
|
1458
|
+
this.hideCursor();
|
|
1459
|
+
if (this.input.isTTY) {
|
|
1460
|
+
this.input.setRawMode(true);
|
|
1461
|
+
}
|
|
1462
|
+
}
|
|
1463
|
+
// private subscriber: Actions | undefined
|
|
1464
|
+
readline;
|
|
1465
|
+
unref() {
|
|
1466
|
+
this.showCursor();
|
|
1467
|
+
this.input.unref();
|
|
1468
|
+
}
|
|
1469
|
+
captureInput(actions) {
|
|
1470
|
+
debug("Subscribe to user input...");
|
|
1471
|
+
const keypress = (value, key) => {
|
|
1472
|
+
const action = parseAction(key);
|
|
1473
|
+
if (typeof action === "undefined") {
|
|
1474
|
+
this.bell();
|
|
1475
|
+
} else if (action === "abort") {
|
|
1476
|
+
this.showCursor();
|
|
1477
|
+
process.exit(1);
|
|
1478
|
+
} else {
|
|
1479
|
+
const cb = actions[action];
|
|
1480
|
+
if (typeof cb === "function") {
|
|
1481
|
+
cb(value, key);
|
|
1482
|
+
} else {
|
|
1483
|
+
this.bell();
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
};
|
|
1487
|
+
this.input.on("keypress", keypress);
|
|
1488
|
+
return () => {
|
|
1489
|
+
this.input.off("keypress", keypress);
|
|
1490
|
+
debug("Unsubscribe to user input");
|
|
1491
|
+
};
|
|
1492
|
+
}
|
|
1493
|
+
hideCursor() {
|
|
1494
|
+
if (this.input.isTTY) {
|
|
1495
|
+
this.input.write("\x1B[?25l");
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
showCursor() {
|
|
1499
|
+
if (this.input.isTTY) {
|
|
1500
|
+
this.input.write("\x1B[?25h");
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
bell() {
|
|
1504
|
+
if (this.input.isTTY) {
|
|
1505
|
+
exec("afplay /System/Library/Sounds/Tink.aiff");
|
|
1506
|
+
}
|
|
1507
|
+
}
|
|
1508
|
+
};
|
|
1509
|
+
|
|
1510
|
+
// src/cli/lib/renderer.ts
|
|
1511
|
+
var Renderer = class {
|
|
1512
|
+
constructor(output, ins) {
|
|
1513
|
+
this.output = output;
|
|
1514
|
+
this.ins = ins;
|
|
1515
|
+
}
|
|
1516
|
+
fragments = [];
|
|
1517
|
+
unsubs = [];
|
|
1518
|
+
timeout;
|
|
1519
|
+
screen = [];
|
|
1520
|
+
width() {
|
|
1521
|
+
return this.output.columns;
|
|
1522
|
+
}
|
|
1523
|
+
height() {
|
|
1524
|
+
return this.output.rows;
|
|
1525
|
+
}
|
|
1526
|
+
write(fragment) {
|
|
1527
|
+
if (Array.isArray(fragment)) {
|
|
1528
|
+
fragment.forEach((i) => this.write(i));
|
|
1529
|
+
return;
|
|
1530
|
+
}
|
|
1531
|
+
if (typeof fragment === "function") {
|
|
1532
|
+
return fragment({ out: this, in: this.ins });
|
|
1533
|
+
}
|
|
1534
|
+
this.fragments.push(fragment);
|
|
1535
|
+
this.update();
|
|
1536
|
+
return fragment;
|
|
1537
|
+
}
|
|
1538
|
+
update() {
|
|
1539
|
+
clearTimeout(this.timeout);
|
|
1540
|
+
this.timeout = setTimeout(() => {
|
|
1541
|
+
this.flush();
|
|
1542
|
+
}, 0);
|
|
1543
|
+
}
|
|
1544
|
+
flush() {
|
|
1545
|
+
clearTimeout(this.timeout);
|
|
1546
|
+
const walk = (fragment) => {
|
|
1547
|
+
if (typeof fragment === "string") {
|
|
1548
|
+
return fragment;
|
|
1549
|
+
}
|
|
1550
|
+
if (Array.isArray(fragment)) {
|
|
1551
|
+
return fragment.map(walk).join("");
|
|
1552
|
+
}
|
|
1553
|
+
this.unsubs.push(fragment.subscribe(() => {
|
|
1554
|
+
this.update();
|
|
1555
|
+
}));
|
|
1556
|
+
return walk(fragment.get());
|
|
1557
|
+
};
|
|
1558
|
+
this.unsubs.forEach((unsub) => unsub());
|
|
1559
|
+
this.unsubs = [];
|
|
1560
|
+
const screen = walk(this.fragments).split("\n");
|
|
1561
|
+
const oldSize = this.screen.length;
|
|
1562
|
+
const newSize = screen.length;
|
|
1563
|
+
const size = Math.max(oldSize, newSize);
|
|
1564
|
+
const height = this.height();
|
|
1565
|
+
const start = Math.max(oldSize - height, 0);
|
|
1566
|
+
for (let y = start; y < size; y++) {
|
|
1567
|
+
const line = screen[y];
|
|
1568
|
+
if (line !== this.screen[y]) {
|
|
1569
|
+
if (y > oldSize) {
|
|
1570
|
+
const x = (this.screen[y - 1]?.length || 0) - 1;
|
|
1571
|
+
this.output.cursorTo?.(x, y - 1 - start);
|
|
1572
|
+
this.output.write?.("\n" + line);
|
|
1573
|
+
} else {
|
|
1574
|
+
this.output.cursorTo?.(0, y - start);
|
|
1575
|
+
this.output.write?.(line);
|
|
1576
|
+
}
|
|
1577
|
+
this.output.clearLine?.(1);
|
|
1578
|
+
}
|
|
1579
|
+
}
|
|
1580
|
+
this.screen = screen;
|
|
1581
|
+
}
|
|
1582
|
+
clear() {
|
|
1583
|
+
let count = this.output.rows;
|
|
1584
|
+
while (count--) {
|
|
1585
|
+
this.output.write("\n");
|
|
1586
|
+
}
|
|
1587
|
+
this.output.cursorTo?.(0, 0);
|
|
1588
|
+
this.output.clearScreenDown?.();
|
|
1589
|
+
}
|
|
1590
|
+
};
|
|
1591
|
+
|
|
1592
|
+
// src/cli/lib/terminal.ts
|
|
1593
|
+
var createTerminal = (input = process.stdin, output = process.stdout) => {
|
|
1594
|
+
const ins = new Interface(input);
|
|
1595
|
+
const outs = new Renderer(output, ins);
|
|
1596
|
+
return { in: ins, out: outs };
|
|
1597
|
+
};
|
|
1598
|
+
|
|
1599
|
+
// src/cli/ui/layout/logo.ts
|
|
1600
|
+
var logo = () => {
|
|
1601
|
+
return [
|
|
1602
|
+
style.warning("\u26A1\uFE0F "),
|
|
1603
|
+
style.primary("AWS"),
|
|
1604
|
+
style.primary.dim("LESS"),
|
|
1605
|
+
br()
|
|
1606
|
+
];
|
|
1607
|
+
};
|
|
1608
|
+
|
|
1609
|
+
// src/cli/ui/layout/layout.ts
|
|
1610
|
+
var layout = async (cb) => {
|
|
1611
|
+
const term = createTerminal();
|
|
1612
|
+
term.out.clear();
|
|
1613
|
+
term.out.write(logo());
|
|
1614
|
+
try {
|
|
1615
|
+
const options = program.optsWithGlobals();
|
|
1616
|
+
const config2 = await importConfig(options);
|
|
1617
|
+
term.out.write(header(config2));
|
|
1618
|
+
await cb(config2, term.out.write.bind(term.out), term);
|
|
1619
|
+
} catch (error) {
|
|
1620
|
+
if (error instanceof Error) {
|
|
1621
|
+
term.out.write(dialog("error", [error.message]));
|
|
1622
|
+
} else if (typeof error === "string") {
|
|
1623
|
+
term.out.write(dialog("error", [error]));
|
|
1624
|
+
} else {
|
|
1625
|
+
term.out.write(dialog("error", [JSON.stringify(error)]));
|
|
1626
|
+
}
|
|
1627
|
+
debugError(error);
|
|
1628
|
+
} finally {
|
|
1629
|
+
debug("Exit");
|
|
1630
|
+
term.out.write(footer());
|
|
1631
|
+
term.in.unref();
|
|
1632
|
+
setTimeout(() => {
|
|
1633
|
+
process.exit(0);
|
|
1634
|
+
}, 50);
|
|
1635
|
+
}
|
|
1636
|
+
};
|
|
1637
|
+
|
|
1638
|
+
// src/cli/ui/layout/flex-line.ts
|
|
1639
|
+
var stripEscapeCode = (str) => {
|
|
1640
|
+
return str.replace(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, "");
|
|
1641
|
+
};
|
|
1642
|
+
var flexLine = (term, left, right, reserveSpace = 0) => {
|
|
1643
|
+
const deps = [...left, ...right];
|
|
1644
|
+
const strings = deps.filter((dep) => typeof dep === "string");
|
|
1645
|
+
const signals = deps.filter((dep) => dep instanceof Signal);
|
|
1646
|
+
const stringSize = stripEscapeCode(strings.join("")).length;
|
|
1647
|
+
return new Signal([
|
|
1648
|
+
...left,
|
|
1649
|
+
derive(signals, (...deps2) => {
|
|
1650
|
+
const signalSize = stripEscapeCode(deps2.join("")).length;
|
|
1651
|
+
const size = term.out.width() - signalSize - stringSize - reserveSpace;
|
|
1652
|
+
return style.placeholder("\u2500".repeat(size));
|
|
1653
|
+
}),
|
|
1654
|
+
...right
|
|
1655
|
+
]);
|
|
1656
|
+
};
|
|
1657
|
+
|
|
1658
|
+
// src/cli/ui/complex/asset.ts
|
|
1659
|
+
var assetBuilder = (assets) => {
|
|
1660
|
+
return async (term) => {
|
|
1661
|
+
const done = term.out.write(loadingDialog("Building stack assets..."));
|
|
1662
|
+
const groups = new Signal([br()]);
|
|
1663
|
+
term.out.write(groups);
|
|
1664
|
+
const stackNameSize = Math.max(...Object.keys(assets.list()).map((stack) => stack.length));
|
|
1665
|
+
await Promise.all(assets.map(async (stack, assets2) => {
|
|
1666
|
+
const group = new Signal([]);
|
|
1667
|
+
groups.update((groups2) => [...groups2, group]);
|
|
1668
|
+
await Promise.all(assets2.map(async (asset) => {
|
|
1669
|
+
const [icon, stop] = createSpinner();
|
|
1670
|
+
const start = /* @__PURE__ */ new Date();
|
|
1671
|
+
const details = new Signal({});
|
|
1672
|
+
const line = flexLine(term, [
|
|
1673
|
+
icon,
|
|
1674
|
+
" ",
|
|
1675
|
+
style.label(stack.name),
|
|
1676
|
+
" ".repeat(stackNameSize - stack.name.length),
|
|
1677
|
+
" ",
|
|
1678
|
+
style.placeholder(symbol.pointerSmall),
|
|
1679
|
+
" ",
|
|
1680
|
+
style.warning(asset.resource),
|
|
1681
|
+
" ",
|
|
1682
|
+
style.placeholder(symbol.pointerSmall),
|
|
1683
|
+
" ",
|
|
1684
|
+
style.info(asset.resourceName),
|
|
1685
|
+
" "
|
|
1686
|
+
], [
|
|
1687
|
+
" ",
|
|
1688
|
+
derive([details], (details2) => {
|
|
1689
|
+
return Object.entries(details2).map(([key, value]) => {
|
|
1690
|
+
return `${style.label(key)}: ${value}`;
|
|
1691
|
+
}).join(" / ");
|
|
1692
|
+
}),
|
|
1693
|
+
br()
|
|
1694
|
+
]);
|
|
1695
|
+
group.update((group2) => [...group2, line]);
|
|
1696
|
+
const data = await asset.build?.();
|
|
1697
|
+
const time = (/* @__PURE__ */ new Date()).getTime() - start.getTime();
|
|
1698
|
+
details.set({
|
|
1699
|
+
...data,
|
|
1700
|
+
time: style.attr(time) + style.attr.dim("ms")
|
|
1701
|
+
});
|
|
1702
|
+
icon.set(style.success(symbol.success));
|
|
1703
|
+
stop();
|
|
1704
|
+
}));
|
|
1705
|
+
}));
|
|
1706
|
+
done("Done building stack assets");
|
|
1707
|
+
};
|
|
1708
|
+
};
|
|
1709
|
+
|
|
1710
|
+
// src/util/cleanup.ts
|
|
1711
|
+
import { mkdir as mkdir2, rm } from "fs/promises";
|
|
1712
|
+
var cleanUp = async () => {
|
|
1713
|
+
debug("Clean up assembly & asset files");
|
|
1714
|
+
const paths = [
|
|
1715
|
+
assemblyDir,
|
|
1716
|
+
functionDir
|
|
1717
|
+
];
|
|
1718
|
+
await Promise.all(paths.map((path) => rm(path, {
|
|
1719
|
+
recursive: true,
|
|
1720
|
+
force: true,
|
|
1721
|
+
maxRetries: 2
|
|
1722
|
+
})));
|
|
1723
|
+
await Promise.all(paths.map((path) => mkdir2(path, {
|
|
1724
|
+
recursive: true
|
|
1725
|
+
})));
|
|
1726
|
+
};
|
|
1727
|
+
|
|
1728
|
+
// src/cli/command/build.ts
|
|
1729
|
+
var build = (program2) => {
|
|
1730
|
+
program2.command("build").argument("[stack...]", "Optionally filter stacks to build").description("Build your app").action(async (filters) => {
|
|
1731
|
+
await layout(async (config2, write) => {
|
|
1732
|
+
const { app, assets } = await toApp(config2, filters);
|
|
1733
|
+
await cleanUp();
|
|
1734
|
+
await write(assetBuilder(assets));
|
|
1735
|
+
app.synth();
|
|
1736
|
+
});
|
|
1737
|
+
});
|
|
1738
|
+
};
|
|
1739
|
+
|
|
1740
|
+
// src/stack/client.ts
|
|
1741
|
+
import { CloudFormationClient, CreateStackCommand, DeleteStackCommand, DescribeStacksCommand, GetTemplateCommand, OnFailure, TemplateStage, UpdateStackCommand, ValidateTemplateCommand, waitUntilStackCreateComplete, waitUntilStackDeleteComplete, waitUntilStackUpdateComplete } from "@aws-sdk/client-cloudformation";
|
|
1742
|
+
import { S3Client as S3Client2, PutObjectCommand as PutObjectCommand2, ObjectCannedACL as ObjectCannedACL2, StorageClass as StorageClass2 } from "@aws-sdk/client-s3";
|
|
1743
|
+
var StackClient = class {
|
|
1744
|
+
// 30 seconds
|
|
1745
|
+
constructor(config2) {
|
|
1746
|
+
this.config = config2;
|
|
1747
|
+
this.client = new CloudFormationClient({
|
|
1748
|
+
credentials: config2.credentials,
|
|
1749
|
+
region: config2.region
|
|
1750
|
+
});
|
|
1751
|
+
}
|
|
1752
|
+
client;
|
|
1753
|
+
maxWaitTime = 60 * 30;
|
|
1754
|
+
// 30 minutes
|
|
1755
|
+
maxDelay = 30;
|
|
1756
|
+
shouldUploadTemplate(stack) {
|
|
1757
|
+
const body = JSON.stringify(stack.template);
|
|
1758
|
+
const size = Buffer.byteLength(body, "utf8");
|
|
1759
|
+
return size > 5e4;
|
|
1760
|
+
}
|
|
1761
|
+
templateProp(stack) {
|
|
1762
|
+
return this.shouldUploadTemplate(stack) ? {
|
|
1763
|
+
TemplateUrl: assetBucketUrl(this.config, stack.stackName)
|
|
1764
|
+
} : {
|
|
1765
|
+
TemplateBody: JSON.stringify(stack.template)
|
|
1766
|
+
};
|
|
1767
|
+
}
|
|
1768
|
+
async upload(stack) {
|
|
1769
|
+
debug("Upload the", style.info(stack.id), "stack to awsless assets bucket");
|
|
1770
|
+
const client = new S3Client2({
|
|
1771
|
+
credentials: this.config.credentials,
|
|
1772
|
+
region: this.config.region
|
|
1773
|
+
});
|
|
1774
|
+
await client.send(new PutObjectCommand2({
|
|
1775
|
+
Bucket: assetBucketName(this.config),
|
|
1776
|
+
Key: `${stack.stackName}/cloudformation.json`,
|
|
1777
|
+
Body: JSON.stringify(stack.template),
|
|
1778
|
+
ACL: ObjectCannedACL2.private,
|
|
1779
|
+
StorageClass: StorageClass2.STANDARD_IA
|
|
1780
|
+
}));
|
|
1781
|
+
}
|
|
1782
|
+
async create(stack, capabilities) {
|
|
1783
|
+
debug("Create the", style.info(stack.id), "stack");
|
|
1784
|
+
await this.client.send(new CreateStackCommand({
|
|
1785
|
+
StackName: stack.stackName,
|
|
1786
|
+
EnableTerminationProtection: false,
|
|
1787
|
+
OnFailure: OnFailure.DELETE,
|
|
1788
|
+
Capabilities: capabilities,
|
|
1789
|
+
...this.templateProp(stack)
|
|
1790
|
+
}));
|
|
1791
|
+
await waitUntilStackCreateComplete({
|
|
1792
|
+
client: this.client,
|
|
1793
|
+
maxWaitTime: this.maxWaitTime,
|
|
1794
|
+
maxDelay: this.maxDelay
|
|
1795
|
+
}, {
|
|
1796
|
+
StackName: stack.stackName
|
|
1797
|
+
});
|
|
1798
|
+
}
|
|
1799
|
+
async update(stack, capabilities) {
|
|
1800
|
+
debug("Update the", style.info(stack.id), "stack");
|
|
1801
|
+
await this.client.send(new UpdateStackCommand({
|
|
1802
|
+
StackName: stack.stackName,
|
|
1803
|
+
Capabilities: capabilities,
|
|
1804
|
+
...this.templateProp(stack)
|
|
1805
|
+
}));
|
|
1806
|
+
await waitUntilStackUpdateComplete({
|
|
1807
|
+
client: this.client,
|
|
1808
|
+
maxWaitTime: this.maxWaitTime,
|
|
1809
|
+
maxDelay: this.maxDelay
|
|
1810
|
+
}, {
|
|
1811
|
+
StackName: stack.stackName
|
|
1812
|
+
});
|
|
1813
|
+
}
|
|
1814
|
+
async validate(stack) {
|
|
1815
|
+
debug("Validate the", style.info(stack.id), "stack");
|
|
1816
|
+
const result = await this.client.send(new ValidateTemplateCommand({
|
|
1817
|
+
...this.templateProp(stack)
|
|
1818
|
+
}));
|
|
1819
|
+
return result.Capabilities;
|
|
1820
|
+
}
|
|
1821
|
+
async get(name) {
|
|
1822
|
+
debug("Get stack info for:", style.info(name));
|
|
1823
|
+
let result;
|
|
1824
|
+
try {
|
|
1825
|
+
result = await this.client.send(new DescribeStacksCommand({
|
|
1826
|
+
StackName: name
|
|
1827
|
+
}));
|
|
1828
|
+
} catch (error) {
|
|
1829
|
+
if (error instanceof Error && error.name === "ValidationError" && error.message.includes("does not exist")) {
|
|
1830
|
+
return;
|
|
1831
|
+
}
|
|
1832
|
+
throw error;
|
|
1833
|
+
}
|
|
1834
|
+
const stack = result.Stacks?.[0];
|
|
1835
|
+
if (!stack) {
|
|
1836
|
+
debug("Stack not found");
|
|
1837
|
+
return;
|
|
1838
|
+
}
|
|
1839
|
+
const resultTemplate = await this.client.send(new GetTemplateCommand({
|
|
1840
|
+
StackName: name,
|
|
1841
|
+
TemplateStage: TemplateStage.Original
|
|
1842
|
+
}));
|
|
1843
|
+
const outputs = {};
|
|
1844
|
+
stack.Outputs?.forEach((output) => {
|
|
1845
|
+
outputs[output.OutputKey] = output.OutputValue;
|
|
1846
|
+
});
|
|
1847
|
+
debug("Status for: ", style.info(name), "is", stack.StackStatus);
|
|
1848
|
+
return {
|
|
1849
|
+
status: stack.StackStatus,
|
|
1850
|
+
reason: stack.StackStatusReason,
|
|
1851
|
+
outputs,
|
|
1852
|
+
template: resultTemplate.TemplateBody,
|
|
1853
|
+
updatedAt: stack.LastUpdatedTime || stack.CreationTime,
|
|
1854
|
+
createdAt: stack.CreationTime
|
|
1855
|
+
};
|
|
1856
|
+
}
|
|
1857
|
+
async deploy(stack) {
|
|
1858
|
+
const data = await this.get(stack.stackName);
|
|
1859
|
+
debug("Deploy:", style.info(stack.stackName));
|
|
1860
|
+
if (data?.template === JSON.stringify(stack.template)) {
|
|
1861
|
+
debug("No stack changes");
|
|
1862
|
+
return false;
|
|
1863
|
+
}
|
|
1864
|
+
if (this.shouldUploadTemplate(stack)) {
|
|
1865
|
+
await this.upload(stack);
|
|
1866
|
+
}
|
|
1867
|
+
const capabilities = await this.validate(stack);
|
|
1868
|
+
if (!data) {
|
|
1869
|
+
await this.create(stack, capabilities);
|
|
1870
|
+
} else if (data.status.includes("IN_PROGRESS")) {
|
|
1871
|
+
throw new Error(`Stack is in progress: ${data.status}`);
|
|
1872
|
+
} else {
|
|
1873
|
+
await this.update(stack, capabilities);
|
|
1874
|
+
}
|
|
1875
|
+
return true;
|
|
1876
|
+
}
|
|
1877
|
+
async delete(name) {
|
|
1878
|
+
const data = await this.get(name);
|
|
1879
|
+
debug("Delete the", style.info(name), "stack");
|
|
1880
|
+
if (!data) {
|
|
1881
|
+
debug("Already deleted");
|
|
1882
|
+
return;
|
|
1883
|
+
}
|
|
1884
|
+
await this.client.send(new DeleteStackCommand({
|
|
1885
|
+
StackName: name
|
|
1886
|
+
}));
|
|
1887
|
+
await waitUntilStackDeleteComplete({
|
|
1888
|
+
client: this.client,
|
|
1889
|
+
maxWaitTime: this.maxWaitTime,
|
|
1890
|
+
maxDelay: this.maxDelay
|
|
1891
|
+
}, {
|
|
1892
|
+
StackName: name
|
|
1893
|
+
});
|
|
1894
|
+
}
|
|
1895
|
+
};
|
|
1896
|
+
|
|
1897
|
+
// src/cli/error.ts
|
|
1898
|
+
var Cancelled = class extends Error {
|
|
1899
|
+
constructor() {
|
|
1900
|
+
super("Cancelled");
|
|
1901
|
+
}
|
|
1902
|
+
};
|
|
1903
|
+
|
|
1904
|
+
// src/cli/ui/prompt/toggle.ts
|
|
1905
|
+
var togglePrompt = (label, options = {}) => {
|
|
1906
|
+
return (term) => new Promise((resolve) => {
|
|
1907
|
+
const { initial = false, active = "on", inactive = "off" } = options;
|
|
1908
|
+
const icon = new Signal(style.info(symbol.question));
|
|
1909
|
+
const sep = new Signal(style.placeholder(symbol.pointerSmall));
|
|
1910
|
+
const mid = style.placeholder("/");
|
|
1911
|
+
const activeText = new Signal(active);
|
|
1912
|
+
const inactiveText = new Signal(inactive);
|
|
1913
|
+
let value = initial;
|
|
1914
|
+
const activate = () => {
|
|
1915
|
+
activeText.set(style.success.underline(active));
|
|
1916
|
+
inactiveText.set(style.normal(inactive));
|
|
1917
|
+
value = true;
|
|
1918
|
+
};
|
|
1919
|
+
const deactivate = () => {
|
|
1920
|
+
activeText.set(style.normal(active));
|
|
1921
|
+
inactiveText.set(style.success.underline(inactive));
|
|
1922
|
+
value = false;
|
|
1923
|
+
};
|
|
1924
|
+
const toggle = () => {
|
|
1925
|
+
!value ? activate() : deactivate();
|
|
1926
|
+
};
|
|
1927
|
+
const reset = () => {
|
|
1928
|
+
initial ? activate() : deactivate();
|
|
1929
|
+
};
|
|
1930
|
+
reset();
|
|
1931
|
+
const release = term.in.captureInput({
|
|
1932
|
+
reset,
|
|
1933
|
+
exit() {
|
|
1934
|
+
release();
|
|
1935
|
+
icon.set(style.error(symbol.error));
|
|
1936
|
+
sep.set(symbol.ellipsis);
|
|
1937
|
+
resolve(false);
|
|
1938
|
+
},
|
|
1939
|
+
submit() {
|
|
1940
|
+
release();
|
|
1941
|
+
icon.set(style.success(symbol.success));
|
|
1942
|
+
sep.set(symbol.ellipsis);
|
|
1943
|
+
resolve(value);
|
|
1944
|
+
},
|
|
1945
|
+
input(chr) {
|
|
1946
|
+
switch (chr) {
|
|
1947
|
+
case " ":
|
|
1948
|
+
toggle();
|
|
1949
|
+
break;
|
|
1950
|
+
case "1":
|
|
1951
|
+
activate();
|
|
1952
|
+
break;
|
|
1953
|
+
case "0":
|
|
1954
|
+
deactivate();
|
|
1955
|
+
break;
|
|
1956
|
+
}
|
|
1957
|
+
},
|
|
1958
|
+
delete: deactivate,
|
|
1959
|
+
left: deactivate,
|
|
1960
|
+
right: activate,
|
|
1961
|
+
down: deactivate,
|
|
1962
|
+
up: activate
|
|
1963
|
+
});
|
|
1964
|
+
term.out.write([icon, " ", style.label(label), " ", sep, " ", inactiveText, " ", mid, " ", activeText, br()]);
|
|
1965
|
+
});
|
|
1966
|
+
};
|
|
1967
|
+
|
|
1968
|
+
// src/cli/ui/prompt/confirm.ts
|
|
1969
|
+
var confirmPrompt = (label, options = {}) => {
|
|
1970
|
+
return togglePrompt(label, {
|
|
1971
|
+
...options,
|
|
1972
|
+
inactive: "no",
|
|
1973
|
+
active: "yes"
|
|
1974
|
+
});
|
|
1975
|
+
};
|
|
1976
|
+
|
|
1977
|
+
// src/cli/ui/complex/bootstrap.ts
|
|
1978
|
+
var bootstrapDeployer = (config2) => {
|
|
1979
|
+
return async (term) => {
|
|
1980
|
+
debug("Initializing bootstrap");
|
|
1981
|
+
const app = makeApp(config2);
|
|
1982
|
+
const client = new StackClient(config2);
|
|
1983
|
+
const bootstrap2 = bootstrapStack(config2, app);
|
|
1984
|
+
const shouldDeploy = await shouldDeployBootstrap(client, bootstrap2.stackName);
|
|
1985
|
+
if (shouldDeploy) {
|
|
1986
|
+
term.out.write(dialog("warning", [`Your app hasn't been bootstrapped yet`]));
|
|
1987
|
+
const confirmed = await term.out.write(confirmPrompt("Would you like to bootstrap?"));
|
|
1988
|
+
if (!confirmed) {
|
|
1989
|
+
throw new Cancelled();
|
|
1990
|
+
}
|
|
1991
|
+
const done = term.out.write(loadingDialog("Bootstrapping..."));
|
|
1992
|
+
const assembly = app.synth();
|
|
1993
|
+
await client.deploy(assembly.stacks[0]);
|
|
1994
|
+
done("Done deploying the bootstrap stack");
|
|
1995
|
+
} else {
|
|
1996
|
+
term.out.write(dialog("success", [
|
|
1997
|
+
"App has already been bootstrapped"
|
|
1998
|
+
]));
|
|
1999
|
+
}
|
|
2000
|
+
debug("Bootstrap initialized");
|
|
2001
|
+
};
|
|
2002
|
+
};
|
|
2003
|
+
|
|
2004
|
+
// src/cli/command/bootstrap.ts
|
|
2005
|
+
var bootstrap = (program2) => {
|
|
2006
|
+
program2.command("bootstrap").description("Create the awsless bootstrap stack").action(async () => {
|
|
2007
|
+
await layout(async (config2, write) => {
|
|
2008
|
+
await write(bootstrapDeployer(config2));
|
|
2009
|
+
});
|
|
2010
|
+
});
|
|
2011
|
+
};
|
|
2012
|
+
|
|
2013
|
+
// src/cli/ui/complex/stack-tree.ts
|
|
2014
|
+
var stackTree = (nodes, statuses) => {
|
|
2015
|
+
return (term) => {
|
|
2016
|
+
const render = (nodes2, deep = 0, parents = []) => {
|
|
2017
|
+
const size = nodes2.length - 1;
|
|
2018
|
+
nodes2.forEach((node, i) => {
|
|
2019
|
+
const id = node.stack.artifactId;
|
|
2020
|
+
const status2 = statuses[id];
|
|
2021
|
+
const first = i === 0 && deep === 0;
|
|
2022
|
+
const last = i === size;
|
|
2023
|
+
const more = i < size;
|
|
2024
|
+
const line = flexLine(term, [
|
|
2025
|
+
...parents.map((parent) => {
|
|
2026
|
+
return style.label(
|
|
2027
|
+
parent ? "\u2502".padEnd(3) : " ".repeat(3)
|
|
2028
|
+
);
|
|
2029
|
+
}),
|
|
2030
|
+
style.label(
|
|
2031
|
+
first && size === 0 ? " " : first ? "\u250C\u2500" : last ? "\u2514\u2500" : "\u251C\u2500"
|
|
2032
|
+
),
|
|
2033
|
+
" ",
|
|
2034
|
+
style.info(id),
|
|
2035
|
+
" "
|
|
2036
|
+
], [
|
|
2037
|
+
// style.placeholder(' [ '),
|
|
2038
|
+
" ",
|
|
2039
|
+
status2,
|
|
2040
|
+
// style.placeholder(' ] '),
|
|
2041
|
+
br()
|
|
2042
|
+
]);
|
|
2043
|
+
term.out.write(line);
|
|
2044
|
+
render(node.children, deep + 1, [...parents, more]);
|
|
2045
|
+
});
|
|
2046
|
+
};
|
|
2047
|
+
render(nodes);
|
|
2048
|
+
};
|
|
2049
|
+
};
|
|
2050
|
+
|
|
2051
|
+
// src/cli/ui/__components/basic.ts
|
|
2052
|
+
var br2 = () => {
|
|
2053
|
+
return "\n";
|
|
2054
|
+
};
|
|
2055
|
+
|
|
2056
|
+
// src/cli/ui/__components/spinner.ts
|
|
2057
|
+
var frames2 = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
2058
|
+
var length2 = frames2.length;
|
|
2059
|
+
var createSpinner2 = () => {
|
|
2060
|
+
const index = new Signal(0);
|
|
2061
|
+
const frame = derive([index], (index2) => style.info(frames2[index2 % length2]));
|
|
2062
|
+
const interval = setInterval(() => {
|
|
2063
|
+
index.update((i) => i + 1);
|
|
2064
|
+
}, 80);
|
|
2065
|
+
return [
|
|
2066
|
+
frame,
|
|
2067
|
+
() => {
|
|
2068
|
+
clearInterval(interval);
|
|
2069
|
+
}
|
|
2070
|
+
];
|
|
2071
|
+
};
|
|
2072
|
+
|
|
2073
|
+
// src/cli/ui/__components/dialog.ts
|
|
2074
|
+
var dialog2 = (type, lines) => {
|
|
2075
|
+
const padding = 3;
|
|
2076
|
+
const icon = style[type](symbol[type].padEnd(padding));
|
|
2077
|
+
return lines.map((line, i) => {
|
|
2078
|
+
if (i === 0) {
|
|
2079
|
+
return icon + line;
|
|
2080
|
+
} else {
|
|
2081
|
+
return " ".repeat(padding) + line;
|
|
2082
|
+
}
|
|
2083
|
+
}).join(br2()) + br2();
|
|
2084
|
+
};
|
|
2085
|
+
var loadingDialog2 = (message) => {
|
|
2086
|
+
const [icon, stop] = createSpinner2();
|
|
2087
|
+
const description = new Signal(message);
|
|
2088
|
+
const time = new Signal("");
|
|
2089
|
+
const start = /* @__PURE__ */ new Date();
|
|
2090
|
+
return (term) => {
|
|
2091
|
+
term.out.write([
|
|
2092
|
+
icon,
|
|
2093
|
+
" ",
|
|
2094
|
+
description,
|
|
2095
|
+
" ",
|
|
2096
|
+
time,
|
|
2097
|
+
br2()
|
|
2098
|
+
]);
|
|
2099
|
+
return (message2) => {
|
|
2100
|
+
const end = /* @__PURE__ */ new Date();
|
|
2101
|
+
const diff = end.getTime() - start.getTime();
|
|
2102
|
+
description.set(message2);
|
|
2103
|
+
time.set(style.attr(diff) + style.attr.dim("ms"));
|
|
2104
|
+
stop();
|
|
2105
|
+
icon.set(style.success(symbol.success));
|
|
2106
|
+
};
|
|
2107
|
+
};
|
|
2108
|
+
};
|
|
2109
|
+
|
|
2110
|
+
// src/cli/command/status.ts
|
|
2111
|
+
var status = (program2) => {
|
|
2112
|
+
program2.command("status").argument("[stacks...]", "Optionally filter stacks to lookup status").description("View the application status").action(async (filters) => {
|
|
2113
|
+
await layout(async (config2, write) => {
|
|
2114
|
+
const { app, assets, dependencyTree } = await toApp(config2, filters);
|
|
2115
|
+
await cleanUp();
|
|
2116
|
+
await write(assetBuilder(assets));
|
|
2117
|
+
write(br2());
|
|
2118
|
+
const assembly = app.synth();
|
|
2119
|
+
const doneLoading = write(loadingDialog2("Loading stack information..."));
|
|
2120
|
+
const client = new StackClient(config2);
|
|
2121
|
+
const statuses = [];
|
|
2122
|
+
const stackStatuses = {};
|
|
2123
|
+
assembly.stacks.forEach((stack) => {
|
|
2124
|
+
stackStatuses[stack.id] = new Signal(style.info("Loading..."));
|
|
2125
|
+
});
|
|
2126
|
+
write(br2());
|
|
2127
|
+
write(stackTree(dependencyTree, stackStatuses));
|
|
2128
|
+
write(br2());
|
|
2129
|
+
debug("Load metadata for all deployed stacks on AWS");
|
|
2130
|
+
await Promise.all(assembly.stacks.map(async (stack, i) => {
|
|
2131
|
+
const info = await client.get(stack.stackName);
|
|
2132
|
+
const name = stack.id;
|
|
2133
|
+
const signal = stackStatuses[name];
|
|
2134
|
+
await new Promise((resolve) => setTimeout(resolve, i * 1e3));
|
|
2135
|
+
if (!info) {
|
|
2136
|
+
signal.set(style.error("non-existent"));
|
|
2137
|
+
statuses.push("non-existent");
|
|
2138
|
+
} else if (info.template !== JSON.stringify(stack.template)) {
|
|
2139
|
+
signal.set(style.warning("out-of-date"));
|
|
2140
|
+
statuses.push("out-of-date");
|
|
2141
|
+
} else {
|
|
2142
|
+
signal.set(style.success("up-to-date"));
|
|
2143
|
+
statuses.push("up-to-date");
|
|
2144
|
+
}
|
|
2145
|
+
}));
|
|
2146
|
+
doneLoading("Done loading stack information");
|
|
2147
|
+
debug("Done loading data for all deployed stacks on AWS");
|
|
2148
|
+
if (statuses.includes("non-existent") || statuses.includes("out-of-date")) {
|
|
2149
|
+
write(dialog2("warning", ["Your app has undeployed changes !!!"]));
|
|
2150
|
+
} else {
|
|
2151
|
+
write(dialog2("success", ["Your app has not been changed"]));
|
|
2152
|
+
}
|
|
2153
|
+
});
|
|
2154
|
+
});
|
|
2155
|
+
};
|
|
2156
|
+
|
|
2157
|
+
// src/cli/command/deploy.ts
|
|
2158
|
+
var deploy = (program2) => {
|
|
2159
|
+
program2.command("deploy").argument("[stacks...]", "Optionally filter stacks to deploy").description("Deploy your app to AWS").action(async (filters) => {
|
|
2160
|
+
await layout(async (config2, write) => {
|
|
2161
|
+
await write(bootstrapDeployer(config2));
|
|
2162
|
+
const { app, stackNames, assets, dependencyTree } = await toApp(config2, filters);
|
|
2163
|
+
const formattedFilter = stackNames.map((i) => style.info(i)).join(style.placeholder(", "));
|
|
2164
|
+
debug("Stacks to deploy", formattedFilter);
|
|
2165
|
+
const deployAll = filters.length === 0;
|
|
2166
|
+
const deploySingle = filters.length === 1;
|
|
2167
|
+
const confirm = await write(confirmPrompt(deployAll ? `Are you sure you want to deploy ${style.warning("all")} stacks?` : deploySingle ? `Are you sure you want to deploy the ${formattedFilter} stack?` : `Are you sure you want to deploy the [ ${formattedFilter} ] stacks?`));
|
|
2168
|
+
if (!confirm) {
|
|
2169
|
+
throw new Cancelled();
|
|
2170
|
+
}
|
|
2171
|
+
await cleanUp();
|
|
2172
|
+
await write(assetBuilder(assets));
|
|
2173
|
+
write(br());
|
|
2174
|
+
const donePublishing = write(loadingDialog("Publishing stack assets to AWS..."));
|
|
2175
|
+
await Promise.all(assets.map(async (_, assets2) => {
|
|
2176
|
+
await Promise.all(assets2.map(async (asset) => {
|
|
2177
|
+
await asset.publish?.();
|
|
2178
|
+
}));
|
|
2179
|
+
}));
|
|
2180
|
+
donePublishing("Done publishing stack assets to AWS");
|
|
2181
|
+
const assembly = app.synth();
|
|
2182
|
+
const statuses = {};
|
|
2183
|
+
assembly.stacks.map((stack) => {
|
|
2184
|
+
statuses[stack.id] = new Signal(style.info("waiting"));
|
|
2185
|
+
});
|
|
2186
|
+
const doneDeploying = write(loadingDialog("Deploying stacks to AWS..."));
|
|
2187
|
+
write(br());
|
|
2188
|
+
write(stackTree(dependencyTree, statuses));
|
|
2189
|
+
const client = new StackClient(config2);
|
|
2190
|
+
const deploymentLine = createDeploymentLine(dependencyTree);
|
|
2191
|
+
for (const stacks of deploymentLine) {
|
|
2192
|
+
await Promise.allSettled(stacks.map(async (stack) => {
|
|
2193
|
+
const stackArtifect = assembly.stacks.find((item) => item.id === stack.artifactId);
|
|
2194
|
+
statuses[stack.artifactId].set(style.warning("deploying"));
|
|
2195
|
+
try {
|
|
2196
|
+
await client.deploy(stackArtifect);
|
|
2197
|
+
} catch (error) {
|
|
2198
|
+
debugError(error);
|
|
2199
|
+
statuses[stack.artifactId].set(style.error("failed"));
|
|
2200
|
+
throw error;
|
|
2201
|
+
}
|
|
2202
|
+
statuses[stack.artifactId].set(style.success("deployed"));
|
|
2203
|
+
}));
|
|
2204
|
+
}
|
|
2205
|
+
doneDeploying("Done deploying stacks to AWS");
|
|
2206
|
+
});
|
|
2207
|
+
});
|
|
2208
|
+
};
|
|
2209
|
+
|
|
2210
|
+
// src/cli/ui/prompt/text.ts
|
|
2211
|
+
var textPrompt = (label, options = {}) => {
|
|
2212
|
+
return (term) => {
|
|
2213
|
+
return new Promise((resolve) => {
|
|
2214
|
+
const done = new Signal(false);
|
|
2215
|
+
const cursor = new Signal(0);
|
|
2216
|
+
const icon = new Signal(style.info(symbol.question));
|
|
2217
|
+
const value = new Signal([]);
|
|
2218
|
+
const custom = derive([value], options.renderer ?? ((value2) => value2));
|
|
2219
|
+
const formatted = derive([custom, cursor, done], (value2, cursor2, done2) => {
|
|
2220
|
+
if (done2) {
|
|
2221
|
+
return value2.join("");
|
|
2222
|
+
}
|
|
2223
|
+
return [...value2, " "].map((chr, i) => {
|
|
2224
|
+
return i === cursor2 ? style.cursor(chr) : chr;
|
|
2225
|
+
}).join("");
|
|
2226
|
+
});
|
|
2227
|
+
const sep = new Signal(style.placeholder(symbol.pointerSmall));
|
|
2228
|
+
const release = term.in.captureInput({
|
|
2229
|
+
reset() {
|
|
2230
|
+
value.set([]);
|
|
2231
|
+
cursor.set(0);
|
|
2232
|
+
},
|
|
2233
|
+
exit() {
|
|
2234
|
+
release();
|
|
2235
|
+
done.set(true);
|
|
2236
|
+
icon.set(style.success(symbol.success));
|
|
2237
|
+
sep.set(symbol.ellipsis);
|
|
2238
|
+
value.set([]);
|
|
2239
|
+
resolve("");
|
|
2240
|
+
},
|
|
2241
|
+
submit() {
|
|
2242
|
+
release();
|
|
2243
|
+
done.set(true);
|
|
2244
|
+
icon.set(style.success(symbol.success));
|
|
2245
|
+
sep.set(symbol.ellipsis);
|
|
2246
|
+
resolve(value.get().join(""));
|
|
2247
|
+
},
|
|
2248
|
+
input: (chr) => {
|
|
2249
|
+
value.update((value2) => [
|
|
2250
|
+
...value2.slice(0, cursor.get()),
|
|
2251
|
+
chr,
|
|
2252
|
+
...value2.slice(cursor.get())
|
|
2253
|
+
]);
|
|
2254
|
+
cursor.update((cursor2) => cursor2 + 1);
|
|
2255
|
+
},
|
|
2256
|
+
delete() {
|
|
2257
|
+
value.update((value2) => [...value2].filter((_, i) => i !== cursor.get() - 1));
|
|
2258
|
+
cursor.update((cursor2) => Math.max(0, cursor2 - 1));
|
|
2259
|
+
},
|
|
2260
|
+
left() {
|
|
2261
|
+
cursor.update((cursor2) => Math.max(0, cursor2 - 1));
|
|
2262
|
+
},
|
|
2263
|
+
right() {
|
|
2264
|
+
cursor.update((cursor2) => Math.min(value.get().length, cursor2 + 1));
|
|
2265
|
+
}
|
|
2266
|
+
});
|
|
2267
|
+
term.out.write([icon, " ", style.label(label), " ", sep, " ", formatted, br()]);
|
|
2268
|
+
});
|
|
2269
|
+
};
|
|
2270
|
+
};
|
|
2271
|
+
|
|
2272
|
+
// src/cli/command/config/set.ts
|
|
2273
|
+
var set = (program2) => {
|
|
2274
|
+
program2.command("set <name>").description("Set a config value").action(async (name) => {
|
|
2275
|
+
await layout(async (config2, write) => {
|
|
2276
|
+
const params = new Params(config2);
|
|
2277
|
+
write(list({
|
|
2278
|
+
"Set config parameter": style.info(name)
|
|
2279
|
+
}));
|
|
2280
|
+
write(br());
|
|
2281
|
+
const value = await write(textPrompt("Enter config value"));
|
|
2282
|
+
if (value === "") {
|
|
2283
|
+
write(dialog("error", [`Provided config value can't be empty`]));
|
|
2284
|
+
} else {
|
|
2285
|
+
const done = write(loadingDialog(`Saving remote config parameter`));
|
|
2286
|
+
await params.set(name, value);
|
|
2287
|
+
done(`Done saving remote config parameter`);
|
|
2288
|
+
}
|
|
2289
|
+
});
|
|
2290
|
+
});
|
|
2291
|
+
};
|
|
2292
|
+
|
|
2293
|
+
// src/cli/command/config/get.ts
|
|
2294
|
+
var get = (program2) => {
|
|
2295
|
+
program2.command("get <name>").description("Get a config value").action(async (name) => {
|
|
2296
|
+
await layout(async (config2, write) => {
|
|
2297
|
+
const params = new Params(config2);
|
|
2298
|
+
const done = write(loadingDialog(`Getting remote config parameter`));
|
|
2299
|
+
const value = await params.get(name);
|
|
2300
|
+
done(`Done getting remote config parameter`);
|
|
2301
|
+
write(br());
|
|
2302
|
+
write(list({
|
|
2303
|
+
Name: name,
|
|
2304
|
+
Value: value || style.error("(empty)")
|
|
2305
|
+
}));
|
|
2306
|
+
});
|
|
2307
|
+
});
|
|
2308
|
+
};
|
|
2309
|
+
|
|
2310
|
+
// src/cli/command/config/delete.ts
|
|
2311
|
+
var del = (program2) => {
|
|
2312
|
+
program2.command("delete <name>").description("Delete a config value").action(async (name) => {
|
|
2313
|
+
await layout(async (config2, write) => {
|
|
2314
|
+
const params = new Params(config2);
|
|
2315
|
+
write(dialog("warning", [`Your deleting the ${style.info(name)} config parameter`]));
|
|
2316
|
+
const confirm = await write(confirmPrompt("Are you sure?"));
|
|
2317
|
+
if (!confirm) {
|
|
2318
|
+
throw new Cancelled();
|
|
2319
|
+
}
|
|
2320
|
+
const done = write(loadingDialog(`Deleting remote config parameter`));
|
|
2321
|
+
const value = await params.get(name);
|
|
2322
|
+
await params.delete(name);
|
|
2323
|
+
done(`Done deleting remote config parameter`);
|
|
2324
|
+
write(br());
|
|
2325
|
+
write(list({
|
|
2326
|
+
Name: name,
|
|
2327
|
+
Value: value || style.error("(empty)")
|
|
2328
|
+
}));
|
|
2329
|
+
});
|
|
2330
|
+
});
|
|
2331
|
+
};
|
|
2332
|
+
|
|
2333
|
+
// src/cli/command/config/list.ts
|
|
2334
|
+
var list2 = (program2) => {
|
|
2335
|
+
program2.command("list").description(`List all config value's`).action(async () => {
|
|
2336
|
+
await layout(async (config2, write) => {
|
|
2337
|
+
const params = new Params(config2);
|
|
2338
|
+
const done = write(loadingDialog("Loading config parameters..."));
|
|
2339
|
+
const values = await params.list();
|
|
2340
|
+
done("Done loading config values");
|
|
2341
|
+
if (Object.keys(values).length > 0) {
|
|
2342
|
+
write(br());
|
|
2343
|
+
write(list(values));
|
|
2344
|
+
} else {
|
|
2345
|
+
write(dialog("warning", ["No config parameters found"]));
|
|
2346
|
+
}
|
|
2347
|
+
});
|
|
2348
|
+
});
|
|
2349
|
+
};
|
|
2350
|
+
|
|
2351
|
+
// src/cli/command/config/index.ts
|
|
2352
|
+
var commands = [
|
|
2353
|
+
set,
|
|
2354
|
+
get,
|
|
2355
|
+
del,
|
|
2356
|
+
list2
|
|
2357
|
+
];
|
|
2358
|
+
var config = (program2) => {
|
|
2359
|
+
const command = program2.command("config").description("Manage config values");
|
|
2360
|
+
commands.forEach((cb) => cb(command));
|
|
2361
|
+
};
|
|
2362
|
+
|
|
2363
|
+
// src/cli/program.ts
|
|
2364
|
+
var program = new Command();
|
|
2365
|
+
program.name("awsless");
|
|
2366
|
+
program.option("--config-file <string>", "The config file location");
|
|
2367
|
+
program.option("--stage <string>", "The stage to use, defaults to prod stage", "prod");
|
|
2368
|
+
program.option("--profile <string>", "The AWS profile to use");
|
|
2369
|
+
program.option("--region <string>", "The AWS region to use");
|
|
2370
|
+
program.option("-m --mute", "Mute sound effects");
|
|
2371
|
+
program.option("-v --verbose", "Print verbose logs");
|
|
2372
|
+
program.on("option:verbose", () => {
|
|
2373
|
+
process.env.VERBOSE = program.opts().verbose ? "1" : void 0;
|
|
2374
|
+
});
|
|
2375
|
+
var commands2 = [
|
|
2376
|
+
bootstrap,
|
|
2377
|
+
status,
|
|
2378
|
+
build,
|
|
2379
|
+
deploy,
|
|
2380
|
+
config
|
|
2381
|
+
// diff,
|
|
2382
|
+
// remove,
|
|
2383
|
+
// test,
|
|
2384
|
+
// test2,
|
|
2385
|
+
];
|
|
2386
|
+
commands2.forEach((command) => command(program));
|
|
2387
|
+
|
|
2388
|
+
// src/bin.ts
|
|
2389
|
+
program.parse(process.argv);
|