Skip to content

Latest commit

 

History

History
247 lines (176 loc) · 10.7 KB

File metadata and controls

247 lines (176 loc) · 10.7 KB

AGENTS.md

This file provides guidance to AI agents when working with code in this repository.

Project Overview

IAM Floyd is an AWS IAM policy statement generator with a fluent interface. It generates TypeScript classes for all AWS services and their actions, resources, and condition keys from AWS documentation. The project supports both standalone usage (iam-floyd) and AWS CDK integration (cdk-iam-floyd).

Core Architecture

Generated Code Structure

  • lib/generated/policy-statements/ - Generated TypeScript class per AWS service (460+ files)
  • lib/generated/index.ts - Re-exports all service classes
  • lib/generated/aws-managed-policies/ - Generated AWS managed policies
  • lib/shared/ - Hand-written core: PolicyStatement, All, Operator, AccessLevel
  • lib/collection/ - Predefined policy collection utilities
  • lib/generator/ - Scrapes AWS docs and generates lib/generated/ via cheerio + ts-morph

PolicyStatement Inheritance Chain

Built in 10 numbered layers (lib/shared/policy-statement/):

1-base → 2-conditions → 3-actions → 4-resources → 5-effect
       → 6-arn-defaults → 8-principals → 10-final (PolicyStatement)

Each *.CDK.ts file is the CDK variant of that layer (swapped in by bin/mkcdk.ts).

Dual Package Strategy

One codebase produces two npm packages:

  • iam-floyd - Standalone (uses built-in base class)
  • cdk-iam-floyd - Extends aws_iam.PolicyStatement from AWS CDK

bin/mkcdk.ts transforms between variants by swapping *.CDK.ts files and rewriting constructors.

Development Commands

Build

make build           # tsc --build --force tsconfig.main.json
make package         # build + npm pack
make clean           # remove node_modules, *.js, *.d.ts
make install         # clean + npm i

Code Generation

make generate        # generate lib/generated/ from AWS docs (25hr cache)
make generate-force  # NOCACHE=1 - ignores time-based cache
make index-managed-policies  # regenerate AWS managed policies index

Testing

The project has no unit test framework. Tests are integration-style: TypeScript examples are compiled, run, and their output is diffed against stored .result files.

make test            # compile examples/ + diff against *.result files (standalone)
make test-typescript # same, via Test.TypeScript.Makefile
make cdk-test        # CDK test: real deploy + destroy via AWS CDK
make cdk-all         # cdk + install + build + cdk-test

Run a single example test manually:

# 1. Compile a single example
npx tsc -p tsconfig.test-iam-floyd.json

# 2. Run and compare output
node examples/allow/allow.js > /tmp/out.txt
diff /tmp/out.txt examples/allow/allow.result

# Or run the integration test harness directly:
npx ts-node test/main.ts

Regenerate expected results (after intentional changes):

make regenerate-code-example-results

Linting

make eslint          # npx eslint .

CDK Variant

make cdk             # transforms codebase to CDK variant (modifies lib/shared, lib/generated, package.json)
make uncdk           # reverts via git stash (lib/generated, lib/shared, package.json)

Fixing AWS Documentation Errors (lib/generator/fixes.ts)

The generator scrapes live AWS docs, which sometimes contain errors or inconsistencies. fixes.ts is the central place to patch these before code is generated. When the generator produces wrong output, add a fix here rather than editing generated files.

fixes object (keyed by URL slug)

Each top-level key is the URL slug of a service's IAM docs page (e.g. ec2, ssm, 'neptune-db'). Supported sub-keys:

Sub-key Effect
ignore: true Skip generating this service entirely (used for EOL services)
name: 'slug' Override the generated filename and class name (needed when the same service prefix spans multiple doc pages, e.g. pinpointemailserviceses-pinpoint)
service: 'prefix' Override the IAM service prefix used in the generated code
resourceTypes.<name>.arn Replace the ARN template for a resource type with a corrected one
conditions.<key>.key Rewrite the condition key string (used when docs have a concrete example key like RequestTag/tag-key instead of the parametric form RequestTag/${TagKey})
conditions.<key>.methodName Override the generated ifXxx() method name for a condition
conditions.<key>.operator.type Override the inferred operator type (e.g. force date instead of string)
conditions.<key>.operator.override Set typeOverride on the condition (used for custom operator generation)

