@awsless/awsless 0.0.14 → 0.0.15

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 CHANGED
@@ -26,13 +26,6 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
26
26
  // src/cli/program.ts
27
27
  var import_commander = require("commander");
28
28
 
29
- // src/app.ts
30
- var import_aws_cdk_lib9 = 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
29
  // src/cli/style.ts
37
30
  var import_chalk = __toESM(require("chalk"), 1);
38
31
  var symbol = {
@@ -84,7 +77,7 @@ var flushDebug = () => {
84
77
  // src/util/param.ts
85
78
  var import_client_ssm = require("@aws-sdk/client-ssm");
86
79
  var configParameterPrefix = (config) => {
87
- return `/${config.stage}/awsless/${config.name}`;
80
+ return `/awsless/${config.name}/${config.stage}`;
88
81
  };
89
82
  var Params = class {
90
83
  constructor(config) {
@@ -165,85 +158,411 @@ var Params = class {
165
158
  }
166
159
  };
167
160
 
168
- // src/stack.ts
169
- var toStack = ({ config, assets, app, stackConfig, plugins }) => {
170
- const stackName = `${config.name}-${stackConfig.name}`;
171
- const stack = new import_aws_cdk_lib.Stack(app, stackConfig.name, {
172
- stackName,
173
- env: {
174
- account: config.account,
175
- region: config.region
176
- },
177
- tags: {
178
- APP: config.name,
179
- STAGE: config.stage,
180
- STACK: stackConfig.name
161
+ // src/formation/util.ts
162
+ var import_change_case = require("change-case");
163
+ var ref = (logicalId) => {
164
+ return { Ref: logicalId };
165
+ };
166
+ var sub = (value, params) => {
167
+ if (params) {
168
+ return { "Fn::Sub": [value, params] };
169
+ }
170
+ return { "Fn::Sub": value };
171
+ };
172
+ var getAtt = (logicalId, attr) => {
173
+ return { "Fn::GetAtt": [logicalId, attr] };
174
+ };
175
+ var importValue = (name) => {
176
+ return { "Fn::ImportValue": name };
177
+ };
178
+ var formatLogicalId = (id) => {
179
+ return (0, import_change_case.pascalCase)(id);
180
+ };
181
+ var formatName = (name) => {
182
+ return (0, import_change_case.paramCase)(name);
183
+ };
184
+
185
+ // src/formation/resource.ts
186
+ var Resource = class {
187
+ constructor(type, logicalId, children = []) {
188
+ this.type = type;
189
+ this.children = children;
190
+ this.logicalId = formatLogicalId(`${logicalId}-${type.replace(/^AWS::/, "")}`);
191
+ }
192
+ logicalId;
193
+ deps = /* @__PURE__ */ new Set();
194
+ dependsOn(...dependencies) {
195
+ for (const dependency of dependencies) {
196
+ this.deps.add(dependency);
181
197
  }
182
- });
183
- debug("Define stack:", style.info(stackConfig.name));
198
+ return this;
199
+ }
200
+ attr(name, value) {
201
+ if (typeof value === "undefined") {
202
+ return {};
203
+ }
204
+ return {
205
+ [name]: value
206
+ };
207
+ }
208
+ toJSON() {
209
+ return {
210
+ [this.logicalId]: {
211
+ Type: this.type,
212
+ DependsOn: [...this.deps].map((dep) => dep.logicalId),
213
+ Properties: this.properties()
214
+ }
215
+ };
216
+ }
217
+ };
218
+ var Group = class {
219
+ constructor(children) {
220
+ this.children = children;
221
+ }
222
+ };
223
+
224
+ // src/formation/resource/iam/inline-policy.ts
225
+ var InlinePolicy = class {
226
+ constructor(name, props = {}) {
227
+ this.name = name;
228
+ this.statements = props.statements || [];
229
+ }
230
+ statements;
231
+ addStatement(...statements) {
232
+ this.statements.push(...statements.flat());
233
+ return this;
234
+ }
235
+ toJSON() {
236
+ return {
237
+ PolicyName: this.name,
238
+ PolicyDocument: {
239
+ Version: "2012-10-17",
240
+ Statement: this.statements.map((statement) => ({
241
+ Effect: statement.effect || "Allow",
242
+ Action: statement.actions,
243
+ Resource: statement.resources
244
+ }))
245
+ }
246
+ };
247
+ }
248
+ };
249
+
250
+ // src/formation/resource/iam/managed-policy.ts
251
+ var ManagedPolicy = class {
252
+ constructor(arn) {
253
+ this.arn = arn;
254
+ }
255
+ static fromAwsManagedPolicyName(name) {
256
+ const arn = sub("arn:${AWS::Partition}:iam::aws:policy/service-role/" + name);
257
+ return new ManagedPolicy(arn);
258
+ }
259
+ static fromManagedPolicyArn(arn) {
260
+ return new ManagedPolicy(arn);
261
+ }
262
+ };
263
+
264
+ // src/formation/resource/iam/role.ts
265
+ var Role = class extends Resource {
266
+ constructor(logicalId, props = {}) {
267
+ super("AWS::IAM::Role", logicalId);
268
+ this.props = props;
269
+ this.name = formatName(logicalId);
270
+ }
271
+ name;
272
+ inlinePolicies = /* @__PURE__ */ new Set();
273
+ managedPolicies = /* @__PURE__ */ new Set();
274
+ get arn() {
275
+ return getAtt(this.logicalId, "Arn");
276
+ }
277
+ addManagedPolicy(...policies) {
278
+ for (const policy of policies) {
279
+ this.managedPolicies.add(policy);
280
+ }
281
+ return this;
282
+ }
283
+ addInlinePolicy(...policies) {
284
+ for (const policy of policies) {
285
+ this.inlinePolicies.add(policy);
286
+ }
287
+ return this;
288
+ }
289
+ properties() {
290
+ return {
291
+ ...this.props.assumedBy ? {
292
+ AssumeRolePolicyDocument: {
293
+ Version: "2012-10-17",
294
+ Statement: [{
295
+ Action: "sts:AssumeRole",
296
+ Effect: "Allow",
297
+ Principal: {
298
+ Service: this.props.assumedBy
299
+ }
300
+ }]
301
+ }
302
+ } : {},
303
+ ManagedPolicyArns: [...this.managedPolicies].map((policy) => policy.arn),
304
+ Policies: [...this.inlinePolicies].map((policy) => policy.toJSON())
305
+ };
306
+ }
307
+ };
308
+
309
+ // src/formation/resource/lambda/function.ts
310
+ var Function = class extends Resource {
311
+ constructor(logicalId, props) {
312
+ const policy = new InlinePolicy(logicalId);
313
+ const role = new Role(logicalId, {
314
+ assumedBy: "lambda.amazonaws.com"
315
+ });
316
+ role.addInlinePolicy(policy);
317
+ role.addManagedPolicy(ManagedPolicy.fromAwsManagedPolicyName("AWSLambdaBasicExecutionRole"));
318
+ super("AWS::Lambda::Function", logicalId, [
319
+ role,
320
+ props.code
321
+ ]);
322
+ this.props = props;
323
+ this.dependsOn(role);
324
+ this.role = role;
325
+ this.policy = policy;
326
+ this.name = formatName(this.props.name || logicalId);
327
+ this.environmentVariables = props.environment ? { ...props.environment } : {};
328
+ }
329
+ name;
330
+ role;
331
+ policy;
332
+ environmentVariables;
333
+ addPermissions(...permissions) {
334
+ this.policy.addStatement(...permissions);
335
+ return this;
336
+ }
337
+ addEnvironment(name, value) {
338
+ this.environmentVariables[name] = value;
339
+ return this;
340
+ }
341
+ get id() {
342
+ return ref(this.logicalId);
343
+ }
344
+ get arn() {
345
+ return getAtt(this.logicalId, "Arn");
346
+ }
347
+ get permissions() {
348
+ return {
349
+ actions: [
350
+ "lambda:InvokeFunction",
351
+ "lambda:InvokeAsync"
352
+ ],
353
+ resources: [this.arn]
354
+ };
355
+ }
356
+ properties() {
357
+ return {
358
+ FunctionName: this.name,
359
+ MemorySize: this.props.memorySize?.toMegaBytes() ?? 128,
360
+ Runtime: this.props.runtime ?? "nodejs18.x",
361
+ Timeout: this.props.timeout?.toSeconds() ?? 10,
362
+ Architectures: [this.props.architecture ?? "arm64"],
363
+ Role: this.role.arn,
364
+ ...this.props.code.toCodeJson(),
365
+ EphemeralStorage: {
366
+ Size: this.props.ephemeralStorageSize?.toMegaBytes() ?? 512
367
+ },
368
+ Environment: {
369
+ Variables: this.environmentVariables
370
+ }
371
+ };
372
+ }
373
+ };
374
+
375
+ // src/formation/asset.ts
376
+ var import_change_case2 = require("change-case");
377
+ var Asset = class {
378
+ constructor(type, id) {
379
+ this.type = type;
380
+ this.id = (0, import_change_case2.paramCase)(id);
381
+ }
382
+ id;
383
+ };
384
+
385
+ // src/formation/stack.ts
386
+ var Stack = class {
387
+ constructor(name, region) {
388
+ this.name = name;
389
+ this.region = region;
390
+ }
391
+ exports = /* @__PURE__ */ new Map();
392
+ resources = /* @__PURE__ */ new Set();
393
+ tags = /* @__PURE__ */ new Map();
394
+ assets = /* @__PURE__ */ new Set();
395
+ add(...resources) {
396
+ for (const item of resources) {
397
+ if (item instanceof Asset) {
398
+ this.assets.add(item);
399
+ } else {
400
+ this.add(...item.children);
401
+ if (item instanceof Resource) {
402
+ this.resources.add(item);
403
+ }
404
+ }
405
+ }
406
+ return this;
407
+ }
408
+ export(name, value) {
409
+ name = formatName(name);
410
+ this.exports.set(name, value);
411
+ return this;
412
+ }
413
+ import(name) {
414
+ name = formatName(name);
415
+ if (!this.exports.has(name)) {
416
+ throw new Error(`Undefined export value: ${name}`);
417
+ }
418
+ return importValue(name);
419
+ }
420
+ tag(name, value) {
421
+ this.tags.set(name, value);
422
+ return this;
423
+ }
424
+ find(resourceType) {
425
+ return [...this.resources].filter((resource) => resource instanceof resourceType);
426
+ }
427
+ [Symbol.iterator]() {
428
+ return this.resources.values();
429
+ }
430
+ // get resources() {
431
+ // return [ ...this.list.values() ]
432
+ // }
433
+ get size() {
434
+ return this.resources.size;
435
+ }
436
+ toJSON() {
437
+ const resources = {};
438
+ const outputs = {};
439
+ for (const resource of this) {
440
+ Object.assign(resources, resource.toJSON());
441
+ }
442
+ for (const [name, value] of this.exports.entries()) {
443
+ Object.assign(outputs, {
444
+ [formatLogicalId(name)]: {
445
+ Export: { Name: name },
446
+ Value: value
447
+ }
448
+ });
449
+ }
450
+ return {
451
+ Resources: resources,
452
+ Outputs: outputs
453
+ };
454
+ }
455
+ toString(pretty = false) {
456
+ return JSON.stringify(
457
+ this.toJSON(),
458
+ void 0,
459
+ pretty ? 4 : void 0
460
+ );
461
+ }
462
+ };
463
+
464
+ // src/stack.ts
465
+ var toStack = ({ config, app, stackConfig, bootstrap: bootstrap2, usEastBootstrap, plugins }) => {
466
+ const name = stackConfig.name;
467
+ const stack = new Stack(name, config.region).tag("app", config.name).tag("stage", config.stage).tag("stack", name);
468
+ debug("Define stack:", style.info(name));
469
+ debug("Run plugin onStack listeners");
184
470
  const bindings = [];
185
471
  const bind = (cb) => {
186
472
  bindings.push(cb);
187
473
  };
188
- debug("Run plugin onStack listeners");
189
- const functions = plugins.map((plugin) => plugin.onStack?.({
190
- config,
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
- region: config.region,
210
- account: config.account,
211
- partition: "aws",
212
- service: "ssm",
213
- resource: "parameter",
214
- resourceName: configParameterPrefix(config)
215
- })
216
- // Fn.sub('arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter' + configParameterPrefix(config)),
217
- ]
218
- });
219
- functions.forEach((lambda) => lambda.addToRolePolicy(allowConfigParameters));
474
+ for (const plugin of plugins) {
475
+ plugin.onStack?.({
476
+ config,
477
+ app,
478
+ stack,
479
+ stackConfig,
480
+ bootstrap: bootstrap2,
481
+ usEastBootstrap,
482
+ bind
483
+ });
484
+ }
485
+ if (stack.size === 0) {
486
+ throw new Error(`Stack ${style.info(name)} has no resources defined`);
487
+ }
488
+ const functions = stack.find(Function);
489
+ for (const bind2 of bindings) {
490
+ for (const fn of functions) {
491
+ bind2(fn);
492
+ }
493
+ }
494
+ for (const fn of functions) {
495
+ fn.addPermissions({
496
+ actions: [
497
+ "ssm:GetParameter",
498
+ "ssm:GetParameters",
499
+ "ssm:GetParametersByPath"
500
+ ],
501
+ resources: [
502
+ sub("arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:parameter" + configParameterPrefix(config))
503
+ ]
504
+ });
505
+ }
220
506
  return {
221
507
  stack,
222
- functions,
223
- bindings,
224
508
  depends: stackConfig.depends
225
509
  };
226
510
  };
227
511
 
228
- // src/util/path.ts
229
- var import_path = require("path");
230
- var rootDir = process.cwd();
231
- var outDir = (0, import_path.join)(rootDir, ".awsless");
232
- var assemblyDir = (0, import_path.join)(outDir, "assembly");
233
- var assetDir = (0, import_path.join)(outDir, "asset");
234
- var cacheDir = (0, import_path.join)(outDir, "cache");
512
+ // src/util/deployment.ts
513
+ var createDependencyTree = (stacks) => {
514
+ const list3 = stacks.map(({ stack, config }) => ({
515
+ stack,
516
+ depends: config?.depends?.map((dep) => dep.name) || []
517
+ }));
518
+ const findChildren = (list4, parents) => {
519
+ const children = [];
520
+ const rests = [];
521
+ for (const item of list4) {
522
+ const isChild = item.depends.filter((dep) => !parents.includes(dep)).length === 0;
523
+ if (isChild) {
524
+ children.push(item);
525
+ } else {
526
+ rests.push(item);
527
+ }
528
+ }
529
+ if (!rests.length) {
530
+ return children.map(({ stack }) => ({
531
+ stack,
532
+ children: []
533
+ }));
534
+ }
535
+ return children.map(({ stack }) => {
536
+ return {
537
+ stack,
538
+ children: findChildren(rests, [...parents, stack.name])
539
+ };
540
+ });
541
+ };
542
+ return findChildren(list3, []);
543
+ };
544
+ var createDeploymentLine = (stacks) => {
545
+ const line = [];
546
+ const walk = (stacks2, level) => {
547
+ stacks2.forEach((node) => {
548
+ if (!line[level]) {
549
+ line[level] = [];
550
+ }
551
+ line[level].push(node.stack);
552
+ walk(node.children, level + 1);
553
+ });
554
+ };
555
+ walk(stacks, 0);
556
+ return line;
557
+ };
235
558
 
236
- // src/stack/app-bootstrap.ts
237
- var import_aws_cdk_lib8 = require("aws-cdk-lib");
559
+ // src/plugins/cron/index.ts
560
+ var import_zod7 = require("zod");
238
561
 
239
562
  // src/plugin.ts
240
563
  var definePlugin = (plugin) => plugin;
241
564
 
242
- // src/plugins/cron/index.ts
243
- var import_zod10 = require("zod");
244
-
245
565
  // src/plugins/cron/schema/schedule.ts
246
- var import_aws_events = require("aws-cdk-lib/aws-events");
247
566
  var import_zod = require("zod");
248
567
  var import_aws_cron_expression_validator = require("aws-cron-expression-validator");
249
568
  var RateExpressionSchema = import_zod.z.custom((value) => {
@@ -252,7 +571,7 @@ var RateExpressionSchema = import_zod.z.custom((value) => {
252
571
  const number = parseInt(str);
253
572
  return number > 0;
254
573
  }).safeParse(value).success;
255
- }, "Invalid rate expression").transform(import_aws_events.Schedule.expression);
574
+ }, "Invalid rate expression");
256
575
  var CronExpressionSchema = import_zod.z.custom((value) => {
257
576
  return import_zod.z.string().startsWith("cron(").endsWith(")").safeParse(value).success;
258
577
  }, "Invalid cron expression").superRefine((value, ctx) => {
@@ -272,58 +591,81 @@ var CronExpressionSchema = import_zod.z.custom((value) => {
272
591
  });
273
592
  }
274
593
  }
275
- }).transform(import_aws_events.Schedule.expression);
594
+ });
276
595
  var ScheduleExpressionSchema = RateExpressionSchema.or(CronExpressionSchema);
277
596
 
278
- // src/plugins/cron/index.ts
279
- var import_aws_events2 = require("aws-cdk-lib/aws-events");
597
+ // src/plugins/function.ts
598
+ var import_zod6 = require("zod");
280
599
 
281
- // src/util/resource.ts
282
- var import_change_case = require("change-case");
283
- var toId = (resource, id) => {
284
- return (0, import_change_case.pascalCase)(`${resource}-${id}`);
285
- };
286
- var toName = (stack, id) => {
287
- return (0, import_change_case.paramCase)(`${stack.stackName}-${id}`);
288
- };
289
- var toExportName = (name) => {
290
- return (0, import_change_case.paramCase)(name);
291
- };
292
- var toEnvKey = (resource, id) => {
293
- return `RESOURCE_${resource.toUpperCase()}_${id}`;
294
- };
295
- var addResourceEnvironment = (stack, resource, id, lambda) => {
296
- const key = toEnvKey(resource, id);
297
- const value = toName(stack, id);
298
- lambda.addEnvironment(key, value, {
299
- removeInEdge: true
300
- });
301
- };
600
+ // src/schema/duration.ts
601
+ var import_zod2 = require("zod");
302
602
 
303
- // src/plugins/function/index.ts
304
- var import_zod9 = require("zod");
603
+ // src/formation/property/duration.ts
604
+ var Duration = class {
605
+ constructor(value) {
606
+ this.value = value;
607
+ }
608
+ static milliseconds(value) {
609
+ return new Duration(value);
610
+ }
611
+ static seconds(value) {
612
+ return new Duration(value * 1e3 /* seconds */);
613
+ }
614
+ static minutes(value) {
615
+ return new Duration(value * 6e4 /* minutes */);
616
+ }
617
+ static hours(value) {
618
+ return new Duration(value * 36e5 /* hours */);
619
+ }
620
+ static days(value) {
621
+ return new Duration(value * 864e5 /* days */);
622
+ }
623
+ toMilliseconds() {
624
+ return this.value;
625
+ }
626
+ toSeconds() {
627
+ return Math.floor(this.value / 1e3 /* seconds */);
628
+ }
629
+ toMinutes() {
630
+ return Math.floor(this.value / 6e4 /* minutes */);
631
+ }
632
+ toHours() {
633
+ return Math.floor(this.value / 36e5 /* hours */);
634
+ }
635
+ toDays() {
636
+ return Math.floor(this.value / 864e5 /* days */);
637
+ }
638
+ };
305
639
 
306
640
  // src/schema/duration.ts
307
- var import_zod2 = require("zod");
308
- var import_core = require("aws-cdk-lib/core");
309
641
  function toDuration(duration) {
310
642
  const [count, unit] = duration.split(" ");
311
643
  const countNum = parseInt(count);
312
644
  const unitLower = unit.toLowerCase();
313
645
  if (unitLower.startsWith("second")) {
314
- return import_core.Duration.seconds(countNum);
646
+ return Duration.seconds(countNum);
315
647
  } else if (unitLower.startsWith("minute")) {
316
- return import_core.Duration.minutes(countNum);
648
+ return Duration.minutes(countNum);
317
649
  } else if (unitLower.startsWith("hour")) {
318
- return import_core.Duration.hours(countNum);
650
+ return Duration.hours(countNum);
319
651
  } else if (unitLower.startsWith("day")) {
320
- return import_core.Duration.days(countNum);
652
+ return Duration.days(countNum);
321
653
  }
322
- return import_core.Duration.days(0);
654
+ return Duration.days(0);
323
655
  }
324
656
  var DurationSchema = import_zod2.z.custom((value) => {
325
657
  return import_zod2.z.string().regex(/[0-9]+ (seconds?|minutes?|hours?|days?)/).safeParse(value).success;
326
658
  }, "Invalid duration").transform(toDuration);
659
+ var durationMin = (min) => {
660
+ return (duration) => {
661
+ return duration.toSeconds() >= min.toSeconds();
662
+ };
663
+ };
664
+ var durationMax = (max) => {
665
+ return (duration) => {
666
+ return duration.toSeconds() <= max.toSeconds();
667
+ };
668
+ };
327
669
 
328
670
  // src/schema/local-file.ts
329
671
  var import_promises = require("fs/promises");
@@ -337,202 +679,86 @@ var LocalFileSchema = import_zod3.z.string().refine(async (path) => {
337
679
  return true;
338
680
  }, `File doesn't exist`);
339
681
 
340
- // src/plugins/function/index.ts
341
- var import_aws_lambda3 = require("aws-cdk-lib/aws-lambda");
342
-
343
- // src/plugins/function/schema/runtime.ts
344
- var import_aws_lambda = require("aws-cdk-lib/aws-lambda");
682
+ // src/schema/resource-id.ts
345
683
  var import_zod4 = require("zod");
