@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,17 +1,14 @@
1
1
  # Simulated Cognito IDP
2
2
 
3
- Yulin includes a simulated Cognito user pool directory for tests and local development. Pools and
4
- their app clients are held in memory, and every operation is authorized by simulated IAM.
3
+ Yulin simulates Cognito user pools, app clients, users, groups, tokens, hosted domains and Lambda
4
+ triggers. Use `simAws.cognitoIdp()` directly or intercept a `CognitoIdentityProviderClient`.
5
+ Cognito types are available from `@kensio/yulin/cognito`.
5
6
 
6
- Only user pools are simulated. Cognito identity pools, which exchange a token for AWS credentials,
7
- are a different service and are outside the simulation.
8
-
9
- Cognito-specific types are imported from the `@kensio/yulin/cognito` subpath.
7
+ Cognito identity pools are unsupported.
10
8
 
11
9
  ## Creating a pool and an app client
12
10
 
13
- A pool needs a name. Everything else has a default, and the defaults here are the ones real Cognito
14
- applies.
11
+ A user pool needs a name. Other properties use Cognito's defaults.
15
12
 
16
13
  ```typescript sim-cognito-create-user-pool
17
14
  /**
@@ -46,16 +43,14 @@ const appClient = await cognito.createUserPoolClient(
46
43
  console.log(appClient.UserPoolClient?.ClientId); // 26 lowercase characters
47
44
  ```
48
45
 
49
- A pool id names the region the pool was created in, as real pool ids do. Application code that
50
- splits the id on the underscore to find the region works here for the same reason it works on AWS.
46
+ A pool ID contains the region before its underscore, matching Cognito's ID format.
51
47
 
52
48
  Two pools may share a name. Only the id identifies one.
53
49
 
54
50
  ## Password policy
55
51
 
56
- A pool created without a `Policies` of its own gets the real default of eight characters, with an
57
- uppercase letter, a lowercase letter, a number and a symbol each required. A request setting some of
58
- those keeps the defaults for the rest.
52
+ A pool created without `Policies` requires at least eight characters with uppercase, lowercase,
53
+ numeric and symbol characters. Setting part of the policy keeps the defaults for omitted fields.
59
54
 
60
55
  ```typescript sim-cognito-password-policy
61
56
  /**
@@ -86,10 +81,9 @@ with `InvalidPasswordException`, saying which rule it broke.
86
81
 
87
82
  ## Users
88
83
 
89
- `AdminCreateUser` creates a user in `FORCE_CHANGE_PASSWORD`, where real Cognito leaves a user an
90
- admin made. It has a temporary password and cannot sign in with it. Setting a permanent password
91
- moves the user to `CONFIRMED`. The sign-in flows read that status. A user in
92
- `FORCE_CHANGE_PASSWORD` gets the `NEW_PASSWORD_REQUIRED` challenge, not tokens.
84
+ `AdminCreateUser` creates a user in `FORCE_CHANGE_PASSWORD` with a temporary password. Sign-in
85
+ returns the `NEW_PASSWORD_REQUIRED` challenge until `AdminSetUserPassword` sets a permanent
86
+ password. A permanent password changes the status to `CONFIRMED`.
93
87
 
94
88
  ```typescript sim-cognito-create-user
