@kensio/yulin 1.21.6 → 1.21.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.
Files changed (74) hide show
  1. package/README.md +22 -2
  2. package/dist/service/eventbridge/cfn/bus/sim-cfn-event-bus-creator.js +1 -0
  3. package/dist/service/eventbridge/cfn/bus/sim-cfn-event-bus-properties.d.ts +4 -0
  4. package/dist/service/eventbridge/cfn/bus/sim-cfn-event-bus-properties.js +26 -1
  5. package/dist/service/eventbridge/cfn/rule/sim-cfn-event-rule-creator.js +3 -1
  6. package/dist/service/eventbridge/cfn/rule/sim-cfn-event-rule-properties.d.ts +0 -7
  7. package/dist/service/eventbridge/cfn/rule/sim-cfn-event-rule-properties.js +0 -20
  8. package/dist/service/eventbridge/cfn/rule/sim-cfn-event-rule-unsimulated-properties.d.ts +13 -0
  9. package/dist/service/eventbridge/cfn/rule/sim-cfn-event-rule-unsimulated-properties.js +59 -0
  10. package/dist/service/eventbridge/cfn/sim-cfn-event-bridge-resource-error.d.ts +1 -1
  11. package/dist/service/eventbridge/cfn/sim-cfn-event-bridge-resource-error.js +1 -1
  12. package/dist/service/lambda/cfn/event-source-mapping/sim-cfn-lambda-event-source-mapping-properties.js +1 -1
  13. package/dist/service/lambda/cfn/event-source-mapping/sim-cfn-lambda-event-source-mapping-property-rules.d.ts +3 -2
  14. package/dist/service/lambda/cfn/event-source-mapping/sim-cfn-lambda-event-source-mapping-property-rules.js +25 -3
  15. package/dist/service/ses/command/authorize/sim-ses-authorizer.d.ts +9 -0
  16. package/dist/service/ses/command/authorize/sim-ses-authorizer.js +18 -2
  17. package/dist/service/ses/command/send/sim-ses-send-email.js +2 -1
  18. package/dist/service/sns/cfn/topic/sim-cfn-sns-topic-creator.js +1 -0
  19. package/dist/service/sns/cfn/topic/sim-cfn-sns-topic-properties.d.ts +9 -4
  20. package/dist/service/sns/cfn/topic/sim-cfn-sns-topic-properties.js +18 -6
  21. package/dist/service/sns/cfn/topic/sim-cfn-sns-topic-property-names.d.ts +10 -0
  22. package/dist/service/sns/cfn/topic/sim-cfn-sns-topic-property-names.js +16 -1
  23. package/dist/service/ssm/cfn/parameter/sim-cfn-ssm-parameter-creator.js +1 -1
  24. package/dist/service/ssm/cfn/parameter/sim-cfn-ssm-parameter-properties.d.ts +11 -6
  25. package/dist/service/ssm/cfn/parameter/sim-cfn-ssm-parameter-properties.js +18 -9
  26. package/docs/README.md +40 -4
  27. package/docs/ai-skill/README.md +57 -54
  28. package/docs/cli/README.md +84 -94
  29. package/docs/factories/README.md +42 -54
  30. package/docs/lint/README.md +41 -67
  31. package/docs/non-aws-dependencies/README.md +72 -168
  32. package/docs/sdk/README.md +109 -95
  33. package/docs/serve/README.md +192 -898
  34. package/docs/services/acm/README.md +24 -40
  35. package/docs/services/apigateway/README.md +52 -71
  36. package/docs/services/apigatewayv2/README.md +55 -74
  37. package/docs/services/athena/README.md +17 -26
  38. package/docs/services/backup/README.md +29 -39
  39. package/docs/services/bedrock/README.md +38 -52
  40. package/docs/services/cloudformation/README.md +43 -55
  41. package/docs/services/cloudfront/README.md +69 -95
  42. package/docs/services/cloudwatch/README.md +40 -54
  43. package/docs/services/cognito/README.md +30 -45
  44. package/docs/services/dynamodb/README.md +34 -51
  45. package/docs/services/ecr/README.md +36 -77
  46. package/docs/services/ecs/README.md +26 -46
  47. package/docs/services/elbv2/README.md +19 -29
  48. package/docs/services/eventbridge/README.md +24 -20
  49. package/docs/services/firehose/README.md +24 -32
  50. package/docs/services/glue/README.md +41 -78
  51. package/docs/services/iam/README.md +13 -15
  52. package/docs/services/kinesis/README.md +53 -93
  53. package/docs/services/kms/README.md +22 -32
  54. package/docs/services/lambda/README.md +60 -80
  55. package/docs/services/logs/README.md +41 -50
  56. package/docs/services/organizations/README.md +50 -85
  57. package/docs/services/personalize/README.md +28 -44
  58. package/docs/services/rekognition/README.md +26 -38
  59. package/docs/services/route53/README.md +17 -17
  60. package/docs/services/s3/README.md +47 -51
  61. package/docs/services/scheduler/README.md +41 -52
  62. package/docs/services/secretsmanager/README.md +27 -42
  63. package/docs/services/ses/README.md +24 -34
  64. package/docs/services/sns/README.md +31 -33
  65. package/docs/services/sqs/README.md +14 -14
  66. package/docs/services/ssm/README.md +19 -21
  67. package/docs/services/stepfunctions/README.md +18 -20
  68. package/docs/services/sts/README.md +32 -45
  69. package/docs/services/wafv2/README.md +12 -17
  70. package/docs/terraform/README.md +108 -126
  71. package/docs/testing/README.md +228 -0
  72. package/docs/time/README.md +87 -117
  73. package/llms.txt +2 -1
  74. package/package.json +1 -1