Exported fixer functions

  • conditionFixer(service, condition) — Normalises condition types (ArrayOfStringstring, longnumeric, etc.) and applies any key/operator overrides from fixes. Logs yellow [L1/L2 fix …] messages to stdout.
  • conditionKeyFixer(service, key) — Rewrites a raw condition key string using any conditions.<key>.key override defined in fixes.
  • arnFixer(service, resource, arn) — Applies a cascade of ARN normalisation rules:
    1. Uppercases the first letter of every ${placeholder} (L1)
    2. Replaces trailing * wildcards with ${ResourceName} (L2)
    3. Deduplicates repeated placeholder names by appending a counter (L2, Rekognition workaround)
    4. Applies the hard-coded ARN override from fixes if present (L3) After fixing, validates the result against the canonical ARN regex and warns if it doesn't match (with a known-good exception list).
  • serviceFixer(service) — Rewrites the IAM service prefix if a service override is defined in fixes.

How to add a fix

  1. Find the service's URL slug from its IAM docs URL (e.g. https://…/list_amazons3.html → slug is s3).
  2. Add an entry to the fixes object in lib/generator/fixes.ts.
  3. Run make generate-force to regenerate with the fix applied.

File Modification Rules

CRITICAL: Never manually edit files in lib/generated/. They are auto-generated from AWS documentation and will be overwritten on next make generate.

Allowed manual edits:

  • lib/shared/ - Core shared classes
  • lib/collection/ - Predefined collections
  • lib/generator/ - Generation logic and fixes
  • bin/ - CLI scripts
  • test/ - Integration test scripts
  • examples/ - Example files and their .result counterparts

Code Style

Formatting (Prettier + ESLint)

  • Indentation: 2 spaces (tabs only in Makefiles)
  • Quotes: Single quotes in TypeScript/JS; double quotes in YAML/JSON
  • Trailing newline: Required on all files
  • No trailing whitespace
  • Line endings: LF (\n)

Run make eslint to check; Prettier is enforced via eslint-plugin-prettier.

TypeScript

Strict settings enforced in tsconfig.json:

strict: true
noImplicitAny: true
strictNullChecks: true
strictPropertyInitialization: true
noUnusedLocals: true
noUnusedParameters: true
noImplicitReturns: true
target: ES2020, module: CommonJS
  • No any: Prefer explicit types or generics.
  • Unused vars: Prefix with _ to suppress (argsIgnorePattern: ^_).
  • Naming conventions: Enforced via @typescript-eslint/naming-convention. Use camelCase for variables/functions, PascalCase for classes/interfaces.
  • Template literals: Prefer `${x}` over 'a' + x (prefer-template: error).
  • Deprecated APIs: deprecation/deprecation rule is set to error — do not use deprecated APIs.
  • No require(): Use ES import/export.

Imports

  • Use named imports where possible: import { Foo } from './foo'
  • Relative imports within lib/; absolute for node_modules
  • lib/generated/ is excluded from ESLint entirely

Fluent Interface Pattern

All service classes return this to allow chaining:

new Statement.S3()
  .allow()
  .toGetObject()
  .on('arn:aws:s3:::my-bucket/*')
  .ifAwsSourceVpc('vpc-123');

JSDoc

  • All public methods in generated files have JSDoc with Access Level, conditions, resources, and an AWS docs URL
  • In lib/shared/, document non-obvious public methods and parameters

Generated Code Conventions

  • export class ServiceName extends PolicyStatement
  • Action methods: toXxx() — e.g., toGetObject(), toListBuckets()
  • Resource methods: onXxx() — e.g., onBucket(), onObject()
  • Condition methods: ifXxx() — e.g., ifAwsSourceIp(), ifS3Prefix()
  • All methods return this for chaining

TypeScript Compilation

  • tsconfig.json - Dev/generation (includes all files, uses SWC via ts-node)
  • tsconfig.main.json - Production build (excludes bin/, lib/generator/, test/, CDK files)
  • tsconfig.test-iam-floyd.json - Compiles examples/ excluding .cdk.ts
  • tsconfig.test-cdk-iam-floyd.json - Compiles examples/**/*.cdk.ts

SWC is used for faster transpilation: "ts-node": { "swc": true } in tsconfig.json.

Git Commit Conventions

Follow conventional commits:

  • feat: description - New features
  • fix: description - Bug fixes
  • chore(deps): description - Dependency updates
  • docs: description - Documentation changes
  • refactor: description - Code refactoring
  • Simple prose for automated updates: "Updates AWS managed policies"

Never commit directly to main. Use a feature branch. Commits may fail spell-checking; add missing words with dict-add <word>.

CI Workflows (.github/workflows/)

  • generate.yml - Daily: scrapes AWS docs, bumps patch version, opens PR with automerge label
  • index-managed-policies.yml - Daily: updates managed policies, opens PR with automerge label
  • test-and-publish.yml - On PR/push to main: make install test-typescript + CDK deploy test + npm publish
  • automerge.yml - Auto-merges PRs labeled automerge after tests pass
  • test-docs.yml - Builds Sphinx docs on docs/** changes