@intentius/chant-lexicon-aws 0.0.5 → 0.0.8
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/README.md +9 -425
- package/dist/integrity.json +7 -7
- package/dist/manifest.json +1 -1
- package/dist/meta.json +3767 -3706
- package/dist/rules/ext001.ts +2 -4
- package/dist/rules/s3-encryption.ts +2 -3
- package/dist/skills/chant-aws.md +72 -0
- package/dist/types/index.d.ts +57174 -57070
- package/package.json +2 -2
- package/src/codegen/__snapshots__/snapshot.test.ts.snap +18 -18
- package/src/codegen/docs.ts +104 -349
- package/src/codegen/generate.ts +2 -14
- package/src/codegen/package.ts +2 -0
- package/src/codegen/sam.ts +11 -11
- package/src/codegen/typecheck.test.ts +1 -1
- package/src/generated/index.d.ts +57174 -57070
- package/src/generated/index.ts +1356 -1351
- package/src/generated/lexicon-aws.json +3767 -3706
- package/src/import/generator.test.ts +5 -5
- package/src/import/generator.ts +4 -4
- package/src/import/roundtrip-fixtures.test.ts +8 -28
- package/src/import/roundtrip.test.ts +5 -5
- package/src/integration.test.ts +21 -21
- package/src/lint/post-synth/ext001.ts +2 -4
- package/src/lint/rules/rules.test.ts +8 -8
- package/src/lint/rules/s3-encryption.ts +2 -3
- package/src/lsp/completions.ts +2 -0
- package/src/lsp/hover.ts +2 -0
- package/src/nested-stack.ts +1 -1
- package/src/plugin.test.ts +13 -15
- package/src/plugin.ts +126 -149
- package/src/serializer.test.ts +42 -43
- package/src/serializer.ts +7 -16
- package/src/spec/parse.ts +2 -2
- package/dist/skills/aws-cloudformation.md +0 -41
- package/src/codegen/rollback.test.ts +0 -80
- package/src/codegen/rollback.ts +0 -20
package/src/serializer.ts
CHANGED
|
@@ -3,7 +3,6 @@ import { isPropertyDeclarable } from "@intentius/chant/declarable";
|
|
|
3
3
|
import type { Serializer, SerializerResult } from "@intentius/chant/serializer";
|
|
4
4
|
import type { LexiconOutput } from "@intentius/chant/lexicon-output";
|
|
5
5
|
import { walkValue, type SerializerVisitor } from "@intentius/chant/serializer-walker";
|
|
6
|
-
import { toPascalCase } from "@intentius/chant/codegen/case";
|
|
7
6
|
import { isChildProject, type ChildProjectInstance } from "@intentius/chant/child-project";
|
|
8
7
|
import { isStackOutput, type StackOutput } from "@intentius/chant/stack-output";
|
|
9
8
|
|
|
@@ -68,7 +67,7 @@ interface CFOutput {
|
|
|
68
67
|
*/
|
|
69
68
|
function cfnVisitor(entityNames: Map<Declarable, string>): SerializerVisitor {
|
|
70
69
|
return {
|
|
71
|
-
attrRef: (name, attr) => ({ "Fn::
|
|
70
|
+
attrRef: (name, attr) => ({ "Fn::GetAtt": [name, attr] }),
|
|
72
71
|
resourceRef: (name) => ({ Ref: name }),
|
|
73
72
|
propertyDeclarable: (entity, walk) => {
|
|
74
73
|
if (!("props" in entity) || typeof entity.props !== "object" || entity.props === null) {
|
|
@@ -78,26 +77,19 @@ function cfnVisitor(entityNames: Map<Declarable, string>): SerializerVisitor {
|
|
|
78
77
|
const cfProps: Record<string, unknown> = {};
|
|
79
78
|
for (const [key, value] of Object.entries(props)) {
|
|
80
79
|
if (value !== undefined) {
|
|
81
|
-
|
|
82
|
-
cfProps[cfKey] = walk(value);
|
|
80
|
+
cfProps[key] = walk(value);
|
|
83
81
|
}
|
|
84
82
|
}
|
|
85
83
|
return Object.keys(cfProps).length > 0 ? cfProps : undefined;
|
|
86
84
|
},
|
|
87
|
-
transformKey: toPascalCase,
|
|
88
85
|
};
|
|
89
86
|
}
|
|
90
87
|
|
|
91
88
|
/**
|
|
92
89
|
* Convert a value to CF-compatible JSON using the generic walker.
|
|
93
90
|
*/
|
|
94
|
-
function toCFValue(value: unknown, entityNames: Map<Declarable, string
|
|
95
|
-
|
|
96
|
-
if (!convertKeys) {
|
|
97
|
-
// When not converting keys, use a visitor without transformKey
|
|
98
|
-
return walkValue(value, entityNames, { ...visitor, transformKey: undefined });
|
|
99
|
-
}
|
|
100
|
-
return walkValue(value, entityNames, visitor);
|
|
91
|
+
function toCFValue(value: unknown, entityNames: Map<Declarable, string>): unknown {
|
|
92
|
+
return walkValue(value, entityNames, cfnVisitor(entityNames));
|
|
101
93
|
}
|
|
102
94
|
|
|
103
95
|
/**
|
|
@@ -116,8 +108,7 @@ function toProperties(
|
|
|
116
108
|
|
|
117
109
|
for (const [key, value] of Object.entries(props)) {
|
|
118
110
|
if (value !== undefined) {
|
|
119
|
-
|
|
120
|
-
cfProps[cfKey] = toCFValue(value, entityNames, true);
|
|
111
|
+
cfProps[key] = toCFValue(value, entityNames);
|
|
121
112
|
}
|
|
122
113
|
}
|
|
123
114
|
|
|
@@ -231,7 +222,7 @@ function serializeToTemplate(
|
|
|
231
222
|
const logicalName = ref.getLogicalName();
|
|
232
223
|
if (logicalName) {
|
|
233
224
|
const output: CFOutput = {
|
|
234
|
-
Value: { "Fn::
|
|
225
|
+
Value: { "Fn::GetAtt": [logicalName, ref.attribute] },
|
|
235
226
|
};
|
|
236
227
|
if (stackOutput.description) {
|
|
237
228
|
output.Description = stackOutput.description;
|
|
@@ -247,7 +238,7 @@ function serializeToTemplate(
|
|
|
247
238
|
for (const output of outputs) {
|
|
248
239
|
template.Outputs[output.outputName] = {
|
|
249
240
|
Value: {
|
|
250
|
-
"Fn::
|
|
241
|
+
"Fn::GetAtt": [output.sourceEntity, output.sourceAttribute],
|
|
251
242
|
},
|
|
252
243
|
};
|
|
253
244
|
}
|
package/src/spec/parse.ts
CHANGED
|
@@ -36,7 +36,7 @@ export interface ParsedAttribute {
|
|
|
36
36
|
|
|
37
37
|
export interface ParsedPropertyType {
|
|
38
38
|
name: string;
|
|
39
|
-
|
|
39
|
+
specType: string;
|
|
40
40
|
properties: ParsedProperty[];
|
|
41
41
|
}
|
|
42
42
|
|
|
@@ -130,7 +130,7 @@ export function parseCFNSchema(data: string | Buffer): SchemaParseResult {
|
|
|
130
130
|
}
|
|
131
131
|
propertyTypes.push({
|
|
132
132
|
name: `${shortName}_${defName}`,
|
|
133
|
-
|
|
133
|
+
specType: defName,
|
|
134
134
|
properties: defProps,
|
|
135
135
|
});
|
|
136
136
|
}
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: aws-cloudformation
|
|
3
|
-
description: AWS CloudFormation best practices and common patterns
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# AWS CloudFormation with Chant
|
|
7
|
-
|
|
8
|
-
## Common Resource Types
|
|
9
|
-
|
|
10
|
-
- `AWS::S3::Bucket` — Object storage
|
|
11
|
-
- `AWS::Lambda::Function` — Serverless compute
|
|
12
|
-
- `AWS::DynamoDB::Table` — NoSQL database
|
|
13
|
-
- `AWS::IAM::Role` — Identity and access management
|
|
14
|
-
- `AWS::SNS::Topic` — Pub/sub messaging
|
|
15
|
-
- `AWS::SQS::Queue` — Message queue
|
|
16
|
-
- `AWS::EC2::SecurityGroup` — Network firewall rules
|
|
17
|
-
|
|
18
|
-
## Intrinsic Functions
|
|
19
|
-
|
|
20
|
-
- `Sub` — String interpolation with `${}` syntax
|
|
21
|
-
- `Ref` — Reference a resource or parameter
|
|
22
|
-
- `GetAtt` — Get a resource attribute (e.g. ARN)
|
|
23
|
-
- `If` — Conditional value based on a condition
|
|
24
|
-
- `Join` — Join strings with a delimiter
|
|
25
|
-
- `Select` — Pick an item from a list by index
|
|
26
|
-
|
|
27
|
-
## Pseudo Parameters
|
|
28
|
-
|
|
29
|
-
- `AWS::StackName` — Name of the current stack
|
|
30
|
-
- `AWS::Region` — Current deployment region
|
|
31
|
-
- `AWS::AccountId` — Current AWS account ID
|
|
32
|
-
- `AWS::Partition` — Partition (aws, aws-cn, aws-us-gov)
|
|
33
|
-
|
|
34
|
-
## Best Practices
|
|
35
|
-
|
|
36
|
-
1. **Always enable encryption** — Use `BucketEncryption` for S3, `SSESpecification` for DynamoDB
|
|
37
|
-
2. **Block public access** — Set `PublicAccessBlockConfiguration` on all S3 buckets
|
|
38
|
-
3. **Use least-privilege IAM** — Avoid `*` in IAM policy actions and resources
|
|
39
|
-
4. **Enable versioning** — Turn on `VersioningConfiguration` for data buckets
|
|
40
|
-
5. **Use Sub for dynamic names** — `Sub\`\${AWS::StackName}-suffix\`` for unique naming
|
|
41
|
-
6. **Share config via barrel files** — Put common settings in `_.ts` and import as `* as _`
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import { describe, test, expect } from "bun:test";
|
|
2
|
-
import { mkdirSync, writeFileSync, rmSync, readFileSync } from "fs";
|
|
3
|
-
import { join } from "path";
|
|
4
|
-
import { tmpdir } from "os";
|
|
5
|
-
import { snapshotArtifacts, saveSnapshot, restoreSnapshot, listSnapshots } from "./rollback";
|
|
6
|
-
|
|
7
|
-
function makeTempDir(): string {
|
|
8
|
-
const dir = join(tmpdir(), `chant-rollback-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
9
|
-
mkdirSync(dir, { recursive: true });
|
|
10
|
-
return dir;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
describe("rollback", () => {
|
|
14
|
-
test("snapshot captures generated files", () => {
|
|
15
|
-
const dir = makeTempDir();
|
|
16
|
-
const genDir = join(dir, "generated");
|
|
17
|
-
mkdirSync(genDir, { recursive: true });
|
|
18
|
-
|
|
19
|
-
writeFileSync(join(genDir, "lexicon-aws.json"), '{"Bucket":{"kind":"resource"}}');
|
|
20
|
-
writeFileSync(join(genDir, "index.d.ts"), "declare class Bucket {}");
|
|
21
|
-
writeFileSync(join(genDir, "index.ts"), "export const Bucket = {};");
|
|
22
|
-
|
|
23
|
-
const snapshot = snapshotArtifacts(genDir);
|
|
24
|
-
expect(snapshot.files["lexicon-aws.json"]).toBeDefined();
|
|
25
|
-
expect(snapshot.files["index.d.ts"]).toBeDefined();
|
|
26
|
-
expect(snapshot.files["index.ts"]).toBeDefined();
|
|
27
|
-
expect(snapshot.hashes["lexicon-aws.json"]).toBeDefined();
|
|
28
|
-
expect(snapshot.resourceCount).toBe(1);
|
|
29
|
-
|
|
30
|
-
rmSync(dir, { recursive: true, force: true });
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
test("save and list snapshots", () => {
|
|
34
|
-
const dir = makeTempDir();
|
|
35
|
-
const snapshotsDir = join(dir, ".snapshots");
|
|
36
|
-
|
|
37
|
-
const snapshot = {
|
|
38
|
-
timestamp: "2025-01-01T00:00:00.000Z",
|
|
39
|
-
files: { "test.json": "{}" },
|
|
40
|
-
hashes: { "test.json": "abc123" },
|
|
41
|
-
resourceCount: 0,
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
saveSnapshot(snapshot, snapshotsDir);
|
|
45
|
-
const list = listSnapshots(snapshotsDir);
|
|
46
|
-
expect(list.length).toBe(1);
|
|
47
|
-
expect(list[0].timestamp).toBe("2025-01-01T00:00:00.000Z");
|
|
48
|
-
|
|
49
|
-
rmSync(dir, { recursive: true, force: true });
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
test("restore snapshot overwrites generated files", () => {
|
|
53
|
-
const dir = makeTempDir();
|
|
54
|
-
const genDir = join(dir, "generated");
|
|
55
|
-
const snapshotsDir = join(dir, ".snapshots");
|
|
56
|
-
mkdirSync(genDir, { recursive: true });
|
|
57
|
-
|
|
58
|
-
// Write original files
|
|
59
|
-
writeFileSync(join(genDir, "lexicon-aws.json"), '{"original":true}');
|
|
60
|
-
|
|
61
|
-
// Snapshot them
|
|
62
|
-
const snapshot = snapshotArtifacts(genDir);
|
|
63
|
-
const snapshotPath = saveSnapshot(snapshot, snapshotsDir);
|
|
64
|
-
|
|
65
|
-
// Overwrite with new content
|
|
66
|
-
writeFileSync(join(genDir, "lexicon-aws.json"), '{"modified":true}');
|
|
67
|
-
expect(readFileSync(join(genDir, "lexicon-aws.json"), "utf-8")).toBe('{"modified":true}');
|
|
68
|
-
|
|
69
|
-
// Restore
|
|
70
|
-
restoreSnapshot(snapshotPath, genDir);
|
|
71
|
-
expect(readFileSync(join(genDir, "lexicon-aws.json"), "utf-8")).toBe('{"original":true}');
|
|
72
|
-
|
|
73
|
-
rmSync(dir, { recursive: true, force: true });
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
test("listSnapshots returns empty for nonexistent dir", () => {
|
|
77
|
-
const list = listSnapshots("/nonexistent/path");
|
|
78
|
-
expect(list).toHaveLength(0);
|
|
79
|
-
});
|
|
80
|
-
});
|
package/src/codegen/rollback.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Re-export from core with AWS-specific artifact names.
|
|
3
|
-
*/
|
|
4
|
-
import {
|
|
5
|
-
snapshotArtifacts as _snapshotArtifacts,
|
|
6
|
-
saveSnapshot,
|
|
7
|
-
restoreSnapshot,
|
|
8
|
-
listSnapshots,
|
|
9
|
-
} from "@intentius/chant/codegen/rollback";
|
|
10
|
-
export type { ArtifactSnapshot, SnapshotInfo } from "@intentius/chant/codegen/rollback";
|
|
11
|
-
export { saveSnapshot, restoreSnapshot, listSnapshots };
|
|
12
|
-
|
|
13
|
-
const AWS_ARTIFACT_NAMES = ["lexicon-aws.json", "index.d.ts", "index.ts"];
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Snapshot AWS lexicon artifacts.
|
|
17
|
-
*/
|
|
18
|
-
export function snapshotArtifacts(generatedDir: string) {
|
|
19
|
-
return _snapshotArtifacts(generatedDir, AWS_ARTIFACT_NAMES);
|
|
20
|
-
}
|