@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 +2339 -1129
- package/dist/bin.js +2336 -1126
- package/dist/index.cjs +19 -22
- package/dist/index.d.ts +906 -917
- package/dist/index.js +15 -20
- package/package.json +4 -1
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
|
|
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/
|
|
169
|
-
var
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
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
|
-
|
|
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
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
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/
|
|
229
|
-
var
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
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/
|
|
237
|
-
var
|
|
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")
|
|
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
|
-
})
|
|
594
|
+
});
|
|
276
595
|
var ScheduleExpressionSchema = RateExpressionSchema.or(CronExpressionSchema);
|
|
277
596
|
|
|
278
|
-
// src/plugins/
|
|
279
|
-
var
|
|
597
|
+
// src/plugins/function.ts
|
|
598
|
+
var import_zod6 = require("zod");
|
|
280
599
|
|
|
281
|
-
// src/
|
|
282
|
-
var
|
|
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/
|
|
304
|
-
var
|
|
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
|
|
646
|
+
return Duration.seconds(countNum);
|
|
315
647
|
} else if (unitLower.startsWith("minute")) {
|
|
316
|
-
return
|
|
648
|
+
return Duration.minutes(countNum);
|
|
317
649
|
} else if (unitLower.startsWith("hour")) {
|
|
318
|
-
return
|
|
650
|
+
return Duration.hours(countNum);
|
|
319
651
|
} else if (unitLower.startsWith("day")) {
|
|
320
|
-
return
|
|
652
|
+
return Duration.days(countNum);
|
|
321
653
|
}
|
|
322
|
-
return
|
|
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/
|
|
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
|
|
347
|
-
|
|
348
|
-
|
|
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/
|
|
370
|
-
var
|
|
371
|
-
|
|
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
|
|
725
|
+
return Size.kiloBytes(countNum);
|
|
381
726
|
} else if (unit === "MB") {
|
|
382
|
-
return
|
|
727
|
+
return Size.megaBytes(countNum);
|
|
383
728
|
} else if (unit === "GB") {
|
|
384
|
-
return
|
|
729
|
+
return Size.gigaBytes(countNum);
|
|
385
730
|
}
|
|
386
731
|
throw new TypeError(`Invalid size ${size}`);
|
|
387
732
|
}
|
|
388
|
-
var SizeSchema =
|
|
389
|
-
return
|
|
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
|
-
|
|
393
|
-
|
|
394
|
-
|
|
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
|
|
411
|
-
|
|
412
|
-
|
|
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/
|
|
439
|
-
var
|
|
440
|
-
var
|
|
441
|
-
|
|
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/
|
|
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
|
|
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/
|
|
581
|
-
var
|
|
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
|
-
|
|
935
|
+
import_zod6.z.object({
|
|
584
936
|
file: LocalFileSchema,
|
|
585
|
-
timeout:
|
|
937
|
+
timeout: TimeoutSchema.optional(),
|
|
586
938
|
runtime: RuntimeSchema.optional(),
|
|
587
|
-
memorySize:
|
|
939
|
+
memorySize: MemorySizeSchema.optional(),
|
|
588
940
|
architecture: ArchitectureSchema.optional(),
|
|
589
|
-
ephemeralStorageSize:
|
|
590
|
-
retryAttempts:
|
|
591
|
-
environment:
|
|
941
|
+
ephemeralStorageSize: EphemeralStorageSizeSchema.optional(),
|
|
942
|
+
retryAttempts: RetryAttemptsSchema.optional(),
|
|
943
|
+
environment: EnvironmentSchema.optional()
|
|
592
944
|
})
|
|
593
945
|
]);
|
|
594
|
-
var schema =
|
|
595
|
-
defaults:
|
|
596
|
-
function:
|
|
597
|
-
timeout:
|
|
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:
|
|
600
|
-
architecture: ArchitectureSchema.default("
|
|
601
|
-
ephemeralStorageSize:
|
|
602
|
-
retryAttempts:
|
|
603
|
-
environment:
|
|
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:
|
|
607
|
-
functions:
|
|
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(
|
|
617
|
-
|
|
618
|
-
|
|
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
|
|
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
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
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
|
|
632
|
-
lambda.addEnvironment("STAGE", config.stage
|
|
633
|
-
lambda.addEnvironment("STACK", stack.
|
|
634
|
-
if (
|
|
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:
|
|
671
|
-
stacks:
|
|
672
|
-
crons:
|
|
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
|
-
|
|
1070
|
+
payload: import_zod7.z.unknown().optional()
|
|
676
1071
|
})).optional()
|
|
677
1072
|
}).array()
|
|
678
1073
|
}),
|
|
679
|
-
onStack(
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
const
|
|
683
|
-
new
|
|
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
|
-
|
|
687
|
-
targets: [target]
|
|
1080
|
+
payload: props.payload
|
|
688
1081
|
});
|
|
689
|
-
|
|
690
|
-
}
|
|
1082
|
+
stack.add(lambda, source);
|
|
1083
|
+
}
|
|
691
1084
|
}
|
|
692
1085
|
});
|
|
693
1086
|
|
|
694
1087
|
// src/plugins/queue.ts
|
|
695
|
-
var
|
|
696
|
-
|
|
697
|
-
|
|
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:
|
|
701
|
-
defaults:
|
|
702
|
-
queue
|
|
703
|
-
|
|
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:
|
|
712
|
-
queues
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
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
|
-
|
|
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
|
|
731
|
-
|
|
732
|
-
...props
|
|
733
|
-
|
|
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
|
-
|
|
736
|
-
lambda.addEventSource(new import_aws_lambda_event_sources.SqsEventSource(queue2));
|
|
1277
|
+
stack.add(queue2, lambda, source);
|
|
737
1278
|
bind((lambda2) => {
|
|
738
|
-
queue2.
|
|
739
|
-
addResourceEnvironment(stack, "queue", id, lambda2);
|
|
1279
|
+
lambda2.addPermissions(queue2.permissions);
|
|
740
1280
|
});
|
|
741
|
-
|
|
742
|
-
});
|
|
1281
|
+
}
|
|
743
1282
|
}
|
|
744
1283
|
});
|
|
745
1284
|
|
|
746
|
-
// src/plugins/table
|
|
747
|
-
var
|
|
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/
|
|
762
|
-
var
|
|
763
|
-
var
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
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
|
|
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:
|
|
796
|
-
stacks:
|
|
797
|
-
tables
|
|
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
|
-
|
|
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
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
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
|
-
|
|
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 || {})
|
|
828
|
-
const
|
|
829
|
-
|
|
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.
|
|
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
|
|
858
|
-
|
|
859
|
-
|
|
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:
|
|
863
|
-
stacks:
|
|
864
|
-
stores
|
|
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 || [])
|
|
869
|
-
const bucket = new
|
|
870
|
-
|
|
871
|
-
accessControl:
|
|
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.
|
|
1522
|
+
lambda.addPermissions(bucket.permissions);
|
|
876
1523
|
});
|
|
877
|
-
}
|
|
1524
|
+
}
|
|
878
1525
|
}
|
|
879
1526
|
});
|
|
880
1527
|
|
|
881
1528
|
// src/plugins/topic.ts
|
|
882
|
-
var
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
var
|
|
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:
|
|
889
|
-
stacks:
|
|
890
|
-
topics
|
|
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
|
-
|
|
894
|
-
const allTopicNames = config.stacks.map((
|
|
895
|
-
return Object.keys(
|
|
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
|
-
|
|
899
|
-
new
|
|
900
|
-
|
|
901
|
-
|
|
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 {
|
|
907
|
-
|
|
908
|
-
const lambda =
|
|
909
|
-
const
|
|
910
|
-
|
|
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
|
-
|
|
924
|
-
}
|
|
1635
|
+
stack.add(lambda, source);
|
|
1636
|
+
}
|
|
925
1637
|
}
|
|
926
1638
|
});
|
|
927
1639
|
|
|
928
|
-
// src/plugins/
|
|
929
|
-
var
|
|
930
|
-
var
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
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
|
-
|
|
940
|
-
};
|
|
1658
|
+
});
|
|
941
1659
|
|
|
942
|
-
// src/plugins/
|
|
943
|
-
var
|
|
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/
|
|
948
|
-
var
|
|
949
|
-
var
|
|
950
|
-
|
|
951
|
-
|
|
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/
|
|
954
|
-
var
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
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
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
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:
|
|
1068
|
-
stacks:
|
|
1069
|
-
pubsub
|
|
1070
|
-
|
|
1071
|
-
|
|
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
|
-
|
|
1077
|
-
const { stack, stackConfig, bind } = ctx;
|
|
1735
|
+
onApp({ bind }) {
|
|
1078
1736
|
bind((lambda) => {
|
|
1079
|
-
lambda.
|
|
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/
|
|
1104
|
-
var
|
|
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/
|
|
1115
|
-
var
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
}
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
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:
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
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
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
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
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
}
|
|
1161
|
-
|
|
1162
|
-
config.
|
|
1163
|
-
|
|
1164
|
-
|
|
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
|
-
|
|
1226
|
-
const
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
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
|
-
|
|
1252
|
-
}
|
|
1253
|
-
}
|
|
2206
|
+
stack.add(lambda, source);
|
|
2207
|
+
}
|
|
2208
|
+
}
|
|
1254
2209
|
}
|
|
1255
2210
|
});
|
|
1256
2211
|
|
|
1257
|
-
// src/plugins/domain
|
|
1258
|
-
var
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
// src/
|
|
1279
|
-
var
|
|
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:
|
|
1283
|
-
domains:
|
|
2284
|
+
schema: import_zod15.z.object({
|
|
2285
|
+
domains: import_zod15.z.record(DomainNameSchema, import_zod15.z.object({
|
|
1284
2286
|
name: DomainNameSchema.optional(),
|
|
1285
|
-
type:
|
|
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:
|
|
2289
|
+
records: import_zod15.z.string().array()
|
|
1288
2290
|
}).array()).optional()
|
|
1289
2291
|
}),
|
|
1290
|
-
|
|
1291
|
-
Object.entries(config.domains || {})
|
|
1292
|
-
const hostedZone = new
|
|
1293
|
-
|
|
1294
|
-
|
|
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
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
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
|
-
|
|
2323
|
+
graphqlPlugin
|
|
2324
|
+
// httpPlugin,
|
|
1339
2325
|
];
|
|
1340
2326
|
|
|
1341
|
-
// src/
|
|
1342
|
-
var
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
}
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
}
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
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/
|
|
1364
|
-
var
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
}
|
|
1372
|
-
|
|
1373
|
-
|
|
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
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
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 (!
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
1482
|
-
const
|
|
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
|
|
2512
|
+
const { stack } = toStack({
|
|
1490
2513
|
config,
|
|
1491
2514
|
stackConfig,
|
|
1492
|
-
|
|
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
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
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
|
|
1505
|
-
|
|
1506
|
-
|
|
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
|
|
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
|
|
2569
|
+
var import_zod19 = require("zod");
|
|
1539
2570
|
|
|
1540
2571
|
// src/schema/stack.ts
|
|
1541
|
-
var
|
|
1542
|
-
var StackSchema =
|
|
2572
|
+
var import_zod16 = require("zod");
|
|
2573
|
+
var StackSchema = import_zod16.z.object({
|
|
1543
2574
|
name: ResourceIdSchema,
|
|
1544
|
-
depends:
|
|
2575
|
+
depends: import_zod16.z.array(import_zod16.z.lazy(() => StackSchema)).optional()
|
|
1545
2576
|
});
|
|
1546
2577
|
|
|
1547
2578
|
// src/schema/region.ts
|
|
1548
|
-
var
|
|
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 =
|
|
2596
|
+
var RegionSchema = import_zod17.z.enum(regions);
|
|
1566
2597
|
|
|
1567
2598
|
// src/schema/plugin.ts
|
|
1568
|
-
var
|
|
1569
|
-
var PluginSchema =
|
|
1570
|
-
name:
|
|
1571
|
-
schema:
|
|
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:
|
|
1574
|
-
onStack:
|
|
1575
|
-
onApp:
|
|
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 =
|
|
2611
|
+
var AppSchema = import_zod19.z.object({
|
|
1581
2612
|
name: ResourceIdSchema,
|
|
1582
2613
|
region: RegionSchema,
|
|
1583
|
-
profile:
|
|
1584
|
-
stage:
|
|
1585
|
-
defaults:
|
|
1586
|
-
stacks:
|
|
1587
|
-
|
|
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
|
|
1592
|
-
var
|
|
1593
|
-
var
|
|
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,
|
|
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,
|
|
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,
|
|
2666
|
+
let { code: code2 } = await (0, import_core.transformFile)(file, {
|
|
1623
2667
|
isModule: true
|
|
1624
2668
|
});
|
|
1625
|
-
const path2 = (0,
|
|
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,
|
|
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,
|
|
1642
|
-
await (0,
|
|
1643
|
-
await (0,
|
|
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,
|
|
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((
|
|
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/
|
|
2167
|
-
var
|
|
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(...
|
|
2174
|
-
const
|
|
2175
|
-
await Promise.all(
|
|
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(
|
|
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(
|
|
2185
|
-
" ".repeat(stackNameSize -
|
|
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.
|
|
2190
|
-
" ".repeat(
|
|
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.
|
|
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
|
|
3298
|
+
debug("Clean up template, cache, and asset files");
|
|
2225
3299
|
const paths = [
|
|
2226
|
-
|
|
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
|
|
3335
|
+
const { app } = await toApp(config, filters);
|
|
2245
3336
|
await cleanUp();
|
|
2246
|
-
await write(assetBuilder(
|
|
2247
|
-
app
|
|
3337
|
+
await write(assetBuilder(app));
|
|
3338
|
+
await write(templateBuilder(app));
|
|
2248
3339
|
});
|
|
2249
3340
|
});
|
|
2250
3341
|
};
|
|
2251
3342
|
|
|
2252
|
-
// src/
|
|
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
|
|
3372
|
+
var import_client_s3 = require("@aws-sdk/client-s3");
|
|
3373
|
+
var import_change_case11 = require("change-case");
|
|
2255
3374
|
var StackClient = class {
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
this.
|
|
2259
|
-
this.
|
|
2260
|
-
|
|
2261
|
-
|
|
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
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
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
|
-
|
|
2275
|
-
|
|
3398
|
+
const template = stack.toString();
|
|
3399
|
+
return this.shouldUploadTemplate(template) ? {
|
|
3400
|
+
TemplateUrl: assetBucketUrl(this.account, this.region, stack)
|
|
2276
3401
|
} : {
|
|
2277
|
-
TemplateBody:
|
|
3402
|
+
TemplateBody: template
|
|
2278
3403
|
};
|
|
2279
3404
|
}
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
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
|
|
2287
|
-
Bucket:
|
|
2288
|
-
Key: `${stack.
|
|
2289
|
-
Body:
|
|
2290
|
-
ACL:
|
|
2291
|
-
StorageClass:
|
|
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.
|
|
2296
|
-
|
|
2297
|
-
|
|
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
|
|
3441
|
+
client,
|
|
2305
3442
|
maxWaitTime: this.maxWaitTime,
|
|
2306
3443
|
maxDelay: this.maxDelay
|
|
2307
3444
|
}, {
|
|
2308
|
-
StackName: stack.
|
|
3445
|
+
StackName: this.stackName(stack.name)
|
|
2309
3446
|
});
|
|
2310
3447
|
}
|
|
2311
3448
|
async update(stack, capabilities) {
|
|
2312
|
-
debug("Update the", style.info(stack.
|
|
2313
|
-
|
|
2314
|
-
|
|
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
|
|
3458
|
+
client,
|
|
2320
3459
|
maxWaitTime: this.maxWaitTime,
|
|
2321
3460
|
maxDelay: this.maxDelay
|
|
2322
3461
|
}, {
|
|
2323
|
-
StackName: stack.
|
|
3462
|
+
StackName: this.stackName(stack.name)
|
|
2324
3463
|
});
|
|
2325
3464
|
}
|
|
2326
3465
|
async validate(stack) {
|
|
2327
|
-
debug("Validate the", style.info(stack.
|
|
2328
|
-
const
|
|
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
|
|
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
|
|
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
|
|
2371
|
-
|
|
2372
|
-
|
|
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(
|
|
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
|
|
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
|
|
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 =
|
|
2494
|
-
const client = new StackClient(config);
|
|
2495
|
-
const
|
|
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
|
-
|
|
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
|
|
2532
|
-
const status2 = statuses[
|
|
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(
|
|
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,
|
|
3709
|
+
const { app, dependencyTree } = await toApp(config, filters);
|
|
2568
3710
|
await cleanUp();
|
|
2569
|
-
await write(assetBuilder(
|
|
2570
|
-
|
|
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
|
-
|
|
2576
|
-
stackStatuses[stack.
|
|
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(
|
|
2581
|
-
const info = await client.get(stack.
|
|
2582
|
-
const
|
|
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 !==
|
|
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,
|
|
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(
|
|
2623
|
-
|
|
2624
|
-
await
|
|
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
|
-
|
|
2633
|
-
statuses[stack.
|
|
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.
|
|
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(
|
|
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
|
];
|