@kensio/yulin 1.20.15 → 1.20.16

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 (95) hide show
  1. package/README.md +13 -0
  2. package/dist/sdk/module/sim-sdk-module-client-interceptor.d.ts +5 -0
  3. package/dist/sdk/module/sim-sdk-module-client-interceptor.js +14 -0
  4. package/dist/sdk/module/sim-sdk-static-client-factory.d.ts +26 -0
  5. package/dist/sdk/module/sim-sdk-static-client-factory.js +59 -0
  6. package/dist/sdk/send-patch.d.ts +8 -0
  7. package/dist/sdk/send-patch.js +11 -0
  8. package/dist/service/aws/caller/sim-aws-caller-resolver.d.ts +28 -6
  9. package/dist/service/aws/caller/sim-aws-caller-resolver.js +18 -11
  10. package/dist/service/aws/caller/sim-aws-caller.d.ts +9 -0
  11. package/dist/service/aws/caller/sim-aws-resolved-caller.d.ts +19 -0
  12. package/dist/service/aws/caller/sim-aws-resolved-caller.js +26 -0
  13. package/dist/service/aws/factory/sim-aws-account-region-service-builder.d.ts +6 -0
  14. package/dist/service/aws/factory/sim-aws-account-region-service-builder.js +3 -0
  15. package/dist/service/aws/factory/sim-aws-account-service-cache.d.ts +6 -0
  16. package/dist/service/aws/factory/sim-aws-account-service-cache.js +5 -0
  17. package/dist/service/aws/factory/sim-aws-service-factory.d.ts +5 -0
  18. package/dist/service/aws/factory/sim-aws-service-factory.js +6 -0
  19. package/dist/service/aws/sim-aws-properties.d.ts +15 -0
  20. package/dist/service/aws/sim-aws.js +2 -1
  21. package/dist/service/iam/authorize/context/sim-iam-auth-z-caller-context-builder.d.ts +30 -7
  22. package/dist/service/iam/authorize/context/sim-iam-auth-z-caller-context-builder.js +20 -11
  23. package/dist/service/iam/authorize/context/sim-iam-auth-z-context-builder.d.ts +2 -1
  24. package/dist/service/iam/authorize/context/sim-iam-auth-z-context-builder.js +5 -1
  25. package/dist/service/iam/authorize/match/sim-iam-policy-principal-matcher.d.ts +10 -0
  26. package/dist/service/iam/authorize/match/sim-iam-policy-principal-matcher.js +15 -3
  27. package/dist/service/iam/authorize/sim-iam-account-auth-z.d.ts +11 -4
  28. package/dist/service/iam/authorize/sim-iam-account-auth-z.js +5 -3
  29. package/dist/service/iam/authorize/sim-iam-authorizer.d.ts +2 -1
  30. package/dist/service/iam/authorize/sim-iam-authorizer.js +1 -0
  31. package/dist/service/iam/role/sim-iam-role-with-policy.factory.d.ts +9 -0
  32. package/dist/service/iam/role/sim-iam-role-with-policy.factory.js +4 -2
  33. package/dist/service/iam/sim-iam-account-parts.d.ts +8 -0
  34. package/dist/service/iam/sim-iam-account-parts.js +1 -0
  35. package/dist/service/sts/auth-z/assume-role-auth-z-coordinator.d.ts +9 -2
  36. package/dist/service/sts/auth-z/assume-role-auth-z-coordinator.js +2 -2
  37. package/dist/service/sts/auth-z/assume-role-source-account-auth-z.d.ts +10 -4
  38. package/dist/service/sts/auth-z/assume-role-source-account-auth-z.js +8 -7
  39. package/dist/service/sts/auth-z/assume-role-target-auth-z.d.ts +2 -2
  40. package/dist/service/sts/auth-z/assume-role-trust-policy-authorizer.d.ts +6 -2
  41. package/dist/service/sts/auth-z/assume-role-trust-policy-authorizer.js +14 -11
  42. package/dist/service/sts/command/assume-role/assume-role.handler.d.ts +5 -1
  43. package/dist/service/sts/command/assume-role/assume-role.handler.js +5 -2
  44. package/dist/service/sts/command/get-caller-identity/get-caller-identity.handler.d.ts +5 -1
  45. package/dist/service/sts/command/get-caller-identity/get-caller-identity.handler.js +4 -1
  46. package/dist/service/sts/service-role/sim-service-role.js +5 -1
  47. package/dist/service/sts/sim-sts.d.ts +9 -1
  48. package/dist/service/sts/sim-sts.js +4 -0
  49. package/docs/README.md +54 -0
  50. package/docs/ai-skill/README.md +69 -0
  51. package/docs/cli/README.md +284 -0
  52. package/docs/factories/README.md +140 -0
  53. package/docs/lint/README.md +176 -0
  54. package/docs/non-aws-dependencies/README.md +261 -0
  55. package/docs/sdk/README.md +272 -0
  56. package/docs/serve/README.md +1126 -0
  57. package/docs/services/acm/README.md +823 -0
  58. package/docs/services/apigateway/README.md +1919 -0
  59. package/docs/services/apigatewayv2/README.md +2678 -0
  60. package/docs/services/athena/README.md +1044 -0
  61. package/docs/services/bedrock/README.md +350 -0
  62. package/docs/services/cloudformation/README.md +3440 -0
  63. package/docs/services/cloudfront/README.md +2874 -0
  64. package/docs/services/cloudwatch/README.md +420 -0
  65. package/docs/services/cognito/README.md +4923 -0
  66. package/docs/services/dynamodb/README.md +3784 -0
  67. package/docs/services/ecr/README.md +302 -0
  68. package/docs/services/ecs/README.md +2010 -0
  69. package/docs/services/elbv2/README.md +1897 -0
  70. package/docs/services/eventbridge/README.md +1110 -0
  71. package/docs/services/firehose/README.md +805 -0
  72. package/docs/services/glue/README.md +501 -0
  73. package/docs/services/iam/README.md +1283 -0
  74. package/docs/services/kinesis/README.md +454 -0
  75. package/docs/services/kms/README.md +659 -0
  76. package/docs/services/lambda/README.md +3645 -0
  77. package/docs/services/logs/README.md +868 -0
  78. package/docs/services/organizations/README.md +574 -0
  79. package/docs/services/personalize/README.md +835 -0
  80. package/docs/services/rekognition/README.md +1070 -0
  81. package/docs/services/route53/README.md +1783 -0
  82. package/docs/services/s3/README.md +3024 -0
  83. package/docs/services/scheduler/README.md +806 -0
  84. package/docs/services/secretsmanager/README.md +667 -0
  85. package/docs/services/ses/README.md +1011 -0
  86. package/docs/services/sns/README.md +1904 -0
  87. package/docs/services/sqs/README.md +1185 -0
  88. package/docs/services/ssm/README.md +1072 -0
  89. package/docs/services/stepfunctions/README.md +1332 -0
  90. package/docs/services/sts/README.md +245 -0
  91. package/docs/services/wafv2/README.md +1378 -0
  92. package/docs/terraform/README.md +248 -0
  93. package/docs/time/README.md +284 -0
  94. package/llms.txt +59 -0
  95. package/package.json +7 -4