95
89
  /**
@@ -145,11 +139,9 @@ console.log(read.UserAttributes?.find((each) => each.Name === "sub")?.Value);
145
139
  A password set without `Permanent: true` is temporary, and leaves the user in
146
140
  `FORCE_CHANGE_PASSWORD` again.
147
141
 
148
- A user's `sub` is a UUID Cognito allocates, reported among its attributes. It is not the username,
149
- and code treating the two as interchangeable fails here, and not in a deployment. Admin
150
- operations here name a user by its username only. Real Cognito also accepts a `sub` where an
151
- operation asks for a username. That is one thing that works there and not here. The refusal says so
152
- when the username given is some user's `sub`.
142
+ A user's `sub` is a generated UUID and differs from the username. Yulin's admin operations accept
143
+ the username only. Cognito also accepts a `sub` for some operations, which Yulin reports as an
144
+ unsupported lookup.
153
145
 
154
146
  Attributes come back under `Attributes` from `AdminCreateUser` and `ListUsers`, and under
155
147
  `UserAttributes` from `AdminGetUser`, as the real API names them.
@@ -160,11 +152,9 @@ Attributes come back under `Attributes` from `AdminCreateUser` and `ListUsers`,
160
152
 
161
153
  ## Signing in by email or phone number
162
154
 
163
- A pool created with `UsernameAttributes` signs its users in by that attribute, not by a username
164
- they chose. Cognito generates a UUID as the username for such a user, and the value the
165
- request called the username goes into the attribute the pool signs in by. That generated username
166
- is what `AdminGetUser` reports and what the `cognito:username` claim carries. An application reading
167
- "the username" off such a pool reads a UUID.
155
+ A pool with `UsernameAttributes` signs users in with an email address or phone number. Cognito
156
+ generates a UUID for the stored username. `AdminGetUser` and the `cognito:username` token claim
157
+ report that UUID.
168
158
 
169
159
  A CDK `UserPool` with `signInAliases: { email: true }` emits `UsernameAttributes: ["email"]`, the
170
160
  usual way to build an email sign-in pool.
@@ -384,13 +374,11 @@ outside the simulation.
384
374
 
385
375
  ## Signing up
386
376
 
387
- `SignUp` is the other way a user gets into a pool. It names an app client rather than a pool, is
388
- authorized by no IAM policy, and leaves the user in `UNCONFIRMED` with the password it chose.
377
+ `SignUp` creates an `UNCONFIRMED` user through an app client. The operation does not use IAM
378
+ authorization.
389
379
 
390
- Real Cognito emails or texts a confirmation code at that point. Nothing here delivers a message, and
391
- the code is readable from the pool instead, through `confirmationCode` on the pool object. That is a
392
- deliberate divergence. Real Cognito never reports a code back to anyone, and reading one is what
393
- makes a registration flow testable at all.
380
+ Read the confirmation code from `confirmationCode` on the simulated pool. Cognito sends the code by
381
+ email or text and never exposes it through the service API.
394
382
 
395
383
  The pool also records the message it would have sent, holding the wording and the code a user
396
384
  would have read. That is in [Messages a pool would have sent](#messages-a-pool-would-have-sent)
@@ -500,9 +488,8 @@ the deployed pool would give. A pool created without the setting allows sign-up,
500
488
 
501
489
  ## Resetting a forgotten password
502
490
 
503
- A user that cannot get in asks for a code with `ForgotPassword` and sets a new password with
504
- `ConfirmForgotPassword`. Both name an app client, and neither is authorized by an IAM policy. They
505
- are the pair an application calls when it has built its own sign-in screens.
491
+ `ForgotPassword` sends a code and `ConfirmForgotPassword` sets the new password. Both operations use
492
+ an app client and bypass IAM authorization.
506
493
 
507
494
  The code goes to the same place a sign-up code goes, and is read back the same way, through
508
495
  `confirmationCode` on the pool object. `ForgotPassword` answers with `CodeDeliveryDetails` naming
@@ -633,9 +620,8 @@ are refused for one.
633
620
 
634
621
  ## Messages a pool would have sent
635
622
 
636
- Nothing here delivers an email or a text message. A pool records what it would have sent instead,
637
- and `sentMessages` on the pool object hands the record over. Each message carries the recipient, the
638
- medium, the subject, the body and the occasion it was sent on.
623
+ The pool records outgoing email and text messages in `sentMessages`. Each entry contains the
624
+ recipient, medium, subject, body and message type.
639
625
 
640
626
  A message is recorded on five occasions. Those are a `SignUp`, a `ResendConfirmationCode`, an
641
627
  `AdminCreateUser` that did not ask for `MessageAction: SUPPRESS`, an MFA code sent by text message,
@@ -2060,10 +2046,9 @@ own users at the same address, which real Cognito keeps as two accounts until
2060
2046
  `AdminLinkProviderForUser` merges them. That operation is unsimulated.
2061
2047
 
2062
2048
  Posting the page carries on into the sign-in the authorize endpoint already runs, ending in the same
2063
- `<ProviderName>_<subject>` user and the same authorization code. Nothing is kept on the provider
2064
- afterwards: a further authorize request asks again, because real Cognito asks the provider afresh
2065
- every time. A provider that `signInAs` has already put somebody at skips the page, which is what
2066
- leaves a test that says who is signing in seeing none of this.
2049
+ `<ProviderName>_<subject>` user and the same authorization code. Provider state is discarded after
2050
+ the request. A later authorize request asks again because real Cognito asks the provider afresh each
2051
+ time. A provider configured with `signInAs` skips the page and uses the configured identity.
2067
2052
 
2068
2053
  `/signup` is a link from that page. Its form asks for a username, a password and the attributes the
2069
2054
  pool needs, which are the ones its `Schema` made required and the ones its `AutoVerifiedAttributes`
@@ -3704,8 +3689,8 @@ console.log(described.UserPoolClient?.ClientName);
3704
3689
 
3705
3690
  A registered pool behaves like any other. It answers `DescribeUserPoolCommand` and
3706
3691
  `ListUserPoolsCommand`, holds users, groups and app clients, and serves its JWKS and OpenID
3707
- configuration on localhost. Everything written against a pool id follows from the id it was
3708
- registered under: its ARN, its issuer URL, the `iss` claim of its tokens and its `ProviderName`. A
3692
+ configuration on localhost. The registered ID determines the pool ARN, issuer URL, token `iss` claim
3693
+ and `ProviderName`. A
3709
3694
  policy naming `arn:aws:cognito-idp:eu-west-2:111111111111:userpool/eu-west-2_aBcDeFgHi` authorizes
3710
3695
  the handler that reads the pool, which is what a template carrying the id in two places needs.
3711
3696
 
@@ -1,18 +1,13 @@
1
1
  # Simulated DynamoDB
2
2
 
3
- Yulin includes a simulated DynamoDB for tests and local development. Tables are held in memory, and
4
- every operation is authorized by simulated IAM.
5
-
6
- This page covers creating, describing, listing and deleting tables. What a request says is checked
7
- the way real DynamoDB checks it. A table that can be created here is one that could be created on
8
- AWS.
9
-
10
- DynamoDB-specific types are imported from the `@kensio/yulin/dynamodb` subpath.
3
+ Yulin simulates DynamoDB tables, indexes, items, streams and time to live in memory. Use
4
+ `simAws.dynamoDb()` directly or intercept a `DynamoDBClient`. DynamoDB types are available from
5
+ `@kensio/yulin/dynamodb`.
11
6
 
12
7
  ## Creating a table
13
8
 
14
- `CreateTable` needs a `TableName`, a `KeySchema`, and an `AttributeDefinitions` entry for every
15
- attribute the key schema names.
9
+ `CreateTable` requires a table name, a key schema and an attribute definition for every key
10
+ attribute.
16
11
 
17
12
  ```typescript sim-dynamodb-create-table
18
13
  /**
@@ -45,8 +40,7 @@ await simAws.backgroundTasksComplete();
45
40
  A new table is `CREATING`, and activation is scheduled as background work. Call
46
41
  `simAws.backgroundTasksComplete()` when a test needs the table to be `ACTIVE`.
47
42
 
48
- The description carries back what the request asked for. That is the key schema, the attribute
49
- definitions, the table ARN, a table ID, and the billing and capacity the table was created with.
43
+ The response describes the table's keys, attribute definitions, ARN, ID, billing mode and capacity.
50
44
 
51
45
  ## Key schema and attribute definitions
52
46
 
@@ -62,9 +56,8 @@ attribute as the type the table declared for it.
62
56
 
63
57
  ## Billing modes and throughput
64
58
 
65
- `BillingMode` defaults to `PROVISIONED`, making `ProvisionedThroughput` required with at least one
66
- read and one write capacity unit. A request that leaves both out asks for a provisioned table with
67
- no capacity, and is refused.
59
+ `BillingMode` defaults to `PROVISIONED`. A provisioned table requires at least one read and one write
60
+ capacity unit in `ProvisionedThroughput`.
68
61
 
69
62
  `PAY_PER_REQUEST` refuses `ProvisionedThroughput`, since an on-demand table has no capacity to
70
63
  provision.
@@ -111,8 +104,8 @@ reports for one.
111
104
 
112
105
  ## Global secondary indexes
113
106
 
114
- `GlobalSecondaryIndexes` on `CreateTable` declares indexes with a key of their own over the same
115
- items. Each index needs an `IndexName`, a `KeySchema` and a `Projection`.
107
+ `GlobalSecondaryIndexes` adds indexes over the table's items. Each index requires an `IndexName`,
108
+ `KeySchema` and `Projection`.
116
109
 
117
110
  ```typescript sim-dynamodb-global-secondary-index
118
111
  /**
@@ -583,9 +576,8 @@ console.log(description.Table?.TableStatus); // "ACTIVE"
583
576
 
584
577
  ## Tagging tables
585
578
 
586
- A table is tagged by `CreateTable`, or afterwards by `TagResource`. `UntagResource` takes tags off,
587
- and `ListTagsOfResource` reads them back. The three tag commands name their resource by ARN, in
588
- `ResourceArn`, where the table commands take a name or an ARN.
579
+ Add tags during `CreateTable` or later with `TagResource`. Remove them with `UntagResource` and read
580
+ them with `ListTagsOfResource`. Tag commands require the table ARN in `ResourceArn`.
589
581
 
590
582
  ```typescript sim-dynamodb-tag-table
591
583
  /**
@@ -683,9 +675,8 @@ calling `Tags.of(stack).add("Environment", "test")` gets a tagged table.
683
675
 
684
676
  ## Writing items
685
677
 
686
- `PutItem` writes one item, replacing the whole item under its primary key rather than merging into
687
- it. The item is there by the time the call returns. A write and the read that follows it need no
688
- step in between.
678
+ `PutItem` replaces the complete item stored under the primary key. The write is visible when the
679
+ command returns.
689
680
 
690
681
  ```typescript sim-dynamodb-put-item
691
682
  /**
@@ -739,10 +730,9 @@ anywhere else in the item.
739
730
 
740
731
  ## Reading and deleting items
741
732
 
742
- `GetItem` reads one item by its primary key, and `DeleteItem` removes one the same way. The `Key`
743
- both take is the whole primary key and nothing else. A missing key element, an attribute outside the
744
- key, or a value whose type fails to match the table's `AttributeDefinitions` is a
745
- `ValidationException` naming the attribute at fault.
733
+ `GetItem` reads an item by primary key. `DeleteItem` removes it. The `Key` must contain every key
734
+ attribute and no others, using the types declared in `AttributeDefinitions`. Invalid keys raise
735
+ `ValidationException`.
746
736
 
747
737
  ```typescript sim-dynamodb-get-delete-item
748
738
  /**
@@ -822,9 +812,8 @@ Both take the table's name or its ARN, as the table commands do.
822
812
 
823
813
  ## Updating items
824
814
 
825
- `UpdateItem` changes part of an item, where `PutItem` replaces the whole thing. What to change is
826
- written as an `UpdateExpression` made of `SET`, `REMOVE`, `ADD` and `DELETE` clauses, in any order.
827
- Each keyword appears at most once, and the actions inside a clause are separated by commas.
815
+ `UpdateItem` changes selected attributes. Its `UpdateExpression` may contain `SET`, `REMOVE`, `ADD`
816
+ and `DELETE` clauses in any order. Each clause may appear once and contain comma-separated actions.
828
817
 
829
818
  A `SET` action is `path = operand`, where an operand is a value from `ExpressionAttributeValues`,
830
819
  another document path, or a call to `if_not_exists(path, operand)` or `list_append(one, other)`. Two
@@ -1066,12 +1055,11 @@ placeholder used by either counts as used.
1066
1055
 
1067
1056
  ## Conditional writes
1068
1057
 
1069
- `PutItem`, `DeleteItem` and `UpdateItem` take a `ConditionExpression`, checked against whatever is
1070
- stored under the key before anything changes. A condition that fails to hold leaves the item exactly
1071
- as it was and throws `ConditionalCheckFailedException`, with the name and message real DynamoDB
1072
- uses.
1058
+ `PutItem`, `DeleteItem` and `UpdateItem` evaluate `ConditionExpression` against the stored item before
1059
+ writing. A failed condition leaves the item unchanged and raises
1060
+ `ConditionalCheckFailedException`.
1073
1061
 
1074
- That is how a write becomes an insert if absent, and how a version attribute becomes optimistic
1062
+ Use `attribute_not_exists` for insert-only writes or compare a version attribute for optimistic
1075
1063
  locking.
1076
1064
 
1077
1065
  ```typescript sim-dynamodb-conditional-write
@@ -1176,9 +1164,8 @@ expression, in both directions. A placeholder the request leaves undefined is a
1176
1164
 
1177
1165
  ## Projecting attributes
1178
1166
 
1179
- `GetItem` takes a `ProjectionExpression`, a comma-separated list of document paths. Only those paths
1180
- come back. A path is an attribute name, then any number of `.attribute` dereferences and `[n]` list
1181
- indexes, such as `address.city` or `lines[0].sku`.
1167
+ `ProjectionExpression` is a comma-separated list of document paths returned by `GetItem`. A path can
1168
+ contain map attributes and list indexes, such as `address.city` or `lines[0].sku`.
1182
1169
 
1183
1170
  An attribute name that is a DynamoDB reserved word, or that has a character an expression cannot
1184
1171
  carry, is written as a `#name` placeholder and defined in `ExpressionAttributeNames`.
@@ -1259,12 +1246,11 @@ index and a path past that depth are each a `ValidationException` naming the pat
1259
1246
 
1260
1247
  ## Querying an item collection
1261
1248
 
1262
- A table with a sort key holds an item collection under each partition key, holding the items with
1263
- that partition key, ordered by their sort key. `Query` reads one of those collections.
1249
+ A table with a sort key keeps one ordered item collection per partition key. `Query` reads one
1250
+ collection.
1264
1251
 
1265
- `KeyConditionExpression` says which. It is one equality on the partition key, optionally joined by
1266
- `AND` to one condition on the sort key. The sort key condition is `=`, `<`, `<=`, `>`, `>=`,
1267
- `BETWEEN` or `begins_with`, and both bounds of a `BETWEEN` are inside the range.
1252
+ `KeyConditionExpression` requires equality on the partition key. It may add one sort key condition
1253
+ with `AND`. Sort key operators are `=`, `<`, `<=`, `>`, `>=`, `BETWEEN` and `begins_with`.
1268
1254
 
1269
1255
  ```typescript sim-dynamodb-query
1270
1256
  /**
@@ -1371,8 +1357,8 @@ in `ExpressionAttributeNames`, as in any other expression.
1371
1357
 
1372
1358
  ### Paging a collection
1373
1359
 
1374
- `Limit` counts the items a query evaluated. `LastEvaluatedKey` is the primary key of the item the
1375
- walk stopped on, and the next request passes it back as `ExclusiveStartKey` to resume after it.
1360
+ `Limit` counts evaluated items. Pass `LastEvaluatedKey` back as `ExclusiveStartKey` to continue after
1361
+ the last evaluated item.
1376
1362
 
1377
1363
  ```typescript sim-dynamodb-query-paging
1378
1364
  /**
@@ -1447,9 +1433,7 @@ from a different partition key is refused, since it names a collection this quer
1447
1433
 
1448
1434
  ## Reading a global secondary index
1449
1435
 
1450
- `IndexName` on `Query` and `Scan` reads an index in place of the table. The key condition is held to
1451
- the index key schema and not the table's, which is the point. The index is how an access pattern the
1452
- table key cannot serve gets served.
1436
+ Set `IndexName` on `Query` or `Scan` to read an index. Key conditions then use the index key schema.
1453
1437
 
1454
1438
  ```typescript sim-dynamodb-query-index
1455
1439
  /**
@@ -1572,9 +1556,8 @@ refused.
1572
1556
 
1573
1557
  ## Scanning a table
1574
1558
 
1575
- `Scan` reads every item in a table. It needs no key knowledge at all. That is what makes it the
1576
- operation test setup and assertions reach for, and the wrong operation for most application access
1577
- patterns, since it reads the whole table however few items the caller wanted.
1559
+ `Scan` reads every item in a table without a key condition. It is useful for test assertions but
1560
+ usually reads more data than application code needs.
1578
1561
 
1579
1562
  ```typescript sim-dynamodb-scan
1580
1563
  /**
@@ -1,31 +1,20 @@
1
1
  # Simulated ECR
2
2
 
3
- Yulin includes a simulated Amazon ECR for tests and local development. It holds repositories, and
4
- each repository holds images by tag, where a simulated image is a real in-process handler.
5
-
6
- That is what this service is for. A container image Lambda function cannot run here, because Yulin
7
- never reads an image, so something has to say what the code inside that image actually is. A
8
- repository is the natural place to say it. It is the stable name for the thing that holds the code,
9
- outliving any tag and any CDK construct ID, and a repository holding a registered handler outlives
10
- the stack that declared it too.
3
+ Yulin represents an ECR image with an in-process Lambda handler. Register the handler against a
4
+ repository and tag, then deploy or create a container image function that uses the image URI.
11
5
 
12
6
  ECR-specific types are imported from the `@kensio/yulin/ecr` subpath.
13
7
 
14
8
  ## What a simulated image is
15
9
 
16
- An image URI is only ever an identifier here. No image is pulled or inspected, and no layer,
17
- manifest, digest or scan finding exists. A simulated image is a handler function, registered against
18
- a repository and a tag.
10
+ An image URI identifies a registered handler. Image content remains outside the simulation.
19
11
 
20
- Registering one is a Yulin-native operation, named `simulateImage` to keep it clear of a simulated
21
- `PutImage`. Real `PutImage` takes an image manifest for layers that were pushed over the Docker
22
- registry protocol. None of that happens in this process, and a simulated `PutImage` would be a
23
- command taking an argument nothing could produce.
12
+ Use the Yulin-specific `simulateImage` method to register a handler. The simulated surface has no
13
+ ECR SDK or Docker registry operations.
24
14
 
25
15
  ## Registering a handler as an image
26
16
 
27
- Register the handler once, in test setup, and every function that runs an image from that repository
28
- is created from it.
17
+ Register the handler during test setup. Lambda resolves image functions from the same repository.
29
18
 
30
19
  ```typescript sim-ecr-register-image
31
20
  /**
@@ -82,36 +71,22 @@ console.log(Buffer.from(output.Payload).toString());
82
71
  await simAws.backgroundTasksComplete();
83
72
  ```
84
73
 
85
- Naming a repository is what creates it. A repository holds only its images. There is no prior
86
- declaration to make, and a test can name the repository its templates already point at.
74
+ Calling `repository(name)` creates the repository if it is absent.
87
75
 
88
- The repository takes a bare name such as `orders` or `platform/orders`, and never a full image URI.
89
- The account, the region and the registry host around it come from the simulated ECR the repository
90
- belongs to. A name real ECR would refuse is refused here too.
76
+ Pass a repository name such as `orders` or `platform/orders`. The selected simulated account and
77
+ Region supply the registry host.
91
78
 
92
79
  ## How an image URI is matched
93
80
 
94
- Resolving an image URI happens in two steps, and the tag means something different in each.
95
-
96
- Finding the repository ignores the tag. A function's `Code.ImageUri` is matched on the registry host
97
- and the repository name, with any tag or digest dropped, because no tag is stable enough to write
98
- into a test. A CDK image asset is tagged with the asset content hash, which changes whenever the
99
- image source does, and a pipeline-built image is usually tagged with a git sha or a build number
100
- passed in as a stack parameter. An `ImageUri` built by `Fn::Sub` or from a stack parameter is
101
- matched on what it resolves to.
81
+ Yulin first matches the registry host and repository name. It then looks for the requested tag. A
82
+ registered tag selects that handler. An unknown tag, a digest or no tag selects the most recently
83
+ registered handler. This fallback lets content-hash and build-number tags resolve without copying
84
+ those generated values into the test.
102
85
 
103
- Choosing the image in that repository does read the tag. A tag the repository holds selects exactly
104
- that image, and any other tag, or none at all, falls back to the image registered most recently.
86
+ The registry host determines the account and Region. Repositories with the same name in different
87
+ scopes remain separate. Cross-account image references are supported.
105
88
 
106
- The registry host is part of the match, and the account and the region have to agree. A function can
107
- run an image from another account's repository, as it can on real AWS, and a same-named repository
108
- in another account is a different repository.
109
-
110
- So `orders:blue` runs the handler registered under `blue` where the repository holds one, and the
111
- handler registered most recently otherwise. That is how a blue/green pair of images in one
112
- repository can back two functions differently, while a content hash tag nobody registered still
113
- finds something to run. Registering a tag again both replaces what it held and makes it the most
114
- recent registration.
89
+ Registering a tag again replaces its handler and makes that handler the most recent registration.
115
90
 
116
91
  ```typescript sim-ecr-image-tags
117
92
  /**
@@ -152,18 +127,15 @@ console.log(Buffer.from(output.Payload).toString()); // "blue handler"
152
127
  await simAws.backgroundTasksComplete();
153
128
  ```
154
129
 
155
- A function created directly through `CreateFunction` resolves its image the same way a template
156
- function does, as the example above shows. A function whose image resolves to no handler is refused,
157
- the way real Lambda refuses a function whose image it cannot pull.
130
+ Direct `CreateFunction` calls and CloudFormation deployments use the same resolution rules. Lambda
131
+ refuses an image URI that resolves to no handler.
158
132
 
159
133
  ## Repositories in CloudFormation
160
134
 
161
- `AWS::ECR::Repository` creates a simulated repository. A template declares a repository and never an
162
- image, as real CloudFormation does. A deployed repository starts empty unless a handler has already
163
- been registered in it.
135
+ Simulated CloudFormation creates `AWS::ECR::Repository`. The repository starts empty unless a
136
+ handler was already registered under its name.
164
137
 
165
- `Ref` returns the repository name, and `Fn::GetAtt` exposes `Arn` and `RepositoryUri`. An
166
- application stack can build its function's `ImageUri` from the repository a platform stack declared.
138
+ `Ref` returns the repository name. `Fn::GetAtt` supports `Arn` and `RepositoryUri`.
167
139
 
168
140
  ```typescript sim-ecr-cloudformation-repository
169
141
  /**
@@ -236,34 +208,23 @@ console.log(Buffer.from(output.Payload).toString());
236
208
  await simAws.backgroundTasksComplete();
237
209
  ```
238
210
 
239
- A repository a handler is already registered in is adopted, and the order these happen in makes no
240
- difference. The image can exist before the stack that declares the repository, as it does in real
241
- life.
211
+ A deployment adopts a repository that already contains a registered handler.
242
212
 
243
- Tearing the stack down removes the repository only where it holds no simulated image. One that does
244
- is left where it is, and the deletion is recorded as skipped, because the handler in it was
245
- registered outside any stack and is what every later deploy resolves to. Real ECR also refuses to
246
- delete a repository that still holds images, which fails the stack unless the template says
247
- `EmptyOnDelete`. Here the refusal is recorded and the teardown carries on, since what is being
248
- protected is a test's own registration.
213
+ Stack teardown removes an empty repository. A repository containing a handler remains in place and
214
+ its deletion is recorded as skipped. Teardown continues after the skipped deletion.
249
215
 
250
- Every other property a repository can declare is recorded as an ignored property, and the repository
251
- is created without it. That covers `ImageScanningConfiguration`, `ImageTagMutability`,
216
+ Other repository properties are recorded as ignored. These include `ImageScanningConfiguration`, `ImageTagMutability`,
252
217
  `LifecyclePolicy`, `RepositoryPolicyText`, `EncryptionConfiguration`, `EmptyOnDelete` and `Tags`.
253
218
 
254
219
  ## Where a function's handler comes from
255
220
 
256
- Two things can back a container image function, and a deploy is looked at in this order:
221
+ Yulin resolves a container image function in this order:
257
222
 
258
- 1. An [executable binding](https://yulinsim.dev/services/lambda/#executable-bindings) given to that deploy, including one
259
- naming the image repository. A binding is the more specific thing to have said, since it is about
260
- one deploy.
261
- 2. The simulated ECR repository the function's `Code.ImageUri` names. That is a standing statement
262
- about what the image is, made once and good for every stack that runs it.
223
+ 1. An [executable binding](https://yulinsim.dev/services/lambda/#executable-bindings) supplied for the deployment.
224
+ 2. The handler registered in the ECR repository named by `Code.ImageUri`.
263
225
 
264
- A function with no binding and no registered image is skipped with a diagnostic, and the rest of the
265
- stack deploys. The reason separates a missing repository from an empty one, since those send you to
266
- different places. One is a wrong name, and the other is a handler that was never registered.
226
+ A function with no binding or registered image is skipped. The diagnostic distinguishes a missing
227
+ repository from a repository with no handlers.
267
228
 
268
229
  ## Available functionality
269
230
 
@@ -278,16 +239,14 @@ different places. One is a wrong name, and the other is a handler that was never
278
239
 
279
240
  ## Limitations
280
241
 
281
- Current documented limitations:
282
-
283
242
  - No image content, layer, digest, manifest or scan behaviour is simulated. A repository holds
284
243
  handlers, and no image is ever pulled or inspected.
285
244
  - There are no ECR SDK commands. `CreateRepository`, `DescribeRepositories`, `PutImage`,
286
245
  `DescribeImages`, `BatchDeleteImage` and `GetAuthorizationToken` are all absent. Registering an
287
246
  image is a Yulin-native operation because real `PutImage` takes a manifest for layers pushed over
288
247
  the Docker registry protocol, and that protocol never runs in this process.
289
- - Nothing authorizes against a repository. With no requests to authorize, a repository policy goes
290
- unread and simulated IAM stays out of it.
248
+ - CloudFormation authorizes `ecr:CreateRepository` and `ecr:DeleteRepository`. Handler registration
249
+ has no caller and skips authorization. Repository policies are ignored.
291
250
  - Lifecycle policies go unevaluated, and no simulated image ever expires. Tag mutability goes
292
251
  unenforced, and registering the same tag again replaces what it held.
293
252
  - Naming a repository creates it. There is no `CreateRepository` to fail for a name already taken,
@@ -295,8 +254,8 @@ Current documented limitations:
295
254
  - A stack teardown records the deletion of a repository holding a simulated image and carries on,
296
255
  where real CloudFormation fails the stack unless the template says `EmptyOnDelete`. The repository
297
256
  and its handler are left in place, and `EmptyOnDelete` itself goes unread.
298
- - Nothing tracks which stack created a repository. A repository holding no simulated image is
299
- removed by the teardown of any stack that declared it, and made again by the next deploy.
257
+ - The repository model records no owning stack. Teardown of any declaring stack removes an empty
258
+ repository.
300
259
  - Repository tags, registry policies, pull through cache rules, replication configuration and ECR
301
- Public are not simulated.
302
- - ECR has no HTTP API under `serveSimAws`, and nothing for a Docker client to talk to.
260
+ Public are absent.
261
+ - `serveSimAws` exposes no ECR HTTP API or Docker registry endpoint.