@@ -1,18 +1,12 @@
1
1
  # Simulated CloudFront
2
2
 
3
- Yulin includes a simulated CloudFront service for tests and local development.
3
+ Yulin simulates CloudFront distributions, origins, cache behaviour and edge functions. Use
4
+ `simAws.cloudFront()` as part of a simulated AWS environment or create a standalone
5
+ `SimCloudFront`. `serveSimAws` exposes distributions over localhost.
4
6
 
5
- Sim CloudFront can be used directly through `SimAws`, and it can also be served on localhost
6
- alongside other simulated AWS services, so application code can make HTTP requests through a
7
- CloudFront-like layer without talking to real AWS.
7
+ ## Create a distribution
8
8
 
9
- `SimCloudFront` can also be instantiated on its own, in which case it has its own isolated state,
10
- standing apart from any wider simulated AWS environment.
11
-
12
- ## Basic Distribution setup
13
-
14
- Create a simulated AWS environment, add a sim S3 Bucket, and create a sim CloudFront Distribution
15
- pointing at that Bucket.
9
+ Create an S3 bucket, then create a distribution whose origin uses the bucket's REST endpoint.
16
10
 
17
11
  ```typescript sim-cloudfront-distribution-s3-origin
18
12
  /**
@@ -95,19 +89,16 @@ console.log(distributionCreation.Distribution?.DomainName);
95
89
 
96
90
  ## What an S3 Origin can read
97
91
 
98
- An S3 Origin reads its Bucket through the ordinary GetObject command. The Bucket policy decides what
99
- the Distribution can serve. An Origin with no origin access control reads anonymously, the unsigned
100
- request real CloudFront sends to the S3 REST endpoint. An Object has to be publicly readable for the
101
- Distribution to serve it, and a Bucket with no policy answers 403 for every Object.
92
+ An S3 Origin reads its bucket through `GetObject`. The bucket policy decides what the distribution
93
+ can serve. Without an origin access control, CloudFront reads anonymously and the object must be
94
+ publicly readable. A private bucket returns 403.
102
95
 
103
96
  An Origin that does have an origin access control reads as the CloudFront service principal. The
104
97
  Bucket stays private and its policy names the Distribution. See
105
98
  [Origin access controls](#origin-access-controls) for the Bucket policy that takes.
106
99
 
107
- That is what the two commands in the example above do. `PutPublicAccessBlockCommand` opts out of the
108
- block on public Bucket policies, then `PutBucketPolicyCommand` grants `s3:GetObject` to
109
- `Principal: "*"`. The same pair is what a static website Bucket needs, and it is what CDK's
110
- `publicReadAccess: true` generates.
100
+ The example disables the block on public bucket policies, then grants `s3:GetObject` to
101
+ `Principal: "*"`. CDK's `publicReadAccess: true` produces the same result.
111
102
 
112
103
  A denied read reaches the viewer as a 403 from the Origin, and a Distribution's custom error
113
104
  response for 403 replaces it. The usual single-page-app setup, rewriting 403 to `/index.html`,
@@ -118,10 +109,8 @@ an Origin that signs nothing.
118
109
 
119
110
  ## Static sites, default root objects and error pages
120
111
 
121
- A static site behind CloudFront usually leans on two Distribution settings. `DefaultRootObject`
122
- makes a request for the site root return the home page. `CustomErrorResponses` makes a URL that
123
- matches no object return the site's own error page in place of the Origin's. Sim CloudFront applies
124
- both, and a test can assert what a visitor would actually see.
112
+ `DefaultRootObject` maps the distribution root to an object such as `index.html`.
113
+ `CustomErrorResponses` replaces selected Origin errors with another object and status code.
125
114
 
126
115
  ```typescript sim-cloudfront-static-site