@@ -0,0 +1,272 @@
1
+ # Simulated AWS SDK
2
+
3
+ Yulin can intercept AWS SDK clients and route their Commands to simulated AWS services. The code
4
+ under test uses the AWS SDK as it would in production, and needs no knowledge of the simulator.
5
+
6
+ This is the recommended way to test implementation code that already uses the AWS SDK. Direct
7
+ interaction with `SimAws` remains useful for seeding and inspecting simulated state from within
8
+ tests.
9
+
10
+ ## How it works
11
+
12
+ `SimSdk` replaces the `send` method of an intercepted SDK client. Each sent Command is routed by
13
+ name to the matching operation of a simulated AWS service, and the result comes back to the caller
14
+ as a normal SDK response. Every Command is served in process.
15
+
16
+ Every `SimSdk` owns a simulated AWS environment. You can let it create its own, or give it an
17
+ existing one to share:
18
+
19
+ - `new SimSdk()` creates an isolated `SimAws` internally, available as `simSdk.simAws`.
20
+ - `new SimSdk({ simAws })` wraps a `SimAws` you already have.
21
+
22
+ ## Basic usage
23
+
24
+ Intercept an SDK client class, then use the SDK as normal:
25
+
26
+ ```typescript sim-sdk-intercept-s3
27
+ /**
28
+ * Intercepting the S3 SDK client with simulated AWS behind it.
29
+ */
30
+
31
+ import {
32
+ CreateBucketCommand,
33
+ GetObjectCommand,
34
+ PutObjectCommand,
35
+ S3Client,
36
+ } from "@aws-sdk/client-s3";
37
+ import { SimSdk } from "@kensio/yulin/sdk";
38
+
39
+ const simSdk = new SimSdk();
40
+ simSdk.intercept(S3Client); // Intercepts every instance of the class.
41
+
42
+ // From here on, this is ordinary AWS SDK code.
43
+ const s3Client = new S3Client({ region: "eu-west-2" });
44
+ await s3Client.send(new CreateBucketCommand({ Bucket: "foo-bucket" }));
45
+ await s3Client.send(
46
+ new PutObjectCommand({
47
+ Bucket: "foo-bucket",
48
+ Key: "hello.txt",
49
+ Body: "Hello, world!",
50
+ }),
51
+ );
52
+
53
+ const output = await s3Client.send(
54
+ new GetObjectCommand({ Bucket: "foo-bucket", Key: "hello.txt" }),
55
+ );
56
+ console.log(await output.Body?.transformToString()); // "Hello, world!"
57
+
58
+ simSdk.restoreAll();
59
+ ```
60
+
61
+ You can intercept a client class or a client instance:
62
+
63
+ - **A class** (`simSdk.intercept(S3Client)`) intercepts every instance of it, including instances
64
+ the code under test constructs later. This is the most common choice.
65
+ - **An instance** (`simSdk.intercept(s3Client)`) intercepts only that instance. Use it when one
66
+ client should hit the simulator and the others are handled some other way.
67
+
68
+ A client can only have one interception at a time. Intercepting an already-intercepted client
69
+ throws a diagnostic error, and the existing interception stays in place.
70
+
71
+ ## Account and Region scope
72
+
73
+ Each sent Command resolves its own simulated Account and Region scope:
74
+
75
+ 1. The **Region** comes from the sending client's own configuration, such as
76
+ `new S3Client({ region: "eu-west-2" })`, falling back to the simulation default.
77
+ 2. The **Account** comes from the ambient `simAws.runAs(...)` caller when one is set, falling back
78
+ to the simulation default Account.
79
+
80
+ The resolved caller reaches the simulated service, and simulated [IAM](https://yulinsim.dev/services/iam/)
81
+ authorization applies to it exactly as it does for direct sim service use. A caller without
82
+ permission for a Command is denied, as on real AWS. Where no caller can be identified, Commands run
83
+ as the simulation's `defaultCaller`, and as the default Account root where the simulation was given
84
+ none. See
85
+ [Name the caller a simulation uses by default](https://yulinsim.dev/services/iam/#name-the-caller-a-simulation-uses-by-default).
86
+
87
+ `runAs` runs a function with an ambient simulated caller, such as an IAM Role. Commands sent during
88
+ the run are attributed to that caller, with no changes to the client or the code under test:
89
+
90
+ ```typescript sim-sdk-run-as
91
+ /**
92
+ * Attributing intercepted SDK Commands to a caller with runAs.
93
+ */
94
+
95
+ import { CreateRoleCommand, PutRolePolicyCommand } from "@aws-sdk/client-iam";
96
+ import {
97
+ CreateBucketCommand,
98
+ ListBucketsCommand,
99
+ S3Client,
100
+ } from "@aws-sdk/client-s3";
101
+ import { SimAws } from "@kensio/yulin";
102
+ import { SimSdk } from "@kensio/yulin/sdk";
103
+
104
+ const simAws = new SimAws();
105
+ const simSdk = new SimSdk({ simAws });
106
+
107
+ // Seed a Bucket, and a Role allowed to list Buckets, in a simulated Account.
108
+ const account = simAws.account("222222222222");
109
+ await account
110
+ .s3()
111
+ .createBucket(new CreateBucketCommand({ Bucket: "team-bucket" }));
112
+ await account.iam().createRole(
113
+ new CreateRoleCommand({
114
+ RoleName: "TeamRole",
115
+ AssumeRolePolicyDocument: JSON.stringify({
116
+ Version: "2012-10-17",
117
+ Statement: {
118
+ Effect: "Allow",
119
+ Principal: { AWS: "arn:aws:iam::222222222222:root" },
120
+ Action: "sts:AssumeRole",
121
+ },
122
+ }),
123
+ }),
124
+ );
125
+ await account.iam().putRolePolicy(
126
+ new PutRolePolicyCommand({
127
+ RoleName: "TeamRole",
128
+ PolicyName: "list-buckets",
129
+ PolicyDocument: JSON.stringify({
130
+ Version: "2012-10-17",
131
+ Statement: {
132
+ Effect: "Allow",
133
+ Action: "s3:ListAllMyBuckets",
134
+ Resource: "*",
135
+ },
136
+ }),
137
+ }),
138
+ );
139
+
140
+ const s3Client = new S3Client({ region: "us-east-1" });
141
+ simSdk.intercept(s3Client);
142
+
143
+ await simAws.runAs(
144
+ { kind: "arn", arn: "arn:aws:iam::222222222222:role/TeamRole" },
145
+ async () => {
146
+ // Sent as the TeamRole caller: resolved in Account 222222222222 and
147
+ // authorized against the Role's simulated IAM permissions.
148
+ const output = await s3Client.send(new ListBucketsCommand({}));
149
+ console.log(output.Buckets); // [{ Name: "team-bucket" }]
150
+ },
151
+ );
152
+
153
+ simSdk.restoreAll();
154
+ ```
155
+
156
+ The ambient caller belongs to its own `SimAws` instance. Separate simulations in the same process
157
+ each keep their own.
158
+
159
+ ## Restoring interception
160
+
161
+ Restoring puts back the client's real SDK `send`:
162
+
163
+ - `interception.restore()` restores one interception. `simSdk.intercept(...)` returns the handle.
164
+ - `simSdk.restoreAll()` restores everything intercepted through that `SimSdk`.
165
+ - `SimSdk` and interception handles are disposable. `using simSdk = new SimSdk();` restores
166
+ automatically at the end of the scope.
167
+
168
+ ## Choosing Commands to intercept
169
+
170
+ By default every Command sent through an intercepted client is routed to the simulator. To
171
+ intercept only specific Commands, pass an allow list of Command classes or names:
172
+ `simSdk.intercept(s3Client, { commands: [GetObjectCommand] })`. Commands outside the allow list
173
+ throw a diagnostic error.
174
+
175
+ ## The DynamoDB document client
176
+
177
+ `@aws-sdk/lib-dynamodb` takes plain JavaScript values. Application code writes
178
+ `{ id: "a", count: 1 }` where the base client wants `{ id: { S: "a" }, count: { N: "1" } }`.
179
+ Intercept the document client and its Commands reach simulated DynamoDB with the values already
180
+ converted. Code written against the document client runs against the simulator unchanged.
181
+
182
+ ```typescript sim-sdk-document-client
183
+ /**
184
+ * An intercepted DynamoDB document client, writing plain JavaScript values.
185
+ */
186
+
187
+ import { CreateTableCommand, DynamoDBClient } from "@aws-sdk/client-dynamodb";
188
+ import {
189
+ DynamoDBDocumentClient,
190
+ GetCommand,
191
+ PutCommand,
192
+ } from "@aws-sdk/lib-dynamodb";
193
+
194
+ import { SimSdk } from "@kensio/yulin/sdk";
195
+
196
+ using simSdk = new SimSdk();
197
+
198
+ const documents = DynamoDBDocumentClient.from(
199
+ new DynamoDBClient({ region: "eu-west-2" }),
200
+ );
201
+
202
+ // The document client is what gets intercepted, not the client it was built
203
+ // from.
204
+ simSdk.intercept(documents);
205
+
206
+ // A document client forwards a Command it has no document form of, so the
207
+ // table is created through the same client.
208
+ await documents.send(
209
+ new CreateTableCommand({
210
+ TableName: "OrdersTable",
211
+ KeySchema: [{ AttributeName: "orderId", KeyType: "HASH" }],
212
+ AttributeDefinitions: [{ AttributeName: "orderId", AttributeType: "S" }],
213
+ BillingMode: "PAY_PER_REQUEST",
214
+ }),
215
+ );
216
+ await simSdk.simAws.backgroundTasksComplete();
217
+
218
+ await documents.send(
219
+ new PutCommand({
220
+ TableName: "OrdersTable",
221
+ Item: { orderId: "order-1", total: 42, paid: true },
222
+ }),
223
+ );
224
+
225
+ const read = await documents.send(
226
+ new GetCommand({ TableName: "OrdersTable", Key: { orderId: "order-1" } }),
227
+ );
228
+
229
+ console.log(read.Item?.["total"]); // 42
230
+ console.log(read.Item?.["paid"]); // true
231
+ ```
232
+
233
+ `DynamoDBDocumentClient.from(client)` builds a separate object of its own class. Intercepting the
234
+ base client therefore leaves Commands sent through the document client alone. Intercept the
235
+ document client. Both can be intercepted at once, and they reach
236
+ the same simulated tables, since the document client shares the base client's config and so
237
+ resolves the same Account and Region.
238
+
239
+ The real document client converts values in middleware, which runs inside the `send` that
240
+ interception replaces. So the conversion happens at the interception boundary instead, using the
241
+ option defaults `lib-dynamodb` sets (not the `util-dynamodb` ones). Which native types map to which
242
+ descriptors is in [the sim DynamoDB docs](https://yulinsim.dev/services/dynamodb/#the-document-client).
243
+
244
+ ## Supported services and Commands
245
+
246
+ These simulated services support SDK interception: ACM, API Gateway v2, CloudFormation, CloudFront,
247
+ CloudWatch, CloudWatch Logs, Cognito, DynamoDB, DynamoDB Streams, ECS, Elastic Load Balancing v2,
248
+ EventBridge, EventBridge Scheduler, IAM, KMS, Lambda, Rekognition, Route53, S3, Secrets Manager,
249
+ SES, SNS, SQS, SSM, STS and WAFv2. Each service's own docs list the Commands it
250
+ simulates.
251
+
252
+ A gap in that coverage is refused on send, with a different error for each kind:
253
+
254
+ - A Command the simulated service doesn't support throws `SimSdkUnsupportedCommandError`, naming the
255
+ Command and listing the Commands that service does support.
256
+ - A client for an AWS service Yulin doesn't simulate at all throws `SimSdkUnknownServiceError`,
257
+ naming the service. There is no Command list to report, since no simulated service was resolved.
258
+
259
+ ## Limitations
260
+
261
+ - Only `client.send(command)` is intercepted. SDK utilities that bypass `send`, such as
262
+ `getSignedUrl`, run against real AWS. Paginators and waiters go through `send`, so they work. Presigning works without interception. Point the client at the simulated endpoint, as
263
+ [the sim S3 presigned URL docs](https://yulinsim.dev/services/s3/#presigned-urls) show.
264
+ - Simulated errors carry SDK-shaped `name` and `$metadata`, but are not instances of the real SDK
265
+ exception classes. Match an error by its `error.name`. An `instanceof` check against the SDK class
266
+ fails.
267
+ - The callback form of `send(command, callback)` is not supported. Use the promise form.
268
+ - The translate config a document client is built with,
269
+ `DynamoDBDocumentClient.from(client, { marshallOptions, unmarshallOptions })`, is ignored. The
270
+ conversion always uses the defaults. `removeUndefinedValues: true` in particular has no effect
271
+ here, and an `undefined` attribute is refused where AWS would have dropped it. The refusal names
272
+ the attribute and says so.