@intentius/chant-lexicon-aws 0.124.43 → 0.124.45
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/codegen/generate-lexicon.d.ts +2 -6
- package/dist/codegen/generate-lexicon.d.ts.map +1 -1
- package/dist/deep-observe.d.ts +6 -2
- package/dist/deep-observe.d.ts.map +1 -1
- package/dist/deep-observe.js +17 -7
- package/dist/import/live-export.d.ts.map +1 -1
- package/dist/import/live-export.js +8 -4
- package/dist/integrity.json +5 -4
- package/dist/lint/audit-catalog.d.ts.map +1 -1
- package/dist/lint/audit-catalog.js +1 -0
- package/dist/lint/audit-lineage.d.ts.map +1 -1
- package/dist/lint/audit-lineage.js +4 -0
- package/dist/lint/post-synth/index.d.ts.map +1 -1
- package/dist/lint/post-synth/index.js +2 -0
- package/dist/lint/post-synth/waw017.d.ts.map +1 -1
- package/dist/lint/post-synth/waw017.js +3 -2
- package/dist/lint/post-synth/waw071.d.ts +19 -0
- package/dist/lint/post-synth/waw071.d.ts.map +1 -0
- package/dist/lint/post-synth/waw071.js +76 -0
- package/dist/manifest.json +1 -1
- package/dist/meta.json +3618 -1206
- package/dist/okf/index.md +1 -0
- package/dist/okf/rules/WAW071.md +19 -0
- package/dist/okf/types/Key.md +1 -0
- package/dist/okf/types/Map.md +1 -0
- package/dist/okf/types/SsmParameter.md +4 -0
- package/dist/okf/types/Type.md +1 -0
- package/dist/okf/types/UserPool.md +1 -0
- package/dist/rules/waw017.ts +3 -2
- package/dist/rules/waw071.ts +85 -0
- package/dist/serializer.d.ts +12 -0
- package/dist/serializer.d.ts.map +1 -1
- package/dist/serializer.js +44 -10
- package/dist/spec/parse.d.ts +27 -5
- package/dist/spec/parse.d.ts.map +1 -1
- package/dist/spec/parse.js +56 -0
- package/dist/taggable.d.ts +28 -0
- package/dist/taggable.d.ts.map +1 -1
- package/dist/taggable.js +60 -9
- package/package.json +2 -2
- package/src/codegen/generate-lexicon.ts +2 -2
- package/src/deep-observe.test.ts +19 -0
- package/src/deep-observe.ts +17 -6
- package/src/generated/lexicon-aws.json +3618 -1206
- package/src/import/live-export.test.ts +18 -0
- package/src/import/live-export.ts +8 -4
- package/src/lint/audit-catalog.ts +1 -0
- package/src/lint/audit-lineage.ts +4 -0
- package/src/lint/post-synth/index.ts +2 -0
- package/src/lint/post-synth/waw017.ts +3 -2
- package/src/lint/post-synth/waw071.test.ts +50 -0
- package/src/lint/post-synth/waw071.ts +85 -0
- package/src/serializer-tag-shapes.test.ts +83 -0
- package/src/serializer.ts +46 -9
- package/src/spec/parse.test.ts +39 -0
- package/src/spec/parse.ts +83 -1
- package/src/taggable.ts +78 -11
package/src/taggable.ts
CHANGED
|
@@ -8,37 +8,104 @@ import { readFileSync } from "fs";
|
|
|
8
8
|
import { join } from "path";
|
|
9
9
|
import { moduleSourceDir } from "@intentius/chant/module-source";
|
|
10
10
|
|
|
11
|
+
/** How a type's tags are written: a `[{Key, Value}]` list or a key-to-value map. */
|
|
12
|
+
export type TagShape = "list" | "map";
|
|
13
|
+
|
|
14
|
+
/** The property a type's tags live in, and its shape (#4350). */
|
|
15
|
+
export interface TagPlacement {
|
|
16
|
+
property: string;
|
|
17
|
+
shape: TagShape;
|
|
18
|
+
}
|
|
19
|
+
|
|
11
20
|
interface LexiconEntry {
|
|
12
21
|
kind: string;
|
|
13
22
|
resourceType: string;
|
|
14
|
-
tagging?: {
|
|
23
|
+
tagging?: {
|
|
24
|
+
taggable: boolean;
|
|
25
|
+
tagOnCreate: boolean;
|
|
26
|
+
tagUpdatable: boolean;
|
|
27
|
+
tagProperty?: string;
|
|
28
|
+
tagShape?: TagShape;
|
|
29
|
+
};
|
|
15
30
|
[key: string]: unknown;
|
|
16
31
|
}
|
|
17
32
|
|
|
18
|
-
let _cached:
|
|
33
|
+
let _cached: Map<string, TagPlacement | null> | undefined;
|
|
19
34
|
|
|
20
35
|
/**
|
|
21
|
-
*
|
|
22
|
-
*
|
|
36
|
+
* Every taggable type, mapped to where its tags go. `null` means the schema
|
|
37
|
+
* puts the tags somewhere chant can't stamp (inside an array, or under a
|
|
38
|
+
* wrapper object), so the type gets no stamped tags.
|
|
23
39
|
*/
|
|
24
|
-
export function
|
|
40
|
+
export function loadTagPlacements(): Map<string, TagPlacement | null> {
|
|
25
41
|
if (_cached) return _cached;
|
|
26
42
|
|
|
27
|
-
const
|
|
43
|
+
const map = new Map<string, TagPlacement | null>();
|
|
28
44
|
try {
|
|
29
45
|
const lexiconPath = join(moduleSourceDir(import.meta.url), "generated", "lexicon-aws.json");
|
|
30
46
|
const content = readFileSync(lexiconPath, "utf-8");
|
|
31
47
|
const data = JSON.parse(content) as Record<string, LexiconEntry>;
|
|
32
48
|
|
|
33
49
|
for (const [_name, entry] of Object.entries(data)) {
|
|
34
|
-
if (entry.kind
|
|
35
|
-
|
|
36
|
-
}
|
|
50
|
+
if (entry.kind !== "resource" || !entry.resourceType || !entry.tagging?.taggable) continue;
|
|
51
|
+
const { tagProperty, tagShape } = entry.tagging;
|
|
52
|
+
map.set(entry.resourceType, tagProperty && tagShape ? { property: tagProperty, shape: tagShape } : null);
|
|
37
53
|
}
|
|
38
54
|
} catch {
|
|
39
55
|
// Lexicon not available — skip
|
|
40
56
|
}
|
|
41
57
|
|
|
42
|
-
_cached =
|
|
43
|
-
return
|
|
58
|
+
_cached = map;
|
|
59
|
+
return map;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Load taggable resource types from the lexicon JSON.
|
|
64
|
+
* Result is cached after first call.
|
|
65
|
+
*/
|
|
66
|
+
export function loadTaggableResources(): Set<string> {
|
|
67
|
+
return new Set(loadTagPlacements().keys());
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Where `type`'s tags go, or undefined when it isn't taggable or can't be placed. */
|
|
71
|
+
export function tagPlacementFor(type: string): TagPlacement | undefined {
|
|
72
|
+
return loadTagPlacements().get(type) ?? undefined;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
76
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** True for a CloudFormation intrinsic (`Ref`, `Fn::*`), which can't be merged into. */
|
|
80
|
+
export function isTagIntrinsic(value: unknown): boolean {
|
|
81
|
+
if (!isPlainObject(value)) return false;
|
|
82
|
+
const keys = Object.keys(value);
|
|
83
|
+
return keys.length === 1 && (keys[0] === "Ref" || keys[0].startsWith("Fn::"));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Read a tag value of either shape into a key-to-value map: a `[{Key, Value}]`
|
|
88
|
+
* list or a plain map. An intrinsic or anything else reads as `{}`.
|
|
89
|
+
*/
|
|
90
|
+
export function readTagValue(value: unknown): Record<string, unknown> {
|
|
91
|
+
const out: Record<string, unknown> = {};
|
|
92
|
+
if (Array.isArray(value)) {
|
|
93
|
+
for (const t of value) {
|
|
94
|
+
if (isPlainObject(t) && typeof t.Key === "string") out[t.Key] = t.Value;
|
|
95
|
+
}
|
|
96
|
+
return out;
|
|
97
|
+
}
|
|
98
|
+
if (isPlainObject(value) && !isTagIntrinsic(value)) return { ...value };
|
|
99
|
+
return out;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* The tags on a resource's property tree, read through the type's own tag
|
|
104
|
+
* property (`UserPoolTags` on a Cognito user pool), falling back to `Tags`
|
|
105
|
+
* when the type is unknown. Either shape reads (#4350).
|
|
106
|
+
*/
|
|
107
|
+
export function readResourceTags(type: string | undefined, properties: Record<string, unknown> | undefined): Record<string, unknown> {
|
|
108
|
+
if (!properties) return {};
|
|
109
|
+
const placement = type ? tagPlacementFor(type) : undefined;
|
|
110
|
+
return readTagValue(properties[placement?.property ?? "Tags"]);
|
|
44
111
|
}
|