@suss/manifest-aws 0.36.0 → 0.37.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +96 -36
- package/dist/index.d.cts +55 -18
- package/dist/index.d.ts +55 -18
- package/dist/index.js +92 -36
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -32,14 +32,18 @@ var index_exports = {};
|
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
CLOUDFORMATION_YAML_TAGS: () => CLOUDFORMATION_YAML_TAGS,
|
|
34
34
|
MAX_STACK_DEPTH: () => MAX_STACK_DEPTH,
|
|
35
|
+
apiScopedKey: () => apiScopedKey,
|
|
36
|
+
documentRef: () => documentRef,
|
|
35
37
|
inheritedEnvVars: () => inheritedEnvVars,
|
|
38
|
+
loadCloudFormationDocument: () => loadCloudFormationDocument,
|
|
36
39
|
loadCloudFormationTemplate: () => loadCloudFormationTemplate,
|
|
37
40
|
loadTemplateTree: () => loadTemplateTree,
|
|
38
41
|
parseHandler: () => import_ir_core2.parseHandler,
|
|
39
42
|
qualifiedLogicalId: () => qualifiedLogicalId,
|
|
40
43
|
readAppSyncResolvers: () => readAppSyncResolvers,
|
|
44
|
+
readEventPattern: () => readEventPattern,
|
|
45
|
+
readResourceLines: () => readResourceLines,
|
|
41
46
|
readServerlessFunctions: () => readServerlessFunctions,
|
|
42
|
-
reduceEventPattern: () => reduceEventPattern,
|
|
43
47
|
refTarget: () => refTarget,
|
|
44
48
|
resolveBucketChannel: () => resolveBucketChannel,
|
|
45
49
|
resolveEventBusToken: () => resolveEventBusToken,
|
|
@@ -97,6 +101,9 @@ var CLOUDFORMATION_YAML_TAGS = [
|
|
|
97
101
|
].flatMap((tag) => everyNodeKind(tag, (value) => value))
|
|
98
102
|
];
|
|
99
103
|
function loadCloudFormationTemplate(templatePath) {
|
|
104
|
+
return loadCloudFormationDocument(templatePath).template;
|
|
105
|
+
}
|
|
106
|
+
function loadCloudFormationDocument(templatePath) {
|
|
100
107
|
const resolved = import_node_path.default.resolve(templatePath);
|
|
101
108
|
if (!import_node_fs.default.existsSync(resolved)) {
|
|
102
109
|
throw new Error(`CloudFormation template not found: ${resolved}`);
|
|
@@ -107,7 +114,30 @@ function loadCloudFormationTemplate(templatePath) {
|
|
|
107
114
|
if (parsed === null || typeof parsed !== "object") {
|
|
108
115
|
throw new Error(`CloudFormation template is not an object: ${resolved}`);
|
|
109
116
|
}
|
|
110
|
-
return
|
|
117
|
+
return {
|
|
118
|
+
template: parsed,
|
|
119
|
+
resourceLines: readResourceLines(raw)
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
function readResourceLines(raw) {
|
|
123
|
+
const lines = /* @__PURE__ */ new Map();
|
|
124
|
+
const lineCounter = new import_yaml.LineCounter();
|
|
125
|
+
const document = import_yaml.default.parseDocument(raw, {
|
|
126
|
+
customTags: CLOUDFORMATION_YAML_TAGS,
|
|
127
|
+
lineCounter
|
|
128
|
+
});
|
|
129
|
+
const resources = document.get("Resources", true);
|
|
130
|
+
if (!(0, import_yaml.isMap)(resources)) {
|
|
131
|
+
return lines;
|
|
132
|
+
}
|
|
133
|
+
for (const item of resources.items) {
|
|
134
|
+
const key = item.key;
|
|
135
|
+
if (!(0, import_yaml.isScalar)(key) || key.range === void 0 || key.range === null) {
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
lines.set(String(key.value), lineCounter.linePos(key.range[0]).line);
|
|
139
|
+
}
|
|
140
|
+
return lines;
|
|
111
141
|
}
|
|
112
142
|
function refTarget(value) {
|
|
113
143
|
if (typeof value === "string") {
|
|
@@ -332,40 +362,36 @@ function resolveBucketChannel(value) {
|
|
|
332
362
|
}
|
|
333
363
|
|
|
334
364
|
// src/eventPattern.ts
|
|
335
|
-
|
|
365
|
+
var STATED_FIELDS = /* @__PURE__ */ new Set(["detail-type", "source"]);
|
|
366
|
+
function readEventPattern(pattern) {
|
|
336
367
|
if (pattern === null || typeof pattern !== "object") {
|
|
337
|
-
return
|
|
338
|
-
kind: "unresolvable",
|
|
339
|
-
reason: "rule declares no EventPattern"
|
|
340
|
-
};
|
|
341
|
-
}
|
|
342
|
-
const detailType = pattern["detail-type"];
|
|
343
|
-
if (detailType === void 0) {
|
|
344
|
-
return {
|
|
345
|
-
kind: "unresolvable",
|
|
346
|
-
reason: "EventPattern has no detail-type; v0 reduces routing to exact detail-type match only"
|
|
347
|
-
};
|
|
348
|
-
}
|
|
349
|
-
if (!Array.isArray(detailType)) {
|
|
350
|
-
return {
|
|
351
|
-
kind: "unresolvable",
|
|
352
|
-
reason: "detail-type is not a plain array of string literals"
|
|
353
|
-
};
|
|
368
|
+
return null;
|
|
354
369
|
}
|
|
355
|
-
const
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
370
|
+
const record = pattern;
|
|
371
|
+
const detailTypes = literalValues(record["detail-type"]);
|
|
372
|
+
const sources = literalValues(record.source);
|
|
373
|
+
const unsettled = [
|
|
374
|
+
...detailTypes === "unsettled" ? ["detail-type"] : [],
|
|
375
|
+
...sources === "unsettled" ? ["source"] : []
|
|
376
|
+
];
|
|
377
|
+
return {
|
|
378
|
+
...Array.isArray(detailTypes) ? { detailTypes } : {},
|
|
379
|
+
...Array.isArray(sources) ? { sources } : {},
|
|
380
|
+
unsettled,
|
|
381
|
+
filtersOtherFields: Object.keys(record).some(
|
|
382
|
+
(key) => !STATED_FIELDS.has(key)
|
|
383
|
+
),
|
|
384
|
+
emittedByAws: Array.isArray(sources) && sources.some((source) => source.startsWith("aws."))
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
function literalValues(value) {
|
|
388
|
+
if (value === void 0) {
|
|
389
|
+
return null;
|
|
364
390
|
}
|
|
365
|
-
if (
|
|
366
|
-
return
|
|
391
|
+
if (!Array.isArray(value) || value.length === 0 || !value.every((entry) => typeof entry === "string")) {
|
|
392
|
+
return "unsettled";
|
|
367
393
|
}
|
|
368
|
-
return
|
|
394
|
+
return value;
|
|
369
395
|
}
|
|
370
396
|
function resolveEventBusToken(value) {
|
|
371
397
|
if (value === null || value === void 0) {
|
|
@@ -484,12 +510,40 @@ var CHILD_TEMPLATE_PROPERTY = {
|
|
|
484
510
|
"AWS::Serverless::Application": "Location"
|
|
485
511
|
};
|
|
486
512
|
var MAX_STACK_DEPTH = 10;
|
|
513
|
+
function documentRef(document, value) {
|
|
514
|
+
const id = refTarget(value);
|
|
515
|
+
if (id === null) {
|
|
516
|
+
return null;
|
|
517
|
+
}
|
|
518
|
+
return document.parameterTargets.get(id) ?? qualifiedLogicalId(document.stackPath, id);
|
|
519
|
+
}
|
|
520
|
+
function parameterTargetsOf(parent, passed) {
|
|
521
|
+
const targets = /* @__PURE__ */ new Map();
|
|
522
|
+
if (passed === null || typeof passed !== "object") {
|
|
523
|
+
return targets;
|
|
524
|
+
}
|
|
525
|
+
const resources = parent.template.Resources ?? {};
|
|
526
|
+
for (const [name, value] of Object.entries(
|
|
527
|
+
passed
|
|
528
|
+
)) {
|
|
529
|
+
const id = refTarget(value);
|
|
530
|
+
if (id === null || typeof value === "string") {
|
|
531
|
+
continue;
|
|
532
|
+
}
|
|
533
|
+
const target = parent.parameterTargets.get(id) ?? (resources[id] === void 0 ? null : qualifiedLogicalId(parent.stackPath, id));
|
|
534
|
+
if (target !== null) {
|
|
535
|
+
targets.set(name, target);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
return targets;
|
|
539
|
+
}
|
|
487
540
|
function loadTemplateTree(rootPath) {
|
|
488
541
|
const rootFile = import_node_path2.default.resolve(rootPath);
|
|
489
542
|
const root = {
|
|
490
543
|
path: rootFile,
|
|
491
544
|
stackPath: [],
|
|
492
|
-
|
|
545
|
+
...loadCloudFormationDocument(rootFile),
|
|
546
|
+
parameterTargets: /* @__PURE__ */ new Map()
|
|
493
547
|
};
|
|
494
548
|
const documents = [root];
|
|
495
549
|
const unfollowed = [];
|
|
@@ -533,7 +587,8 @@ function childStacksOf(document, ancestors) {
|
|
|
533
587
|
document,
|
|
534
588
|
ancestors,
|
|
535
589
|
logicalId,
|
|
536
|
-
location: resource.Properties?.[property]
|
|
590
|
+
location: resource.Properties?.[property],
|
|
591
|
+
parameters: resource.Properties?.Parameters
|
|
537
592
|
})
|
|
538
593
|
);
|
|
539
594
|
}
|
|
@@ -595,7 +650,8 @@ function childStack(opts) {
|
|
|
595
650
|
document: {
|
|
596
651
|
path: childPath,
|
|
597
652
|
stackPath,
|
|
598
|
-
|
|
653
|
+
...loadCloudFormationDocument(childPath),
|
|
654
|
+
parameterTargets: parameterTargetsOf(opts.document, opts.parameters)
|
|
599
655
|
}
|
|
600
656
|
};
|
|
601
657
|
} catch (err) {
|
|
@@ -692,14 +748,18 @@ function readServerlessFunctions(template) {
|
|
|
692
748
|
0 && (module.exports = {
|
|
693
749
|
CLOUDFORMATION_YAML_TAGS,
|
|
694
750
|
MAX_STACK_DEPTH,
|
|
751
|
+
apiScopedKey,
|
|
752
|
+
documentRef,
|
|
695
753
|
inheritedEnvVars,
|
|
754
|
+
loadCloudFormationDocument,
|
|
696
755
|
loadCloudFormationTemplate,
|
|
697
756
|
loadTemplateTree,
|
|
698
757
|
parseHandler,
|
|
699
758
|
qualifiedLogicalId,
|
|
700
759
|
readAppSyncResolvers,
|
|
760
|
+
readEventPattern,
|
|
761
|
+
readResourceLines,
|
|
701
762
|
readServerlessFunctions,
|
|
702
|
-
reduceEventPattern,
|
|
703
763
|
refTarget,
|
|
704
764
|
resolveBucketChannel,
|
|
705
765
|
resolveEventBusToken,
|
package/dist/index.d.cts
CHANGED
|
@@ -35,6 +35,19 @@ declare const CLOUDFORMATION_YAML_TAGS: YAML.CollectionTag[];
|
|
|
35
35
|
* malformed manifest is a load-time error rather than an empty result.
|
|
36
36
|
*/
|
|
37
37
|
declare function loadCloudFormationTemplate(templatePath: string): CloudFormationTemplate;
|
|
38
|
+
interface LoadedTemplate {
|
|
39
|
+
template: CloudFormationTemplate;
|
|
40
|
+
/** The 1-based line each resource's logical id is written on. */
|
|
41
|
+
resourceLines: Map<string, number>;
|
|
42
|
+
}
|
|
43
|
+
/** `loadCloudFormationTemplate`, plus the line each resource starts on. */
|
|
44
|
+
declare function loadCloudFormationDocument(templatePath: string): LoadedTemplate;
|
|
45
|
+
/**
|
|
46
|
+
* The line each key under `Resources` is written on. JSON is valid YAML,
|
|
47
|
+
* so one reader covers both formats. A text that does not parse gives
|
|
48
|
+
* an empty map, and its summaries keep line 1.
|
|
49
|
+
*/
|
|
50
|
+
declare function readResourceLines(raw: string): Map<string, number>;
|
|
38
51
|
/**
|
|
39
52
|
* CloudFormation references come out of the parser in four forms:
|
|
40
53
|
* - { Ref: "LogicalId" }
|
|
@@ -81,6 +94,8 @@ interface AppSyncResolverBinding {
|
|
|
81
94
|
lambdaFunctionLogicalIds: string[];
|
|
82
95
|
}
|
|
83
96
|
declare function readAppSyncResolvers(template: CloudFormationTemplate): AppSyncResolverBinding[];
|
|
97
|
+
/** A name only means something next to the API it was declared in. */
|
|
98
|
+
declare function apiScopedKey(apiId: string | null, name: string): string;
|
|
84
99
|
|
|
85
100
|
/**
|
|
86
101
|
* Resolves the references AWS manifests use to point at a queue, a topic,
|
|
@@ -128,25 +143,32 @@ declare function resolveBucketChannel(value: unknown): string | null;
|
|
|
128
143
|
* CloudFormation template and in a serverless.yml eventBridge event, so
|
|
129
144
|
* both readers reduce them here.
|
|
130
145
|
*/
|
|
146
|
+
/** A pattern field that is not a plain list of strings. */
|
|
147
|
+
type UnsettledPatternField = "detail-type" | "source";
|
|
131
148
|
/**
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
149
|
+
* What a rule's EventPattern says about the two fields a send states.
|
|
150
|
+
* A field the pattern leaves out matches every value, so it has no
|
|
151
|
+
* entry here. A field written as anything but a list of strings, such
|
|
152
|
+
* as a prefix filter, is listed in `unsettled`.
|
|
135
153
|
*/
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
154
|
+
interface EventPatternFields {
|
|
155
|
+
detailTypes?: string[];
|
|
156
|
+
sources?: string[];
|
|
157
|
+
unsettled: UnsettledPatternField[];
|
|
158
|
+
/**
|
|
159
|
+
* The pattern also filters on a field a send does not state, such as
|
|
160
|
+
* `detail`, so a send that agrees on source and detail-type still
|
|
161
|
+
* reaches the rule only for some events.
|
|
162
|
+
*/
|
|
163
|
+
filtersOtherFields: boolean;
|
|
164
|
+
/**
|
|
165
|
+
* A source starts with `aws.`. Only AWS services send those, and
|
|
166
|
+
* PutEvents refuses them from anyone else.
|
|
167
|
+
*/
|
|
168
|
+
emittedByAws: boolean;
|
|
169
|
+
}
|
|
170
|
+
/** Null when the rule declares no EventPattern at all. */
|
|
171
|
+
declare function readEventPattern(pattern: unknown): EventPatternFields | null;
|
|
150
172
|
/**
|
|
151
173
|
* Resolve a rule's EventBusName to the channel's bus token: the CFN
|
|
152
174
|
* logical id when it's a Ref/GetAtt, the name segment of an event-bus
|
|
@@ -217,7 +239,22 @@ interface TemplateDocument {
|
|
|
217
239
|
*/
|
|
218
240
|
stackPath: string[];
|
|
219
241
|
template: CloudFormationTemplate;
|
|
242
|
+
/** The 1-based line each resource's logical id is written on. */
|
|
243
|
+
resourceLines: Map<string, number>;
|
|
244
|
+
/**
|
|
245
|
+
* The resource each of this document's parameters points at, for a
|
|
246
|
+
* parameter the parent stack sets to a reference. The id is qualified
|
|
247
|
+
* by its own document's stack path, so a child's `!Ref ApiId` can be
|
|
248
|
+
* read as the API its parent declares. Empty for the root.
|
|
249
|
+
*/
|
|
250
|
+
parameterTargets: Map<string, string>;
|
|
220
251
|
}
|
|
252
|
+
/**
|
|
253
|
+
* The qualified logical id a reference in this document points at: the
|
|
254
|
+
* resource the parent passed in as a parameter, or the document's own.
|
|
255
|
+
* Null for a value only known at deploy time.
|
|
256
|
+
*/
|
|
257
|
+
declare function documentRef(document: Pick<TemplateDocument, "stackPath" | "parameterTargets">, value: unknown): string | null;
|
|
221
258
|
/**
|
|
222
259
|
* Why a child template was not read. Every value is a limit on what the
|
|
223
260
|
* reader could reach, so a consumer should report it as a read that
|
|
@@ -333,4 +370,4 @@ interface ServerlessFunctionInfo {
|
|
|
333
370
|
*/
|
|
334
371
|
declare function readServerlessFunctions(template: CloudFormationTemplate): ServerlessFunctionInfo[];
|
|
335
372
|
|
|
336
|
-
export { type AppSyncResolverBinding, CLOUDFORMATION_YAML_TAGS, type CloudFormationResource, type CloudFormationTemplate,
|
|
373
|
+
export { type AppSyncResolverBinding, CLOUDFORMATION_YAML_TAGS, type CloudFormationResource, type CloudFormationTemplate, type EventPatternFields, type LoadedTemplate, MAX_STACK_DEPTH, type ServerlessFunctionInfo, type ServerlessHttpRoute, type ServerlessNonHttpEvent, type TemplateDocument, type TemplateTree, type UnfollowedReason, type UnfollowedStack, type UnsettledPatternField, apiScopedKey, documentRef, inheritedEnvVars, loadCloudFormationDocument, loadCloudFormationTemplate, loadTemplateTree, qualifiedLogicalId, readAppSyncResolvers, readEventPattern, readResourceLines, readServerlessFunctions, refTarget, resolveBucketChannel, resolveEventBusToken, resolveQueueChannel, resolveResourceChannel, resolveTopicChannel, resourcesWithGlobals, unfollowedStackMessage };
|
package/dist/index.d.ts
CHANGED
|
@@ -35,6 +35,19 @@ declare const CLOUDFORMATION_YAML_TAGS: YAML.CollectionTag[];
|
|
|
35
35
|
* malformed manifest is a load-time error rather than an empty result.
|
|
36
36
|
*/
|
|
37
37
|
declare function loadCloudFormationTemplate(templatePath: string): CloudFormationTemplate;
|
|
38
|
+
interface LoadedTemplate {
|
|
39
|
+
template: CloudFormationTemplate;
|
|
40
|
+
/** The 1-based line each resource's logical id is written on. */
|
|
41
|
+
resourceLines: Map<string, number>;
|
|
42
|
+
}
|
|
43
|
+
/** `loadCloudFormationTemplate`, plus the line each resource starts on. */
|
|
44
|
+
declare function loadCloudFormationDocument(templatePath: string): LoadedTemplate;
|
|
45
|
+
/**
|
|
46
|
+
* The line each key under `Resources` is written on. JSON is valid YAML,
|
|
47
|
+
* so one reader covers both formats. A text that does not parse gives
|
|
48
|
+
* an empty map, and its summaries keep line 1.
|
|
49
|
+
*/
|
|
50
|
+
declare function readResourceLines(raw: string): Map<string, number>;
|
|
38
51
|
/**
|
|
39
52
|
* CloudFormation references come out of the parser in four forms:
|
|
40
53
|
* - { Ref: "LogicalId" }
|
|
@@ -81,6 +94,8 @@ interface AppSyncResolverBinding {
|
|
|
81
94
|
lambdaFunctionLogicalIds: string[];
|
|
82
95
|
}
|
|
83
96
|
declare function readAppSyncResolvers(template: CloudFormationTemplate): AppSyncResolverBinding[];
|
|
97
|
+
/** A name only means something next to the API it was declared in. */
|
|
98
|
+
declare function apiScopedKey(apiId: string | null, name: string): string;
|
|
84
99
|
|
|
85
100
|
/**
|
|
86
101
|
* Resolves the references AWS manifests use to point at a queue, a topic,
|
|
@@ -128,25 +143,32 @@ declare function resolveBucketChannel(value: unknown): string | null;
|
|
|
128
143
|
* CloudFormation template and in a serverless.yml eventBridge event, so
|
|
129
144
|
* both readers reduce them here.
|
|
130
145
|
*/
|
|
146
|
+
/** A pattern field that is not a plain list of strings. */
|
|
147
|
+
type UnsettledPatternField = "detail-type" | "source";
|
|
131
148
|
/**
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
149
|
+
* What a rule's EventPattern says about the two fields a send states.
|
|
150
|
+
* A field the pattern leaves out matches every value, so it has no
|
|
151
|
+
* entry here. A field written as anything but a list of strings, such
|
|
152
|
+
* as a prefix filter, is listed in `unsettled`.
|
|
135
153
|
*/
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
154
|
+
interface EventPatternFields {
|
|
155
|
+
detailTypes?: string[];
|
|
156
|
+
sources?: string[];
|
|
157
|
+
unsettled: UnsettledPatternField[];
|
|
158
|
+
/**
|
|
159
|
+
* The pattern also filters on a field a send does not state, such as
|
|
160
|
+
* `detail`, so a send that agrees on source and detail-type still
|
|
161
|
+
* reaches the rule only for some events.
|
|
162
|
+
*/
|
|
163
|
+
filtersOtherFields: boolean;
|
|
164
|
+
/**
|
|
165
|
+
* A source starts with `aws.`. Only AWS services send those, and
|
|
166
|
+
* PutEvents refuses them from anyone else.
|
|
167
|
+
*/
|
|
168
|
+
emittedByAws: boolean;
|
|
169
|
+
}
|
|
170
|
+
/** Null when the rule declares no EventPattern at all. */
|
|
171
|
+
declare function readEventPattern(pattern: unknown): EventPatternFields | null;
|
|
150
172
|
/**
|
|
151
173
|
* Resolve a rule's EventBusName to the channel's bus token: the CFN
|
|
152
174
|
* logical id when it's a Ref/GetAtt, the name segment of an event-bus
|
|
@@ -217,7 +239,22 @@ interface TemplateDocument {
|
|
|
217
239
|
*/
|
|
218
240
|
stackPath: string[];
|
|
219
241
|
template: CloudFormationTemplate;
|
|
242
|
+
/** The 1-based line each resource's logical id is written on. */
|
|
243
|
+
resourceLines: Map<string, number>;
|
|
244
|
+
/**
|
|
245
|
+
* The resource each of this document's parameters points at, for a
|
|
246
|
+
* parameter the parent stack sets to a reference. The id is qualified
|
|
247
|
+
* by its own document's stack path, so a child's `!Ref ApiId` can be
|
|
248
|
+
* read as the API its parent declares. Empty for the root.
|
|
249
|
+
*/
|
|
250
|
+
parameterTargets: Map<string, string>;
|
|
220
251
|
}
|
|
252
|
+
/**
|
|
253
|
+
* The qualified logical id a reference in this document points at: the
|
|
254
|
+
* resource the parent passed in as a parameter, or the document's own.
|
|
255
|
+
* Null for a value only known at deploy time.
|
|
256
|
+
*/
|
|
257
|
+
declare function documentRef(document: Pick<TemplateDocument, "stackPath" | "parameterTargets">, value: unknown): string | null;
|
|
221
258
|
/**
|
|
222
259
|
* Why a child template was not read. Every value is a limit on what the
|
|
223
260
|
* reader could reach, so a consumer should report it as a read that
|
|
@@ -333,4 +370,4 @@ interface ServerlessFunctionInfo {
|
|
|
333
370
|
*/
|
|
334
371
|
declare function readServerlessFunctions(template: CloudFormationTemplate): ServerlessFunctionInfo[];
|
|
335
372
|
|
|
336
|
-
export { type AppSyncResolverBinding, CLOUDFORMATION_YAML_TAGS, type CloudFormationResource, type CloudFormationTemplate,
|
|
373
|
+
export { type AppSyncResolverBinding, CLOUDFORMATION_YAML_TAGS, type CloudFormationResource, type CloudFormationTemplate, type EventPatternFields, type LoadedTemplate, MAX_STACK_DEPTH, type ServerlessFunctionInfo, type ServerlessHttpRoute, type ServerlessNonHttpEvent, type TemplateDocument, type TemplateTree, type UnfollowedReason, type UnfollowedStack, type UnsettledPatternField, apiScopedKey, documentRef, inheritedEnvVars, loadCloudFormationDocument, loadCloudFormationTemplate, loadTemplateTree, qualifiedLogicalId, readAppSyncResolvers, readEventPattern, readResourceLines, readServerlessFunctions, refTarget, resolveBucketChannel, resolveEventBusToken, resolveQueueChannel, resolveResourceChannel, resolveTopicChannel, resourcesWithGlobals, unfollowedStackMessage };
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// src/templateLoader.ts
|
|
2
2
|
import fs from "fs";
|
|
3
3
|
import path from "path";
|
|
4
|
-
import YAML from "yaml";
|
|
4
|
+
import YAML, { isMap, isScalar, LineCounter } from "yaml";
|
|
5
5
|
var everyNodeKind = (tag, resolve) => {
|
|
6
6
|
const forNode = (value) => resolve(plainly(value));
|
|
7
7
|
return [
|
|
@@ -44,6 +44,9 @@ var CLOUDFORMATION_YAML_TAGS = [
|
|
|
44
44
|
].flatMap((tag) => everyNodeKind(tag, (value) => value))
|
|
45
45
|
];
|
|
46
46
|
function loadCloudFormationTemplate(templatePath) {
|
|
47
|
+
return loadCloudFormationDocument(templatePath).template;
|
|
48
|
+
}
|
|
49
|
+
function loadCloudFormationDocument(templatePath) {
|
|
47
50
|
const resolved = path.resolve(templatePath);
|
|
48
51
|
if (!fs.existsSync(resolved)) {
|
|
49
52
|
throw new Error(`CloudFormation template not found: ${resolved}`);
|
|
@@ -54,7 +57,30 @@ function loadCloudFormationTemplate(templatePath) {
|
|
|
54
57
|
if (parsed === null || typeof parsed !== "object") {
|
|
55
58
|
throw new Error(`CloudFormation template is not an object: ${resolved}`);
|
|
56
59
|
}
|
|
57
|
-
return
|
|
60
|
+
return {
|
|
61
|
+
template: parsed,
|
|
62
|
+
resourceLines: readResourceLines(raw)
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
function readResourceLines(raw) {
|
|
66
|
+
const lines = /* @__PURE__ */ new Map();
|
|
67
|
+
const lineCounter = new LineCounter();
|
|
68
|
+
const document = YAML.parseDocument(raw, {
|
|
69
|
+
customTags: CLOUDFORMATION_YAML_TAGS,
|
|
70
|
+
lineCounter
|
|
71
|
+
});
|
|
72
|
+
const resources = document.get("Resources", true);
|
|
73
|
+
if (!isMap(resources)) {
|
|
74
|
+
return lines;
|
|
75
|
+
}
|
|
76
|
+
for (const item of resources.items) {
|
|
77
|
+
const key = item.key;
|
|
78
|
+
if (!isScalar(key) || key.range === void 0 || key.range === null) {
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
lines.set(String(key.value), lineCounter.linePos(key.range[0]).line);
|
|
82
|
+
}
|
|
83
|
+
return lines;
|
|
58
84
|
}
|
|
59
85
|
function refTarget(value) {
|
|
60
86
|
if (typeof value === "string") {
|
|
@@ -279,40 +305,36 @@ function resolveBucketChannel(value) {
|
|
|
279
305
|
}
|
|
280
306
|
|
|
281
307
|
// src/eventPattern.ts
|
|
282
|
-
|
|
308
|
+
var STATED_FIELDS = /* @__PURE__ */ new Set(["detail-type", "source"]);
|
|
309
|
+
function readEventPattern(pattern) {
|
|
283
310
|
if (pattern === null || typeof pattern !== "object") {
|
|
284
|
-
return
|
|
285
|
-
kind: "unresolvable",
|
|
286
|
-
reason: "rule declares no EventPattern"
|
|
287
|
-
};
|
|
288
|
-
}
|
|
289
|
-
const detailType = pattern["detail-type"];
|
|
290
|
-
if (detailType === void 0) {
|
|
291
|
-
return {
|
|
292
|
-
kind: "unresolvable",
|
|
293
|
-
reason: "EventPattern has no detail-type; v0 reduces routing to exact detail-type match only"
|
|
294
|
-
};
|
|
295
|
-
}
|
|
296
|
-
if (!Array.isArray(detailType)) {
|
|
297
|
-
return {
|
|
298
|
-
kind: "unresolvable",
|
|
299
|
-
reason: "detail-type is not a plain array of string literals"
|
|
300
|
-
};
|
|
311
|
+
return null;
|
|
301
312
|
}
|
|
302
|
-
const
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
313
|
+
const record = pattern;
|
|
314
|
+
const detailTypes = literalValues(record["detail-type"]);
|
|
315
|
+
const sources = literalValues(record.source);
|
|
316
|
+
const unsettled = [
|
|
317
|
+
...detailTypes === "unsettled" ? ["detail-type"] : [],
|
|
318
|
+
...sources === "unsettled" ? ["source"] : []
|
|
319
|
+
];
|
|
320
|
+
return {
|
|
321
|
+
...Array.isArray(detailTypes) ? { detailTypes } : {},
|
|
322
|
+
...Array.isArray(sources) ? { sources } : {},
|
|
323
|
+
unsettled,
|
|
324
|
+
filtersOtherFields: Object.keys(record).some(
|
|
325
|
+
(key) => !STATED_FIELDS.has(key)
|
|
326
|
+
),
|
|
327
|
+
emittedByAws: Array.isArray(sources) && sources.some((source) => source.startsWith("aws."))
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
function literalValues(value) {
|
|
331
|
+
if (value === void 0) {
|
|
332
|
+
return null;
|
|
311
333
|
}
|
|
312
|
-
if (
|
|
313
|
-
return
|
|
334
|
+
if (!Array.isArray(value) || value.length === 0 || !value.every((entry) => typeof entry === "string")) {
|
|
335
|
+
return "unsettled";
|
|
314
336
|
}
|
|
315
|
-
return
|
|
337
|
+
return value;
|
|
316
338
|
}
|
|
317
339
|
function resolveEventBusToken(value) {
|
|
318
340
|
if (value === null || value === void 0) {
|
|
@@ -431,12 +453,40 @@ var CHILD_TEMPLATE_PROPERTY = {
|
|
|
431
453
|
"AWS::Serverless::Application": "Location"
|
|
432
454
|
};
|
|
433
455
|
var MAX_STACK_DEPTH = 10;
|
|
456
|
+
function documentRef(document, value) {
|
|
457
|
+
const id = refTarget(value);
|
|
458
|
+
if (id === null) {
|
|
459
|
+
return null;
|
|
460
|
+
}
|
|
461
|
+
return document.parameterTargets.get(id) ?? qualifiedLogicalId(document.stackPath, id);
|
|
462
|
+
}
|
|
463
|
+
function parameterTargetsOf(parent, passed) {
|
|
464
|
+
const targets = /* @__PURE__ */ new Map();
|
|
465
|
+
if (passed === null || typeof passed !== "object") {
|
|
466
|
+
return targets;
|
|
467
|
+
}
|
|
468
|
+
const resources = parent.template.Resources ?? {};
|
|
469
|
+
for (const [name, value] of Object.entries(
|
|
470
|
+
passed
|
|
471
|
+
)) {
|
|
472
|
+
const id = refTarget(value);
|
|
473
|
+
if (id === null || typeof value === "string") {
|
|
474
|
+
continue;
|
|
475
|
+
}
|
|
476
|
+
const target = parent.parameterTargets.get(id) ?? (resources[id] === void 0 ? null : qualifiedLogicalId(parent.stackPath, id));
|
|
477
|
+
if (target !== null) {
|
|
478
|
+
targets.set(name, target);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
return targets;
|
|
482
|
+
}
|
|
434
483
|
function loadTemplateTree(rootPath) {
|
|
435
484
|
const rootFile = path2.resolve(rootPath);
|
|
436
485
|
const root = {
|
|
437
486
|
path: rootFile,
|
|
438
487
|
stackPath: [],
|
|
439
|
-
|
|
488
|
+
...loadCloudFormationDocument(rootFile),
|
|
489
|
+
parameterTargets: /* @__PURE__ */ new Map()
|
|
440
490
|
};
|
|
441
491
|
const documents = [root];
|
|
442
492
|
const unfollowed = [];
|
|
@@ -480,7 +530,8 @@ function childStacksOf(document, ancestors) {
|
|
|
480
530
|
document,
|
|
481
531
|
ancestors,
|
|
482
532
|
logicalId,
|
|
483
|
-
location: resource.Properties?.[property]
|
|
533
|
+
location: resource.Properties?.[property],
|
|
534
|
+
parameters: resource.Properties?.Parameters
|
|
484
535
|
})
|
|
485
536
|
);
|
|
486
537
|
}
|
|
@@ -542,7 +593,8 @@ function childStack(opts) {
|
|
|
542
593
|
document: {
|
|
543
594
|
path: childPath,
|
|
544
595
|
stackPath,
|
|
545
|
-
|
|
596
|
+
...loadCloudFormationDocument(childPath),
|
|
597
|
+
parameterTargets: parameterTargetsOf(opts.document, opts.parameters)
|
|
546
598
|
}
|
|
547
599
|
};
|
|
548
600
|
} catch (err) {
|
|
@@ -638,14 +690,18 @@ function readServerlessFunctions(template) {
|
|
|
638
690
|
export {
|
|
639
691
|
CLOUDFORMATION_YAML_TAGS,
|
|
640
692
|
MAX_STACK_DEPTH,
|
|
693
|
+
apiScopedKey,
|
|
694
|
+
documentRef,
|
|
641
695
|
inheritedEnvVars,
|
|
696
|
+
loadCloudFormationDocument,
|
|
642
697
|
loadCloudFormationTemplate,
|
|
643
698
|
loadTemplateTree,
|
|
644
699
|
parseHandler2 as parseHandler,
|
|
645
700
|
qualifiedLogicalId,
|
|
646
701
|
readAppSyncResolvers,
|
|
702
|
+
readEventPattern,
|
|
703
|
+
readResourceLines,
|
|
647
704
|
readServerlessFunctions,
|
|
648
|
-
reduceEventPattern,
|
|
649
705
|
refTarget,
|
|
650
706
|
resolveBucketChannel,
|
|
651
707
|
resolveEventBusToken,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@suss/manifest-aws",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.37.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Parse a CloudFormation or SAM template into plain data, the facts every suss reader of an AWS template works from.",
|
|
6
6
|
"keywords": [
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"typecheck": "tsc --noEmit"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@suss/ir-core": "0.
|
|
49
|
+
"@suss/ir-core": "0.37.1",
|
|
50
50
|
"yaml": "^2.4.0"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|