346
- var runtimes = {
347
- "container": import_aws_lambda.Runtime.FROM_IMAGE,
348
- "rust": import_aws_lambda.Runtime.PROVIDED_AL2,
349
- "nodejs16.x": import_aws_lambda.Runtime.NODEJS_16_X,
350
- "nodejs18.x": import_aws_lambda.Runtime.NODEJS_18_X,
351
- "python3.9": import_aws_lambda.Runtime.PYTHON_3_9,
352
- "python3.10": import_aws_lambda.Runtime.PYTHON_3_10,
353
- "go1.x": import_aws_lambda.Runtime.PROVIDED_AL2,
354
- "go": import_aws_lambda.Runtime.PROVIDED_AL2
355
- };
356
- var toRuntime = (runtime) => {
357
- return runtimes[runtime];
358
- };
359
- var RuntimeSchema = import_zod4.z.enum(Object.keys(runtimes)).transform(toRuntime);
360
-
361
- // src/plugins/function/schema/architecture.ts
362
- var import_aws_lambda2 = require("aws-cdk-lib/aws-lambda");
684
+ var ResourceIdSchema = import_zod4.z.string().min(3).max(24).regex(/[a-z\-]+/, "Invalid resource ID");
685
+
686
+ // src/schema/size.ts
363
687
  var import_zod5 = require("zod");
364
- var toArchitecture = (architecture) => {
365
- return architecture === "x86_64" ? import_aws_lambda2.Architecture.X86_64 : import_aws_lambda2.Architecture.ARM_64;
366
- };
367
- var ArchitectureSchema = import_zod5.z.enum(["x86_64", "arm_64"]).transform(toArchitecture);
368
688
 
369
- // src/schema/resource-id.ts
370
- var import_zod6 = require("zod");
371
- var ResourceIdSchema = import_zod6.z.string().min(3).max(24).regex(/[a-z\-]+/, "Invalid resource ID");
689
+ // src/formation/property/size.ts
690
+ var Size = class {
691
+ constructor(bytes) {
692
+ this.bytes = bytes;
693
+ }
694
+ static bytes(value) {
695
+ return new Size(value);
696
+ }
697
+ static kiloBytes(value) {
698
+ return new Size(value * 1024 /* kilo */);
699
+ }
700
+ static megaBytes(value) {
701
+ return new Size(value * 1048576 /* mega */);
702
+ }
703
+ static gigaBytes(value) {
704
+ return new Size(value * 1073741824 /* giga */);
705
+ }
706
+ toBytes() {
707
+ return this.bytes;
708
+ }
709
+ toKiloBytes() {
710
+ return Math.floor(this.bytes / 1024 /* kilo */);
711
+ }
712
+ toMegaBytes() {
713
+ return Math.floor(this.bytes / 1048576 /* mega */);
714
+ }
715
+ toGigaBytes() {
716
+ return Math.floor(this.bytes / 1073741824 /* giga */);
717
+ }
718
+ };
372
719
 
373
720
  // src/schema/size.ts
374
- var import_core2 = require("aws-cdk-lib/core");
375
- var import_zod7 = require("zod");
376
721
  function toSize(size) {
377
722
  const [count, unit] = size.split(" ");
378
723
  const countNum = parseInt(count);
379
724
  if (unit === "KB") {
380
- return import_core2.Size.kibibytes(countNum);
725
+ return Size.kiloBytes(countNum);
381
726
  } else if (unit === "MB") {
382
- return import_core2.Size.mebibytes(countNum);
727
+ return Size.megaBytes(countNum);
383
728
  } else if (unit === "GB") {
384
- return import_core2.Size.gibibytes(countNum);
729
+ return Size.gigaBytes(countNum);
385
730
  }
386
731
  throw new TypeError(`Invalid size ${size}`);
387
732
  }
