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