@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,24 +1,17 @@
1
- # Linting CFF JS2
1
+ # Lint CloudFront Functions code
2
2
 
3
- CloudFront Functions run JS2, ECMAScript 5.1 with a named subset of ES 6 to 12 on top, rather than
4
- a current JavaScript engine. A class or a `for...of` is a syntax error, and CloudFront refuses the
5
- code when you upload it. A call to `fetch` parses, and then fails at the edge, where that global is
6
- absent and there is no network to reach. Both are a long way from where the code was written, and
7
- Yulin publishes lint configs that refuse the same things in the editor.
3
+ Yulin provides ESLint and Oxlint configs for the CloudFront Functions JavaScript 2.0 runtime. Both
4
+ configs apply to `**/*.cff.js`, the filename pattern used by
5
+ [simulated CloudFront](https://yulinsim.dev/services/cloudfront/ "Simulated CloudFront usage docs").
8
6
 
9
- The configs apply to `**/*.cff.js` files. That is the naming
10
- [sim CloudFront](https://yulinsim.dev/services/cloudfront/ "Simulated CloudFront usage docs") already uses for
11
- CloudFront Function source.
12
-
13
- Every restriction comes from the runtime's
14
- [own feature list](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/functions-javascript-runtime-20.html),
15
- an allow-list, and anything outside it is unsupported. None of it is house style. A rule
16
- banning syntax JS2 accepts would send you away from code that works.
7
+ The runtime is ECMAScript 5.1 with selected features from ES 6 to 12. The lint rules follow AWS's
8
+ [runtime feature list](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/functions-javascript-runtime-20.html).
9
+ They report unsupported syntax and globals before CloudFront rejects the function.
17
10
 
18
11
  ## Setting it up with ESLint
19
12
 
20
- `@kensio/yulin/eslint` exports a flat config to spread into your own. It restricts itself to
21
- `**/*.cff.js`. The rest of your config still applies everywhere else.
13
+ `@kensio/yulin/eslint` exports a flat config. Spread it into your config after any general rules it
14
+ needs to override:
22
15
 
23
16
  ```typescript cff-js2-eslint-config
24
17
  /**
@@ -38,18 +31,15 @@ export default defineConfig(
38
31
  );
39
32
  ```
40
33
 
41
- It goes after any config whose rules it needs to turn off. That is a short list. `const`, `let`,
42
- template literals, arrow functions, rest parameters and `async`/`await` all work in JS2, and the
43
- rules asking for them stay on. Only `object-shorthand` is switched off, because shorthand property
44
- names are ES 6 literal syntax the runtime lacks.
34
+ The config keeps rules for syntax that JS2 supports, including `const`, `let`, template literals,
35
+ arrow functions, rest parameters, and `async`/`await`. It switches off `object-shorthand` because
36
+ JS2 does not support shorthand property names.
45
37
 
46
- `eslint` and `typescript-eslint` are optional peer dependencies, needed only if you use this export.
38
+ Install the optional `eslint` and `typescript-eslint` peer dependencies when you use this export.
47
39
 
48
40
  ## Setting it up with Oxlint
49
41
 
50
- `@kensio/yulin/oxlint` ships the same rules as an Oxlint config fragment to extend. The rules
51
- themselves are one plugin loaded by both linters. The two configs report the same things in the same
52
- places.
42
+ Extend Yulin's generated Oxlint config by its path in `node_modules`:
53
43
 
54
44
  ```json
55
45
  {
@@ -60,16 +50,14 @@ places.
60
50
  }
61
51
  ```
62
52
 
63
- Oxlint's `extends` takes a file path rather than a package name. The path into `node_modules` is
64
- written out in full. The file sits in the package root. A build that moves what it emits under
65
- `dist/` leaves the path alone. The fragment brings its own `overrides` entry scoped to `**/*.cff.js`
66
- and the JS plugin the rules live in, and leaves every other file to your own rules.
53
+ Oxlint's `extends` setting takes a file path. The fragment loads Yulin's JavaScript plugin and scopes
54
+ its rules to `**/*.cff.js`. Your own rules continue to apply to other files.
67
55
 
68
- The plugin is loaded through Oxlint's JS plugin support, which needs Oxlint 1.77 or later.
56
+ This config requires Oxlint 1.77 or later because it uses JavaScript plugin support.
69
57
 
70
58
  ## What is reported
71
59
 
72
- Each restriction is its own rule, under the `cff-js2` name in both linters.
60
+ Both configs load the same rules under the `cff-js2` name:
73
61
 
74
62
  | Rule | What it refuses |
75
63
  | ------------------------------- | -------------------------------------------------------- |
@@ -83,20 +71,16 @@ Each restriction is its own rule, under the `cff-js2` name in both linters.
83
71
  | `cff-js2/no-for-of` | `for...of`, in favour of an index-based loop |
84
72
  | `cff-js2/no-unavailable-global` | Globals the runtime does not have, such as `fetch` |
85
73
 
86
- `cff-js2/no-unavailable-global` covers three groups. `fetch`, `XMLHttpRequest` and `WebSocket` need
87
- a network the runtime lacks. `process` needs Node.js. `setTimeout`, `setInterval`, `setImmediate`
88
- and `clearTimeout` need an event loop a Function never gets. The rule resolves names through scope
89
- rather than matching them as text, and a local variable named `fetch` or a property named
90
- `event.fetch` is left alone. Each report says why the global is missing, because `fetch` being
91
- absent for want of a network and `setTimeout` for want of an event loop call for different rewrites.
74
+ `cff-js2/no-unavailable-global` reports browser network APIs such as `fetch`, Node.js globals such as
75
+ `process`, and timer functions such as `setTimeout`. It resolves variables through JavaScript scope.
76
+ A local variable named `fetch` and a property such as `event.fetch` are allowed.
92
77
 
93
- Alongside these, both configs turn on `no-eval`, `no-new-func` and `no-implied-eval`, which the
94
- runtime refuses outright. They also set `no-unused-vars` to leave `handler` alone, since it is the
95
- entry point CloudFront calls.
78
+ The configs also enable `no-eval`, `no-new-func`, and `no-implied-eval`. Their `no-unused-vars`
79
+ setting ignores the exported `handler` function because CloudFront calls it as the entry point.
96
80
 
97
- ## What is not reported
81
+ ## Supported JS2 syntax
98
82
 
99
- These all work in JS2 and no rule here objects to them:
83
+ The configs allow these JS2 features:
100
84
 
101
85
  - Template literals, including interpolation and nesting
102
86
  - Arrow functions and rest parameters
@@ -110,7 +94,7 @@ These all work in JS2 and no rule here objects to them:
110
94
 
111
95
  ## Turning one restriction off
112
96
 
113
- Rules are individual. A restriction you disagree with can be switched off on its own. In ESLint:
97
+ You can switch off one restriction without disabling the rest. For ESLint:
114
98
 
115
99
  ```typescript cff-js2-eslint-relax
116
100
  /**
@@ -129,7 +113,7 @@ export default defineConfig(...cloudFrontFunctionsJs2, {
129
113
  });
130
114
  ```
131
115
 
132
- In Oxlint, the same thing goes in an `overrides` entry after the `extends`:
116
+ For Oxlint, add an `overrides` entry after `extends`:
133
117
 
134
118
  ```json
135
119
  {
@@ -147,30 +131,20 @@ In Oxlint, the same thing goes in an `overrides` entry after the `extends`:
147
131
 
148
132
  ## Available functionality
149
133
 
150
- - A flat ESLint config at `@kensio/yulin/eslint`, exported as `cloudFrontFunctionsJs2`
151
- - An Oxlint config fragment at `cffjs2.oxlintrc.json` in the package root, with the object it is
152
- generated from exported as `cloudFrontFunctionsJs2Oxlint` from `@kensio/yulin/oxlint`
153
- - Nine `cff-js2` rules, one per restriction, shared by both linters
154
- - Scoping to `**/*.cff.js`, leaving a repository's own rules untouched elsewhere
134
+ - `@kensio/yulin/eslint` exports the `cloudFrontFunctionsJs2` flat config.
135
+ - `cffjs2.oxlintrc.json` in the package root contains the Oxlint config. The source object is exported
136
+ as `cloudFrontFunctionsJs2Oxlint` from `@kensio/yulin/oxlint`.
137
+ - Both configs enable nine `cff-js2` rules and scope them to `**/*.cff.js`.
155
138
 
156
139
  ## Limitations
157
140
 
158
- Where the configs knowingly stop short:
159
-
160
- - **`async` arguments and closures are not checked.** JS2 supports `async` and `await`, but not
161
- `async` arguments or closures, and `await` only inside an `async` function. Where the runtime's
162
- wording stops short of saying exactly which forms those are, no rule guesses at it. A Function can
163
- pass the lint and still be refused for one.
164
- - **The list of unavailable globals is the useful part of one, not all of it.** It names what test
165
- code and Node habits reach for. A global outside that list goes unreported, even if JS2 lacks it.
166
- - **The rules are syntactic.** Nothing here knows CloudFront's size limit on Function code or its CPU
167
- budget. A Function that passes the lint can still be refused at publication for being too large or
168
- too slow. No rule checks which methods of a supported built-in you call either, and the runtime
169
- supports only some of them.
170
- - **Only the `cff-js2` rules are shared between the linters.** ESLint and Oxlint each bring their own
171
- built-in rules, and their own defaults for which are on. A `.cff.js` file linted by both may pick
172
- up findings from one that the other lacks.
173
- - **Oxlint's JS plugin support is in alpha.** It is what loads these rules into Oxlint, and its API
174
- is still moving upstream.
175
- - **The Oxlint fragment is extended by path.** Oxlint has no package-name resolution in `extends`.
176
- The written-out path into `node_modules` depends on your installer's layout.
141
+ - The rules do not check the JS2 restrictions on `async` arguments and closures. They also do not
142
+ check that `await` appears only inside an `async` function.
143
+ - `cff-js2/no-unavailable-global` covers common browser, Node.js, and timer globals. Other globals
144
+ missing from JS2 may go unreported.
145
+ - The rules do not check CloudFront's function size or CPU limits. They also do not validate every
146
+ method on each supported built-in.
147
+ - ESLint and Oxlint have different built-in rules. Only the `cff-js2` rules are shared.
148
+ - Oxlint's JavaScript plugin support is in alpha and may change upstream.
149
+ - Oxlint resolves the config through its path in `node_modules`. That path depends on the package
150
+ manager's installation layout.
@@ -1,33 +1,20 @@
1
- # Non-AWS dependencies
1
+ # Use non-AWS dependencies
2
2
 
3
- Most applications talk to something other than AWS, such as a Redis or a Postgres. This page covers
4
- what happens to those when the application runs under Yulin.
3
+ Yulin leaves databases, caches, and other non-AWS dependencies under your control.
5
4
 
6
- ## Two kinds of dependency
5
+ ## Pass connection details through environment variables
7
6
 
8
- A simulated Lambda function and a simulated ECS container both run your own code in the same Node.js
9
- process as the test. Everything that code talks to falls into one of two categories.
7
+ A simulated Lambda function or ECS container runs in the same Node.js process as your test. AWS
8
+ SDK calls can stay inside Yulin through [SDK interception](https://yulinsim.dev/sdk/). Calls to
9
+ other libraries work normally.
10
10
 
11
- Simulated AWS services are handled by Yulin. A DynamoDB call reaches an in-memory table and an S3
12
- call reaches an in-memory bucket, with no network involved. [AWS SDK interception](https://yulinsim.dev/sdk/)
13
- is how an ordinary SDK client in the code under test gets there.
14
-
15
- Everything else is yours to provide, and connects the way it normally would. Yulin leaves it alone
16
- entirely, with no simulation and no interception. Code that opens a Redis connection opens a real
17
- one, to whatever address it was given.
18
-
19
- ## Pointing the code at your own dependency
20
-
21
- A deployed Lambda function or ECS container reads its connection details from environment variables.
22
- The simulated ones do the same. A function's `Environment.Variables` and a container definition's
23
- `environment` are visible through `process.env` while the code runs, so pointing the application
24
- somewhere else means setting the value it already reads. No Yulin feature is involved, and the code
25
- under test stays as it is.
11
+ Configure those libraries through the same environment variables that you use in production. For
12
+ example, a deployed function could receive an ElastiCache URL while a test receives a localhost
13
+ URL:
26
14
 
27
15
  ```typescript non-aws-dependency-lambda
28
16
  /**
29
- * Pointing a simulated Lambda function at a dependency Yulin does not
30
- * simulate, alongside one it does.
17
+ * Giving a simulated Lambda function the address of an external dependency.
31
18
  */
32
19
 
33
20
  import { CreateFunctionCommand, InvokeCommand } from "@aws-sdk/client-lambda";
@@ -44,17 +31,12 @@ await lambda.createFunction(
44
31
  Role: "arn:aws:iam::111111111111:role/RatesRole",
45
32
  Environment: {
46
33
  Variables: {
47
- // Simulated by Yulin, reached with no network involved.
48
34
  TABLE_NAME: "rates",
49
- // Yours. A deployment points this at ElastiCache. A test points it
50
- // at whatever it wants the code to talk to instead.
51
35
  CACHE_URL: "redis://127.0.0.1:6379",
52
36
  },
53
37
  },
54
38
  Code: {
55
39
  ZipFile: makeLambdaZipFileInput(() => ({
56
- // Building a Redis client from the second value involves nothing of
57
- // Yulin's. It is an ordinary environment variable read.
58
40
  tableName: process.env["TABLE_NAME"],
59
41
  cacheUrl: process.env["CACHE_URL"],
60
42
  })),
@@ -66,141 +48,56 @@ const output = await lambda.invoke(
66
48
  new InvokeCommand({ FunctionName: "rates" }),
67
49
  );
68
50
 
69
- if (output.Payload === undefined) throw new Error("No invoke Payload");
70
- // {"tableName":"rates","cacheUrl":"redis://127.0.0.1:6379"}
51
+ if (output.Payload === undefined) throw new Error("No invoke payload");
52
+
71
53
  console.log(Buffer.from(output.Payload).toString());
54
+ // {"tableName":"rates","cacheUrl":"redis://127.0.0.1:6379"}
72
55
  ```
73
56
 
74
- What that address points at is up to the test. A Redis running on localhost, one the test suite
75
- starts in a container, or a stand-in the test defines itself all work, because the application is
76
- doing what it always does with the value it is given.
77
-
78
- ## A container reading from both
57
+ `TABLE_NAME` identifies a simulated AWS resource. `CACHE_URL` points to a dependency supplied by
58
+ the test. That dependency could be a local process, a test container, or an in-process
59
+ implementation of the interface your application uses.
79
60
 
80
- The worked example below runs one simulated ECS container that writes to a simulated DynamoDB table
81
- and reads from a cache of its own. The two categories sit next to each other in the same handler and
82
- are configured the same way, through the container definition's `environment`.
61
+ ## Configure an ECS container in the same way
83
62
 
84
- The cache here is a stand-in defined by the example, and it works with no server running. A real
85
- client built from `CACHE_URL` would go in the same place.
63
+ An ECS container binding reads the container definition's `environment` entries through
64
+ `process.env` while its handler runs:
86
65
 
87
66
  ```typescript non-aws-dependency-ecs
88
67
  /**
89
- * A simulated ECS container writing to a simulated DynamoDB table and reading
90
- * from a cache of its own.
68
+ * Passing a cache URL to a simulated ECS container.
91
69
  */
92
70
 
93
- import {
94
- CreateTableCommand,
95
- DynamoDBClient,
96
- GetItemCommand,
97
- PutItemCommand,
98
- } from "@aws-sdk/client-dynamodb";
99
71
  import {
100
72
  CreateClusterCommand,
101
73
  RegisterTaskDefinitionCommand,
102
74
  RunTaskCommand,
103
75
  } from "@aws-sdk/client-ecs";
104
- import { CreateRoleCommand, PutRolePolicyCommand } from "@aws-sdk/client-iam";
105
76
 
106
- import { SimSdk } from "@kensio/yulin/sdk";
107
-
108
- /**
109
- * Where the worker reads exchange rates from.
110
- *
111
- * A deployment builds a Redis client from the URL. This example stands one in,
112
- * so nothing has to be running for it to work.
113
- */
114
- class RateCache {
115
- constructor(private readonly url: string) {}
116
-
117
- rate(currency: string): Promise<string> {
118
- console.log(`reading ${currency} from ${this.url}`);
119
- // reading GBP from redis://127.0.0.1:6379
120
- return Promise.resolve("1.27");
121
- }
122
- }
77
+ import { SimAws } from "@kensio/yulin";
123
78
 
124
- using simSdk = new SimSdk();
125
- const { simAws } = simSdk;
79
+ const simAws = new SimAws();
126
80
  const ecs = simAws.ecs();
127
-
128
- simSdk.intercept(DynamoDBClient);
129
-
130
- await simAws.dynamoDb().createTable(
131
- new CreateTableCommand({
132
- TableName: "rates",
133
- KeySchema: [{ AttributeName: "currency", KeyType: "HASH" }],
134
- AttributeDefinitions: [{ AttributeName: "currency", AttributeType: "S" }],
135
- BillingMode: "PAY_PER_REQUEST",
136
- }),
137
- );
138
-
139
- const taskRole = await simAws.iam().createRole(
140
- new CreateRoleCommand({
141
- RoleName: "RatesTaskRole",
142
- AssumeRolePolicyDocument: JSON.stringify({
143
- Version: "2012-10-17",
144
- Statement: {
145
- Effect: "Allow",
146
- Principal: { Service: "ecs-tasks.amazonaws.com" },
147
- Action: "sts:AssumeRole",
148
- },
149
- }),
150
- }),
151
- );
152
-
153
- await simAws.iam().putRolePolicy(
154
- new PutRolePolicyCommand({
155
- RoleName: "RatesTaskRole",
156
- PolicyName: "WriteRates",
157
- PolicyDocument: JSON.stringify({
158
- Version: "2012-10-17",
159
- Statement: {
160
- Effect: "Allow",
161
- Action: "dynamodb:PutItem",
162
- Resource:
163
- `arn:aws:dynamodb:${simAws.defaultRegionName}:` +
164
- `${simAws.defaultAccountId}:table/rates`,
165
- },
166
- }),
167
- }),
168
- );
81
+ const connections: string[] = [];
169
82
 
170
83
  await ecs.createCluster(new CreateClusterCommand({}));
171
84
 
172
85
  ecs.bindContainer({
173
86
  family: "rates-worker",
174
87
  containerName: "app",
175
- run: async () => {
176
- // Both reads happen inside the handler, so they see the container's own
177
- // variables rather than the test process's.
178
- const cache = new RateCache(process.env["CACHE_URL"] ?? "");
179
- const rate = await cache.rate("GBP");
180
-
181
- await new DynamoDBClient({}).send(
182
- new PutItemCommand({
183
- TableName: process.env["TABLE_NAME"],
184
- Item: { currency: { S: "GBP" }, rate: { S: rate } },
185
- }),
186
- );
88
+ run: () => {
89
+ connections.push(process.env["CACHE_URL"] ?? "");
187
90
  },
188
91
  });
189
92
 
190
93
  await ecs.registerTaskDefinition(
191
94
  new RegisterTaskDefinitionCommand({
192
95
  family: "rates-worker",
193
- taskRoleArn: taskRole.Role.Arn,
194
96
  containerDefinitions: [
195
97
  {
196
98
  name: "app",
197
99
  image: "rates-worker:1",
198
- environment: [
199
- // Simulated by Yulin, reached with no network involved.
200
- { name: "TABLE_NAME", value: "rates" },
201
- // Yours, connected to the way it normally would be.
202
- { name: "CACHE_URL", value: "redis://127.0.0.1:6379" },
203
- ],
100
+ environment: [{ name: "CACHE_URL", value: "redis://127.0.0.1:6379" }],
204
101
  },
205
102
  ],
206
103
  }),
@@ -209,53 +106,60 @@ await ecs.registerTaskDefinition(
209
106
  await ecs.runTask(new RunTaskCommand({ taskDefinition: "rates-worker" }));
210
107
  await simAws.backgroundTasksComplete();
211
108
 
212
- const stored = await simAws.dynamoDb().getItem(
213
- new GetItemCommand({
214
- TableName: "rates",
215
- Key: { currency: { S: "GBP" } },
216
- }),
217
- );
218
-
219
- console.log(stored.Item?.["rate"]?.S); // "1.27"
109
+ console.log(connections); // ["redis://127.0.0.1:6379"]
220
110
  ```
221
111
 
222
- The DynamoDB write is authorized as the task role, in the same way it would be in a deployment. The
223
- cache read passes through no authorization at all, because IAM has no part in it.
112
+ The host environment remains available unless the container overrides a variable. ECS task
113
+ overrides and simulated Secrets Manager or SSM values are also applied before the handler runs.
114
+ See the [ECS guide](https://yulinsim.dev/services/ecs/) for the full precedence rules.
224
115
 
225
- ## Sidecar containers are not started
116
+ ## Run the dependency yourself
226
117
 
227
- A task definition sometimes declares the dependency itself as a second container, such as a Redis
228
- running next to the application in the same task. Yulin never looks inside a container image, and
229
- the only thing it can run is JavaScript or TypeScript in its own process. An image holding a Redis
230
- server is beyond it.
118
+ Your test or development script starts and stops the server named by a connection string.
231
119
 
232
- The container is stored and reported back as declared, and it is recorded as not simulated when the
233
- task runs, with a reason saying so. An application expecting it has to be given something else to
234
- talk to. Set the variable holding the address to something that answers, whether that is a Redis you
235
- run yourself or a stand-in, in the same way as for any other dependency of your own.
120
+ This also applies to ECS sidecars. Yulin stores every container definition, but it only runs a
121
+ container that has a JavaScript or TypeScript binding. It cannot run a Redis or database image. An
122
+ unbound sidecar is recorded as unsimulated when the task runs.
236
123
 
237
- ## Reads happen inside the handler
124
+ Point the application container at a dependency that is available to the host process. A localhost
125
+ address often works because the simulated container runs in that process, without a separate
126
+ container network.
238
127
 
239
- Handler code gets the function's or the container's variables while it runs, and reads the host
240
- process environment otherwise. A read at module scope, as in `const url = process.env.CACHE_URL` at
241
- the top of a file, happens when the test imports that file rather than when the code runs. It sees
242
- the host value.
128
+ ## Read environment variables while the handler runs
243
129
 
244
- That matters here because a connection is often built at module scope. Read inside the handler, or
245
- build the client there, to get the configured value. Sim Lambda warns on the console when the
246
- difference changes what the code sees, which is covered under
247
- [environment variables](https://yulinsim.dev/services/lambda/#environment-variables) on the simulated Lambda
248
- page. Zip code running in the vm runtime is unaffected, because it is imported during an invocation.
130
+ Referenced Lambda handlers and ECS bindings come from modules already imported by the test. A
131
+ module-level read happens during that import:
249
132
 
250
- ## Limitations
133
+ ```typescript
134
+ const cacheUrl = process.env["CACHE_URL"];
135
+ ```
136
+
137
+ That value comes from the host environment. Read the variable inside the handler when it must come
138
+ from the simulated function or container configuration:
139
+
140
+ ```typescript
141
+ async function handler(): Promise<void> {
142
+ const cacheUrl = process.env["CACHE_URL"];
143
+ // Build or call the dependency here.
144
+ }
145
+ ```
251
146
 
252
- Current documented limitations:
147
+ Simulated Lambda prints a warning when a declared value conflicts with a value that module-level
148
+ code could have read. Lambda code loaded from a zip archive is imported during its first invocation
149
+ and sees the function environment at module scope.
150
+
151
+ ## Available functionality
152
+
153
+ - Lambda `Environment.Variables` are applied during an invocation.
154
+ - ECS container `environment`, `secrets`, and task overrides are applied during a container run.
155
+ - Host environment variables remain available for names left out of the workload configuration.
156
+ - AWS SDK interception continues to work from code that also uses external dependencies.
157
+
158
+ ## Limitations
253
159
 
254
- - Everything outside the simulated AWS services is yours to run and to tear down. Yulin starts none
255
- of it.
256
- - A dependency declared as a sidecar container in an ECS task definition is not started, because
257
- Yulin never runs a container image.
258
- - A connection built at module scope reads the host environment rather than the function's or the
259
- container's, since the value is read before anything runs.
260
- - There is no interception point for a non-AWS dependency. The way to send the code somewhere else
261
- is the environment variable it already reads, or an injection point in the code itself.
160
+ - Yulin does not create, start, reset, or stop non-AWS dependencies.
161
+ - Unbound ECS sidecar images are stored but never run.
162
+ - Referenced handlers and container bindings read the host value when they access `process.env` at
163
+ module scope.
164
+ - Yulin has no interception API for non-AWS clients. Configure the client through your application's
165
+ existing environment variables or dependency injection point.