127
116
  /**
@@ -242,13 +231,10 @@ try {
242
231
  }
243
232
  ```
244
233
 
245
- The default root object stands in for a request to the root of the Distribution and nothing else. A
246
- request for `/blog/` is passed to the Origin as it arrived, even where that folder holds its own
247
- `index.html`. That is where CloudFront differs from an S3 website index document. The substituted
248
- path is what the rest of request handling sees, and a Cache Behavior pattern and a `viewer-request`
249
- CloudFront Function both act on the object being served. The value names an object at the Origin. It may be a
250
- path such as `public/index.html`, and it must not begin with a forward slash. Sim CloudFront refuses one that does with `InvalidDefaultRootObject`. The alternative would
251
- be a Distribution that answers its own root with a 403.
234
+ The default root object applies only to `/`. A request for `/blog/` reaches the Origin unchanged,
235
+ even if the Origin contains `/blog/index.html`. Cache Behaviors and viewer-request functions see the
236
+ substituted root path. The value may contain a path such as `public/index.html`, but it must not start
237
+ with `/`. Invalid values raise `InvalidDefaultRootObject`.
252
238
 
253
239
  A custom error response replaces the Origin's response when its status matches `ErrorCode`. The
254
240
  codes CloudFront supports are 400, 403, 404, 405, 414, 416, 500, 501, 502, 503 and 504. The response
@@ -276,7 +262,7 @@ status the Distribution serves no page for. See
276
262
 
277
263
  ## Serve simulated CloudFront on localhost
278
264
 
279
- Use `serveSimAws` when you want to make real HTTP requests to the simulated system on localhost.
265
+ Use `serveSimAws` to send HTTP requests through the simulated distribution on localhost.
280
266
 
281
267
  ```typescript serve-sim-cloudfront-localhost
282
268
  /**
@@ -390,15 +376,14 @@ the Distribution behind it. See
390
376
 
391
377
  ## Custom Origins
392
378
 
393
- An Origin with a `CustomOriginConfig` is one CloudFront reaches over HTTP, in place of reading an S3
394
- Bucket. Sim CloudFront resolves its `DomainName` in the simulated environment and serves the request
395
- in process. A Distribution can front a simulated HTTP API endpoint
379
+ An Origin with `CustomOriginConfig` routes requests to another simulated HTTP service. CloudFront
380
+ resolves `DomainName` in the simulated environment and serves the request in process. A distribution
381
+ can front a simulated HTTP API endpoint
396
382
  (`<api-id>.execute-api.<region>.amazonaws.com`), a simulated Lambda Function URL
397
383
  (`<url-id>.lambda-url.<region>.on.aws`), or anything a simulated Route53 record points at one of
398
384
  those.
399
385
 
400
- That covers the common arrangement of one Distribution serving static assets from a Bucket and
401
- sending `/api/*` to an API:
386
+ The following distribution serves static files from S3 and sends `/api/*` to an HTTP API:
402
387
 
403
388
  ```typescript sim-cloudfront-distribution-custom-origin
404
389
  /**
@@ -568,14 +553,13 @@ try {
568
553
  }
569
554
  ```
570
555
 
571
- The Origin domain is resolved when a request is served, and the Distribution and the service behind
572
- its Origin can be created in either order, whichever way round a CloudFormation template happens to
573
- declare them.
556
+ The Origin domain is resolved for each request. The distribution and Origin service may be created
557
+ in either order.
574
558
 
575
559
  `OriginPath` is prefixed to the request path, as it is for an S3 Origin. An Origin path of `/v1`
576
560
  sends a request for `/things` on to `/v1/things`.
577
561
 
578
- Three things follow from the request never leaving the process:
562
+ In-process routing has these limits:
579
563
 
580
564
  - A domain unknown to the simulation fails with an error naming the Origin and the
581
565
  domain. No real request is made to it, and external HTTP Origins are unsupported.
@@ -588,10 +572,9 @@ Three things follow from the request never leaving the process:
588
572
 
589
573
  ## Custom headers on an Origin
590
574
 
591
- CloudFront adds an Origin's custom headers to every request it sends that Origin. An origin that
592
- answers only requests carrying a header nothing else knows is how AWS documents
593
- [restricting a custom origin to CloudFront](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-overview.html#forward-custom-headers-restrict-access),
594
- and a sim Distribution sends them the same way.
575
+ CloudFront adds an Origin's custom headers to every request sent to that Origin. A service can use a
576
+ private header value to
577
+ [restrict access to CloudFront](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-overview.html#forward-custom-headers-restrict-access).
595
578
 
596
579
  The CloudFront API and CloudFormation name the field differently, and both spellings are accepted
597
580
  here. The API has `CustomHeaders` inside an `Origin`, and `AWS::CloudFront::Distribution` has
@@ -741,10 +724,10 @@ An S3 Origin takes the headers and reaches nothing with them. Sim CloudFront rea
741
724
  `GetObject` and builds no HTTP request for a header to travel on, and real S3 ignores a header it
742
725
  has no use for.
743
726
 
744
- ## An Origin declared twice
727
+ ## Duplicate Origins
745
728
 
746
- Two Origins over one domain name are ordinary. They differ by `OriginPath`, by their custom headers
747
- or by how CloudFront connects, and a Behavior points at whichever one it wants.
729
+ Several Origins may use the same domain when their paths, headers, or connection settings differ.
730
+ Each Behavior selects an Origin by ID.
748
731
 
749
732
  Two Origins that match in every property but the `Id` are one Origin written twice. Copying a
750
733
  Behavior and its Origin together, then editing the path pattern, leaves exactly that behind. Sim
@@ -840,9 +823,8 @@ value however they were ordered or cased.
840
823
 
841
824
  ## Viewer certificates
842
825
 
843
- A Distribution with alternate domain names needs an ACM certificate, and CloudFront accepts only
844
- certain ones. Sim CloudFront applies the same rules. A Distribution that real CloudFront would
845
- reject at deploy time is rejected here first, with `InvalidViewerCertificate`:
826
+ A distribution with alternate domain names requires a valid ACM certificate. Yulin raises
827
+ `InvalidViewerCertificate` when:
846
828
 
847
829
  - the certificate must be in `us-east-1`, wherever the rest of your infrastructure lives
848
830
  - the certificate must exist and be `ISSUED`
@@ -912,8 +894,8 @@ as well.
912
894
 
913
895
  ## Disabling and deleting a Distribution
914
896
 
915
- `DeleteDistributionCommand` removes a Distribution. CloudFront will only delete one that has stopped
916
- serving. The sequence is `UpdateDistributionCommand` with `Enabled: false` first, then the deletion. Deleting an enabled Distribution answers `DistributionNotDisabled`, as it does in AWS.
897
+ Disable a distribution with `UpdateDistributionCommand` before deleting it. Deleting an enabled
898
+ distribution raises `DistributionNotDisabled`.
917
899
 
918
900
  `UpdateDistributionCommand` takes a whole `DistributionConfig`, and applies the update as a
919
901
  replacement. Anything left out of the new config is dropped, including alternate domain names and
@@ -1012,17 +994,16 @@ Function code.
1012
994
 
1013
995
  ## Simulated CloudFront Functions
1014
996
 
1015
- The sim CloudFront supports `viewer-request` and `viewer-response` CloudFront Functions.
997
+ Yulin supports `viewer-request` and `viewer-response` CloudFront Functions.
1016
998
 
1017
999
  A `viewer-response` Function runs for an Origin status below 400. CloudFront skips the
1018
1000
  viewer-response event once the Origin has answered 400 or higher (see
1019
1001
  [Limitations](#limitations)), and so does this simulation.
1020
1002
 
1021
- Use `makeCffFunctionCodeInput` to pass a JavaScript handler function to `CreateFunctionCommand`, or
1003
+ Use `makeCffFunctionCodeInput` to pass a JavaScript handler to `CreateFunctionCommand`. Use
1022
1004
  [CloudFormation bindings](https://yulinsim.dev/services/cloudformation/#cloudfront-function-bindings "CloudFront Function bindings usage docs")
1023
- to back a template Resource with one. A handler reference is what to reach for first. It stops on a
1024
- breakpoint and can close over the test's own state. Publish the source itself where the test is
1025
- about the code the Distribution carries, down to the 10 KB it has to fit in. A
1005
+ for a function declared in a template. Handler references support breakpoints and local state. Use
1006
+ source code when the test covers the deployed function or its 10 KB size limit. A
1026
1007
  [Lambda@Edge](#simulated-lambdaedge) function is an ordinary simulated Lambda function, and the same
1027
1008
  choice covers it.
1028
1009
 
@@ -1851,8 +1832,7 @@ parameter holding the version ARN into a support stack in us-east-1. The stack u
1851
1832
  reads that parameter back through a `Custom::CrossRegionStringParameterReader` resource, and the
1852
1833
  Behavior's `LambdaFunctionARN` is an `Fn::GetAtt` on it. Simulated CloudFormation makes that read
1853
1834
  itself, against the Region the resource names, and the association ends up holding the ARN the
1854
- support stack published. Both stacks have to deploy, which is what deploying the whole cloud
1855
- assembly does:
1835
+ support stack published. Deploy the whole cloud assembly to deploy both stacks:
1856
1836
 
1857
1837
  ```typescript
1858
1838
  await simAws.cloudFormation().deployCdkOut("cdk.out");
@@ -2014,8 +1994,8 @@ answered.
2014
1994
  ## Response headers policies
2015
1995
 
2016
1996
  A response headers policy sets headers on everything a cache Behavior serves. Declare one as
2017
- `AWS::CloudFront::ResponseHeadersPolicy` and point a Behavior's `ResponseHeadersPolicyId` at it with
2018
- a `Ref`, which is what CDK's `ResponseHeadersPolicy` construct synthesizes.
1997
+ `AWS::CloudFront::ResponseHeadersPolicy` and reference it from the Behavior's
1998
+ `ResponseHeadersPolicyId`. CDK's `ResponseHeadersPolicy` construct synthesizes this shape.
2019
1999
 
2020
2000
  ```typescript sim-cloudfront-response-headers-policy
2021
2001
  /**
@@ -2206,9 +2186,9 @@ it.
2206
2186
 
2207
2187
  ## Cache policies
2208
2188
 
2209
- A cache policy decides what a cache Behavior keys its cache on and how long an object stays there.
2210
- Declare one as `AWS::CloudFront::CachePolicy` and point a Behavior's `CachePolicyId` at it with a
2211
- `Ref`, which is what CDK's `CachePolicy` construct synthesizes.
2189
+ A cache policy controls the cache key and time to live. Declare one as
2190
+ `AWS::CloudFront::CachePolicy` and reference it from the Behavior's `CachePolicyId`. CDK's
2191
+ `CachePolicy` construct synthesizes this shape.
2212
2192
 
2213
2193
  ```typescript sim-cloudfront-cache-policy
2214
2194
  /**
@@ -2652,8 +2632,8 @@ try {
2652
2632
 
2653
2633
  The Origin's own headers and the cache policy settle the TTL between them, the way they settle it in
2654
2634
  AWS. `s-maxage` is preferred to `max-age`, and `max-age` to `Expires`. Whatever the Origin asks for
2655
- is held between the policy's `MinTTL` and `MaxTTL`. An Origin that asks for nothing gets the greater
2656
- of `MinTTL` and `DefaultTTL`, which is a day on `CachingOptimized`.
2635
+ is held between the policy's `MinTTL` and `MaxTTL`. When the Origin supplies no cache lifetime,
2636
+ CloudFront uses the greater of `MinTTL` and `DefaultTTL`. `CachingOptimized` uses one day.
2657
2637
 
2658
2638
  An `Expires` header has to carry one of the three date formats HTTP allows. Anything else, a
2659
2639
  locale-formatted date included, is read as an object that expired already, the way any HTTP cache
@@ -2878,10 +2858,9 @@ different paths is `InvalidationBatchAlreadyExists`.
2878
2858
 
2879
2859
  ## Origin request policies
2880
2860
 
2881
- An origin request policy decides which of the viewer's headers, cookies and query strings a cache
2882
- Behavior carries to its Origin. Declare one as `AWS::CloudFront::OriginRequestPolicy` and point a
2883
- Behavior's `OriginRequestPolicyId` at it with a `Ref`, which is what CDK's `OriginRequestPolicy`
2884
- construct synthesizes.
2861
+ An origin request policy selects the viewer headers, cookies and query strings sent to the Origin.
2862
+ Declare one as `AWS::CloudFront::OriginRequestPolicy` and reference it from the Behavior's
2863
+ `OriginRequestPolicyId`. CDK's `OriginRequestPolicy` construct synthesizes this shape.
2885
2864
 
2886
2865
  ```typescript sim-cloudfront-origin-request-policy
2887
2866
  /**
@@ -3171,9 +3150,9 @@ Behavior names one without a template creating anything. `AllViewer`
3171
3150
  for it. CDK's `OriginRequestPolicy.ALL_VIEWER` and its seven siblings synthesize those IDs. A stack
3172
3151
  reaching for one deploys.
3173
3152
 
3174
- Each also carries the three sections AWS publishes for it. A Behavior on `AllViewer` here sends the
3175
- Origin everything the viewer sent bar its `Host`, which is the Origin's own domain under every
3176
- policy. One on `CORS-CustomOrigin` sends the `Origin` header alone. One on
3153
+ Each also carries the three sections AWS publishes for it. A Behavior on `AllViewer` sends every
3154
+ viewer value except `Host`. The Origin's domain is used for `Host` under every policy. A Behavior on
3155
+ `CORS-CustomOrigin` sends the `Origin` header alone. One on
3177
3156
  `AllViewerExceptHostHeader` sends what `AllViewer` sends, since the `Host` it withholds was never
3178
3157
  the viewer's here.
3179
3158
 
@@ -3189,10 +3168,9 @@ Behavior, the way an absent cache policy does, and the Behavior reports no polic
3189
3168
 
3190
3169
  ## Origin access controls
3191
3170
 
3192
- An origin access control is how a Distribution authenticates to a private Origin. The Origin then
3193
- admits the Distribution and nothing else. Declare one as `AWS::CloudFront::OriginAccessControl` and
3194
- point an Origin's `OriginAccessControlId` at it with a `Ref`, which is what CDK's
3195
- `S3BucketOrigin.withOriginAccessControl` synthesizes.
3171
+ An origin access control lets a Distribution authenticate to a private Origin. Declare one as
3172
+ `AWS::CloudFront::OriginAccessControl` and reference it from the Origin's `OriginAccessControlId`.
3173
+ CDK's `S3BucketOrigin.withOriginAccessControl` synthesizes this shape.
3196
3174
 
3197
3175
  An `OriginAccessControlOriginType` of `s3` signs for an S3 Bucket Origin, and one of `lambda` signs
3198
3176
  for a Lambda Function URL Origin. The origin type has to match the Origin it is attached to. An `s3`
@@ -3635,11 +3613,9 @@ CloudFront. `await simAws.backgroundTasksComplete()` waits for that.
3635
3613
 
3636
3614
  ### ETags
3637
3615
 
3638
- The key value store commands do check `IfMatch`, where the Distribution and Function commands ignore
3639
- it. Both APIs require it on every write and CloudFront refuses a stale one, which is what stops two
3640
- writers overwriting each other. A write carrying a stale ETag is refused with `PreconditionFailed`,
3641
- and a caller has to thread the ETag through the way it does against CloudFront. Each write returns
3642
- the new ETag for the next one.
3616
+ Key value store commands check `IfMatch`. Distribution and Function commands ignore it. Both key
3617
+ value store APIs require the current ETag for every write. A stale ETag raises
3618
+ `PreconditionFailed`, and each successful write returns the ETag for the next one.
3643
3619
 
3644
3620
  A store has two ETags and they are not interchangeable, as in AWS. Each `DescribeKeyValueStore`
3645
3621
  returns its own. The CloudFront client's versions the store's configuration, and the key value store
@@ -3993,8 +3969,8 @@ Where sim CloudFront knowingly behaves differently from AWS:
3993
3969
  response an `origin-request` function generated has no Origin status behind it, and its own status
3994
3970
  stands in.
3995
3971
  - **CloudFront's disallowed and read-only header lists go unchecked.** Real CloudFront answers 502
3996
- when an edge function adds `Connection` or edits `Content-Length`. Both kinds of function here
3997
- write what they like, apart from the viewer-request `host`, which is restored.
3972
+ when an edge function adds `Connection` or edits `Content-Length`. Yulin accepts those changes.
3973
+ It restores the `host` after a viewer-request function.
3998
3974
  - **An S3 Origin with no origin access control reads its Bucket anonymously.** That is the unsigned
3999
3975
  request real CloudFront sends to the S3 REST endpoint without one. The Bucket policy has to make
4000
3976
  an Object publicly readable for the Distribution to serve it. A legacy
@@ -4020,9 +3996,9 @@ Where sim CloudFront knowingly behaves differently from AWS:
4020
3996
  are absent, and `AWS::CloudFront::OriginAccessControl` is the only way to make one.
4021
3997
  - **A list's `Quantity` is only checked when it is there.** Every CloudFront list carries a count
4022
3998
  alongside its items, and a `Quantity` that disagrees with `Items` is refused with
4023
- `InconsistentQuantities`, as CloudFront refuses it. A list arriving as a plain array, which is the
4024
- CloudFormation shape, has no count to disagree with, and a template goes unchecked this way. So
4025
- does a hand-written `{ Items: [...] }` with the count left out. The AWS SDK types make omitting
3999
+ `InconsistentQuantities`, as CloudFront refuses it. CloudFormation uses a plain array without a
4000
+ count, so templates skip this validation. A hand-written `{ Items: [...] }` also skips it when the
4001
+ count is absent. The AWS SDK types make omitting
4026
4002
  `Quantity` a compile error, so what arrives without one is a different mistake from the one this
4027
4003
  catches.
4028
4004
  - **A web ACL a Distribution names has to exist here.** `WebACLId` resolves to a web ACL created in
@@ -4075,11 +4051,9 @@ Where sim CloudFront knowingly behaves differently from AWS:
4075
4051
  share of real responses carry `Server-Timing`. This simulation adds it to every response once
4076
4052
  `Enabled` is true. A test asserting on it never depends on chance. The header's value is a
4077
4053
  fixed placeholder, since nothing here measures an Origin fetch the way CloudFront's edge does.
4078
- - **A viewer's `Host` header never reaches an Origin.** Real CloudFront forwards it where a policy
4079
- names it, which is what `AllViewerExceptHostHeader` exists to stop. Here an Origin request always
4080
- carries the Origin's own domain as `Host`, since the request has to reach the simulated service
4081
- its URL names.
4082
- - **CloudFront's own headers are not generated.** `X-Amz-Cf-Id`, `Via`, `X-Forwarded-For` and the
4083
- `CloudFront-Viewer-*` family are absent from an Origin request, so a policy naming one of them
4084
- forwards nothing. `Host`, `User-Agent` and the normalized `Accept-Encoding` are the three this
4085
- simulation sends of its own accord.
4054
+ - **Origin requests use the Origin's `Host` header.** Real CloudFront can forward the viewer's value
4055
+ when a policy includes it. Yulin always sends the Origin domain because the request must reach the
4056
+ simulated service named by the URL.
4057
+ - **CloudFront-generated headers are absent.** Yulin omits `X-Amz-Cf-Id`, `Via`,
4058
+ `X-Forwarded-For` and the `CloudFront-Viewer-*` family. It generates `Host`, `User-Agent` and a
4059
+ normalized `Accept-Encoding` value.
@@ -1,20 +1,11 @@
1
1
  # Simulated CloudWatch Metrics
2
2
 
3
- Yulin includes a simulated Amazon CloudWatch for tests and local development. It holds custom
4
- metrics. Those are the datapoints `PutMetricData` publishes, and the statistics
5
- `GetMetricStatistics` and `GetMetricData` read back from them, without an AWS account. It also holds alarms over those
6
- metrics, which evaluate on the simulation's clock and notify an SNS topic when they change state.
7
-
8
- Code that publishes a business metric is code teams already have, and until now the only way to test
9
- it was to assert that the SDK client had been called. That proves the call was made. What it
10
- measured goes untested.
11
-
12
- Most of what lives here is custom metrics. Simulated Lambda publishes its own `AWS/Lambda`
13
- `Invocations`, `Errors`, `Duration` and `IteratorAge`, simulated Cognito publishes a pool's
14
- `AWS/Cognito` counts, and no other simulated service publishes into an `AWS/` namespace yet. A
15
- query for one nothing measures comes back empty, in place of a number that was never taken. A test
16
- can stand a datapoint up for one of those itself, which is what drives an alarm on a service metric
17
- to a state change.
3
+ Yulin simulates CloudWatch metrics and alarms in memory. Application code can publish datapoints and
4
+ read their statistics through the normal CloudWatch commands. Alarms evaluate on simulated time and
5
+ can notify simulated SNS topics.
6
+
7
+ Simulated Lambda publishes `AWS/Lambda` metrics, and simulated Cognito publishes `AWS/Cognito`
8
+ metrics. Tests can seed other AWS-managed metrics through the service writer described below.
18
9
 
19
10
  A custom metric's datapoints arrive either from `PutMetricData` or from a CloudWatch Logs metric
20
11
  filter counting matching log events. See the [CloudWatch Logs docs](../logs/README.md) for the
@@ -24,8 +15,8 @@ CloudWatch specific types are imported from the `@kensio/yulin/cloudwatch` subpa
24
15
 
25
16
  ## Publishing and reading back a metric
26
17
 
27
- A metric is identified by its namespace, its name and its dimensions together. Publishing a value
28
- and asking for it back at a period is the whole loop:
18
+ A metric is identified by its namespace, name, and exact set of dimensions. Publish a value and
19
+ read it back over a period:
29
20
 
30
21
  ```typescript sim-cloudwatch-publish-and-read
31
22
  /**
@@ -84,18 +75,14 @@ the way out. A query naming a unit CloudWatch lacks fails here as it would in an
84
75
 
85
76
  ## Metrics are identified by their dimensions
86
77
 
87
- Real CloudWatch leaves a custom metric unrolled across its dimensions, and so does this. The same
88
- metric name published under two channels is two metrics. A read naming no dimensions reaches only the
89
- metric that was published with none, and never aggregates across the others.
90
-
91
- That is the behaviour teams most often get wrong, and it is worth a test of its own. A dashboard
92
- query written against a metric name alone finds nothing at all if every publish carried a dimension.
78
+ CloudWatch treats each exact set of dimensions as a separate metric. Publishing the same metric name
79
+ with two dimension values creates two metrics. A query without dimensions reads only datapoints
80
+ published without dimensions.
93
81
 
94
82
  ## Metrics and simulated time
95
83
 
96
- A datum carrying no `Timestamp` is stamped from the simulation's clock. A test with
97
- a frozen clock therefore gets timestamps it can assert on exactly, and one that moves time on gets
98
- datapoints in the period it moved to:
84
+ A datum without a `Timestamp` uses the simulation's clock. Set or advance the clock to place
85
+ datapoints in exact periods:
99
86
 
100
87
  ```typescript sim-cloudwatch-simulated-time
101
88
  /**
@@ -153,11 +140,14 @@ window drops a metric out of the listing without anything having to expire it.
153
140
 
154
141
  ## Metrics a simulated service publishes
155
142
 
156
- Real CloudWatch holds two kinds of metric. A caller publishes a custom one through `PutMetricData`, and AWS publishes its own under a namespace beginning `AWS/` that no caller may write into. Both kinds are read the same way.
143
+ Callers publish custom metrics through `PutMetricData`. AWS-managed metrics use namespaces beginning
144
+ with `AWS/`, which callers cannot publish into.
157
145
 
158
- Two services publish their own. Simulated Cognito counts what a user pool was asked to do, under `AWS/Cognito` and dimensioned by `UserPool` and `UserPoolClient`, which the [Cognito docs](../cognito/README.md) cover.
146
+ Simulated Cognito publishes counts under `AWS/Cognito`, dimensioned by `UserPool` and
147
+ `UserPoolClient`. See the [Cognito docs](../cognito/README.md).
159
148
 
160
- Simulated Lambda publishes three of its own. Every invocation counts `Invocations` and a `Duration`, and one whose handler threw counts an `Errors` alongside them, all under `AWS/Lambda` and dimensioned by `FunctionName`. Nothing has to be turned on, and no execution Role needs a permission for it, which is how real Lambda behaves.
149
+ Simulated Lambda publishes `Invocations`, `Duration`, and `Errors` under `AWS/Lambda`, dimensioned by
150
+ `FunctionName`. These metrics require no extra configuration or execution-role permission.
161
151
 
162
152
  ```typescript sim-cloudwatch-lambda-errors
163
153
  /**
@@ -217,13 +207,15 @@ const { MetricAlarms } = await simAws
217
207
  console.log(MetricAlarms?.[0]?.StateValue);
218
208
  ```
219
209
 
220
- `Duration` is measured on the simulation's clock rather than the host's, so it is a number a test can assert on. A handler that moves the clock reports the time it moved, and one that returns without touching it reports nothing spent. `IteratorAge` is measured the same way, and the Lambda documentation covers what a stream event source mapping reports.
210
+ `Duration` uses the simulation's clock rather than the host's. A handler that advances the clock
211
+ reports that elapsed time, while a handler that leaves it unchanged reports zero duration.
212
+ `IteratorAge` uses the same clock. The Lambda documentation describes the value reported by a stream
213
+ event source mapping.
221
214
 
222
215
  ## Seeding a metric AWS publishes
223
216
 
224
- `PutMetricData` refuses a namespace beginning `AWS/`, exactly as an account does. An alarm watching a metric no simulated service publishes therefore sits in `INSUFFICIENT_DATA`, and its arithmetic goes untested however carefully the alarm itself is declared.
225
-
226
- The service writer is the way in. It is the same route simulated Lambda's own metrics take, reached from a test through `cloudWatch().serviceWriter()`, and it stands a datapoint up in any namespace.
217
+ `PutMetricData` refuses namespaces beginning with `AWS/`. To test an alarm for another AWS-managed
218
+ metric, add the datapoint through `cloudWatch().serviceWriter()`.
227
219
 
228
220
  ```typescript sim-cloudwatch-seed-service-metric
229
221
  /**
@@ -286,14 +278,13 @@ console.log(MetricAlarms?.[0]?.StateValue);
286
278
 
287
279
  A datapoint arriving without a `timestamp` is stamped with the simulation's clock. One carrying its own lands where it says, which fills a window without the clock having to be walked through it.
288
280
 
289
- `PutMetricData` is untouched by any of this. A caller naming a reserved namespace is refused exactly as before, and only the account's own machinery and a test reach the store this way.
281
+ The service writer is a test setup API. `PutMetricData` keeps its reserved-namespace validation.
290
282
 
291
283
  ## Alarms
292
284
 
293
- An alarm watches one metric and changes state on the simulation's clock, with no real timer behind
294
- it. Each evaluation is scheduled at the next period boundary. A frozen clock evaluates nothing, and
295
- advancing time by twenty minutes walks twenty one-minute evaluations and settles before the next
296
- line of the test runs.
285
+ An alarm watches one metric and changes state on the simulation's clock. Each evaluation is scheduled
286
+ at the next period boundary. Advancing time by twenty minutes runs twenty one-minute evaluations and
287
+ settles before the next line of the test.
297
288
 
298
289
  ```typescript sim-cloudwatch-alarm
299
290
  /**
@@ -353,8 +344,8 @@ console.log(described.MetricAlarms?.at(0)?.StateValue);
353
344
  ```
354
345
 
355
346
  A new alarm is in `INSUFFICIENT_DATA` until it has evaluated a period, as on real CloudWatch. The
356
- window it looks back over reaches behind the moment the alarm was created. An alarm over a metric
357
- nothing publishes into, with `TreatMissingData: "breaching"`, therefore fires on its first
347
+ window it looks back over reaches behind the moment the alarm was created. An alarm over an empty
348
+ metric, with `TreatMissingData: "breaching"`, therefore fires on its first
358
349
  evaluation, without waiting for the periods to accumulate. That is what an account does too.
359
350
 
360
351
  ### Reaching a subscriber
@@ -458,10 +449,9 @@ had never named them.
458
449
 
459
450
  ## Permissions
460
451
 
461
- CloudWatch metrics have no ARN, leaving a policy nothing to name. Every metric action here is
462
- granted on `*`. A policy written against something like
463
- `arn:aws:cloudwatch:eu-west-2:111111111111:metric/Orders/Failed` reaches nothing, here and in an
464
- account.
452
+ CloudWatch metrics have no ARN, so every metric action uses a resource of `*`. A policy written
453
+ against a fabricated metric ARN such as
454
+ `arn:aws:cloudwatch:eu-west-2:111111111111:metric/Orders/Failed` is invalid in Yulin and AWS.
465
455
 
466
456
  Alarms are the exception, and do have an ARN. `PutMetricAlarm`, `DeleteAlarms` and `SetAlarmState`
467
457
  authorize against `arn:aws:cloudwatch:<region>:<account>:alarm:<name>`, while `DescribeAlarms` and
@@ -525,7 +515,7 @@ await simAws.cloudWatch().putMetricData(
525
515
  // Publishing into any other namespace as this Role is denied.
526
516
  ```
527
517
 
528
- ## What is simulated
518
+ ## Supported operations
529
519
 
530
520
  - `PutMetricData`, with `Value`, `StatisticValues` and `Values`/`Counts`.
531
521
  - `ListMetrics`, filtered by namespace, metric name and dimensions, with `RecentlyActive` and
@@ -546,11 +536,9 @@ await simAws.cloudWatch().putMetricData(
546
536
  - `AWS::CloudWatch::Alarm` in simulated CloudFormation, deployed through `PutMetricAlarm` and taken
547
537
  down with the stack.
548
538
 
549
- ## What is refused, and how it says so
539
+ ## Unsupported operations and options
550
540
 
551
- Anything real CloudWatch would accept and this leaves undone is refused with a message saying so,
552
- rather than accepted and ignored. A silently dropped filter is worse than a failure, because the
553
- test still passes and no longer means what it says.
541
+ Yulin rejects unsupported CloudWatch behavior instead of ignoring it:
554
542
 
555
543
  - **Composite and anomaly detection alarms.** `Metrics` and `ThresholdMetricId` on `PutMetricAlarm`
556
544
  are refused. There is no trained model here for an anomaly band to come from.
@@ -571,8 +559,6 @@ test still passes and no longer means what it says.
571
559
  through the service writer. A CloudWatch Logs metric filter naming a reserved namespace is refused
572
560
  when it publishes, as `PutMetricData` refuses a caller naming one.
573
561
 
574
- Two divergences are deliberate, and not refusals. Real CloudWatch rejects a datapoint more than two
575
- weeks old or more than two hours in the future, and this accepts any timestamp, letting a test seed
576
- a window without arranging the clock around it. And datapoints come back earliest first, which real
577
- CloudWatch's contract permits without promising, because a test reading the third period of five
578
- needs an order it can rely on.
562
+ Two behaviors differ from AWS. Yulin accepts datapoints more than two weeks old or more than two
563
+ hours in the future, which makes it easier to seed a test window. It also returns datapoints in
564
+ ascending timestamp order so tests receive deterministic results.