388
- var SizeSchema = import_zod7.z.custom((value) => {
389
- return import_zod7.z.string().regex(/[0-9]+ (KB|MB|GB)/).safeParse(value).success;
733
+ var SizeSchema = import_zod5.z.custom((value) => {
734
+ return import_zod5.z.string().regex(/[0-9]+ (KB|MB|GB)/).safeParse(value).success;
390
735
  }, "Invalid size").transform(toSize);
391
-
392
- // src/plugins/function/util/build.ts
393
- var import_jszip = __toESM(require("jszip"), 1);
394
- var import_path3 = require("path");
395
- var import_promises2 = require("fs/promises");
396
- var import_filesize = require("filesize");
397
- var zipFiles = (files) => {
398
- const zip = new import_jszip.default();
399
- for (const file of files) {
400
- zip.file(file.name, file.code);
401
- }
402
- return zip.generateAsync({
403
- type: "nodebuffer",
404
- compression: "DEFLATE",
405
- compressionOptions: {
406
- level: 9
407
- }
408
- });
736
+ var sizeMin = (min) => {
737
+ return (size) => {
738
+ return size.toBytes() >= min.toBytes();
739
+ };
409
740
  };
410
- var writeBuildHash = async (config, stack, id, hash) => {
411
- const funcPath = (0, import_path3.join)(assetDir, "function", config.name, stack.artifactId, id);
412
- const versionFile = (0, import_path3.join)(funcPath, "HASH");
413
- await (0, import_promises2.writeFile)(versionFile, hash);
414
- };
415
- var writeBuildFiles = async (config, stack, id, files) => {
416
- const bundle = await zipFiles(files);
417
- const funcPath = (0, import_path3.join)(assetDir, "function", config.name, stack.artifactId, id);
418
- const filesPath = (0, import_path3.join)(funcPath, "files");
419
- const bundleFile = (0, import_path3.join)(funcPath, "bundle.zip");
420
- debug("Bundle size of", style.info((0, import_path3.join)(config.name, stack.artifactId, id)), "is", style.attr((0, import_filesize.filesize)(bundle.byteLength)));
421
- await (0, import_promises2.mkdir)(filesPath, { recursive: true });
422
- await (0, import_promises2.writeFile)(bundleFile, bundle);
423
- await Promise.all(files.map(async (file) => {
424
- const fileName = (0, import_path3.join)(filesPath, file.name);
425
- await (0, import_promises2.mkdir)((0, import_path3.dirname)(fileName), { recursive: true });
426
- await (0, import_promises2.writeFile)(fileName, file.code);
427
- if (file.map) {
428
- const mapName = (0, import_path3.join)(filesPath, `${file.name}.map`);
429
- await (0, import_promises2.writeFile)(mapName, file.map);
430
- }
431
- }));
432
- return {
433
- file: bundleFile,
434
- size: bundle.byteLength
741
+ var sizeMax = (max) => {
742
+ return (size) => {
743
+ return size.toBytes() <= max.toBytes();
435
744
  };
436
745
  };
437
746
 
438
- // src/plugins/function/util/publish.ts
439
- var import_path5 = require("path");
440
- var import_promises3 = require("fs/promises");
441
- var import_client_s3 = require("@aws-sdk/client-s3");
442
-
443
- // src/stack/bootstrap.ts
444
- var import_aws_cdk_lib2 = require("aws-cdk-lib");
445
- var import_aws_s3 = require("aws-cdk-lib/aws-s3");
446
- var assetBucketName = (config) => {
447
- return `awsless-bootstrap-${config.account}-${config.region}`;
448
- };
449
- var assetBucketUrl = (config, stackName) => {
450
- const bucket = assetBucketName(config);
451
- return `https://s3-${config.region}.amazonaws.com/${bucket}/${stackName}/cloudformation.json`;
452
- };
453
- var version = "1";
454
- var bootstrapStack = (config, app) => {
455
- const stack = new import_aws_cdk_lib2.Stack(app, "bootstrap", {
456
- stackName: `awsless-bootstrap`
457
- });
458
- new import_aws_s3.Bucket(stack, "assets", {
459
- bucketName: assetBucketName(config),
460
- versioned: true,
461
- accessControl: import_aws_s3.BucketAccessControl.PRIVATE,
462
- removalPolicy: import_aws_cdk_lib2.RemovalPolicy.DESTROY
463
- });
464
- new import_aws_cdk_lib2.CfnOutput(stack, "version", {
465
- exportName: "version",
466
- value: version
467
- });
468
- return stack;
469
- };
470
- var shouldDeployBootstrap = async (client, name) => {
471
- debug("Check bootstrap status");
472
- const info = await client.get(name);
473
- return !info || info.outputs.version !== version || !["CREATE_COMPLETE", "UPDATE_COMPLETE"].includes(info.status);
747
+ // src/util/byte-size.ts
748
+ var import_filesize = require("filesize");
749
+ var formatByteSize = (size) => {
750
+ const [number, unit] = (0, import_filesize.filesize)(size).toString().split(" ");
751
+ return style.attr(number) + style.attr.dim(unit);
474
752
  };
475
753
 
476
- // src/plugins/function/util/publish.ts
477
- var publishFunctionAsset = async (config, stack, id) => {
478
- const bucket = assetBucketName(config);
479
- const key = `${config.name}/${stack.artifactId}/function/${id}.zip`;
480
- const funcPath = (0, import_path5.join)(assetDir, "function", config.name, stack.artifactId, id);
481
- const bundleFile = (0, import_path5.join)(funcPath, "bundle.zip");
482
- const hashFile = (0, import_path5.join)(funcPath, "HASH");
483
- const hash = await (0, import_promises3.readFile)(hashFile, "utf8");
484
- const file = await (0, import_promises3.readFile)(bundleFile);
485
- const client = new import_client_s3.S3Client({
486
- credentials: config.credentials,
487
- region: config.region
488
- });
489
- let getResult;
490
- try {
491
- getResult = await client.send(new import_client_s3.GetObjectCommand({
492
- Bucket: bucket,
493
- Key: key
494
- }));
495
- } catch (error) {
496
- if (error instanceof Error && error.name === "NoSuchKey") {
497
- } else {
498
- throw error;
499
- }
500
- }
501
- if (getResult?.Metadata?.hash === hash) {
502
- return getResult.VersionId;
503
- }
504
- const putResult = await client.send(new import_client_s3.PutObjectCommand({
505
- Bucket: bucket,
506
- Key: key,
507
- Body: file,
508
- ACL: import_client_s3.ObjectCannedACL.private,
509
- StorageClass: import_client_s3.StorageClass.STANDARD,
510
- Metadata: {
511
- hash
512
- }
513
- }));
514
- return putResult.VersionId;
515
- };
516
-
517
- // src/plugins/function/schema/retry-attempts.ts
518
- var import_zod8 = require("zod");
519
- var RetryAttempts = import_zod8.z.number().int().min(0).max(2);
520
-
521
- // src/util/byte-size.ts
522
- var import_filesize2 = require("filesize");
523
- var formatByteSize = (size) => {
524
- const [number, unit] = (0, import_filesize2.filesize)(size).toString().split(" ");
525
- return style.attr(number) + style.attr.dim(unit);
526
- };
527
-
528
- // src/plugins/function/util/bundler/rollup.ts
754
+ // src/formation/resource/lambda/util/rollup.ts
529
755
  var import_rollup = require("rollup");
530
756
  var import_crypto = require("crypto");
531
757
  var import_rollup_plugin_swc3 = require("rollup-plugin-swc3");
532
758
  var import_plugin_json = __toESM(require("@rollup/plugin-json"), 1);
533
759
  var import_plugin_commonjs = __toESM(require("@rollup/plugin-commonjs"), 1);
534
760
  var import_plugin_node_resolve = __toESM(require("@rollup/plugin-node-resolve"), 1);
535
- var rollupBuild = async (input) => {
761
+ var rollupBundle = async (input) => {
536
762
  const bundle = await (0, import_rollup.rollup)({
537
763
  input,
538
764
  external: (importee) => {
@@ -577,34 +803,160 @@ var rollupBuild = async (input) => {
577
803
  };
578
804
  };
579
805
 
580
- // src/plugins/function/index.ts
581
- var FunctionSchema = import_zod9.z.union([
806
+ // src/formation/resource/lambda/util/zip.ts
807
+ var import_jszip = __toESM(require("jszip"), 1);
808
+ var zipFiles = (files) => {
809
+ const zip = new import_jszip.default();
810
+ for (const file of files) {
811
+ zip.file(file.name, file.code);
812
+ }
813
+ return zip.generateAsync({
814
+ type: "nodebuffer",
815
+ compression: "DEFLATE",
816
+ compressionOptions: {
817
+ level: 9
818
+ }
819
+ });
820
+ };
821
+
822
+ // src/formation/resource/lambda/code.ts
823
+ var import_crypto2 = require("crypto");
824
+ var Code = class {
825
+ static fromFile(id, file, bundler) {
826
+ return new FileCode(id, file, bundler);
827
+ }
828
+ static fromInline(id, code, handler) {
829
+ return new InlineCode(id, code, handler);
830
+ }
831
+ };
832
+ var InlineCode = class extends Asset {
833
+ constructor(id, code, handler = "index.default") {
834
+ super("function", id);
835
+ this.code = code;
836
+ this.handler = handler;
837
+ }
838
+ hash;
839
+ bundle;
840
+ s3;
841
+ async build({ write }) {
842
+ const hash = (0, import_crypto2.createHash)("sha1").update(this.code).digest("hex");
843
+ const bundle = await zipFiles([{
844
+ name: "index.js",
845
+ code: this.code
846
+ }]);
847
+ await Promise.all([
848
+ write("HASH", hash),
849
+ write("bundle.zip", bundle),
850
+ write("files/inline.js", this.code)
851
+ ]);
852
+ this.bundle = bundle;
853
+ this.hash = hash;
854
+ return {
855
+ size: formatByteSize(bundle.byteLength)
856
+ };
857
+ }
858
+ async publish({ publish }) {
859
+ this.s3 = await publish(
860
+ `${this.id}.zip`,
861
+ this.bundle,
862
+ this.hash
863
+ );
864
+ }
865
+ toCodeJson() {
866
+ return {
867
+ Handler: this.handler,
868
+ Code: {
869
+ S3Bucket: this.s3.bucket,
870
+ S3Key: this.s3.key,
871
+ S3ObjectVersion: this.s3.version
872
+ }
873
+ };
874
+ }
875
+ };
876
+ var FileCode = class extends Asset {
877
+ constructor(id, file, bundler) {
878
+ super("function", id);
879
+ this.file = file;
880
+ this.bundler = bundler;
881
+ }
882
+ handler;
883
+ hash;
884
+ bundle;
885
+ s3;
886
+ async build({ write }) {
887
+ const bundler = this.bundler ?? rollupBundle;
888
+ const { hash, files, handler } = await bundler(this.file);
889
+ const bundle = await zipFiles(files);
890
+ await Promise.all([
891
+ write("HASH", hash),
892
+ write("bundle.zip", bundle),
893
+ ...files.map((file) => write(`files/${file.name}`, file.code)),
894
+ ...files.map((file) => file.map ? write(`files/${file.name}.map`, file.map) : void 0)
895
+ ]);
896
+ this.handler = handler;
897
+ this.bundle = bundle;
898
+ this.hash = hash;
899
+ return {
900
+ size: formatByteSize(bundle.byteLength)
901
+ };
902
+ }
903
+ async publish({ publish }) {
904
+ this.s3 = await publish(
905
+ `${this.id}.zip`,
906
+ this.bundle,
907
+ this.hash
908
+ );
909
+ }
910
+ toCodeJson() {
911
+ return {
912
+ Handler: this.handler,
913
+ Code: {
914
+ S3Bucket: this.s3?.bucket ?? "",
915
+ S3Key: this.s3?.key ?? "",
916
+ S3ObjectVersion: this.s3?.version ?? ""
917
+ }
918
+ };
919
+ }
920
+ };
921
+
922
+ // src/plugins/function.ts
923
+ var MemorySizeSchema = SizeSchema.refine(sizeMin(Size.megaBytes(128)), "Minimum memory size is 128 MB").refine(sizeMax(Size.gigaBytes(10)), "Minimum memory size is 10 GB");
924
+ var TimeoutSchema = DurationSchema.refine(durationMin(Duration.seconds(10)), "Minimum timeout duration is 10 seconds").refine(durationMax(Duration.minutes(15)), "Maximum timeout duration is 15 minutes");
925
+ var EphemeralStorageSizeSchema = SizeSchema.refine(sizeMin(Size.megaBytes(512)), "Minimum ephemeral storage size is 512 MB").refine(sizeMax(Size.gigaBytes(10)), "Minimum ephemeral storage size is 10 GB");
926
+ var EnvironmentSchema = import_zod6.z.record(import_zod6.z.string(), import_zod6.z.string()).optional();
927
+ var ArchitectureSchema = import_zod6.z.enum(["x86_64", "arm64"]);
928
+ var RetryAttemptsSchema = import_zod6.z.number().int().min(0).max(2);
929
+ var RuntimeSchema = import_zod6.z.enum([
930
+ "nodejs16.x",
931
+ "nodejs18.x"
932
+ ]);
933
+ var FunctionSchema = import_zod6.z.union([
582
934
  LocalFileSchema,
583
- import_zod9.z.object({
935
+ import_zod6.z.object({
584
936
  file: LocalFileSchema,
585
- timeout: DurationSchema.optional(),
937
+ timeout: TimeoutSchema.optional(),
586
938
  runtime: RuntimeSchema.optional(),
587
- memorySize: SizeSchema.optional(),
939
+ memorySize: MemorySizeSchema.optional(),
588
940
  architecture: ArchitectureSchema.optional(),
589
- ephemeralStorageSize: SizeSchema.optional(),
590
- retryAttempts: RetryAttempts,
591
- environment: import_zod9.z.record(import_zod9.z.string(), import_zod9.z.string()).optional()
941
+ ephemeralStorageSize: EphemeralStorageSizeSchema.optional(),
942
+ retryAttempts: RetryAttemptsSchema.optional(),
943
+ environment: EnvironmentSchema.optional()
592
944
  })
593
945
  ]);
594
- var schema = import_zod9.z.object({
595
- defaults: import_zod9.z.object({
596
- function: import_zod9.z.object({
597
- timeout: DurationSchema.default("10 seconds"),
946
+ var schema = import_zod6.z.object({
947
+ defaults: import_zod6.z.object({
948
+ function: import_zod6.z.object({
949
+ timeout: TimeoutSchema.default("10 seconds"),
598
950
  runtime: RuntimeSchema.default("nodejs18.x"),
599
- memorySize: SizeSchema.default("128 MB"),
600
- architecture: ArchitectureSchema.default("arm_64"),
601
- ephemeralStorageSize: SizeSchema.default("512 MB"),
602
- retryAttempts: RetryAttempts.default(2),
603
- environment: import_zod9.z.record(import_zod9.z.string(), import_zod9.z.string()).optional()
951
+ memorySize: MemorySizeSchema.default("128 MB"),
952
+ architecture: ArchitectureSchema.default("arm64"),
953
+ ephemeralStorageSize: EphemeralStorageSizeSchema.default("512 MB"),
954
+ retryAttempts: RetryAttemptsSchema.default(2),
955
+ environment: EnvironmentSchema.optional()
604
956
  }).default({})
605
957
  }).default({}),
606
- stacks: import_zod9.z.object({
607
- functions: import_zod9.z.record(
958
+ stacks: import_zod6.z.object({
959
+ functions: import_zod6.z.record(
608
960
  ResourceIdSchema,
609
961
  FunctionSchema
610
962
  ).optional()
@@ -613,200 +965,466 @@ var schema = import_zod9.z.object({
613
965
  var functionPlugin = definePlugin({
614
966
  name: "function",
615
967
  schema,
616
- onStack(context) {
617
- return Object.entries(context.stackConfig.functions || {}).map(([id, fileOrProps]) => {
618
- return toFunction(context, id, fileOrProps);
619
- });
968
+ onStack(ctx) {
969
+ for (const [id, props] of Object.entries(ctx.stackConfig.functions || {})) {
970
+ const lambda = toLambdaFunction(ctx, id, props);
971
+ ctx.stack.add(lambda);
972
+ }
620
973
  }
621
974
  });
622
- var toFunction = ({ config, stack, assets }, id, fileOrProps) => {
975
+ var toLambdaFunction = (ctx, id, fileOrProps) => {
976
+ const config = ctx.config;
977
+ const stack = ctx.stack;
623
978
  const props = typeof fileOrProps === "string" ? { ...config.defaults?.function, file: fileOrProps } : { ...config.defaults?.function, ...fileOrProps };
624
- const lambda = new import_aws_lambda3.Function(stack, toId("function", id), {
625
- functionName: toName(stack, id),
626
- handler: "index.default",
627
- code: import_aws_lambda3.Code.fromInline("export default () => {}"),
628
- ...props,
629
- memorySize: props.memorySize.toMebibytes()
979
+ const lambda = new Function(id, {
980
+ name: `${config.name}-${stack.name}-${id}`,
981
+ code: Code.fromFile(id, props.file),
982
+ ...props
630
983
  });
631
- lambda.addEnvironment("APP", config.name, { removeInEdge: true });
632
- lambda.addEnvironment("STAGE", config.stage, { removeInEdge: true });
633
- lambda.addEnvironment("STACK", stack.artifactId, { removeInEdge: true });
634
- if (lambda.runtime.toString().startsWith("nodejs")) {
635
- lambda.addEnvironment("AWS_NODEJS_CONNECTION_REUSE_ENABLED", "1", {
636
- removeInEdge: true
637
- });
984
+ lambda.addEnvironment("APP", config.name);
985
+ lambda.addEnvironment("STAGE", config.stage);
986
+ lambda.addEnvironment("STACK", stack.name);
987
+ if (props.runtime.startsWith("nodejs")) {
988
+ lambda.addEnvironment("AWS_NODEJS_CONNECTION_REUSE_ENABLED", "1");
638
989
  }
639
- assets.add({
640
- stackName: stack.artifactId,
641
- resource: "function",
642
- resourceName: id,
643
- async build() {
644
- const result = await rollupBuild(props.file);
645
- const bundle = await writeBuildFiles(config, stack, id, result.files);
646
- await writeBuildHash(config, stack, id, result.hash);
647
- const func = lambda.node.defaultChild;
648
- func.handler = result.handler;
649
- return {
650
- size: formatByteSize(bundle.size)
651
- };
652
- },
653
- async publish() {
654
- const version2 = await publishFunctionAsset(config, stack, id);
655
- const func = lambda.node.defaultChild;
656
- func.code = {
657
- s3Bucket: assetBucketName(config),
658
- s3Key: `${config.name}/${stack.artifactId}/function/${id}.zip`,
659
- s3ObjectVersion: version2
660
- };
661
- }
662
- });
663
990
  return lambda;
664
991
  };
665
992
 
993
+ // src/formation/resource/events/rule.ts
994
+ var Rule = class extends Resource {
995
+ constructor(logicalId, props) {
996
+ super("AWS::Events::Rule", logicalId);
997
+ this.props = props;
998
+ this.name = formatName(this.props.name || logicalId);
999
+ }
1000
+ name;
1001
+ get id() {
1002
+ return ref(this.logicalId);
1003
+ }
1004
+ get arn() {
1005
+ return getAtt(this.logicalId, "Arn");
1006
+ }
1007
+ properties() {
1008
+ return {
1009
+ Name: this.name,
1010
+ ...this.attr("State", "ENABLED"),
1011
+ ...this.attr("Description", this.props.description),
1012
+ ...this.attr("ScheduleExpression", this.props.schedule),
1013
+ ...this.attr("RoleArn", this.props.roleArn),
1014
+ ...this.attr("EventBusName", this.props.eventBusName),
1015
+ ...this.attr("EventPattern", this.props.eventPattern),
1016
+ Targets: this.props.targets.map((target) => ({
1017
+ Arn: target.arn,
1018
+ Id: target.id,
1019
+ ...this.attr("Input", target.input && JSON.stringify(target.input))
1020
+ }))
1021
+ };
1022
+ }
1023
+ };
1024
+
1025
+ // src/formation/resource/lambda/permission.ts
1026
+ var Permission2 = class extends Resource {
1027
+ constructor(logicalId, props) {
1028
+ super("AWS::Lambda::Permission", logicalId);
1029
+ this.props = props;
1030
+ }
1031
+ properties() {
1032
+ return {
1033
+ FunctionName: this.props.functionArn,
1034
+ Action: this.props.action || "lambda:InvokeFunction",
1035
+ Principal: this.props.principal,
1036
+ SourceArn: this.props.sourceArn
1037
+ };
1038
+ }
1039
+ };
1040
+
1041
+ // src/formation/resource/lambda/event-source/events.ts
1042
+ var EventsEventSource = class extends Group {
1043
+ constructor(id, lambda, props) {
1044
+ const rule = new Rule(id, {
1045
+ schedule: props.schedule,
1046
+ targets: [{
1047
+ id,
1048
+ arn: lambda.arn,
1049
+ input: props.payload
1050
+ }]
1051
+ });
1052
+ const permission = new Permission2(id, {
1053
+ action: "lambda:InvokeFunction",
1054
+ principal: "events.amazonaws.com",
1055
+ functionArn: lambda.arn,
1056
+ sourceArn: rule.arn
1057
+ });
1058
+ super([rule, permission]);
1059
+ }
1060
+ };
1061
+
666
1062
  // src/plugins/cron/index.ts
667
- var import_aws_events_targets = require("aws-cdk-lib/aws-events-targets");
668
1063
  var cronPlugin = definePlugin({
669
1064
  name: "cron",
670
- schema: import_zod10.z.object({
671
- stacks: import_zod10.z.object({
672
- crons: import_zod10.z.record(ResourceIdSchema, import_zod10.z.object({
1065
+ schema: import_zod7.z.object({
1066
+ stacks: import_zod7.z.object({
1067
+ crons: import_zod7.z.record(ResourceIdSchema, import_zod7.z.object({
673
1068
  consumer: FunctionSchema,
674
1069
  schedule: ScheduleExpressionSchema,
675
- description: import_zod10.z.string().max(512).optional()
1070
+ payload: import_zod7.z.unknown().optional()
676
1071
  })).optional()
677
1072
  }).array()
678
1073
  }),
679
- onStack(context) {
680
- return Object.entries(context.stackConfig.crons || {}).map(([id, props]) => {
681
- const lambda = toFunction(context, id, props.consumer);
682
- const target = new import_aws_events_targets.LambdaFunction(lambda);
683
- new import_aws_events2.Rule(context.stack, toId("cron", id), {
684
- ruleName: toName(context.stack, id),
1074
+ onStack(ctx) {
1075
+ const { stack, stackConfig } = ctx;
1076
+ for (const [id, props] of Object.entries(stackConfig.crons || {})) {
1077
+ const lambda = toLambdaFunction(ctx, id, props.consumer);
1078
+ const source = new EventsEventSource(id, lambda, {
685
1079
  schedule: props.schedule,
686
- description: props.description,
687
- targets: [target]
1080
+ payload: props.payload
688
1081
  });
689
- return lambda;
690
- });
1082
+ stack.add(lambda, source);
1083
+ }
691
1084
  }
692
1085
  });
693
1086
 
694
1087
  // src/plugins/queue.ts
695
- var import_zod11 = require("zod");
696
- var import_aws_sqs = require("aws-cdk-lib/aws-sqs");
697
- var import_aws_lambda_event_sources = require("aws-cdk-lib/aws-lambda-event-sources");
1088
+ var import_zod8 = require("zod");
1089
+
1090
+ // src/formation/resource/sqs/queue.ts
1091
+ var Queue = class extends Resource {
1092
+ constructor(logicalId, props = {}) {
1093
+ super("AWS::SQS::Queue", logicalId);
1094
+ this.props = props;
1095
+ this.name = formatName(this.props.name || logicalId);
1096
+ }
1097
+ name;
1098
+ get arn() {
1099
+ return getAtt(this.logicalId, "Arn");
1100
+ }
1101
+ get url() {
1102
+ return getAtt(this.logicalId, "QueueUrl");
1103
+ }
1104
+ get permissions() {
1105
+ return {
1106
+ actions: [
1107
+ "sqs:SendMessage",
1108
+ "sqs:ReceiveMessage",
1109
+ "sqs:GetQueueUrl",
1110
+ "sqs:GetQueueAttributes"
1111
+ ],
1112
+ resources: [this.arn]
1113
+ };
1114
+ }
1115
+ properties() {
1116
+ return {
1117
+ QueueName: this.name,
1118
+ DelaySeconds: this.props.deliveryDelay?.toSeconds() ?? 0,
1119
+ MaximumMessageSize: this.props.maxMessageSize?.toBytes() ?? Size.kiloBytes(256).toBytes(),
1120
+ MessageRetentionPeriod: this.props.retentionPeriod?.toSeconds() ?? Duration.days(4).toSeconds(),
1121
+ ReceiveMessageWaitTimeSeconds: this.props.receiveMessageWaitTime?.toSeconds() ?? 0,
1122
+ VisibilityTimeout: this.props.visibilityTimeout?.toSeconds() ?? 30
1123
+ };
1124
+ }
1125
+ };
1126
+
1127
+ // src/formation/resource/lambda/event-source-mapping.ts
1128
+ var import_change_case3 = require("change-case");
1129
+ var EventSourceMapping = class extends Resource {
1130
+ constructor(logicalId, props) {
1131
+ super("AWS::Lambda::EventSourceMapping", logicalId);
1132
+ this.props = props;
1133
+ }
1134
+ properties() {
1135
+ return {
1136
+ Enabled: true,
1137
+ FunctionName: this.props.functionArn,
1138
+ EventSourceArn: this.props.sourceArn,
1139
+ ...this.attr("BatchSize", this.props.batchSize),
1140
+ ...this.attr("MaximumBatchingWindowInSeconds", this.props.maxBatchingWindow?.toSeconds()),
1141
+ ...this.attr("MaximumRecordAgeInSeconds", this.props.maxRecordAge?.toSeconds()),
1142
+ ...this.attr("MaximumRetryAttempts", this.props.retryAttempts),
1143
+ ...this.attr("ParallelizationFactor", this.props.parallelizationFactor),
1144
+ ...this.attr("TumblingWindowInSeconds", this.props.tumblingWindow?.toSeconds()),
1145
+ ...this.attr("BisectBatchOnFunctionError", this.props.bisectBatchOnError),
1146
+ ...this.attr("StartingPosition", this.props.startingPosition && (0, import_change_case3.constantCase)(this.props.startingPosition)),
1147
+ ...this.attr("StartingPositionTimestamp", this.props.startingPositionTimestamp),
1148
+ ...this.props.maxConcurrency ? {
1149
+ ScalingConfig: {
1150
+ MaximumConcurrency: this.props.maxConcurrency
1151
+ }
1152
+ } : {},
1153
+ ...this.props.onFailure ? {
1154
+ DestinationConfig: {
1155
+ OnFailure: {
1156
+ Destination: this.props.onFailure
1157
+ }
1158
+ }
1159
+ } : {}
1160
+ };
1161
+ }
1162
+ };
1163
+
1164
+ // src/formation/resource/lambda/event-source/sqs.ts
1165
+ var SqsEventSource = class extends Group {
1166
+ constructor(id, lambda, props) {
1167
+ const source = new EventSourceMapping(id, {
1168
+ functionArn: lambda.arn,
1169
+ sourceArn: props.queueArn,
1170
+ batchSize: props.batchSize ?? 10,
1171
+ maxBatchingWindow: props.maxBatchingWindow,
1172
+ maxConcurrency: props.maxConcurrency,
1173
+ onFailure: props.onFailure
1174
+ });
1175
+ lambda.addPermissions({
1176
+ actions: [
1177
+ "sqs:ReceiveMessage",
1178
+ "sqs:DeleteMessage",
1179
+ "sqs:GetQueueAttributes"
1180
+ ],
1181
+ resources: [props.queueArn]
1182
+ });
1183
+ super([source]);
1184
+ }
1185
+ };
1186
+
1187
+ // src/plugins/queue.ts
698
1188
  var queuePlugin = definePlugin({
699
1189
  name: "queue",
700
- schema: import_zod11.z.object({
701
- defaults: import_zod11.z.object({
702
- queue: import_zod11.z.object({
703
- // fifo: z.boolean().default(false),
1190
+ schema: import_zod8.z.object({
1191
+ defaults: import_zod8.z.object({
1192
+ /** Define the defaults properties for all queue's in your app */
1193
+ queue: import_zod8.z.object({
1194
+ /** The number of seconds that Amazon SQS retains a message.
1195
+ * You can specify a duration value from 1 minute to 14 days.
1196
+ * @default '7 days' */
704
1197
  retentionPeriod: DurationSchema.default("7 days"),
1198
+ /** The length of time during which a message will be unavailable after a message is delivered from the queue.
1199
+ * This blocks other components from receiving the same message and gives the initial component time to process and delete the message from the queue.
1200
+ * You can specify a duration value from 0 to 12 hours.
1201
+ * @default '30 seconds' */
705
1202
  visibilityTimeout: DurationSchema.default("30 seconds"),
1203
+ /** The time in seconds for which the delivery of all messages in the queue is delayed.
1204
+ * You can specify a duration value from 0 to 15 minutes.
1205
+ * @default '0 seconds' */
706
1206
  deliveryDelay: DurationSchema.default("0 seconds"),
1207
+ /** Specifies the duration, in seconds,
1208
+ * that the ReceiveMessage action call waits until a message is in the queue in order to include it in the response,
1209
+ * rather than returning an empty response if a message isn't yet available.
1210
+ * You can specify an integer from 1 to 20.
1211
+ * You can specify a duration value from 1 to 20 seconds.
1212
+ * @default '0 seconds' */
707
1213
  receiveMessageWaitTime: DurationSchema.default("0 seconds"),
1214
+ /** The limit of how many bytes that a message can contain before Amazon SQS rejects it.
1215
+ * You can specify an size value from 1 KB to 256 KB.
1216
+ * @default '256 KB' */
708
1217
  maxMessageSize: SizeSchema.default("256 KB")
709
1218
  }).default({})
710
1219
  }).default({}),
711
- stacks: import_zod11.z.object({
712
- queues: import_zod11.z.record(ResourceIdSchema, import_zod11.z.union([
713
- LocalFileSchema,
714
- import_zod11.z.object({
715
- consumer: FunctionSchema,
716
- // fifo: z.boolean().optional(),
717
- retentionPeriod: DurationSchema.optional(),
718
- visibilityTimeout: DurationSchema.optional(),
719
- deliveryDelay: DurationSchema.optional(),
720
- receiveMessageWaitTime: DurationSchema.optional(),
721
- maxMessageSize: SizeSchema.optional()
722
- })
723
- ])).optional()
1220
+ stacks: import_zod8.z.object({
1221
+ /** Define the queues in your stack
1222
+ * @example
1223
+ * {
1224
+ * queues: {
1225
+ * QUEUE_NAME: 'function.ts'
1226
+ * }
1227
+ * }
1228
+ * */
1229
+ queues: import_zod8.z.record(
1230
+ ResourceIdSchema,
1231
+ import_zod8.z.union([
1232
+ LocalFileSchema,
1233
+ import_zod8.z.object({
1234
+ /** The consuming lambda function properties */
1235
+ consumer: FunctionSchema,
1236
+ /** The number of seconds that Amazon SQS retains a message.
1237
+ * You can specify a duration value from 1 minute to 14 days.
1238
+ * @default '7 days' */
1239
+ retentionPeriod: DurationSchema.optional(),
1240
+ /** The length of time during which a message will be unavailable after a message is delivered from the queue.
1241
+ * This blocks other components from receiving the same message and gives the initial component time to process and delete the message from the queue.
1242
+ * You can specify a duration value from 0 to 12 hours.
1243
+ * @default '30 seconds' */
1244
+ visibilityTimeout: DurationSchema.optional(),
1245
+ /** The time in seconds for which the delivery of all messages in the queue is delayed.
1246
+ * You can specify a duration value from 0 to 15 minutes.
1247
+ * @default '0 seconds' */
1248
+ deliveryDelay: DurationSchema.optional(),
1249
+ /** Specifies the duration, in seconds,
1250
+ * that the ReceiveMessage action call waits until a message is in the queue in order to include it in the response,
1251
+ * rather than returning an empty response if a message isn't yet available.
1252
+ * You can specify an integer from 1 to 20.
1253
+ * You can specify a duration value from 1 to 20 seconds.
1254
+ * @default '0 seconds' */
1255
+ receiveMessageWaitTime: DurationSchema.optional(),
1256
+ /** The limit of how many bytes that a message can contain before Amazon SQS rejects it.
1257
+ * You can specify an size value from 1 KB to 256 KB.
1258
+ * @default '256 KB' */
1259
+ maxMessageSize: SizeSchema.optional()
1260
+ })
1261
+ ])
1262
+ ).optional()
724
1263
  }).array()
725
1264
  }),
726
1265
  onStack(ctx) {
727
1266
  const { stack, config, stackConfig, bind } = ctx;
728
- return Object.entries(stackConfig.queues || {}).map(([id, functionOrProps]) => {
1267
+ for (const [id, functionOrProps] of Object.entries(stackConfig.queues || {})) {
729
1268
  const props = typeof functionOrProps === "string" ? { ...config.defaults.queue, consumer: functionOrProps } : { ...config.defaults.queue, ...functionOrProps };
730
- const queue2 = new import_aws_sqs.Queue(stack, toId("queue", id), {
731
- queueName: toName(stack, id),
732
- ...props,
733
- maxMessageSizeBytes: props.maxMessageSize.toBytes()
1269
+ const queue2 = new Queue(id, {
1270
+ name: `${config.name}-${stack.name}-${id}`,
1271
+ ...props
1272
+ });
1273
+ const lambda = toLambdaFunction(ctx, id, props.consumer);
1274
+ const source = new SqsEventSource(id, lambda, {
1275
+ queueArn: queue2.arn
734
1276
  });
735
- const lambda = toFunction(ctx, id, props.consumer);
736
- lambda.addEventSource(new import_aws_lambda_event_sources.SqsEventSource(queue2));
1277
+ stack.add(queue2, lambda, source);
737
1278
  bind((lambda2) => {
738
- queue2.grantSendMessages(lambda2);
739
- addResourceEnvironment(stack, "queue", id, lambda2);
1279
+ lambda2.addPermissions(queue2.permissions);
740
1280
  });
741
- return lambda;
742
- });
1281
+ }
743
1282
  }
744
1283
  });
745
1284
 
746
- // src/plugins/table/index.ts
747
- var import_zod16 = require("zod");
748
- var import_aws_dynamodb4 = require("aws-cdk-lib/aws-dynamodb");
749
-
750
- // src/plugins/table/schema/class-type.ts
751
- var import_aws_dynamodb = require("aws-cdk-lib/aws-dynamodb");
752
- var import_zod12 = require("zod");
753
- var types = {
754
- "standard": import_aws_dynamodb.TableClass.STANDARD,
755
- "standard-infrequent-access": import_aws_dynamodb.TableClass.STANDARD_INFREQUENT_ACCESS
756
- };
757
- var TableClassSchema = import_zod12.z.enum(Object.keys(types)).transform((value) => {
758
- return types[value];
759
- });
1285
+ // src/plugins/table.ts
1286
+ var import_zod9 = require("zod");
760
1287
 
761
- // src/plugins/table/schema/attribute.ts
762
- var import_aws_dynamodb2 = require("aws-cdk-lib/aws-dynamodb");
763
- var import_zod13 = require("zod");
764
- var types2 = {
765
- string: import_aws_dynamodb2.AttributeType.STRING,
766
- number: import_aws_dynamodb2.AttributeType.NUMBER,
767
- binary: import_aws_dynamodb2.AttributeType.BINARY
1288
+ // src/formation/resource/dynamodb/table.ts
1289
+ var import_change_case4 = require("change-case");
1290
+ var Table = class extends Resource {
1291
+ constructor(logicalId, props) {
1292
+ super("AWS::DynamoDB::Table", logicalId);
1293
+ this.props = props;
1294
+ this.name = formatName(this.props.name || logicalId);
1295
+ this.indexes = { ...this.props.indexes || {} };
1296
+ }
1297
+ name;
1298
+ indexes;
1299
+ addIndex(name, props) {
1300
+ this.indexes[name] = props;
1301
+ }
1302
+ get arn() {
1303
+ return ref(this.logicalId);
1304
+ }
1305
+ get permissions() {
1306
+ return {
1307
+ actions: [
1308
+ "dynamodb:DescribeTable",
1309
+ "dynamodb:PutItem",
1310
+ "dynamodb:GetItem",
1311
+ "dynamodb:DeleteItem",
1312
+ "dynamodb:TransactWrite",
1313
+ "dynamodb:BatchWriteItem",
1314
+ "dynamodb:BatchGetItem",
1315
+ "dynamodb:ConditionCheckItem",
1316
+ "dynamodb:Query",
1317
+ "dynamodb:Scan"
1318
+ ],
1319
+ resources: [this.arn]
1320
+ };
1321
+ }
1322
+ properties() {
1323
+ return {
1324
+ TableName: this.name,
1325
+ BillingMode: "PAY_PER_REQUEST",
1326
+ TableClass: (0, import_change_case4.constantCase)(this.props.class || "standard"),
1327
+ PointInTimeRecoverySpecification: {
1328
+ PointInTimeRecoveryEnabled: this.props.pointInTimeRecovery || false
1329
+ },
1330
+ KeySchema: [
1331
+ { KeyType: "HASH", AttributeName: this.props.hash },
1332
+ ...this.props.sort ? [{ KeyType: "RANGE", AttributeName: this.props.sort }] : []
1333
+ ],
1334
+ AttributeDefinitions: Object.entries(this.props.fields).map(([name, type]) => ({
1335
+ AttributeName: name,
1336
+ AttributeType: type[0].toUpperCase()
1337
+ })),
1338
+ ...this.props.timeToLiveAttribute ? {
1339
+ TimeToLiveSpecification: {
1340
+ AttributeName: this.props.timeToLiveAttribute,
1341
+ Enabled: true
1342
+ }
1343
+ } : {},
1344
+ ...Object.keys(this.indexes).length ? {
1345
+ GlobalSecondaryIndexes: Object.entries(this.indexes).map(([name, props]) => ({
1346
+ IndexName: name,
1347
+ KeySchema: [
1348
+ { KeyType: "HASH", AttributeName: props.hash },
1349
+ ...props.sort ? [{ KeyType: "RANGE", AttributeName: props.sort }] : []
1350
+ ],
1351
+ Projection: {
1352
+ ProjectionType: (0, import_change_case4.constantCase)(props.projection || "all")
1353
+ }
1354
+ }))
1355
+ } : {}
1356
+ };
1357
+ }
768
1358
  };
769
- var AttributeSchema = import_zod13.z.enum(Object.keys(types2)).transform((value) => types2[value]);
770
-
771
- // src/plugins/table/schema/key.ts
772
- var import_zod14 = require("zod");
773
- var KeySchema = import_zod14.z.string().min(1).max(255);
774
-
775
- // src/plugins/table/schema/projection-type.ts
776
- var import_aws_dynamodb3 = require("aws-cdk-lib/aws-dynamodb");
777
- var import_zod15 = require("zod");
778
- var types3 = {
779
- "all": import_aws_dynamodb3.ProjectionType.ALL,
780
- "keys-only": import_aws_dynamodb3.ProjectionType.KEYS_ONLY
781
- };
782
- var ProjectionTypeSchema = import_zod15.z.union([
783
- import_zod15.z.enum(Object.keys(types3)).transform((value) => ({
784
- ProjectionType: types3[value]
785
- })),
786
- import_zod15.z.array(KeySchema).min(0).max(20).transform((keys) => ({
787
- ProjectionType: import_aws_dynamodb3.ProjectionType.INCLUDE,
788
- NonKeyAttributes: keys
789
- }))
790
- ]);
791
1359
 
792
- // src/plugins/table/index.ts
1360
+ // src/plugins/table.ts
1361
+ var KeySchema = import_zod9.z.string().min(1).max(255);
793
1362
  var tablePlugin = definePlugin({
794
1363
  name: "table",
795
- schema: import_zod16.z.object({
796
- stacks: import_zod16.z.object({
797
- tables: import_zod16.z.record(
1364
+ schema: import_zod9.z.object({
1365
+ stacks: import_zod9.z.object({
1366
+ /** Define the tables in your stack
1367
+ * @example
1368
+ * {
1369
+ * tables: {
1370
+ * TABLE_NAME: {
1371
+ * hash: 'id',
1372
+ * fields: {
1373
+ * id: 'number'
1374
+ * }
1375
+ * }
1376
+ * }
1377
+ * }
1378
+ * */
1379
+ tables: import_zod9.z.record(
798
1380
  ResourceIdSchema,
799
- import_zod16.z.object({
1381
+ import_zod9.z.object({
1382
+ /** Specifies the name of the partition / hash key that makes up the primary key for the table. */
800
1383
  hash: KeySchema,
1384
+ /** Specifies the name of the range / sort key that makes up the primary key for the table. */
801
1385
  sort: KeySchema.optional(),
802
- fields: import_zod16.z.record(import_zod16.z.string(), AttributeSchema),
803
- class: TableClassSchema.default("standard"),
804
- pointInTimeRecovery: import_zod16.z.boolean().default(false),
805
- timeToLiveAttribute: import_zod16.z.string().optional(),
806
- indexes: import_zod16.z.record(import_zod16.z.string(), import_zod16.z.object({
1386
+ /** A list of attributes that describe the key schema for the table and indexes.
1387
+ * @example
1388
+ * {
1389
+ * fields: {
1390
+ * id: 'string'
1391
+ * }
1392
+ * }
1393
+ */
1394
+ fields: import_zod9.z.record(import_zod9.z.string(), import_zod9.z.enum(["string", "number", "binary"])),
1395
+ /** The table class of the table.
1396
+ * @default 'standard'
1397
+ */
1398
+ class: import_zod9.z.enum(["standard", "standard-infrequent-access"]).default("standard"),
1399
+ /** Indicates whether point in time recovery is enabled on the table.
1400
+ * @default false
1401
+ */
1402
+ pointInTimeRecovery: import_zod9.z.boolean().default(false),
1403
+ /** The name of the TTL attribute used to store the expiration time for items in the table.
1404
+ * - To update this property, you must first disable TTL and then enable TTL with the new attribute name.
1405
+ */
1406
+ timeToLiveAttribute: KeySchema.optional(),
1407
+ /** Specifies the global secondary indexes to be created on the table.
1408
+ * @example
1409
+ * {
1410
+ * indexes: {
1411
+ * INDEX_NAME: {
1412
+ * hash: 'other'
1413
+ * }
1414
+ * }
1415
+ * }
1416
+ */
1417
+ indexes: import_zod9.z.record(import_zod9.z.string(), import_zod9.z.object({
1418
+ /** Specifies the name of the partition / hash key that makes up the primary key for the global secondary index. */
807
1419
  hash: KeySchema,
1420
+ /** Specifies the name of the range / sort key that makes up the primary key for the global secondary index. */
808
1421
  sort: KeySchema.optional(),
809
- projection: ProjectionTypeSchema.default("all")
1422
+ /** The set of attributes that are projected into the index:
1423
+ * - all - All of the table attributes are projected into the index.
1424
+ * - keys-only - Only the index and primary keys are projected into the index.
1425
+ * @default 'all'
1426
+ */
1427
+ projection: import_zod9.z.enum(["all", "keys-only"]).default("all")
810
1428
  })).optional()
811
1429
  }).refine((props) => {
812
1430
  return (
@@ -823,640 +1441,1031 @@ var tablePlugin = definePlugin({
823
1441
  ).optional()
824
1442
  }).array()
825
1443
  }),
826
- onStack({ stack, stackConfig, bind }) {
827
- Object.entries(stackConfig.tables || {}).map(([id, props]) => {
828
- const buildKey = (attr) => {
829
- return { name: attr, type: props.fields[attr] };
830
- };
831
- const table = new import_aws_dynamodb4.Table(stack, toId("table", id), {
832
- tableName: toName(stack, id),
833
- partitionKey: buildKey(props.hash),
834
- sortKey: props.sort ? buildKey(props.sort) : void 0,
835
- billingMode: import_aws_dynamodb4.BillingMode.PAY_PER_REQUEST,
836
- pointInTimeRecovery: props.pointInTimeRecovery,
837
- timeToLiveAttribute: props.timeToLiveAttribute,
838
- tableClass: props.class
839
- });
840
- Object.entries(props.indexes || {}).forEach(([indexName, entry]) => {
841
- table.addGlobalSecondaryIndex({
842
- indexName,
843
- partitionKey: buildKey(entry.hash),
844
- sortKey: entry.sort ? buildKey(entry.sort) : void 0,
845
- ...entry.projection
846
- });
1444
+ onStack({ config, stack, stackConfig, bind }) {
1445
+ for (const [id, props] of Object.entries(stackConfig.tables || {})) {
1446
+ const table = new Table(id, {
1447
+ name: `${config.name}-${stack.name}-${id}`,
1448
+ ...props
847
1449
  });
1450
+ stack.add(table);
848
1451
  bind((lambda) => {
849
- table.grantReadWriteData(lambda);
850
- addResourceEnvironment(stack, "table", id, lambda);
1452
+ lambda.addPermissions(table.permissions);
851
1453
  });
852
- });
1454
+ }
853
1455
  }
854
1456
  });
855
1457
 
856
1458
  // src/plugins/store.ts
857
- var import_zod17 = require("zod");
858
- var import_aws_s32 = require("aws-cdk-lib/aws-s3");
859
- var import_aws_cdk_lib3 = require("aws-cdk-lib");
1459
+ var import_zod10 = require("zod");
1460
+
1461
+ // src/formation/resource/s3/bucket.ts
1462
+ var import_change_case5 = require("change-case");
1463
+ var Bucket = class extends Resource {
1464
+ constructor(logicalId, props = {}) {
1465
+ super("AWS::S3::Bucket", logicalId);
1466
+ this.props = props;
1467
+ this.name = formatName(this.props.name || logicalId);
1468
+ }
1469
+ name;
1470
+ get arn() {
1471
+ return ref(this.logicalId);
1472
+ }
1473
+ get domainName() {
1474
+ return getAtt(this.logicalId, "DomainName");
1475
+ }
1476
+ get permissions() {
1477
+ return {
1478
+ actions: [
1479
+ "s3:SendMessage",
1480
+ "s3:ReceiveMessage",
1481
+ "s3:GetQueueUrl",
1482
+ "s3:GetQueueAttributes"
1483
+ ],
1484
+ resources: [this.arn]
1485
+ };
1486
+ }
1487
+ properties() {
1488
+ return {
1489
+ BucketName: this.name,
1490
+ AccessControl: (0, import_change_case5.pascalCase)(this.props.accessControl ?? "private"),
1491
+ ...this.props.versioned ? {
1492
+ VersioningConfiguration: {
1493
+ Status: "Enabled"
1494
+ }
1495
+ } : {}
1496
+ };
1497
+ }
1498
+ };
1499
+
1500
+ // src/plugins/store.ts
860
1501
  var storePlugin = definePlugin({
861
1502
  name: "store",
862
- schema: import_zod17.z.object({
863
- stacks: import_zod17.z.object({
864
- stores: import_zod17.z.array(ResourceIdSchema).optional()
1503
+ schema: import_zod10.z.object({
1504
+ stacks: import_zod10.z.object({
1505
+ /** Define the stores in your stack
1506
+ * @example
1507
+ * {
1508
+ * stores: [ 'STORE_NAME' ]
1509
+ * }
1510
+ * */
1511
+ stores: import_zod10.z.array(ResourceIdSchema).optional()
865
1512
  }).array()
866
1513
  }),
867
- onStack({ stack, stackConfig, bind }) {
868
- (stackConfig.stores || []).forEach((id) => {
869
- const bucket = new import_aws_s32.Bucket(stack, toId("store", id), {
870
- bucketName: toName(stack, id),
871
- accessControl: import_aws_s32.BucketAccessControl.PRIVATE,
872
- removalPolicy: import_aws_cdk_lib3.RemovalPolicy.DESTROY
1514
+ onStack({ config, stack, stackConfig, bind }) {
1515
+ for (const id of stackConfig.stores || []) {
1516
+ const bucket = new Bucket(id, {
1517
+ name: `${config.name}-${stack.name}-${id}`,
1518
+ accessControl: "private"
873
1519
  });
1520
+ stack.add(bucket);
874
1521
  bind((lambda) => {
875
- bucket.grantReadWrite(lambda), addResourceEnvironment(stack, "store", id, lambda);
1522
+ lambda.addPermissions(bucket.permissions);
876
1523
  });
877
- });
1524
+ }
878
1525
  }
879
1526
  });
880
1527
 
881
1528
  // src/plugins/topic.ts
882
- var import_zod18 = require("zod");
883
- var import_aws_sns = require("aws-cdk-lib/aws-sns");
884
- var import_aws_lambda_event_sources2 = require("aws-cdk-lib/aws-lambda-event-sources");
885
- var import_aws_cdk_lib4 = require("aws-cdk-lib");
1529
+ var import_zod11 = require("zod");
1530
+
1531
+ // src/formation/resource/sns/topic.ts
1532
+ var Topic = class extends Resource {
1533
+ constructor(logicalId, props = {}) {
1534
+ super("AWS::SNS::Topic", logicalId);
1535
+ this.props = props;
1536
+ this.name = formatName(this.props.name || logicalId);
1537
+ }
1538
+ name;
1539
+ get arn() {
1540
+ return ref(this.logicalId);
1541
+ }
1542
+ get permissions() {
1543
+ return {
1544
+ actions: ["sns:Publish"],
1545
+ resources: [this.arn]
1546
+ };
1547
+ }
1548
+ properties() {
1549
+ return {
1550
+ TopicName: this.name,
1551
+ DisplayName: this.name
1552
+ };
1553
+ }
1554
+ };
1555
+
1556
+ // src/formation/resource/sns/subscription.ts
1557
+ var Subscription = class extends Resource {
1558
+ constructor(logicalId, props) {
1559
+ super("AWS::SNS::Subscription", logicalId);
1560
+ this.props = props;
1561
+ }
1562
+ properties() {
1563
+ return {
1564
+ TopicArn: this.props.topicArn,
1565
+ Protocol: this.props.protocol,
1566
+ Endpoint: this.props.endpoint
1567
+ };
1568
+ }
1569
+ };
1570
+
1571
+ // src/formation/resource/lambda/event-source/sns.ts
1572
+ var SnsEventSource = class extends Group {
1573
+ constructor(id, lambda, props) {
1574
+ const topic = new Subscription(id, {
1575
+ topicArn: props.topicArn,
1576
+ protocol: "lambda",
1577
+ endpoint: lambda.arn
1578
+ });
1579
+ const permission = new Permission2(id, {
1580
+ action: "lambda:InvokeFunction",
1581
+ principal: "sns.amazonaws.com",
1582
+ functionArn: lambda.arn,
1583
+ sourceArn: props.topicArn
1584
+ });
1585
+ super([topic, permission]);
1586
+ }
1587
+ };
1588
+
1589
+ // src/plugins/topic.ts
886
1590
  var topicPlugin = definePlugin({
887
1591
  name: "topic",
888
- schema: import_zod18.z.object({
889
- stacks: import_zod18.z.object({
890
- topics: import_zod18.z.record(ResourceIdSchema, FunctionSchema).optional()
1592
+ schema: import_zod11.z.object({
1593
+ stacks: import_zod11.z.object({
1594
+ /** Define the topics to listen too in your stack
1595
+ * @example
1596
+ * {
1597
+ * topics: {
1598
+ * TOPIC_NAME: 'function.ts'
1599
+ * }
1600
+ * }
1601
+ * */
1602
+ topics: import_zod11.z.record(ResourceIdSchema, FunctionSchema).optional()
891
1603
  }).array()
892
1604
  }),
893
- onBootstrap({ config, stack }) {
894
- const allTopicNames = config.stacks.map((stack2) => {
895
- return Object.keys(stack2.topics || {});
1605
+ onApp({ config, bootstrap: bootstrap2, bind }) {
1606
+ const allTopicNames = config.stacks.map((stack) => {
1607
+ return Object.keys(stack.topics || {});
896
1608
  }).flat();
897
1609
  const uniqueTopicNames = [...new Set(allTopicNames)];
898
- uniqueTopicNames.forEach((id) => {
899
- new import_aws_sns.Topic(stack, toId("topic", id), {
900
- topicName: `${config.name}-${id}`,
901
- displayName: id
1610
+ for (const id of uniqueTopicNames) {
1611
+ const topic = new Topic(id, {
1612
+ name: `${config.name}-${id}`
1613
+ });
1614
+ bootstrap2.add(topic);
1615
+ bootstrap2.export(`topic-${id}-arn`, topic.arn);
1616
+ }
1617
+ bind((lambda) => {
1618
+ lambda.addPermissions({
1619
+ actions: ["sns:Publish"],
1620
+ resources: [
1621
+ sub("arn:${AWS::Partition}:sns:${AWS::Region}:${AWS::AccountId}:${app}-*", {
1622
+ app: config.name
1623
+ })
1624
+ ]
902
1625
  });
903
1626
  });
904
1627
  },
905
1628
  onStack(ctx) {
906
- const { config, stack, stackConfig, bind } = ctx;
907
- return Object.entries(stackConfig.topics || {}).map(([id, props]) => {
908
- const lambda = toFunction(ctx, id, props);
909
- const topic = import_aws_sns.Topic.fromTopicArn(
910
- stack,
911
- toId("topic", id),
912
- import_aws_cdk_lib4.Arn.format({
913
- arnFormat: import_aws_cdk_lib4.ArnFormat.NO_RESOURCE_NAME,
914
- service: "sns",
915
- resource: `${config.name}-${id}`
916
- }, stack)
917
- );
918
- lambda.addEventSource(new import_aws_lambda_event_sources2.SnsEventSource(topic));
919
- bind((lambda2) => {
920
- addResourceEnvironment(stack, "topic", id, lambda2);
921
- topic.grantPublish(lambda2);
1629
+ const { stack, stackConfig, bootstrap: bootstrap2 } = ctx;
1630
+ for (const [id, props] of Object.entries(stackConfig.topics || {})) {
1631
+ const lambda = toLambdaFunction(ctx, id, props);
1632
+ const source = new SnsEventSource(id, lambda, {
1633
+ topicArn: bootstrap2.import(`topic-${id}-arn`)
922
1634
  });
923
- return lambda;
924
- });
1635
+ stack.add(lambda, source);
1636
+ }
925
1637
  }
926
1638
  });
927
1639
 
928
- // src/plugins/graphql/index.ts
929
- var import_zod20 = require("zod");
930
- var import_aws_appsync = require("aws-cdk-lib/aws-appsync");
931
- var import_merge = require("@graphql-tools/merge");
932
- var import_promises4 = require("fs/promises");
933
-
934
- // src/util/array.ts
935
- var toArray = (value) => {
936
- if (Array.isArray(value)) {
937
- return value;
1640
+ // src/plugins/extend.ts
1641
+ var import_zod12 = require("zod");
1642
+ var extendPlugin = definePlugin({
1643
+ name: "extend",
1644
+ schema: import_zod12.z.object({
1645
+ /** Extend your app with custom resources */
1646
+ extend: import_zod12.z.custom().optional(),
1647
+ stacks: import_zod12.z.object({
1648
+ /** Extend your stack with custom resources */
1649
+ extend: import_zod12.z.custom().optional()
1650
+ }).array()
1651
+ }),
1652
+ onApp(ctx) {
1653
+ ctx.config.extend?.(ctx);
1654
+ },
1655
+ onStack(ctx) {
1656
+ ctx.stackConfig.extend?.(ctx);
938
1657
  }
939
- return [value];
940
- };
1658
+ });
941
1659
 
942
- // src/plugins/graphql/index.ts
943
- var import_path6 = require("path");
944
- var import_graphql = require("graphql");
945
- var import_change_case2 = require("change-case");
1660
+ // src/plugins/pubsub.ts
1661
+ var import_zod13 = require("zod");
946
1662
 
947
- // src/plugins/graphql/schema/resolver-field.ts
948
- var import_zod19 = require("zod");
949
- var ResolverFieldSchema = import_zod19.z.custom((value) => {
950
- return import_zod19.z.string().regex(/([a-z0-9\_]+)(\s){1}([a-z0-9\_]+)/gi).safeParse(value).success;
951
- }, `Invalid resolver field. Valid example: "Query list"`);
1663
+ // src/formation/resource/iot/topic-rule.ts
1664
+ var import_change_case6 = require("change-case");
1665
+ var TopicRule = class extends Resource {
1666
+ constructor(logicalId, props) {
1667
+ super("AWS::IoT::TopicRule", logicalId);
1668
+ this.props = props;
1669
+ this.name = (0, import_change_case6.snakeCase)(this.props.name || logicalId);
1670
+ }
1671
+ name;
1672
+ get arn() {
1673
+ return getAtt(this.logicalId, "Arn");
1674
+ }
1675
+ properties() {
1676
+ return {
1677
+ RuleName: this.name,
1678
+ TopicRulePayload: {
1679
+ Sql: this.props.sql,
1680
+ AwsIotSqlVersion: this.props.sqlVersion ?? "2016-03-23",
1681
+ RuleDisabled: false,
1682
+ Actions: this.props.actions.map((action) => ({
1683
+ Lambda: { FunctionArn: action.lambda.functionArn }
1684
+ }))
1685
+ }
1686
+ };
1687
+ }
1688
+ };
952
1689
 
953
- // src/plugins/graphql/index.ts
954
- var import_aws_cdk_lib5 = require("aws-cdk-lib");
955
- var graphqlPlugin = definePlugin({
956
- name: "graphql",
957
- schema: import_zod20.z.object({
958
- defaults: import_zod20.z.object({
959
- graphql: import_zod20.z.record(ResourceIdSchema, import_zod20.z.object({
960
- authorization: import_zod20.z.object({
961
- authorizer: FunctionSchema,
962
- ttl: DurationSchema.default("1 hour")
963
- }).optional(),
964
- mappingTemplate: import_zod20.z.object({
965
- request: LocalFileSchema.optional(),
966
- response: LocalFileSchema.optional()
967
- }).optional()
968
- })).optional()
969
- }).default({}),
970
- stacks: import_zod20.z.object({
971
- graphql: import_zod20.z.record(ResourceIdSchema, import_zod20.z.object({
972
- schema: import_zod20.z.union([
973
- LocalFileSchema,
974
- import_zod20.z.array(LocalFileSchema).min(1)
975
- ]).optional(),
976
- resolvers: import_zod20.z.record(ResolverFieldSchema, FunctionSchema).optional()
977
- })).optional()
978
- }).array()
979
- }),
980
- onBootstrap({ config, stack, assets }) {
981
- const list3 = /* @__PURE__ */ new Set();
982
- Object.values(config.stacks).forEach((stackConfig) => {
983
- Object.keys(stackConfig.graphql || {}).forEach((id) => {
984
- list3.add(id);
985
- });
1690
+ // src/formation/resource/lambda/event-source/iot.ts
1691
+ var IotEventSource = class extends Group {
1692
+ constructor(id, lambda, props) {
1693
+ const topic = new TopicRule(id, {
1694
+ name: props.name,
1695
+ sql: props.sql,
1696
+ sqlVersion: props.sqlVersion,
1697
+ actions: [{ lambda: { functionArn: lambda.arn } }]
986
1698
  });
987
- list3.forEach((id) => {
988
- const file = (0, import_path6.join)(assetDir, "graphql", config.name, id, "schema.graphql");
989
- const authorization = config.defaults.graphql?.[id]?.authorization;
990
- const authProps = {};
991
- if (authorization) {
992
- const authorizer = toFunction({ config, assets, stack }, `${id}-authorizer`, authorization.authorizer);
993
- authProps.additionalAuthenticationProviders = [{
994
- authenticationType: import_aws_appsync.AuthorizationType.LAMBDA,
995
- lambdaAuthorizerConfig: {
996
- authorizerUri: authorizer.functionArn,
997
- authorizerResultTtlInSeconds: authorization.ttl.toSeconds()
998
- }
999
- }];
1000
- }
1001
- const api = new import_aws_appsync.CfnGraphQLApi(stack, toId("graphql", id), {
1002
- ...authProps,
1003
- name: toName(stack, id),
1004
- authenticationType: import_aws_appsync.AuthorizationType.API_KEY
1005
- });
1006
- new import_aws_cdk_lib5.CfnOutput(stack, toId("output", id), {
1007
- exportName: toId("graphql", id),
1008
- value: api.attrApiId
1009
- });
1010
- assets.add({
1011
- stackName: stack.artifactId,
1012
- resource: "schema",
1013
- resourceName: id,
1014
- async build() {
1015
- const schemas = [];
1016
- await Promise.all(Object.values(config.stacks).map(async (stackConfig) => {
1017
- const schemaFiles = toArray(stackConfig.graphql?.[id].schema || []);
1018
- await Promise.all(schemaFiles.map(async (schemaFile) => {
1019
- const schema3 = await (0, import_promises4.readFile)(schemaFile, "utf8");
1020
- schemas.push(schema3);
1021
- }));
1022
- }));
1023
- const schema2 = (0, import_graphql.print)((0, import_merge.mergeTypeDefs)(schemas));
1024
- await (0, import_promises4.mkdir)((0, import_path6.dirname)(file), { recursive: true });
1025
- await (0, import_promises4.writeFile)(file, schema2);
1026
- new import_aws_appsync.CfnGraphQLSchema(stack, toId("schema", id), {
1027
- apiId: api.attrApiId,
1028
- definition: schema2
1029
- });
1030
- }
1031
- });
1699
+ const permission = new Permission2(id, {
1700
+ action: "lambda:InvokeFunction",
1701
+ principal: "iot.amazonaws.com",
1702
+ functionArn: lambda.arn,
1703
+ sourceArn: topic.arn
1032
1704
  });
1033
- },
1034
- onStack(ctx) {
1035
- const { config, stack, stackConfig } = ctx;
1036
- return Object.entries(stackConfig.graphql || {}).map(([id, props]) => {
1037
- const defaults = config.defaults.graphql?.[id] || {};
1038
- return Object.entries(props.resolvers || {}).map(([typeAndField, functionProps]) => {
1039
- const api = import_aws_appsync.GraphqlApi.fromGraphqlApiAttributes(stack, toId("graphql", id), {
1040
- graphqlApiId: import_aws_cdk_lib5.Fn.importValue(toId("graphql", id))
1041
- });
1042
- const [typeName, fieldName] = typeAndField.split(/[\s]+/g);
1043
- const functionId = (0, import_change_case2.paramCase)(`${id}-${typeName}-${fieldName}`);
1044
- const lambda = toFunction(ctx, functionId, functionProps);
1045
- const source = api.addLambdaDataSource(toId("data-source", functionId), lambda, {
1046
- name: toId("data-source", functionId)
1047
- });
1048
- source.createResolver(toId("resolver", functionId), {
1049
- typeName,
1050
- fieldName,
1051
- requestMappingTemplate: defaults.mappingTemplate?.request ? import_aws_appsync.MappingTemplate.fromFile(defaults.mappingTemplate.request) : import_aws_appsync.MappingTemplate.lambdaRequest(),
1052
- responseMappingTemplate: defaults.mappingTemplate?.response ? import_aws_appsync.MappingTemplate.fromFile(defaults.mappingTemplate.response) : import_aws_appsync.MappingTemplate.lambdaResult()
1053
- });
1054
- return lambda;
1055
- });
1056
- }).flat();
1705
+ super([topic, permission]);
1057
1706
  }
1058
- });
1707
+ };
1059
1708
 
1060
1709
  // src/plugins/pubsub.ts
1061
- var import_zod21 = require("zod");
1062
- var import_aws_iot = require("aws-cdk-lib/aws-iot");
1063
- var import_aws_iam2 = require("aws-cdk-lib/aws-iam");
1064
- var import_change_case3 = require("change-case");
1065
1710
  var pubsubPlugin = definePlugin({
1066
1711
  name: "pubsub",
1067
- schema: import_zod21.z.object({
1068
- stacks: import_zod21.z.object({
1069
- pubsub: import_zod21.z.record(ResourceIdSchema, import_zod21.z.object({
1070
- sql: import_zod21.z.string(),
1071
- sqlVersion: import_zod21.z.enum(["2015-10-08", "2016-03-23", "beta"]).default("2016-03-23"),
1712
+ schema: import_zod13.z.object({
1713
+ stacks: import_zod13.z.object({
1714
+ /** Define the pubsub subscriber in your stack
1715
+ * @example
1716
+ * {
1717
+ * pubsub: {
1718
+ * NAME: {
1719
+ * sql: 'SELECT * FROM "table"',
1720
+ * consumer: 'function.ts',
1721
+ * }
1722
+ * }
1723
+ * }
1724
+ */
1725
+ pubsub: import_zod13.z.record(ResourceIdSchema, import_zod13.z.object({
1726
+ /** The SQL statement used to query the iot topic */
1727
+ sql: import_zod13.z.string(),
1728
+ /** The version of the SQL rules engine to use when evaluating the rule */
1729
+ sqlVersion: import_zod13.z.enum(["2015-10-08", "2016-03-23", "beta"]).default("2016-03-23"),
1730
+ /** The consuming lambda function properties */
1072
1731
  consumer: FunctionSchema
1073
1732
  })).optional()
1074
1733
  }).array()
1075
1734
  }),
1076
- onStack(ctx) {
1077
- const { stack, stackConfig, bind } = ctx;
1735
+ onApp({ bind }) {
1078
1736
  bind((lambda) => {
1079
- lambda.addToRolePolicy(new import_aws_iam2.PolicyStatement({
1737
+ lambda.addPermissions({
1080
1738
  actions: ["iot:publish"],
1081
1739
  resources: ["*"]
1082
- }));
1083
- });
1084
- return Object.entries(stackConfig.pubsub || {}).map(([id, props]) => {
1085
- const lambda = toFunction(ctx, id, props.consumer);
1086
- new import_aws_iot.CfnTopicRule(stack, toId("pubsub", id), {
1087
- ruleName: (0, import_change_case3.snakeCase)(toName(stack, id)),
1088
- topicRulePayload: {
1089
- sql: props.sql,
1090
- awsIotSqlVersion: props.sqlVersion,
1091
- actions: [{
1092
- lambda: {
1093
- functionArn: lambda.functionArn
1094
- }
1095
- }]
1096
- }
1097
1740
  });
1098
- return lambda;
1099
1741
  });
1742
+ },
1743
+ onStack(ctx) {
1744
+ const { config, stack, stackConfig } = ctx;
1745
+ for (const [id, props] of Object.entries(stackConfig.pubsub || {})) {
1746
+ const lambda = toLambdaFunction(ctx, id, props.consumer);
1747
+ const source = new IotEventSource(id, lambda, {
1748
+ name: `${config.name}-${stack.name}-${id}`,
1749
+ sql: props.sql,
1750
+ sqlVersion: props.sqlVersion
1751
+ });
1752
+ stack.add(lambda, source);
1753
+ }
1100
1754
  }
1101
1755
  });
1102
1756
 
1103
- // src/plugins/http/index.ts
1104
- var import_zod23 = require("zod");
1105
- var import_aws_ec2 = require("aws-cdk-lib/aws-ec2");
1106
- var import_aws_elasticloadbalancingv2 = require("aws-cdk-lib/aws-elasticloadbalancingv2");
1107
- var import_aws_route53 = require("aws-cdk-lib/aws-route53");
1108
- var import_aws_route53_targets = require("aws-cdk-lib/aws-route53-targets");
1109
- var import_aws_elasticloadbalancingv2_targets = require("aws-cdk-lib/aws-elasticloadbalancingv2-targets");
1110
- var import_aws_cdk_lib6 = require("aws-cdk-lib");
1111
- var import_aws_certificatemanager = require("aws-cdk-lib/aws-certificatemanager");
1112
- var import_change_case4 = require("change-case");
1757
+ // src/plugins/graphql.ts
1758
+ var import_zod14 = require("zod");
1113
1759
 
1114
- // src/plugins/http/schema/route.ts
1115
- var import_zod22 = require("zod");
1116
- var RouteSchema = import_zod22.z.custom((route) => {
1117
- return import_zod22.z.string().regex(/^(POST|GET|PUT|DELETE|HEAD|OPTIONS)(\s\/[a-z0-9\+\_\-\/]*)$/ig).safeParse(route).success;
1118
- }, "Invalid route");
1119
-
1120
- // src/plugins/http/util/priority.ts
1121
- var strToInt = (str) => {
1122
- return parseInt(Buffer.from(str, "utf8").toString("hex"), 16);
1123
- };
1124
- var generatePriority = (stackName, route) => {
1125
- const start = strToInt(stackName) % 500 + 1;
1126
- const end = strToInt(route) % 100;
1127
- const priority = start + "" + end;
1128
- return parseInt(priority, 10);
1129
- };
1130
-
1131
- // src/plugins/http/index.ts
1132
- var httpPlugin = definePlugin({
1133
- name: "http",
1134
- schema: import_zod23.z.object({
1135
- defaults: import_zod23.z.object({
1136
- http: import_zod23.z.record(
1137
- ResourceIdSchema,
1138
- import_zod23.z.object({
1139
- domain: import_zod23.z.string(),
1140
- subDomain: import_zod23.z.string()
1141
- })
1142
- ).optional()
1760
+ // src/util/array.ts
1761
+ var toArray = (value) => {
1762
+ if (Array.isArray(value)) {
1763
+ return value;
1764
+ }
1765
+ return [value];
1766
+ };
1767
+
1768
+ // src/plugins/graphql.ts
1769
+ var import_change_case10 = require("change-case");
1770
+
1771
+ // src/formation/resource/appsync/graphql-api.ts
1772
+ var import_change_case7 = require("change-case");
1773
+ var GraphQL = class extends Group {
1774
+ constructor(logicalId, props) {
1775
+ const api = new GraphQLApi(logicalId, props);
1776
+ const schema2 = new GraphQLSchema(logicalId, {
1777
+ apiId: api.id,
1778
+ definition: props.schema
1779
+ }).dependsOn(api);
1780
+ super([api, schema2]);
1781
+ this.logicalId = logicalId;
1782
+ this.api = api;
1783
+ this.schema = schema2;
1784
+ }
1785
+ api;
1786
+ schema;
1787
+ attachDomainName(domainName, certificateArn) {
1788
+ const id = this.logicalId + domainName;
1789
+ const domain = new DomainName(id, {
1790
+ domainName,
1791
+ certificateArn
1792
+ });
1793
+ const association = new DomainNameApiAssociation(id, {
1794
+ apiId: this.api.id,
1795
+ domainName
1796
+ }).dependsOn(this.api, domain);
1797
+ this.children.push(domain, association);
1798
+ return this;
1799
+ }
1800
+ };
1801
+ var GraphQLApi = class extends Resource {
1802
+ constructor(logicalId, props) {
1803
+ super("AWS::AppSync::GraphQLApi", logicalId);
1804
+ this.props = props;
1805
+ this.name = formatName(this.props.name || logicalId);
1806
+ }
1807
+ name;
1808
+ lambdaAuthProviders = [];
1809
+ get arn() {
1810
+ return ref(this.logicalId);
1811
+ }
1812
+ get id() {
1813
+ return getAtt(this.logicalId, "ApiId");
1814
+ }
1815
+ get url() {
1816
+ return getAtt(this.logicalId, "GraphQLUrl");
1817
+ }
1818
+ get dns() {
1819
+ return getAtt(this.logicalId, "GraphQLDns");
1820
+ }
1821
+ addLambdaAuthProvider(lambdaAuthorizerArn, resultTTL = Duration.seconds(0)) {
1822
+ this.lambdaAuthProviders.push({
1823
+ arn: lambdaAuthorizerArn,
1824
+ ttl: resultTTL
1825
+ });
1826
+ return this;
1827
+ }
1828
+ properties() {
1829
+ return {
1830
+ Name: this.name,
1831
+ AuthenticationType: (0, import_change_case7.constantCase)(this.props.authenticationType || "api-key"),
1832
+ AdditionalAuthenticationProviders: this.lambdaAuthProviders.map((provider) => ({
1833
+ AuthenticationType: "AWS_LAMBDA",
1834
+ LambdaAuthorizerConfig: {
1835
+ AuthorizerUri: provider.arn,
1836
+ AuthorizerResultTtlInSeconds: provider.ttl.toSeconds()
1837
+ }
1838
+ }))
1839
+ };
1840
+ }
1841
+ };
1842
+ var GraphQLSchema = class extends Resource {
1843
+ constructor(logicalId, props) {
1844
+ super("AWS::AppSync::GraphQLSchema", logicalId, [
1845
+ props.definition
1846
+ ]);
1847
+ this.props = props;
1848
+ }
1849
+ properties() {
1850
+ return {
1851
+ ApiId: this.props.apiId,
1852
+ Definition: this.props.definition.toDefinition()
1853
+ };
1854
+ }
1855
+ };
1856
+ var DomainName = class extends Resource {
1857
+ constructor(logicalId, props) {
1858
+ super("AWS::AppSync::DomainName", logicalId);
1859
+ this.props = props;
1860
+ }
1861
+ properties() {
1862
+ return {
1863
+ DomainName: this.props.domainName,
1864
+ CertificateArn: this.props.certificateArn
1865
+ };
1866
+ }
1867
+ };
1868
+ var DomainNameApiAssociation = class extends Resource {
1869
+ constructor(logicalId, props) {
1870
+ super("AWS::AppSync::DomainNameApiAssociation", logicalId);
1871
+ this.props = props;
1872
+ }
1873
+ properties() {
1874
+ return {
1875
+ ApiId: this.props.apiId,
1876
+ DomainName: this.props.domainName
1877
+ };
1878
+ }
1879
+ };
1880
+
1881
+ // src/formation/resource/route53/record-set.ts
1882
+ var RecordSet = class extends Resource {
1883
+ constructor(logicalId, props) {
1884
+ super("AWS::Route53::RecordSet", logicalId);
1885
+ this.props = props;
1886
+ this.name = this.props.name || this.logicalId;
1887
+ }
1888
+ name;
1889
+ properties() {
1890
+ return {
1891
+ HostedZoneId: this.props.hostedZoneId,
1892
+ Name: this.name + ".",
1893
+ Type: this.props.type,
1894
+ TTL: this.props.ttl,
1895
+ ...this.props.records ? {
1896
+ ResourceRecords: this.props.records
1897
+ } : {},
1898
+ ...this.props.alias ? {
1899
+ AliasTarget: {
1900
+ DNSName: this.props.alias,
1901
+ HostedZoneId: this.props.hostedZoneId
1902
+ }
1903
+ } : {}
1904
+ };
1905
+ }
1906
+ };
1907
+
1908
+ // src/formation/resource/appsync/schema.ts
1909
+ var import_graphql = require("graphql");
1910
+ var import_promises2 = require("fs/promises");
1911
+ var import_merge = require("@graphql-tools/merge");
1912
+ var Schema = class extends Asset {
1913
+ constructor(id, files) {
1914
+ super("graphql", id);
1915
+ this.files = files;
1916
+ }
1917
+ schema;
1918
+ async build({ write }) {
1919
+ const files = [this.files].flat();
1920
+ const schemas = await Promise.all(files.map((file) => {
1921
+ return (0, import_promises2.readFile)(file, "utf8");
1922
+ }));
1923
+ const defs = (0, import_merge.mergeTypeDefs)(schemas);
1924
+ const schema2 = (0, import_graphql.print)(defs);
1925
+ await write("schema.gql", schema2);
1926
+ this.schema = schema2;
1927
+ }
1928
+ toDefinition() {
1929
+ return this.schema;
1930
+ }
1931
+ };
1932
+
1933
+ // src/formation/resource/appsync/code.ts
1934
+ var import_promises3 = require("fs/promises");
1935
+ var Code2 = class {
1936
+ static fromFile(id, file) {
1937
+ return new FileCode2(id, file);
1938
+ }
1939
+ static fromInline(id, code) {
1940
+ return new InlineCode2(id, code);
1941
+ }
1942
+ };
1943
+ var InlineCode2 = class extends Asset {
1944
+ constructor(id, code) {
1945
+ super("resolver", id);
1946
+ this.code = code;
1947
+ }
1948
+ toCodeJson() {
1949
+ return {
1950
+ Code: this.code
1951
+ };
1952
+ }
1953
+ };
1954
+ var FileCode2 = class extends Asset {
1955
+ constructor(id, file) {
1956
+ super("resolver", id);
1957
+ this.file = file;
1958
+ }
1959
+ code;
1960
+ async build() {
1961
+ const code = await (0, import_promises3.readFile)(this.file);
1962
+ this.code = code.toString("utf8");
1963
+ return {
1964
+ size: formatByteSize(code.byteLength)
1965
+ };
1966
+ }
1967
+ toCodeJson() {
1968
+ return {
1969
+ Code: this.code
1970
+ };
1971
+ }
1972
+ };
1973
+
1974
+ // src/formation/resource/appsync/data-source.ts
1975
+ var import_change_case8 = require("change-case");
1976
+ var DataSource = class extends Resource {
1977
+ constructor(logicalId, props) {
1978
+ super("AWS::AppSync::DataSource", logicalId);
1979
+ this.props = props;
1980
+ this.name = (0, import_change_case8.snakeCase)(this.props.name || logicalId);
1981
+ }
1982
+ static fromLambda(logicalId, apiId, props) {
1983
+ return new DataSource(logicalId, {
1984
+ apiId,
1985
+ type: "AWS_LAMBDA",
1986
+ serviceRoleArn: props.serviceRoleArn,
1987
+ config: {
1988
+ lambda: {
1989
+ functionArn: props.functionArn
1990
+ }
1991
+ }
1992
+ });
1993
+ }
1994
+ static fromNone(logicalId, apiId) {
1995
+ return new DataSource(logicalId, {
1996
+ apiId,
1997
+ type: "NONE"
1998
+ });
1999
+ }
2000
+ name;
2001
+ get arn() {
2002
+ return ref(this.logicalId);
2003
+ }
2004
+ properties() {
2005
+ return {
2006
+ ApiId: this.props.apiId,
2007
+ Name: this.name,
2008
+ Type: this.props.type,
2009
+ ServiceRoleArn: this.props.serviceRoleArn,
2010
+ ...this.props.config?.lambda ? {
2011
+ LambdaConfig: {
2012
+ LambdaFunctionArn: this.props.config.lambda.functionArn
2013
+ }
2014
+ } : {}
2015
+ };
2016
+ }
2017
+ };
2018
+
2019
+ // src/formation/resource/appsync/function-configuration.ts
2020
+ var import_change_case9 = require("change-case");
2021
+ var FunctionConfiguration = class extends Resource {
2022
+ constructor(logicalId, props) {
2023
+ super("AWS::AppSync::FunctionConfiguration", logicalId, [
2024
+ props.code
2025
+ ]);
2026
+ this.props = props;
2027
+ this.name = (0, import_change_case9.snakeCase)(this.props.name || logicalId);
2028
+ }
2029
+ name;
2030
+ get id() {
2031
+ return getAtt(this.logicalId, "FunctionId");
2032
+ }
2033
+ get arn() {
2034
+ return ref(this.logicalId);
2035
+ }
2036
+ properties() {
2037
+ return {
2038
+ ApiId: this.props.apiId,
2039
+ Name: this.name,
2040
+ DataSourceName: this.props.dataSourceName,
2041
+ ...this.props.code.toCodeJson(),
2042
+ FunctionVersion: "2018-05-29",
2043
+ Runtime: {
2044
+ Name: "APPSYNC_JS",
2045
+ RuntimeVersion: "1.0.0"
2046
+ }
2047
+ };
2048
+ }
2049
+ };
2050
+
2051
+ // src/formation/resource/appsync/resolver.ts
2052
+ var Resolver = class extends Resource {
2053
+ constructor(logicalId, props) {
2054
+ super("AWS::AppSync::Resolver", logicalId);
2055
+ this.props = props;
2056
+ }
2057
+ properties() {
2058
+ return {
2059
+ ApiId: this.props.apiId,
2060
+ Kind: "PIPELINE",
2061
+ TypeName: this.props.typeName,
2062
+ FieldName: this.props.fieldName,
2063
+ PipelineConfig: {
2064
+ Functions: this.props.functions
2065
+ },
2066
+ // DataSourceName: this.props.dataSourceName,
2067
+ ...this.props.code.toCodeJson(),
2068
+ Runtime: {
2069
+ Name: "APPSYNC_JS",
2070
+ RuntimeVersion: "1.0.0"
2071
+ }
2072
+ };
2073
+ }
2074
+ };
2075
+
2076
+ // src/formation/resource/lambda/event-source/appsync.ts
2077
+ var AppsyncEventSource = class extends Group {
2078
+ constructor(id, lambda, props) {
2079
+ const role = new Role(id + "AppSync", {
2080
+ assumedBy: "appsync.amazonaws.com"
2081
+ }).dependsOn(lambda);
2082
+ role.addInlinePolicy(new InlinePolicy(id, {
2083
+ statements: [{
2084
+ actions: ["lambda:InvokeFunction"],
2085
+ resources: [lambda.arn]
2086
+ }]
2087
+ }));
2088
+ const source = DataSource.fromLambda(id, props.apiId, {
2089
+ functionArn: lambda.arn,
2090
+ serviceRoleArn: role.arn
2091
+ }).dependsOn(role).dependsOn(lambda);
2092
+ const config = new FunctionConfiguration(id, {
2093
+ apiId: props.apiId,
2094
+ code: props.code,
2095
+ dataSourceName: source.name
2096
+ }).dependsOn(source);
2097
+ const resolver = new Resolver(id, {
2098
+ apiId: props.apiId,
2099
+ typeName: props.typeName,
2100
+ fieldName: props.fieldName,
2101
+ functions: [config.id],
2102
+ code: props.code
2103
+ }).dependsOn(config);
2104
+ super([role, source, config, resolver]);
2105
+ }
2106
+ };
2107
+
2108
+ // src/plugins/graphql.ts
2109
+ var defaultResolver = `
2110
+ export function request(ctx) {
2111
+ return {
2112
+ operation: 'Invoke',
2113
+ payload: ctx,
2114
+ };
2115
+ }
2116
+
2117
+ export function response(ctx) {
2118
+ return ctx.result
2119
+ }
2120
+ `;
2121
+ var ResolverFieldSchema = import_zod14.z.custom((value) => {
2122
+ return import_zod14.z.string().regex(/([a-z0-9\_]+)(\s){1}([a-z0-9\_]+)/gi).safeParse(value).success;
2123
+ }, `Invalid resolver field. Valid example: "Query list"`);
2124
+ var graphqlPlugin = definePlugin({
2125
+ name: "graphql",
2126
+ schema: import_zod14.z.object({
2127
+ defaults: import_zod14.z.object({
2128
+ graphql: import_zod14.z.record(ResourceIdSchema, import_zod14.z.object({
2129
+ domain: import_zod14.z.string().optional(),
2130
+ subDomain: import_zod14.z.string().optional(),
2131
+ authorization: import_zod14.z.object({
2132
+ authorizer: FunctionSchema,
2133
+ ttl: DurationSchema.default("1 hour")
2134
+ }).optional(),
2135
+ resolver: LocalFileSchema.optional()
2136
+ })).optional()
1143
2137
  }).default({}),
1144
- stacks: import_zod23.z.object({
1145
- http: import_zod23.z.record(
1146
- ResourceIdSchema,
1147
- import_zod23.z.record(RouteSchema, FunctionSchema)
1148
- ).optional()
2138
+ stacks: import_zod14.z.object({
2139
+ graphql: import_zod14.z.record(ResourceIdSchema, import_zod14.z.object({
2140
+ schema: import_zod14.z.union([
2141
+ LocalFileSchema,
2142
+ import_zod14.z.array(LocalFileSchema).min(1)
2143
+ ]).optional(),
2144
+ resolvers: import_zod14.z.record(ResolverFieldSchema, FunctionSchema).optional()
2145
+ })).optional()
1149
2146
  }).array()
1150
2147
  }),
1151
- onBootstrap({ stack, config }) {
1152
- if (Object.keys(config.defaults?.http || {}).length === 0) {
1153
- return;
2148
+ onApp(ctx) {
2149
+ const { config, bootstrap: bootstrap2, usEastBootstrap } = ctx;
2150
+ const apis = /* @__PURE__ */ new Set();
2151
+ for (const stackConfig of config.stacks) {
2152
+ for (const id of Object.keys(stackConfig.graphql || {})) {
2153
+ apis.add(id);
2154
+ }
1154
2155
  }
1155
- const vpc = new import_aws_ec2.Vpc(stack, toId("vpc", "http"), {
1156
- subnetConfiguration: [{
1157
- name: "public",
1158
- subnetType: import_aws_ec2.SubnetType.PUBLIC,
1159
- cidrMask: 24
1160
- }],
1161
- availabilityZones: [
1162
- config.region + "a",
1163
- config.region + "b",
1164
- config.region + "c"
1165
- ]
1166
- });
1167
- const securityGroup = new import_aws_ec2.SecurityGroup(stack, toId("security-group", "http"), {
1168
- vpc
1169
- });
1170
- securityGroup.addIngressRule(import_aws_ec2.Peer.anyIpv4(), import_aws_ec2.Port.tcp(443));
1171
- securityGroup.addIngressRule(import_aws_ec2.Peer.anyIpv6(), import_aws_ec2.Port.tcp(443));
1172
- new import_aws_cdk_lib6.CfnOutput(stack, toId("output", "http-vpc"), {
1173
- exportName: "http-vpc-id",
1174
- value: vpc.vpcId
1175
- });
1176
- new import_aws_cdk_lib6.CfnOutput(stack, toId("output", "http-security-group"), {
1177
- exportName: "http-security-group-id",
1178
- value: securityGroup.securityGroupId
1179
- });
1180
- Object.entries(config.defaults?.http || {}).forEach(([id, props]) => {
1181
- const loadBalancer = new import_aws_elasticloadbalancingv2.ApplicationLoadBalancer(stack, toId("load-balancer", id), {
1182
- vpc,
1183
- securityGroup
1184
- });
1185
- const zone = import_aws_route53.HostedZone.fromHostedZoneAttributes(
1186
- stack,
1187
- toId("hosted-zone", id),
1188
- {
1189
- hostedZoneId: import_aws_cdk_lib6.Token.asString(import_aws_cdk_lib6.Fn.ref(toId("hosted-zone", props.domain))),
1190
- zoneName: props.domain + "."
1191
- }
1192
- );
1193
- const certificate = import_aws_certificatemanager.Certificate.fromCertificateArn(
1194
- stack,
1195
- toId("certificate", id),
1196
- import_aws_cdk_lib6.Token.asString(import_aws_cdk_lib6.Fn.ref(toId("certificate", props.domain)))
1197
- );
1198
- const target = import_aws_route53.RecordTarget.fromAlias(new import_aws_route53_targets.LoadBalancerTarget(loadBalancer));
1199
- const recordName = props.subDomain ? `${props.subDomain}.${props.domain}` : props.domain;
1200
- new import_aws_route53.RecordSet(stack, toId("record-set", id), {
1201
- zone,
1202
- target,
1203
- recordName,
1204
- recordType: import_aws_route53.RecordType.A
1205
- });
1206
- const listener = loadBalancer.addListener(toId("listener", id), {
1207
- port: 443,
1208
- protocol: import_aws_elasticloadbalancingv2.ApplicationProtocol.HTTPS,
1209
- certificates: [certificate],
1210
- defaultAction: import_aws_elasticloadbalancingv2.ListenerAction.fixedResponse(404, {
1211
- contentType: "application/json",
1212
- messageBody: JSON.stringify({
1213
- message: "Route not found"
1214
- })
1215
- })
1216
- });
1217
- new import_aws_cdk_lib6.CfnOutput(stack, toId("output", `http-${id}-listener`), {
1218
- exportName: `http-${id}-listener-arn`,
1219
- value: listener.listenerArn
2156
+ for (const id of apis) {
2157
+ const schema2 = [];
2158
+ for (const stack of config.stacks) {
2159
+ const files = toArray(stack.graphql?.[id]?.schema || []);
2160
+ schema2.push(...files);
2161
+ }
2162
+ const graphql = new GraphQL(id, {
2163
+ name: `${config.name}-${id}`,
2164
+ authenticationType: "api-key",
2165
+ schema: new Schema(id, schema2)
1220
2166
  });
1221
- });
2167
+ bootstrap2.add(graphql).export(`graphql-${id}`, graphql.api.id);
2168
+ const props = config.defaults.graphql?.[id];
2169
+ if (!props) {
2170
+ continue;
2171
+ }
2172
+ if (props.authorization) {
2173
+ const lambda = toLambdaFunction(ctx, `${id}-authorizer`, props.authorization.authorizer);
2174
+ graphql.api.addLambdaAuthProvider(lambda.arn, props.authorization.ttl);
2175
+ bootstrap2.add(lambda);
2176
+ }
2177
+ if (props.domain) {
2178
+ const domainName = props.subDomain ? `${props.subDomain}.${props.domain}` : props.domain;
2179
+ const hostedZoneId = ref(`${props.domain}Route53HostedZone`);
2180
+ const certificateArn = usEastBootstrap.import(`certificate-${props.domain}-arn`);
2181
+ graphql.attachDomainName(domainName, certificateArn);
2182
+ const record = new RecordSet(id, {
2183
+ hostedZoneId,
2184
+ type: "A",
2185
+ name: domainName,
2186
+ alias: graphql.api.dns
2187
+ });
2188
+ bootstrap2.add(record);
2189
+ }
2190
+ }
1222
2191
  },
1223
2192
  onStack(ctx) {
1224
- const { stack, stackConfig } = ctx;
1225
- return Object.entries(stackConfig.http || {}).map(([id, routes]) => {
1226
- const listener = import_aws_elasticloadbalancingv2.ApplicationListener.fromApplicationListenerAttributes(stack, toId("listener", id), {
1227
- listenerArn: import_aws_cdk_lib6.Fn.importValue(`http-${id}-listener-arn`),
1228
- securityGroup: import_aws_ec2.SecurityGroup.fromLookupById(
1229
- stack,
1230
- toId("security-group", id),
1231
- "http-security-group-id"
1232
- )
1233
- });
1234
- return Object.entries(routes).map(([route, props]) => {
1235
- const lambda = toFunction(ctx, (0, import_change_case4.paramCase)(route), props);
1236
- const [method, ...paths] = route.split(" ");
1237
- const path = paths.join(" ");
1238
- new import_aws_elasticloadbalancingv2.ApplicationListenerRule(stack, toId("listener-rule", route), {
1239
- listener,
1240
- priority: generatePriority(stackConfig.name, route),
1241
- action: import_aws_elasticloadbalancingv2.ListenerAction.forward([
1242
- new import_aws_elasticloadbalancingv2.ApplicationTargetGroup(stack, toId("target-group", route), {
1243
- targets: [new import_aws_elasticloadbalancingv2_targets.LambdaTarget(lambda)]
1244
- })
1245
- ]),
1246
- conditions: [
1247
- import_aws_elasticloadbalancingv2.ListenerCondition.httpRequestMethods([method]),
1248
- import_aws_elasticloadbalancingv2.ListenerCondition.pathPatterns([path])
1249
- ]
2193
+ const { stack, stackConfig, bootstrap: bootstrap2 } = ctx;
2194
+ for (const [id, props] of Object.entries(stackConfig.graphql || {})) {
2195
+ const apiId = bootstrap2.import(`graphql-${id}`);
2196
+ for (const [typeAndField, functionProps] of Object.entries(props.resolvers || {})) {
2197
+ const [typeName, fieldName] = typeAndField.split(/[\s]+/g);
2198
+ const entryId = (0, import_change_case10.paramCase)(`${id}-${typeName}-${fieldName}`);
2199
+ const lambda = toLambdaFunction(ctx, entryId, functionProps);
2200
+ const source = new AppsyncEventSource(entryId, lambda, {
2201
+ apiId,
2202
+ typeName,
2203
+ fieldName,
2204
+ code: Code2.fromInline(entryId, defaultResolver)
1250
2205
  });
1251
- return lambda;
1252
- });
1253
- }).flat();
2206
+ stack.add(lambda, source);
2207
+ }
2208
+ }
1254
2209
  }
1255
2210
  });
1256
2211
 
1257
- // src/plugins/domain/index.ts
1258
- var import_zod26 = require("zod");
1259
- var import_aws_route533 = require("aws-cdk-lib/aws-route53");
1260
- var import_aws_certificatemanager2 = require("aws-cdk-lib/aws-certificatemanager");
1261
-
1262
- // src/plugins/domain/schema/record-type.ts
1263
- var import_aws_route532 = require("aws-cdk-lib/aws-route53");
1264
- var import_zod24 = require("zod");
1265
- var types4 = {
1266
- "A": import_aws_route532.RecordType.A,
1267
- "AAAA": import_aws_route532.RecordType.AAAA,
1268
- "MX": import_aws_route532.RecordType.MX,
1269
- "TXT": import_aws_route532.RecordType.TXT,
1270
- "CNAME": import_aws_route532.RecordType.CNAME
1271
- };
1272
- var RecordTypeSchema = import_zod24.z.enum(Object.keys(types4)).transform((value) => types4[value]);
1273
-
1274
- // src/plugins/domain/schema/domain-name.ts
1275
- var import_zod25 = require("zod");
1276
- var DomainNameSchema = import_zod25.z.string().regex(/[a-z\-\_\.]/g, "Invalid domain name");
1277
-
1278
- // src/plugins/domain/index.ts
1279
- var import_aws_cdk_lib7 = require("aws-cdk-lib");
2212
+ // src/plugins/domain.ts
2213
+ var import_zod15 = require("zod");
2214
+
2215
+ // src/formation/resource/route53/hosted-zone.ts
2216
+ var HostedZone = class extends Resource {
2217
+ constructor(logicalId, props = {}) {
2218
+ super("AWS::Route53::HostedZone", logicalId);
2219
+ this.props = props;
2220
+ this.name = this.props.domainName || logicalId;
2221
+ }
2222
+ name;
2223
+ get id() {
2224
+ return ref(this.logicalId);
2225
+ }
2226
+ properties() {
2227
+ return {
2228
+ Name: this.name + "."
2229
+ };
2230
+ }
2231
+ };
2232
+
2233
+ // src/formation/resource/certificate-manager/certificate.ts
2234
+ var Certificate = class extends Resource {
2235
+ constructor(logicalId, props = {}) {
2236
+ super("AWS::CertificateManager::Certificate", logicalId);
2237
+ this.props = props;
2238
+ this.name = this.props.domainName || logicalId;
2239
+ }
2240
+ name;
2241
+ get arn() {
2242
+ return ref(this.logicalId);
2243
+ }
2244
+ properties() {
2245
+ return {
2246
+ DomainName: this.name,
2247
+ ValidationMethod: "DNS",
2248
+ SubjectAlternativeNames: this.props.alternativeNames || []
2249
+ };
2250
+ }
2251
+ };
2252
+
2253
+ // src/formation/resource/route53/record-set-group.ts
2254
+ var RecordSetGroup = class extends Resource {
2255
+ constructor(logicalId, props) {
2256
+ super("AWS::Route53::RecordSetGroup", logicalId);
2257
+ this.props = props;
2258
+ }
2259
+ properties() {
2260
+ return {
2261
+ HostedZoneId: this.props.hostedZoneId,
2262
+ RecordSets: this.props.records.map((props) => ({
2263
+ Name: props.name + ".",
2264
+ Type: props.type,
2265
+ TTL: props.ttl,
2266
+ ...props.records ? {
2267
+ ResourceRecords: props.records
2268
+ } : {},
2269
+ ...props.alias ? {
2270
+ AliasTarget: {
2271
+ DNSName: props.alias,
2272
+ HostedZoneId: this.props.hostedZoneId
2273
+ }
2274
+ } : {}
2275
+ }))
2276
+ };
2277
+ }
2278
+ };
2279
+
2280
+ // src/plugins/domain.ts
2281
+ var DomainNameSchema = import_zod15.z.string().regex(/[a-z\-\_\.]/g, "Invalid domain name");
1280
2282
  var domainPlugin = definePlugin({
1281
2283
  name: "domain",
1282
- schema: import_zod26.z.object({
1283
- domains: import_zod26.z.record(DomainNameSchema, import_zod26.z.object({
2284
+ schema: import_zod15.z.object({
2285
+ domains: import_zod15.z.record(DomainNameSchema, import_zod15.z.object({
1284
2286
  name: DomainNameSchema.optional(),
1285
- type: RecordTypeSchema,
2287
+ type: import_zod15.z.enum(["A", "AAAA", "CAA", "CNAME", "DS", "MX", "NAPTR", "NS", "PTR", "SOA", "SPF", "SRV", "TXT"]),
1286
2288
  ttl: DurationSchema,
1287
- records: import_zod26.z.string().array()
2289
+ records: import_zod15.z.string().array()
1288
2290
  }).array()).optional()
1289
2291
  }),
1290
- onBootstrap({ config, stack }) {
1291
- Object.entries(config.domains || {}).forEach(([domain, dnsRecords]) => {
1292
- const hostedZone = new import_aws_route533.HostedZone(stack, toId("hosted-zone", domain), {
1293
- zoneName: domain,
1294
- addTrailingDot: true
2292
+ onApp({ config, bootstrap: bootstrap2, usEastBootstrap }) {
2293
+ for (const [domain, records] of Object.entries(config.domains || {})) {
2294
+ const hostedZone = new HostedZone(domain);
2295
+ const certificate = new Certificate(domain, {
2296
+ alternativeNames: [`*.${domain}`]
1295
2297
  });
1296
- hostedZone.node.defaultChild.overrideLogicalId(toId("hosted-zone", domain));
1297
- const certificate = new import_aws_certificatemanager2.Certificate(stack, toId("certificate", domain), {
1298
- domainName: domain,
1299
- validation: import_aws_certificatemanager2.CertificateValidation.fromDns(hostedZone),
1300
- subjectAlternativeNames: [`*.${domain}`]
1301
- });
1302
- certificate.node.defaultChild.overrideLogicalId(toId("certificate", domain));
1303
- new import_aws_cdk_lib7.CfnOutput(stack, toId("output-hosted-zone", domain), {
1304
- exportName: toExportName(`hosted-zone-${domain}-id`),
1305
- value: hostedZone.hostedZoneId
1306
- });
1307
- new import_aws_cdk_lib7.CfnOutput(stack, toId("output-certificate", domain), {
1308
- exportName: toExportName(`certificate-${domain}-arn`),
1309
- value: certificate.certificateArn
1310
- });
1311
- if (dnsRecords.length > 0) {
1312
- new import_aws_route533.CfnRecordSetGroup(stack, toId("record-set-group", domain), {
1313
- hostedZoneId: hostedZone.hostedZoneId,
1314
- recordSets: dnsRecords.map((props) => ({
1315
- name: props.name || "",
1316
- type: props.type,
1317
- ttl: props.ttl.toSeconds().toString(),
1318
- resourceRecords: props.records
1319
- }))
1320
- });
2298
+ bootstrap2.add(certificate);
2299
+ usEastBootstrap.add(hostedZone).add(certificate).export(`certificate-${domain}-arn`, certificate.arn);
2300
+ if (records.length > 0) {
2301
+ const group = new RecordSetGroup(domain, {
2302
+ hostedZoneId: hostedZone.id,
2303
+ records
2304
+ }).dependsOn(hostedZone);
2305
+ usEastBootstrap.add(group);
1321
2306
  }
1322
- });
2307
+ }
1323
2308
  }
1324
2309
  });
1325
2310
 
1326
2311
  // src/plugins/index.ts
1327
2312
  var defaultPlugins = [
2313
+ extendPlugin,
1328
2314
  functionPlugin,
1329
2315
  cronPlugin,
1330
2316
  queuePlugin,
1331
2317
  tablePlugin,
1332
2318
  storePlugin,
1333
2319
  topicPlugin,
1334
- // searchPlugin,
1335
- graphqlPlugin,
1336
2320
  pubsubPlugin,
2321
+ // searchPlugin,
1337
2322
  domainPlugin,
1338
- httpPlugin
2323
+ graphqlPlugin
2324
+ // httpPlugin,
1339
2325
  ];
1340
2326
 
1341
- // src/stack/app-bootstrap.ts
1342
- var appBootstrapStack = ({ config, app, assets }) => {
1343
- const stack = new import_aws_cdk_lib8.Stack(app, "bootstrap", {
1344
- stackName: `${config.name}-bootstrap`
1345
- });
1346
- const plugins = [
1347
- ...defaultPlugins,
1348
- ...config.plugins || []
1349
- ];
1350
- debug("Run plugin onBootstrap listeners");
1351
- const functions = plugins.map((plugin) => plugin.onBootstrap?.({
1352
- config,
1353
- app,
1354
- stack,
1355
- assets
1356
- })).filter(Boolean).flat().filter(Boolean);
1357
- return {
1358
- stack,
1359
- functions
1360
- };
2327
+ // src/formation/app.ts
2328
+ var App = class {
2329
+ constructor(name) {
2330
+ this.name = name;
2331
+ }
2332
+ list = /* @__PURE__ */ new Map();
2333
+ add(...stacks) {
2334
+ stacks.forEach((stack) => this.list.set(stack.name, stack));
2335
+ return this;
2336
+ }
2337
+ find(resourceType) {
2338
+ return this.stacks.map((stack) => stack.find(resourceType)).flat();
2339
+ }
2340
+ [Symbol.iterator]() {
2341
+ return this.list.values();
2342
+ }
2343
+ get stacks() {
2344
+ return [...this.list.values()];
2345
+ }
2346
+ // get resources() {
2347
+ // return this.stacks.map(stack => stack.resources).flat()
2348
+ // }
1361
2349
  };
1362
2350
 
1363
- // src/util/deployment.ts
1364
- var flattenDependencyTree = (stacks) => {
1365
- const list3 = [];
1366
- const walk = (stacks2) => {
1367
- stacks2.forEach((node) => {
1368
- list3.push(node);
1369
- walk(node.children);
1370
- });
1371
- };
1372
- walk(stacks);
1373
- return list3;
2351
+ // src/formation/resource/cloud-formation/custom-resource.ts
2352
+ var CustomResource = class extends Resource {
2353
+ constructor(logicalId, props) {
2354
+ super("AWS::CloudFormation::CustomResource", logicalId);
2355
+ this.props = props;
2356
+ }
2357
+ getAtt(name) {
2358
+ return getAtt(this.logicalId, name);
2359
+ }
2360
+ properties() {
2361
+ return {
2362
+ ServiceToken: this.props.serviceToken,
2363
+ ...this.props.properties
2364
+ };
2365
+ }
1374
2366
  };
1375
- var createDependencyTree = (stacks, startingLevel) => {
1376
- const list3 = stacks.map(({ stack, config }) => ({
1377
- stack,
1378
- depends: config?.depends?.map((dep) => dep.name) || []
1379
- }));
1380
- const findChildren = (list4, parents, level) => {
1381
- const children = [];
1382
- const rests = [];
1383
- for (const item of list4) {
1384
- const isChild = item.depends.filter((dep) => !parents.includes(dep)).length === 0;
1385
- if (isChild) {
1386
- children.push(item);
1387
- } else {
1388
- rests.push(item);
1389
- }
2367
+
2368
+ // src/custom/global-export/handler.ts
2369
+ var globalExportsHandlerCode = (
2370
+ /* JS */
2371
+ `
2372
+
2373
+ const { CloudFormationClient, ListExportsCommand } = require('@aws-sdk/client-cloudformation')
2374
+
2375
+ exports.handler = async (event) => {
2376
+ const region = event.ResourceProperties.region
2377
+
2378
+ try {
2379
+ const data = await listExports(region)
2380
+
2381
+ await send(event, region, 'SUCCESS', data)
2382
+ } catch(error) {
2383
+ if (error instanceof Error) {
2384
+ await send(event, region, 'FAILED', {}, error.message)
2385
+ } else {
2386
+ await send(event, region, 'FAILED', {}, 'Unknown error')
2387
+ }
2388
+ }
2389
+ }
2390
+
2391
+ const send = async (event, id, status, data, reason = '') => {
2392
+ const body = JSON.stringify({
2393
+ Status: status,
2394
+ Reason: reason,
2395
+ PhysicalResourceId: id,
2396
+ StackId: event.StackId,
2397
+ RequestId: event.RequestId,
2398
+ LogicalResourceId: event.LogicalResourceId,
2399
+ NoEcho: false,
2400
+ Data: data
2401
+ })
2402
+
2403
+ await fetch(event.ResponseURL, {
2404
+ method: 'PUT',
2405
+ port: 443,
2406
+ body,
2407
+ headers: {
2408
+ 'content-type': '',
2409
+ 'content-length': Buffer.from(body).byteLength,
2410
+ },
2411
+ })
2412
+ }
2413
+
2414
+ const listExports = async (region) => {
2415
+ const client = new CloudFormationClient({ region })
2416
+ const data = {}
2417
+
2418
+ let token
2419
+
2420
+ while(true) {
2421
+ const result = await client.send(new ListExportsCommand({
2422
+ NextToken: token
2423
+ }))
2424
+
2425
+ result.Exports?.forEach(item => {
2426
+ data[item.Name] = item.Value
2427
+ })
2428
+
2429
+ if(result.NextToken) {
2430
+ token = result.NextToken
2431
+ } else {
2432
+ return data
2433
+ }
2434
+ }
2435
+ }
2436
+ `
2437
+ );
2438
+
2439
+ // src/custom/global-export/extend.ts
2440
+ var extendWithGlobalExports = (appName, importable, exportable) => {
2441
+ let crossRegionExports;
2442
+ importable.import = (name) => {
2443
+ name = formatName(name);
2444
+ if (!importable.exports.has(name)) {
2445
+ throw new TypeError(`Undefined global export value: ${name}`);
1390
2446
  }
1391
- if (!rests.length) {
1392
- return children.map(({ stack }) => ({
1393
- stack,
1394
- level,
1395
- children: []
1396
- }));
2447
+ if (!crossRegionExports) {
2448
+ const lambda = new Function("global-exports", {
2449
+ name: `${appName}-global-exports`,
2450
+ code: Code.fromInline(globalExportsHandlerCode, "index.handler")
2451
+ });
2452
+ lambda.addPermissions({
2453
+ actions: ["cloudformation:ListExports"],
2454
+ resources: ["*"]
2455
+ });
2456
+ crossRegionExports = new CustomResource("global-exports", {
2457
+ serviceToken: lambda.arn,
2458
+ properties: {
2459
+ region: importable.region
2460
+ }
2461
+ });
2462
+ exportable.add(crossRegionExports);
1397
2463
  }
1398
- return children.map(({ stack }) => {
1399
- return {
1400
- stack,
1401
- level,
1402
- children: findChildren(rests, [...parents, stack.artifactId], level + 1)
1403
- };
1404
- });
2464
+ return crossRegionExports.getAtt(name);
1405
2465
  };
1406
- return findChildren(list3, [], startingLevel);
1407
- };
1408
- var createDeploymentLine = (stacks) => {
1409
- const flat = flattenDependencyTree(stacks);
1410
- const line = [];
1411
- flat.forEach((node) => {
1412
- const level = node.level;
1413
- if (!line[level]) {
1414
- line[level] = [];
1415
- }
1416
- line[level].push(node.stack);
1417
- });
1418
- return line;
1419
- };
1420
-
1421
- // src/util/assets.ts
1422
- var Assets = class {
1423
- assets = {};
1424
- id = 0;
1425
- add(opts) {
1426
- if (!this.assets[opts.stackName]) {
1427
- this.assets[opts.stackName] = [];
1428
- }
1429
- this.assets[opts.stackName].push({
1430
- ...opts,
1431
- id: this.id++
1432
- });
1433
- }
1434
- list() {
1435
- return this.assets;
1436
- }
1437
- forEach(cb) {
1438
- Object.values(this.assets).forEach((assets) => {
1439
- cb(assets[0].stackName, assets);
1440
- });
1441
- }
1442
- map(cb) {
1443
- return Object.values(this.assets).map((assets) => {
1444
- return cb(assets[0].stackName, assets);
1445
- });
1446
- }
1447
2466
  };
1448
2467
 
1449
2468
  // src/app.ts
1450
- var makeApp = (config) => {
1451
- return new import_aws_cdk_lib9.App({
1452
- outdir: assemblyDir,
1453
- defaultStackSynthesizer: new import_aws_cdk_lib9.DefaultStackSynthesizer({
1454
- fileAssetsBucketName: assetBucketName(config),
1455
- fileAssetPublishingRoleArn: "",
1456
- generateBootstrapVersionRule: false
1457
- })
1458
- });
1459
- };
1460
2469
  var getAllDepends = (filters) => {
1461
2470
  const list3 = [];
1462
2471
  const walk = (deps) => {
@@ -1469,54 +2478,76 @@ var getAllDepends = (filters) => {
1469
2478
  return list3;
1470
2479
  };
1471
2480
  var toApp = async (config, filters) => {
1472
- const assets = new Assets();
1473
- const app = makeApp(config);
2481
+ const app = new App(config.name);
1474
2482
  const stacks = [];
1475
2483
  const plugins = [
1476
2484
  ...defaultPlugins,
1477
2485
  ...config.plugins || []
1478
2486
  ];
1479
2487
  debug("Plugins detected:", plugins.map((plugin) => style.info(plugin.name)).join(", "));
2488
+ const bootstrap2 = new Stack("bootstrap", config.region);
2489
+ const usEastBootstrap = new Stack("us-east-bootstrap", "us-east-1");
2490
+ extendWithGlobalExports(config.name, usEastBootstrap, bootstrap2);
2491
+ app.add(bootstrap2, usEastBootstrap);
1480
2492
  debug("Run plugin onApp listeners");
1481
- plugins.forEach((plugin) => plugin.onApp?.({ config, app, assets }));
1482
- const bootstrap2 = appBootstrapStack({ config, app, assets });
2493
+ const bindings = [];
2494
+ const bind = (cb) => {
2495
+ bindings.push(cb);
2496
+ };
2497
+ for (const plugin of plugins) {
2498
+ plugin.onApp?.({
2499
+ config,
2500
+ app,
2501
+ bootstrap: bootstrap2,
2502
+ usEastBootstrap,
2503
+ bind
2504
+ });
2505
+ }
1483
2506
  debug("Stack filters:", filters.map((filter) => style.info(filter)).join(", "));
1484
2507
  const filterdStacks = filters.length === 0 ? config.stacks : getAllDepends(
1485
2508
  // config.stacks,
1486
2509
  config.stacks.filter((stack) => filters.includes(stack.name))
1487
2510
  );
1488
2511
  for (const stackConfig of filterdStacks) {
1489
- const { stack, bindings } = toStack({
2512
+ const { stack } = toStack({
1490
2513
  config,
1491
2514
  stackConfig,
1492
- assets,
2515
+ bootstrap: bootstrap2,
2516
+ usEastBootstrap,
1493
2517
  plugins,
1494
2518
  app
1495
2519
  });
2520
+ app.add(stack);
1496
2521
  stacks.push({ stack, config: stackConfig });
1497
- bindings.forEach((cb) => bootstrap2.functions.forEach(cb));
1498
2522
  }
1499
- let dependencyTree;
1500
- if (bootstrap2.stack.node.children.length === 0) {
1501
- dependencyTree = createDependencyTree(stacks, 0);
1502
- } else {
2523
+ const functions = app.find(Function);
2524
+ for (const bind2 of bindings) {
2525
+ for (const fn of functions) {
2526
+ bind2(fn);
2527
+ }
2528
+ }
2529
+ let dependencyTree = createDependencyTree(stacks);
2530
+ if (bootstrap2.size > 0) {
1503
2531
  dependencyTree = [{
1504
- stack: bootstrap2.stack,
1505
- level: 0,
1506
- children: createDependencyTree(stacks, 1)
2532
+ stack: bootstrap2,
2533
+ children: dependencyTree
2534
+ }];
2535
+ }
2536
+ if (usEastBootstrap.size > 0) {
2537
+ dependencyTree = [{
2538
+ stack: usEastBootstrap,
2539
+ children: dependencyTree
1507
2540
  }];
1508
2541
  }
1509
2542
  return {
1510
2543
  app,
1511
- assets,
1512
2544
  plugins,
1513
- stackNames: filterdStacks.map((stack) => stack.name),
1514
2545
  dependencyTree
1515
2546
  };
1516
2547
  };
1517
2548
 
1518
2549
  // src/config.ts
1519
- var import_path11 = require("path");
2550
+ var import_path4 = require("path");
1520
2551
 
1521
2552
  // src/util/account.ts
1522
2553
  var import_client_sts = require("@aws-sdk/client-sts");
@@ -1535,17 +2566,17 @@ var getCredentials = (profile) => {
1535
2566
  };
1536
2567
 
1537
2568
  // src/schema/app.ts
1538
- var import_zod30 = require("zod");
2569
+ var import_zod19 = require("zod");
1539
2570
 
1540
2571
  // src/schema/stack.ts
1541
- var import_zod27 = require("zod");
1542
- var StackSchema = import_zod27.z.object({
2572
+ var import_zod16 = require("zod");
2573
+ var StackSchema = import_zod16.z.object({
1543
2574
  name: ResourceIdSchema,
1544
- depends: import_zod27.z.array(import_zod27.z.lazy(() => StackSchema)).optional()
2575
+ depends: import_zod16.z.array(import_zod16.z.lazy(() => StackSchema)).optional()
1545
2576
  });
1546
2577
 
1547
2578
  // src/schema/region.ts
1548
- var import_zod28 = require("zod");
2579
+ var import_zod17 = require("zod");
1549
2580
  var US = ["us-east-2", "us-east-1", "us-west-1", "us-west-2"];
1550
2581
  var AF = ["af-south-1"];
1551
2582
  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"];
@@ -1562,35 +2593,48 @@ var regions = [
1562
2593
  ...ME,
1563
2594
  ...SA
1564
2595
  ];
1565
- var RegionSchema = import_zod28.z.enum(regions);
2596
+ var RegionSchema = import_zod17.z.enum(regions);
1566
2597
 
1567
2598
  // src/schema/plugin.ts
1568
- var import_zod29 = require("zod");
1569
- var PluginSchema = import_zod29.z.object({
1570
- name: import_zod29.z.string(),
1571
- schema: import_zod29.z.custom().optional(),
2599
+ var import_zod18 = require("zod");
2600
+ var PluginSchema = import_zod18.z.object({
2601
+ name: import_zod18.z.string(),
2602
+ schema: import_zod18.z.custom().optional(),
1572
2603
  // depends: z.array(z.lazy(() => PluginSchema)).optional(),
1573
- onBootstrap: import_zod29.z.function().returns(import_zod29.z.any()).optional(),
1574
- onStack: import_zod29.z.function().returns(import_zod29.z.any()).optional(),
1575
- onApp: import_zod29.z.function().returns(import_zod29.z.void()).optional()
2604
+ onBootstrap: import_zod18.z.function().returns(import_zod18.z.any()).optional(),
2605
+ onStack: import_zod18.z.function().returns(import_zod18.z.any()).optional(),
2606
+ onApp: import_zod18.z.function().returns(import_zod18.z.void()).optional()
1576
2607
  // bind: z.function().optional(),
1577
2608
  });
1578
2609
 
1579
2610
  // src/schema/app.ts
1580
- var AppSchema = import_zod30.z.object({
2611
+ var AppSchema = import_zod19.z.object({
1581
2612
  name: ResourceIdSchema,
1582
2613
  region: RegionSchema,
1583
- profile: import_zod30.z.string(),
1584
- stage: import_zod30.z.string().regex(/[a-z]+/).default("prod"),
1585
- defaults: import_zod30.z.object({}).default({}),
1586
- stacks: import_zod30.z.array(StackSchema).min(1),
1587
- plugins: import_zod30.z.array(PluginSchema).optional()
2614
+ profile: import_zod19.z.string(),
2615
+ stage: import_zod19.z.string().regex(/[a-z]+/).default("prod"),
2616
+ defaults: import_zod19.z.object({}).default({}),
2617
+ stacks: import_zod19.z.array(StackSchema).min(1).refine((stacks) => {
2618
+ const unique = new Set(stacks.map((stack) => stack.name));
2619
+ return unique.size === stacks.length;
2620
+ }, "Must be an array of unique stacks"),
2621
+ plugins: import_zod19.z.array(PluginSchema).optional()
1588
2622
  });
1589
2623
 
1590
2624
  // src/util/import.ts
1591
- var import_core3 = require("@swc/core");
1592
- var import_path9 = require("path");
1593
- var import_promises5 = require("fs/promises");
2625
+ var import_core = require("@swc/core");
2626
+ var import_path2 = require("path");
2627
+ var import_promises4 = require("fs/promises");
2628
+
2629
+ // src/util/path.ts
2630
+ var import_path = require("path");
2631
+ var rootDir = process.cwd();
2632
+ var outDir = (0, import_path.join)(rootDir, ".awsless");
2633
+ var templateDir = (0, import_path.join)(outDir, "template");
2634
+ var assetDir = (0, import_path.join)(outDir, "asset");
2635
+ var cacheDir = (0, import_path.join)(outDir, "cache");
2636
+
2637
+ // src/util/import.ts
1594
2638
  var resolveFileNameExtension = async (path) => {
1595
2639
  const options = [
1596
2640
  "",
@@ -1603,7 +2647,7 @@ var resolveFileNameExtension = async (path) => {
1603
2647
  const file = path.replace(/\.js$/, "") + option;
1604
2648
  let stat;
1605
2649
  try {
1606
- stat = await (0, import_promises5.lstat)(file);
2650
+ stat = await (0, import_promises4.lstat)(file);
1607
2651
  } catch (error) {
1608
2652
  continue;
1609
2653
  }
@@ -1614,15 +2658,15 @@ var resolveFileNameExtension = async (path) => {
1614
2658
  throw new Error(`Failed to load file: ${path}`);
1615
2659
  };
1616
2660
  var resolveDir = (path) => {
1617
- return (0, import_path9.dirname)(path).replace(rootDir + "/", "");
2661
+ return (0, import_path2.dirname)(path).replace(rootDir + "/", "");
1618
2662
  };
1619
2663
  var importFile = async (path) => {
1620
2664
  const load = async (file) => {
1621
2665
  debug("Load file", file);
1622
- let { code: code2 } = await (0, import_core3.transformFile)(file, {
2666
+ let { code: code2 } = await (0, import_core.transformFile)(file, {
1623
2667
  isModule: true
1624
2668
  });
1625
- const path2 = (0, import_path9.dirname)(file);
2669
+ const path2 = (0, import_path2.dirname)(file);
1626
2670
  const dir = resolveDir(file);
1627
2671
  code2 = code2.replaceAll("__dirname", `"${dir}"`);
1628
2672
  const matches = code2.match(/(import|export)\s*{\s*[a-z0-9\_\,\s\*]+\s*}\s*from\s*('|")(\.\.?[\/a-z0-9\_\-\.]+)('|");?/ig);
@@ -1631,23 +2675,23 @@ var importFile = async (path) => {
1631
2675
  await Promise.all(matches?.map(async (match) => {
1632
2676
  const parts = /('|")(\.\.?[\/a-z0-9\_\-\.]+)('|")/ig.exec(match);
1633
2677
  const from = parts[2];
1634
- const file2 = await resolveFileNameExtension((0, import_path9.join)(path2, from));
2678
+ const file2 = await resolveFileNameExtension((0, import_path2.join)(path2, from));
1635
2679
  const result = await load(file2);
1636
2680
  code2 = code2.replace(match, result);
1637
2681
  }));
1638
2682
  return code2;
1639
2683
  };
1640
2684
  const code = await load(path);
1641
- const outputFile = (0, import_path9.join)(outDir, "config.js");
1642
- await (0, import_promises5.mkdir)(outDir, { recursive: true });
1643
- await (0, import_promises5.writeFile)(outputFile, code);
2685
+ const outputFile = (0, import_path2.join)(outDir, "config.js");
2686
+ await (0, import_promises4.mkdir)(outDir, { recursive: true });
2687
+ await (0, import_promises4.writeFile)(outputFile, code);
1644
2688
  return import(outputFile);
1645
2689
  };
1646
2690
 
1647
2691
  // src/config.ts
1648
2692
  var importConfig = async (options) => {
1649
2693
  debug("Import config file");
1650
- const fileName = (0, import_path11.join)(process.cwd(), options.configFile || "awsless.config.ts");
2694
+ const fileName = (0, import_path4.join)(process.cwd(), options.configFile || "awsless.config.ts");
1651
2695
  const module2 = await importFile(fileName);
1652
2696
  const appConfig = typeof module2.default === "function" ? await module2.default(options) : module2.default;
1653
2697
  debug("Validate config file");
@@ -1738,7 +2782,7 @@ var Signal = class {
1738
2782
  }
1739
2783
  set(value) {
1740
2784
  this.value = value;
1741
- this.subs.forEach((sub) => sub(value));
2785
+ this.subs.forEach((sub2) => sub2(value));
1742
2786
  }
1743
2787
  update(cb) {
1744
2788
  this.set(cb(this.value));
@@ -1979,6 +3023,9 @@ var Renderer = class {
1979
3023
  async end() {
1980
3024
  this.gap();
1981
3025
  await this.flush();
3026
+ clearTimeout(this.timeout);
3027
+ this.unsubs.forEach((unsub) => unsub());
3028
+ this.unsubs = [];
1982
3029
  const y = this.screen.length - 1;
1983
3030
  await this.setCursor(0, y);
1984
3031
  }
@@ -2143,6 +3190,9 @@ var layout = async (cb) => {
2143
3190
  }
2144
3191
  };
2145
3192
 
3193
+ // src/cli/ui/complex/builder.ts
3194
+ var import_promises5 = require("fs/promises");
3195
+
2146
3196
  // src/cli/ui/layout/flex-line.ts
2147
3197
  var stripEscapeCode = (str) => {
2148
3198
  return str.replace(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, "");
@@ -2163,35 +3213,52 @@ var flexLine = (term, left, right, reserveSpace = 0) => {
2163
3213
  ]);
2164
3214
  };
2165
3215
 
2166
- // src/cli/ui/complex/asset.ts
2167
- var assetBuilder = (assets) => {
3216
+ // src/cli/ui/complex/builder.ts
3217
+ var import_path6 = require("path");
3218
+ var assetBuilder = (app) => {
2168
3219
  return async (term) => {
3220
+ const assets = [];
3221
+ const stacks = [];
3222
+ for (const stack of app) {
3223
+ for (const asset of stack.assets) {
3224
+ if (asset.build) {
3225
+ assets.push(asset);
3226
+ stacks.push(stack);
3227
+ }
3228
+ }
3229
+ }
3230
+ if (assets.length === 0) {
3231
+ return;
3232
+ }
2169
3233
  const done = term.out.write(loadingDialog("Building stack assets..."));
2170
3234
  const groups = new Signal([""]);
2171
3235
  term.out.gap();
2172
3236
  term.out.write(groups);
2173
- const stackNameSize = Math.max(...Object.keys(assets.list()).map((stack) => stack.length));
2174
- const resourceSize = Math.max(...Object.values(assets.list()).map((assets2) => assets2.map((asset) => asset.resource.length)).flat());
2175
- await Promise.all(assets.map(async (stackName, assets2) => {
3237
+ const stackNameSize = Math.max(...stacks.map((stack) => stack.name.length));
3238
+ const assetTypeSize = Math.max(...assets.map((asset) => asset.type.length));
3239
+ await Promise.all(app.stacks.map(async (stack) => {
2176
3240
  const group = new Signal([]);
2177
3241
  groups.update((groups2) => [...groups2, group]);
2178
- await Promise.all(assets2.map(async (asset) => {
3242
+ await Promise.all([...stack.assets].map(async (asset) => {
3243
+ if (!asset.build) {
3244
+ return;
3245
+ }
2179
3246
  const [icon, stop] = createSpinner();
2180
3247
  const details = new Signal({});
2181
3248
  const line = flexLine(term, [
2182
3249
  icon,
2183
3250
  " ",
2184
- style.label(stackName),
2185
- " ".repeat(stackNameSize - stackName.length),
3251
+ style.label(stack.name),
3252
+ " ".repeat(stackNameSize - stack.name.length),
2186
3253
  " ",
2187
3254
  style.placeholder(symbol.pointerSmall),
2188
3255
  " ",
2189
- style.warning(asset.resource),
2190
- " ".repeat(resourceSize - asset.resource.length),
3256
+ style.warning(asset.type),
3257
+ " ".repeat(assetTypeSize - asset.type.length),
2191
3258
  " ",
2192
3259
  style.placeholder(symbol.pointerSmall),
2193
3260
  " ",
2194
- style.info(asset.resourceName),
3261
+ style.info(asset.id),
2195
3262
  " "
2196
3263
  ], [
2197
3264
  " ",
@@ -2204,7 +3271,14 @@ var assetBuilder = (assets) => {
2204
3271
  ]);
2205
3272
  group.update((group2) => [...group2, line]);
2206
3273
  const timer = createTimer();
2207
- const data = await asset.build?.();
3274
+ const data = await asset.build({
3275
+ async write(file, data2) {
3276
+ const fullpath = (0, import_path6.join)(assetDir, asset.type, app.name, stack.name, asset.id, file);
3277
+ const basepath = (0, import_path6.dirname)(fullpath);
3278
+ await (0, import_promises5.mkdir)(basepath, { recursive: true });
3279
+ await (0, import_promises5.writeFile)(fullpath, data2);
3280
+ }
3281
+ });
2208
3282
  details.set({
2209
3283
  ...data,
2210
3284
  time: timer()
@@ -2221,9 +3295,9 @@ var assetBuilder = (assets) => {
2221
3295
  // src/util/cleanup.ts
2222
3296
  var import_promises6 = require("fs/promises");
2223
3297
  var cleanUp = async () => {
2224
- debug("Clean up assembly & asset files");
3298
+ debug("Clean up template, cache, and asset files");
2225
3299
  const paths = [
2226
- assemblyDir,
3300
+ templateDir,
2227
3301
  assetDir,
2228
3302
  cacheDir
2229
3303
  ];
@@ -2237,108 +3311,176 @@ var cleanUp = async () => {
2237
3311
  })));
2238
3312
  };
2239
3313
 
3314
+ // src/cli/ui/complex/template.ts
3315
+ var import_promises7 = require("fs/promises");
3316
+ var import_path9 = require("path");
3317
+ var templateBuilder = (app) => {
3318
+ return async (term) => {
3319
+ const done = term.out.write(loadingDialog("Building stack templates..."));
3320
+ await Promise.all(app.stacks.map(async (stack) => {
3321
+ const template = stack.toString(true);
3322
+ const path = (0, import_path9.join)(templateDir, app.name);
3323
+ const file = (0, import_path9.join)(path, `${stack.name}.json`);
3324
+ await (0, import_promises7.mkdir)(path, { recursive: true });
3325
+ await (0, import_promises7.writeFile)(file, template);
3326
+ }));
3327
+ done("Done building stack templates");
3328
+ };
3329
+ };
3330
+
2240
3331
  // src/cli/command/build.ts
2241
3332
  var build = (program2) => {
2242
3333
  program2.command("build").argument("[stack...]", "Optionally filter stacks to build").description("Build your app").action(async (filters) => {
2243
3334
  await layout(async (config, write) => {
2244
- const { app, assets } = await toApp(config, filters);
3335
+ const { app } = await toApp(config, filters);
2245
3336
  await cleanUp();
2246
- await write(assetBuilder(assets));
2247
- app.synth();
3337
+ await write(assetBuilder(app));
3338
+ await write(templateBuilder(app));
2248
3339
  });
2249
3340
  });
2250
3341
  };
2251
3342
 
2252
- // src/stack/client.ts
3343
+ // src/formation/bootstrap.ts
3344
+ var assetBucketName = (account, region) => {
3345
+ return `awsless-bootstrap-${account}-${region}`;
3346
+ };
3347
+ var assetBucketUrl = (account, region, stack) => {
3348
+ const bucket = assetBucketName(account, region);
3349
+ return `https://s3-${region}.amazonaws.com/${bucket}/${stack.name}/cloudformation.json`;
3350
+ };
3351
+ var version = "1";
3352
+ var bootstrapStack = (account, region) => {
3353
+ const app = new App("awsless");
3354
+ const stack = new Stack("bootstrap", region);
3355
+ stack.add(new Bucket("assets", {
3356
+ name: assetBucketName(account, region),
3357
+ accessControl: "private",
3358
+ versioned: true
3359
+ }));
3360
+ stack.export("version", version);
3361
+ app.add(stack);
3362
+ return { app, stack };
3363
+ };
3364
+ var shouldDeployBootstrap = async (client, stack) => {
3365
+ debug("Check bootstrap status");
3366
+ const info = await client.get(stack.name, stack.region);
3367
+ return !info || info.outputs.version !== version || !["CREATE_COMPLETE", "UPDATE_COMPLETE"].includes(info.status);
3368
+ };
3369
+
3370
+ // src/formation/client.ts
2253
3371
  var import_client_cloudformation = require("@aws-sdk/client-cloudformation");
2254
- var import_client_s32 = require("@aws-sdk/client-s3");
3372
+ var import_client_s3 = require("@aws-sdk/client-s3");
3373
+ var import_change_case11 = require("change-case");
2255
3374
  var StackClient = class {
2256
- // 30 seconds
2257
- constructor(config) {
2258
- this.config = config;
2259
- this.client = new import_client_cloudformation.CloudFormationClient({
2260
- credentials: config.credentials,
2261
- region: config.region
2262
- });
3375
+ constructor(app, account, region, credentials) {
3376
+ this.app = app;
3377
+ this.account = account;
3378
+ this.region = region;
3379
+ this.credentials = credentials;
3380
+ this.assetBucketName = assetBucketName(this.account, this.region);
2263
3381
  }
2264
- client;
2265
3382
  maxWaitTime = 60 * 30;
2266
3383
  // 30 minutes
2267
3384
  maxDelay = 30;
2268
- shouldUploadTemplate(stack) {
2269
- const body = JSON.stringify(stack.template);
2270
- const size = Buffer.byteLength(body, "utf8");
3385
+ // 30 seconds
3386
+ assetBucketName;
3387
+ getClient(region) {
3388
+ return new import_client_cloudformation.CloudFormationClient({
3389
+ credentials: this.credentials,
3390
+ region
3391
+ });
3392
+ }
3393
+ shouldUploadTemplate(template) {
3394
+ const size = Buffer.byteLength(template, "utf8");
2271
3395
  return size > 5e4;
2272
3396
  }
2273
3397
  templateProp(stack) {
2274
- return this.shouldUploadTemplate(stack) ? {
2275
- TemplateUrl: assetBucketUrl(this.config, stack.stackName)
3398
+ const template = stack.toString();
3399
+ return this.shouldUploadTemplate(template) ? {
3400
+ TemplateUrl: assetBucketUrl(this.account, this.region, stack)
2276
3401
  } : {
2277
- TemplateBody: JSON.stringify(stack.template)
3402
+ TemplateBody: template
2278
3403
  };
2279
3404
  }
2280
- async upload(stack) {
2281
- debug("Upload the", style.info(stack.id), "stack to awsless assets bucket");
2282
- const client = new import_client_s32.S3Client({
2283
- credentials: this.config.credentials,
2284
- region: this.config.region
3405
+ stackName(stackName) {
3406
+ return (0, import_change_case11.paramCase)(`${this.app.name}-${stackName}`);
3407
+ }
3408
+ tags(stack) {
3409
+ const tags = [];
3410
+ for (const [name, value] of stack.tags.entries()) {
3411
+ tags.push({ Key: name, Value: value });
3412
+ }
3413
+ return tags;
3414
+ }
3415
+ async upload(stack, template) {
3416
+ debug("Upload the", style.info(stack.name), "stack to awsless assets bucket");
3417
+ const client = new import_client_s3.S3Client({
3418
+ credentials: this.credentials,
3419
+ region: stack.region
2285
3420
  });
2286
- await client.send(new import_client_s32.PutObjectCommand({
2287
- Bucket: assetBucketName(this.config),
2288
- Key: `${stack.stackName}/cloudformation.json`,
2289
- Body: JSON.stringify(stack.template),
2290
- ACL: import_client_s32.ObjectCannedACL.private,
2291
- StorageClass: import_client_s32.StorageClass.STANDARD_IA
3421
+ await client.send(new import_client_s3.PutObjectCommand({
3422
+ Bucket: this.assetBucketName,
3423
+ Key: `${this.app.name}/${stack.name}/cloudformation.json`,
3424
+ Body: template,
3425
+ ACL: import_client_s3.ObjectCannedACL.private,
3426
+ StorageClass: import_client_s3.StorageClass.STANDARD_IA
2292
3427
  }));
2293
3428
  }
2294
3429
  async create(stack, capabilities) {
2295
- debug("Create the", style.info(stack.id), "stack");
2296
- await this.client.send(new import_client_cloudformation.CreateStackCommand({
2297
- StackName: stack.stackName,
3430
+ debug("Create the", style.info(stack.name), "stack");
3431
+ const client = this.getClient(stack.region);
3432
+ await client.send(new import_client_cloudformation.CreateStackCommand({
3433
+ StackName: this.stackName(stack.name),
2298
3434
  EnableTerminationProtection: false,
2299
3435
  OnFailure: import_client_cloudformation.OnFailure.DELETE,
2300
3436
  Capabilities: capabilities,
3437
+ Tags: this.tags(stack),
2301
3438
  ...this.templateProp(stack)
2302
3439
  }));
2303
3440
  await (0, import_client_cloudformation.waitUntilStackCreateComplete)({
2304
- client: this.client,
3441
+ client,
2305
3442
  maxWaitTime: this.maxWaitTime,
2306
3443
  maxDelay: this.maxDelay
2307
3444
  }, {
2308
- StackName: stack.stackName
3445
+ StackName: this.stackName(stack.name)
2309
3446
  });
2310
3447
  }
2311
3448
  async update(stack, capabilities) {
2312
- debug("Update the", style.info(stack.id), "stack");
2313
- await this.client.send(new import_client_cloudformation.UpdateStackCommand({
2314
- StackName: stack.stackName,
3449
+ debug("Update the", style.info(stack.name), "stack");
3450
+ const client = this.getClient(stack.region);
3451
+ await client.send(new import_client_cloudformation.UpdateStackCommand({
3452
+ StackName: this.stackName(stack.name),
2315
3453
  Capabilities: capabilities,
3454
+ Tags: this.tags(stack),
2316
3455
  ...this.templateProp(stack)
2317
3456
  }));
2318
3457
  await (0, import_client_cloudformation.waitUntilStackUpdateComplete)({
2319
- client: this.client,
3458
+ client,
2320
3459
  maxWaitTime: this.maxWaitTime,
2321
3460
  maxDelay: this.maxDelay
2322
3461
  }, {
2323
- StackName: stack.stackName
3462
+ StackName: this.stackName(stack.name)
2324
3463
  });
2325
3464
  }
2326
3465
  async validate(stack) {
2327
- debug("Validate the", style.info(stack.id), "stack");
2328
- const result = await this.client.send(new import_client_cloudformation.ValidateTemplateCommand({
3466
+ debug("Validate the", style.info(stack.name), "stack");
3467
+ const client = this.getClient(stack.region);
3468
+ const result = await client.send(new import_client_cloudformation.ValidateTemplateCommand({
2329
3469
  ...this.templateProp(stack)
2330
3470
  }));
2331
3471
  return result.Capabilities;
2332
3472
  }
2333
- async get(name) {
3473
+ async get(name, region) {
2334
3474
  debug("Get stack info for:", style.info(name));
3475
+ const client = this.getClient(region);
2335
3476
  let result;
2336
3477
  try {
2337
- result = await this.client.send(new import_client_cloudformation.DescribeStacksCommand({
2338
- StackName: name
3478
+ result = await client.send(new import_client_cloudformation.DescribeStacksCommand({
3479
+ StackName: this.stackName(name)
2339
3480
  }));
2340
3481
  } catch (error) {
2341
3482
  if (error instanceof Error && error.name === "ValidationError" && error.message.includes("does not exist")) {
3483
+ debug("Stack not found");
2342
3484
  return;
2343
3485
  }
2344
3486
  throw error;
@@ -2348,8 +3490,8 @@ var StackClient = class {
2348
3490
  debug("Stack not found");
2349
3491
  return;
2350
3492
  }
2351
- const resultTemplate = await this.client.send(new import_client_cloudformation.GetTemplateCommand({
2352
- StackName: name,
3493
+ const resultTemplate = await client.send(new import_client_cloudformation.GetTemplateCommand({
3494
+ StackName: this.stackName(name),
2353
3495
  TemplateStage: import_client_cloudformation.TemplateStage.Original
2354
3496
  }));
2355
3497
  const outputs = {};
@@ -2367,14 +3509,15 @@ var StackClient = class {
2367
3509
  };
2368
3510
  }
2369
3511
  async deploy(stack) {
2370
- const data = await this.get(stack.stackName);
2371
- debug("Deploy:", style.info(stack.stackName));
2372
- if (data?.template === JSON.stringify(stack.template)) {
3512
+ const template = stack.toString();
3513
+ const data = await this.get(stack.name, stack.region);
3514
+ debug("Deploy:", style.info(stack.name));
3515
+ if (data?.template === template) {
2373
3516
  debug("No stack changes");
2374
3517
  return false;
2375
3518
  }
2376
- if (this.shouldUploadTemplate(stack)) {
2377
- await this.upload(stack);
3519
+ if (this.shouldUploadTemplate(template)) {
3520
+ await this.upload(stack, template);
2378
3521
  }
2379
3522
  const capabilities = await this.validate(stack);
2380
3523
  if (!data) {
@@ -2386,22 +3529,23 @@ var StackClient = class {
2386
3529
  }
2387
3530
  return true;
2388
3531
  }
2389
- async delete(name) {
2390
- const data = await this.get(name);
3532
+ async delete(name, region) {
3533
+ const data = await this.get(name, region);
3534
+ const client = this.getClient(region);
2391
3535
  debug("Delete the", style.info(name), "stack");
2392
3536
  if (!data) {
2393
3537
  debug("Already deleted");
2394
3538
  return;
2395
3539
  }
2396
- await this.client.send(new import_client_cloudformation.DeleteStackCommand({
2397
- StackName: name
3540
+ await client.send(new import_client_cloudformation.DeleteStackCommand({
3541
+ StackName: this.stackName(name)
2398
3542
  }));
2399
3543
  await (0, import_client_cloudformation.waitUntilStackDeleteComplete)({
2400
- client: this.client,
3544
+ client,
2401
3545
  maxWaitTime: this.maxWaitTime,
2402
3546
  maxDelay: this.maxDelay
2403
3547
  }, {
2404
- StackName: name
3548
+ StackName: this.stackName(name)
2405
3549
  });
2406
3550
  }
2407
3551
  };
@@ -2490,10 +3634,9 @@ var confirmPrompt = (label, options = {}) => {
2490
3634
  var bootstrapDeployer = (config) => {
2491
3635
  return async (term) => {
2492
3636
  debug("Initializing bootstrap");
2493
- const app = makeApp(config);
2494
- const client = new StackClient(config);
2495
- const bootstrap2 = bootstrapStack(config, app);
2496
- const shouldDeploy = await shouldDeployBootstrap(client, bootstrap2.stackName);
3637
+ const { app, stack } = bootstrapStack(config.account, config.region);
3638
+ const client = new StackClient(app, config.account, config.region, config.credentials);
3639
+ const shouldDeploy = await shouldDeployBootstrap(client, stack);
2497
3640
  if (shouldDeploy) {
2498
3641
  term.out.write(dialog("warning", [`Your app hasn't been bootstrapped yet`]));
2499
3642
  const confirmed = await term.out.write(confirmPrompt("Would you like to bootstrap?"));
@@ -2501,8 +3644,7 @@ var bootstrapDeployer = (config) => {
2501
3644
  throw new Cancelled();
2502
3645
  }
2503
3646
  const done = term.out.write(loadingDialog("Bootstrapping..."));
2504
- const assembly = app.synth();
2505
- await client.deploy(assembly.stacks[0]);
3647
+ await client.deploy(stack);
2506
3648
  done("Done deploying the bootstrap stack");
2507
3649
  } else {
2508
3650
  term.out.write(dialog("success", [
@@ -2528,8 +3670,8 @@ var stackTree = (nodes, statuses) => {
2528
3670
  const render = (nodes2, deep = 0, parents = []) => {
2529
3671
  const size = nodes2.length - 1;
2530
3672
  nodes2.forEach((node, i) => {
2531
- const id = node.stack.artifactId;
2532
- const status2 = statuses[id];
3673
+ const name = node.stack.name;
3674
+ const status2 = statuses[name];
2533
3675
  const first = i === 0 && deep === 0;
2534
3676
  const last = i === size;
2535
3677
  const more = i < size;
@@ -2543,7 +3685,7 @@ var stackTree = (nodes, statuses) => {
2543
3685
  first && size === 0 ? " " : first ? "\u250C\u2500" : last ? "\u2514\u2500" : "\u251C\u2500"
2544
3686
  ),
2545
3687
  " ",
2546
- style.info(id),
3688
+ style.info(name),
2547
3689
  " "
2548
3690
  ], [
2549
3691
  " ",
@@ -2564,28 +3706,27 @@ var stackTree = (nodes, statuses) => {
2564
3706
  var status = (program2) => {
2565
3707
  program2.command("status").argument("[stacks...]", "Optionally filter stacks to lookup status").description("View the application status").action(async (filters) => {
2566
3708
  await layout(async (config, write) => {
2567
- const { app, assets, dependencyTree } = await toApp(config, filters);
3709
+ const { app, dependencyTree } = await toApp(config, filters);
2568
3710
  await cleanUp();
2569
- await write(assetBuilder(assets));
2570
- const assembly = app.synth();
3711
+ await write(assetBuilder(app));
3712
+ await write(templateBuilder(app));
2571
3713
  const doneLoading = write(loadingDialog("Loading stack information..."));
2572
- const client = new StackClient(config);
3714
+ const client = new StackClient(app, config.account, config.region, config.credentials);
2573
3715
  const statuses = [];
2574
3716
  const stackStatuses = {};
2575
- assembly.stacks.forEach((stack) => {
2576
- stackStatuses[stack.id] = new Signal(style.info("Loading..."));
2577
- });
3717
+ for (const stack of app) {
3718
+ stackStatuses[stack.name] = new Signal(style.info("Loading..."));
3719
+ }
2578
3720
  write(stackTree(dependencyTree, stackStatuses));
2579
3721
  debug("Load metadata for all deployed stacks on AWS");
2580
- await Promise.all(assembly.stacks.map(async (stack, i) => {
2581
- const info = await client.get(stack.stackName);
2582
- const name = stack.id;
2583
- const signal = stackStatuses[name];
3722
+ await Promise.all(app.stacks.map(async (stack, i) => {
3723
+ const info = await client.get(stack.name, stack.region);
3724
+ const signal = stackStatuses[stack.name];
2584
3725
  await new Promise((resolve) => setTimeout(resolve, i * 1e3));
2585
3726
  if (!info) {
2586
3727
  signal.set(style.error("non-existent"));
2587
3728
  statuses.push("non-existent");
2588
- } else if (info.template !== JSON.stringify(stack.template)) {
3729
+ } else if (info.template !== stack.toString()) {
2589
3730
  signal.set(style.warning("out-of-date"));
2590
3731
  statuses.push("out-of-date");
2591
3732
  } else {
@@ -2604,12 +3745,77 @@ var status = (program2) => {
2604
3745
  });
2605
3746
  };
2606
3747
 
3748
+ // src/cli/ui/complex/publisher.ts
3749
+ var import_promises8 = require("fs/promises");
3750
+ var import_path11 = require("path");
3751
+ var import_client_s32 = require("@aws-sdk/client-s3");
3752
+ var assetPublisher = (config, app) => {
3753
+ const client = new import_client_s32.S3Client({
3754
+ credentials: config.credentials,
3755
+ region: config.region
3756
+ });
3757
+ return async (term) => {
3758
+ const done = term.out.write(loadingDialog("Publishing stack assets to AWS..."));
3759
+ await Promise.all(app.stacks.map(async (stack) => {
3760
+ await Promise.all([...stack.assets].map(async (asset) => {
3761
+ await asset.publish?.({
3762
+ async read(file) {
3763
+ const path = (0, import_path11.join)(assetDir, asset.type, app.name, stack.name, asset.id, file);
3764
+ const data = await (0, import_promises8.readFile)(path);
3765
+ return data;
3766
+ },
3767
+ async publish(name, data, hash) {
3768
+ const key = `${app.name}/${stack.name}/function/${name}`;
3769
+ const bucket = assetBucketName(config.account, config.region);
3770
+ let getResult;
3771
+ try {
3772
+ getResult = await client.send(new import_client_s32.GetObjectCommand({
3773
+ Bucket: bucket,
3774
+ Key: key
3775
+ }));
3776
+ } catch (error) {
3777
+ if (error instanceof Error && error.name === "NoSuchKey") {
3778
+ } else {
3779
+ throw error;
3780
+ }
3781
+ }
3782
+ if (getResult?.Metadata?.hash === hash) {
3783
+ return {
3784
+ bucket,
3785
+ key,
3786
+ version: getResult.VersionId
3787
+ };
3788
+ }
3789
+ const putResult = await client.send(new import_client_s32.PutObjectCommand({
3790
+ Bucket: bucket,
3791
+ Key: key,
3792
+ Body: data,
3793
+ ACL: import_client_s32.ObjectCannedACL.private,
3794
+ StorageClass: import_client_s32.StorageClass.STANDARD,
3795
+ Metadata: {
3796
+ hash
3797
+ }
3798
+ }));
3799
+ return {
3800
+ bucket,
3801
+ key,
3802
+ version: putResult.VersionId
3803
+ };
3804
+ }
3805
+ });
3806
+ }));
3807
+ }));
3808
+ done("Done publishing stack assets to AWS");
3809
+ };
3810
+ };
3811
+
2607
3812
  // src/cli/command/deploy.ts
2608
3813
  var deploy = (program2) => {
2609
3814
  program2.command("deploy").argument("[stacks...]", "Optionally filter stacks to deploy").description("Deploy your app to AWS").action(async (filters) => {
2610
3815
  await layout(async (config, write) => {
2611
3816
  await write(bootstrapDeployer(config));
2612
- const { app, stackNames, assets, dependencyTree } = await toApp(config, filters);
3817
+ const { app, dependencyTree } = await toApp(config, filters);
3818
+ const stackNames = app.stacks.map((stack) => stack.name);
2613
3819
  const formattedFilter = stackNames.map((i) => style.info(i)).join(style.placeholder(", "));
2614
3820
  debug("Stacks to deploy", formattedFilter);
2615
3821
  const deployAll = filters.length === 0;
@@ -2619,30 +3825,23 @@ var deploy = (program2) => {
2619
3825
  throw new Cancelled();
2620
3826
  }
2621
3827
  await cleanUp();
2622
- await write(assetBuilder(assets));
2623
- const donePublishing = write(loadingDialog("Publishing stack assets to AWS..."));
2624
- await Promise.all(assets.map(async (_, assets2) => {
2625
- await Promise.all(assets2.map(async (asset) => {
2626
- await asset.publish?.();
2627
- }));
2628
- }));
2629
- donePublishing("Done publishing stack assets to AWS");
2630
- const assembly = app.synth();
3828
+ await write(assetBuilder(app));
3829
+ await write(assetPublisher(config, app));
3830
+ await write(templateBuilder(app));
2631
3831
  const statuses = {};
2632
- assembly.stacks.map((stack) => {
2633
- statuses[stack.id] = new Signal(style.info("waiting"));
2634
- });
3832
+ for (const stack of app) {
3833
+ statuses[stack.name] = new Signal(style.info("waiting"));
3834
+ }
2635
3835
  const doneDeploying = write(loadingDialog("Deploying stacks to AWS..."));
2636
3836
  write(stackTree(dependencyTree, statuses));
2637
- const client = new StackClient(config);
3837
+ const client = new StackClient(app, config.account, config.region, config.credentials);
2638
3838
  const deploymentLine = createDeploymentLine(dependencyTree);
2639
3839
  for (const stacks of deploymentLine) {
2640
3840
  const results = await Promise.allSettled(stacks.map(async (stack) => {
2641
- const signal = statuses[stack.artifactId];
2642
- const stackArtifect = assembly.stacks.find((item) => item.id === stack.artifactId);
3841
+ const signal = statuses[stack.name];
2643
3842
  signal.set(style.warning("deploying"));
2644
3843
  try {
2645
- await client.deploy(stackArtifect);
3844
+ await client.deploy(stack);
2646
3845
  } catch (error) {
2647
3846
  debugError(error);
2648
3847
  signal.set(style.error("failed"));
@@ -2810,6 +4009,16 @@ var secrets = (program2) => {
2810
4009
  commands.forEach((cb) => cb(command));
2811
4010
  };
2812
4011
 
4012
+ // src/cli/command/test.ts
4013
+ var test = (program2) => {
4014
+ program2.command("test").action(async () => {
4015
+ await layout(async (config) => {
4016
+ const app = new App("test");
4017
+ const name = "test5";
4018
+ });
4019
+ });
4020
+ };
4021
+
2813
4022
  // src/cli/program.ts
2814
4023
  var program = new import_commander.Command();
2815
4024
  program.name(logo().join("").replace(/\s+/, ""));
@@ -2830,7 +4039,8 @@ var commands2 = [
2830
4039
  status,
2831
4040
  build,
2832
4041
  deploy,
2833
- secrets
4042
+ secrets,
4043
+ test
2834
4044
  // diff,
2835
4045
  // remove,
2836
4046
  ];