@kensio/yulin 1.21.34 → 1.23.0

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 (76) hide show
  1. package/dist/serve/controller/sim-service-controller.d.ts +6 -0
  2. package/dist/serve/controller/sim-service-controller.js +7 -0
  3. package/dist/serve/http/request/sim-aws-request-authenticator.js +3 -0
  4. package/dist/service/apigateway/serve/sim-api-gateway-controller.js +1 -0
  5. package/dist/service/apigateway/serve/sim-rest-api-web-acl-inspection.d.ts +2 -0
  6. package/dist/service/apigateway/serve/sim-rest-api-web-acl-inspection.js +1 -0
  7. package/dist/service/cloudformation/registry/sim-cfn-attribute-reads.d.ts +11 -0
  8. package/dist/service/cloudformation/registry/sim-cfn-attribute-reads.js +64 -0
  9. package/dist/service/cloudformation/registry/sim-cfn-read-handler-authorizer.d.ts +45 -0
  10. package/dist/service/cloudformation/registry/sim-cfn-read-handler-authorizer.js +72 -0
  11. package/dist/service/cloudformation/registry/sim-cfn-read-handler.d.ts +33 -0
  12. package/dist/service/cloudformation/registry/sim-cfn-read-handler.fixture.d.ts +24 -0
  13. package/dist/service/cloudformation/registry/sim-cfn-read-handler.fixture.js +58 -0
  14. package/dist/service/cloudformation/registry/sim-cfn-read-handler.js +36 -0
  15. package/dist/service/cloudformation/registry/sim-cfn-read-handlers.d.ts +10 -0
  16. package/dist/service/cloudformation/registry/sim-cfn-read-handlers.js +23 -0
  17. package/dist/service/cloudformation/registry/sim-cfn-read-handlers.json +7400 -0
  18. package/dist/service/cloudformation/registry/sim-cfn-resource-arn.d.ts +46 -0
  19. package/dist/service/cloudformation/registry/sim-cfn-resource-arn.js +119 -0
  20. package/dist/service/cloudformation/stack/sim-cfn-stack-deploy-context.d.ts +6 -1
  21. package/dist/service/cloudformation/stack/sim-cfn-stack-deploy-context.js +3 -0
  22. package/dist/service/cloudformation/stack/sim-cfn-stack-resource-operations.d.ts +3 -2
  23. package/dist/service/cloudformation/stack/sim-cfn-stack-resource-operations.js +2 -1
  24. package/dist/service/cloudformation/stack/sim-cfn-stack.js +9 -0
  25. package/dist/service/cloudformation/stack/update/sim-cfn-stack-update-plan.d.ts +11 -0
  26. package/dist/service/cloudformation/stack/update/sim-cfn-stack-update-plan.js +22 -0
  27. package/dist/service/cloudformation/stack/update/sim-cfn-stack-updater.d.ts +3 -1
  28. package/dist/service/cloudformation/stack/update/sim-cfn-stack-updater.js +2 -1
  29. package/dist/service/cognito/serve/sim-cognito-controller.js +4 -3
  30. package/dist/service/cognito/serve/sim-cognito-domain-controller.js +2 -0
  31. package/dist/service/cognito/serve/sim-cognito-web-acl-inspection.d.ts +2 -0
  32. package/dist/service/cognito/serve/sim-cognito-web-acl-inspection.js +1 -0
  33. package/dist/service/iam/index.d.ts +1 -0
  34. package/dist/service/iam/index.js +1 -0
  35. package/dist/service/iam/request/error/sim-aws-request-auth.error.d.ts +1 -1
  36. package/dist/service/iam/request/error/sim-aws-request-auth.error.js +1 -1
  37. package/dist/service/iam/request/sim-aws-control-headers.d.ts +2 -1
  38. package/dist/service/iam/request/sim-aws-control-headers.js +4 -1
  39. package/dist/service/iam/request/sim-aws-source-ip-header.d.ts +19 -0
  40. package/dist/service/iam/request/sim-aws-source-ip-header.js +32 -0
  41. package/dist/service/stepfunctions/command/execution/sim-states-execution-start.js +1 -0
  42. package/dist/service/stepfunctions/execution/sim-states-run-task.js +1 -0
  43. package/dist/service/stepfunctions/execution/sim-states-state-outcome.d.ts +1 -0
  44. package/dist/service/stepfunctions/execution/sim-states-walks.d.ts +1 -0
  45. package/dist/service/stepfunctions/execution/sim-states-walks.js +1 -0
  46. package/dist/service/stepfunctions/task/sim-aws-states-task-targets.js +6 -1
  47. package/dist/service/stepfunctions/task/sim-states-execution-role.d.ts +2 -1
  48. package/dist/service/stepfunctions/task/sim-states-execution-role.js +2 -1
  49. package/dist/service/stepfunctions/task/sim-states-task-invocation.d.ts +1 -0
  50. package/dist/service/wafv2/association/sim-waf-associations.js +1 -1
  51. package/dist/service/wafv2/association/sim-waf-protection.d.ts +5 -0
  52. package/dist/service/wafv2/association/sim-waf-request-inspection.d.ts +2 -0
  53. package/dist/service/wafv2/association/sim-waf-request-inspection.js +7 -2
  54. package/dist/service/wafv2/evaluate/sim-waf-evaluation-request.d.ts +5 -0
  55. package/dist/service/wafv2/evaluate/sim-waf-inspected-request.d.ts +12 -1
  56. package/dist/service/wafv2/evaluate/sim-waf-inspected-request.js +12 -1
  57. package/dist/service/wafv2/index.d.ts +2 -1
  58. package/dist/service/wafv2/index.js +1 -0
  59. package/dist/service/wafv2/managed/group/sim-waf-ip-reputation.d.ts +8 -5
  60. package/dist/service/wafv2/managed/group/sim-waf-ip-reputation.js +10 -5
  61. package/dist/service/wafv2/managed/sim-waf-managed-address-declaration.d.ts +32 -0
  62. package/dist/service/wafv2/managed/sim-waf-managed-address-declaration.js +52 -0
  63. package/dist/service/wafv2/managed/sim-waf-managed-address-range.d.ts +21 -0
  64. package/dist/service/wafv2/managed/sim-waf-managed-address-range.js +73 -0
  65. package/dist/service/wafv2/managed/sim-waf-managed-rule-groups.d.ts +7 -0
  66. package/dist/service/wafv2/managed/sim-waf-managed-rule-groups.js +15 -0
  67. package/dist/service/wafv2/managed/sim-waf-managed-rule.type.d.ts +5 -0
  68. package/dist/service/wafv2/managed/sim-waf-managed-rules.d.ts +27 -1
  69. package/dist/service/wafv2/managed/sim-waf-managed-rules.js +33 -13
  70. package/dist/service/wafv2/sim-wafv2.js +2 -1
  71. package/dist/service/wafv2/statement/sim-waf-rate-based-input.d.ts +5 -4
  72. package/dist/service/wafv2/statement/sim-waf-rate-based-input.js +6 -6
  73. package/docs/services/cloudformation/README.md +106 -0
  74. package/docs/services/iam/README.md +6 -0
  75. package/docs/services/wafv2/README.md +105 -14
  76. package/package.json +5 -4
@@ -1,4 +1,3 @@
1
- import { simAwsProxiedSourceIp } from "../../../serve/http/sim-aws-proxied-connection.js";
2
1
  import { invalidSimWafRule } from "./sim-waf-rule-refusals.js";
3
2
  import { refuseUnsimulatedSimWafRateKeyType } from "./sim-waf-unsimulated-rate-based.js";
4
3
  /**
@@ -66,10 +65,11 @@ export function simWafRateWindowMilliseconds(statement, ruleName) {
66
65
  /**
67
66
  * Read how a rate-based statement groups the requests it counts.
68
67
  *
69
- * `IP` counts each client address on its own. Every request in this simulation
70
- * reports `simAwsProxiedSourceIp`, leaving one client for the whole
71
- * simulation. A rate limiting test sends its requests from one client and
72
- * expects the rule to trip, so that is the case anybody would write.
68
+ * `IP` counts each client address on its own. A request comes from
69
+ * `127.0.0.1` unless it states another address with `x-sim-aws-source-ip`, so
70
+ * a test that states none has one client for the whole simulation. That is
71
+ * the case a rate limiting test is usually written about: one client, sending
72
+ * until the rule trips.
73
73
  *
74
74
  * `CONSTANT` counts every request the statement sees together, and AWS asks
75
75
  * for a scope-down statement alongside it to say which requests those are. A
@@ -79,7 +79,7 @@ export function simWafRateAggregation(statement, ruleName) {
79
79
  const keyType = statement.AggregateKeyType;
80
80
  refuseUnsimulatedSimWafRateKeyType(keyType, ruleName);
81
81
  if (keyType === "IP") {
82
- return () => simAwsProxiedSourceIp;
82
+ return (request) => request.sourceIp;
83
83
  }
84
84
  if (keyType === "CONSTANT") {
85
85
  if (statement.ScopeDownStatement === undefined) {
@@ -2165,6 +2165,100 @@ console.log(stacks.size);
2165
2165
  See [simulated IAM](https://yulinsim.dev/services/iam/ "Simulated IAM usage docs") for the documents
2166
2166
  it takes.
2167
2167
 
2168
+ ### The read calls an update makes
2169
+
2170
+ CloudFormation calls a resource's read handler when it needs the resource's attributes and holds no
2171
+ fresh copy of them. A stack update that leaves a resource unchanged, while the new template reads one
2172
+ of its attributes through `Fn::GetAtt` or `Fn::Sub`, is one such case. The handler runs as the
2173
+ stack's execution role. Yulin authorizes each of its calls as the deployment's principal, after the
2174
+ update's own changes and before its outputs are resolved. A denied call fails the update, which is
2175
+ rolled back.
2176
+
2177
+ The calls come from the resource type's entry in the CloudFormation registry. Yulin keeps the ones in
2178
+ the type's own service and authorizes them on the resource's ARN, built from the stack's account and
2179
+ Region and the resource's properties. A resource yulin skips is authorized the same way, since the
2180
+ snapshot and the properties are all this takes. A call IAM scopes to no resource type is
2181
+ authorized on `*`.
2182
+
2183
+ ```typescript sim-cloudformation-read-handler
2184
+ /**
2185
+ * An update refused because the deploy Role may not read an unchanged
2186
+ * resource whose attribute an output reads.
2187
+ */
2188
+
2189
+ import { SimAws } from "@kensio/yulin";
2190
+
2191
+ const simAws = new SimAws({
2192
+ defaultAccountId: "123456789012",
2193
+ defaultRegionName: "eu-west-2",
2194
+ });
2195
+
2196
+ const deployer = await simAws.iam().makeDeployRole({
2197
+ roleName: "cdk-exec",
2198
+ policyDocument: {
2199
+ Version: "2012-10-17",
2200
+ Statement: {
2201
+ Effect: "Allow",
2202
+ Action: [
2203
+ "s3:*",
2204
+ "codeartifact:CreateDomain",
2205
+ "codeartifact:CreateRepository",
2206
+ "codeartifact:DescribeRepository",
2207
+ "codeartifact:ListTagsForResource",
2208
+ ],
2209
+ Resource: "*",
2210
+ },
2211
+ },
2212
+ });
2213
+
2214
+ const mirror = {
2215
+ Domain: {
2216
+ Type: "AWS::CodeArtifact::Domain",
2217
+ Properties: { DomainName: "example" },
2218
+ },
2219
+ Repository: {
2220
+ Type: "AWS::CodeArtifact::Repository",
2221
+ Properties: { DomainName: "example", RepositoryName: "npm" },
2222
+ DependsOn: "Domain",
2223
+ },
2224
+ };
2225
+ const outputs = {
2226
+ RepositoryArn: { Value: { "Fn::GetAtt": ["Repository", "Arn"] } },
2227
+ };
2228
+
2229
+ const cloudFormation = simAws.cloudFormation();
2230
+
2231
+ await cloudFormation.deployTemplate({
2232
+ stackName: "artifacts",
2233
+ template: { Resources: mirror, Outputs: outputs },
2234
+ caller: deployer,
2235
+ });
2236
+
2237
+ await cloudFormation.updateStack({
2238
+ input: {
2239
+ StackName: "artifacts",
2240
+ TemplateBody: JSON.stringify({
2241
+ Resources: { ...mirror, Sources: { Type: "AWS::S3::Bucket" } },
2242
+ Outputs: outputs,
2243
+ }),
2244
+ },
2245
+ });
2246
+
2247
+ try {
2248
+ await cloudFormation.waitForStackUpdateComplete("artifacts");
2249
+ } catch (error) {
2250
+ // "... is not authorized to perform:
2251
+ // codeartifact:GetRepositoryPermissionsPolicy on resource:
2252
+ // arn:aws:codeartifact:eu-west-2:123456789012:repository/example/npm"
2253
+ console.log((error as Error).message);
2254
+ }
2255
+ ```
2256
+
2257
+ A resource the update creates or changes needs no read, because its own handler returned its
2258
+ attributes. A resource whose ARN cannot be built fails the update with the ARN variable it was
2259
+ missing. That happens to a resource yulin skips whose ARN holds an identifier its service would have
2260
+ generated, since only a created resource can give one.
2261
+
2168
2262
  ### One caller for an assembly, and one per Stack
2169
2263
 
2170
2264
  `deployCdkOut(...)` takes a caller for every Stack in the assembly. A Stack deployed by a different
@@ -4579,4 +4673,16 @@ Each service's own docs describe what its resource types support.
4579
4673
  `InvokeRole` and `AddDefaultAuthorizerToCorsPreflight` fail the transform by name.
4580
4674
  `AutoPublishAlias` and `DeploymentPreference` are left out, because the simulator has one version
4581
4675
  of a function and nothing to shift traffic between.
4676
+ - A stack update authorizes the read handler calls of an unchanged resource whose attributes the
4677
+ new template reads, and no other handler calls. The create, update and delete calls are the ones
4678
+ each service makes itself. Creating a resource of a type yulin skips authorizes nothing. Of the read
4679
+ calls, only those in the resource type's own service are authorized. The registry also lists calls
4680
+ into other services (a KMS key, a role a property names), and a handler makes those only when a
4681
+ property leads to them. See [the read calls an update makes](#the-read-calls-an-update-makes).
4682
+ - The read handler calls come from a snapshot of the registry and the Service Authorization
4683
+ Reference, which `pnpm run cfn:read-handlers` refreshes. A resource type the snapshot leaves out is
4684
+ not authorized at all. It leaves out a type whose read handler is unpublished, and one with no
4685
+ resource type in the Service Authorization Reference named after it.
4686
+ - An `Fn::GetAtt` inside an `Fn::If` branch the conditions rule out still counts as reading the
4687
+ resource.
4582
4688
  - Many advanced CloudFormation features are outside the simulation.
@@ -771,6 +771,12 @@ was created, exactly as `runAs` behaves. The header is stripped before the reque
771
771
  simulated service. A Lambda handler echoing `event.headers` sees none of the simulator's control
772
772
  metadata.
773
773
 
774
+ `x-sim-aws-source-ip` is a control header of the same kind. It names the client address a request
775
+ comes from (one IPv4 or IPv6 address), and a request naming none comes from `127.0.0.1`. A web ACL
776
+ reads it (see
777
+ [where a request comes from](https://yulinsim.dev/services/wafv2/#where-a-request-comes-from)). IAM
778
+ policy evaluation does not read it.
779
+
774
780
  ```typescript sim-iam-served-request-caller
775
781
  /**
776
782
  * Naming the caller of an HTTP request into simulated AWS.
@@ -282,10 +282,11 @@ lets evaluation continue, the way it does for every other statement kind.
282
282
  window is measured against [simulated time](https://yulinsim.dev/time/), so `advanceBy` past it drops
283
283
  what the rule counted.
284
284
 
285
- `AggregateKeyType` is `IP` or `CONSTANT`. `IP` counts each client address on its own. Every request
286
- in this simulation reports `127.0.0.1`, leaving a web ACL with one client for the whole simulation.
287
- That is the case a rate limiting test is written about anyway (one client, sending until the rule
288
- trips), and it behaves here as it does on AWS. `CONSTANT` counts every request the statement sees
285
+ `AggregateKeyType` is `IP` or `CONSTANT`. `IP` counts each client address on its own. A request
286
+ comes from `127.0.0.1` unless it names another address (see
287
+ [Where a request comes from](#where-a-request-comes-from)). A test that names none has one client for
288
+ the whole simulation. That is the case a rate limiting test is usually written about (one client,
289
+ sending until the rule trips). `CONSTANT` counts every request the statement sees
289
290
  together, and AWS requires a `ScopeDownStatement` alongside it to say which requests those are.
290
291
 
291
292
  A `ScopeDownStatement` narrows what the rule counts. Every statement kind in
@@ -463,17 +464,103 @@ are.
463
464
 
464
465
  ### Groups that read the caller
465
466
 
466
- The IP reputation, anonymous IP and Bot Control groups decide by who sent a request, and every
467
- request in this simulation comes from `127.0.0.1`. Each of their rules is declared-only. The group
468
- labels the request, takes the rule's documented action and honours overrides, as the groups that
469
- read the request do.
467
+ The IP reputation, anonymous IP and Bot Control groups decide by who sent a request. AWS publishes
468
+ their rule names, actions and labels, and none of the address lists or bot signatures behind them.
469
+ Each of their rules is declared-only. The group labels the request, takes the rule's documented
470
+ action and honours overrides, as the groups that read the request do.
470
471
 
471
472
  `AWSManagedRulesAmazonIpReputationList` holds `AWSManagedIPReputationList` and
472
473
  `AWSManagedReconnaissanceList`, which block, and `AWSManagedIPDDoSList`, which counts as AWS ships
473
474
  it. `AWSManagedRulesAnonymousIpList` holds `AnonymousIPList` (VPNs, proxies and Tor) and
474
475
  `HostingProviderIPList` (hosting and cloud providers other than AWS), which both block. A test
475
- declares a match by rule name, as above, and says which requests come from a flagged address that
476
- way.
476
+ declares which addresses those five rules flag, and sends requests from them.
477
+
478
+ #### Where a request comes from
479
+
480
+ A request reaches this simulation in process or over loopback, and comes from `127.0.0.1`. The
481
+ `x-sim-aws-source-ip` header names another client address (one IPv4 or IPv6 address). It works
482
+ through `SimAwsHttp` and `serveSimAws` alike, and it is stripped before the request reaches the
483
+ simulated service, as `x-sim-aws-caller` is. A value that is no address is refused with a 400 and
484
+ `InvalidRequestCaller`. `evaluateRequest` takes the address as `sourceIp`.
485
+
486
+ The address reaches the web ACL in front of a Cognito user pool or a REST API stage. The IP
487
+ reputation and anonymous IP rules read it, and so does a rate limit aggregating on `IP`.
488
+
489
+ `managedRules().onAddress` says which of those rules flag one address or a CIDR range. Only the
490
+ rules of the IP reputation and anonymous IP groups can be declared by address, and a rule of any
491
+ other group is refused. Declaring the same range again replaces it. An address inside two declared
492
+ ranges is flagged by the rules of both, and a match declared for the request's path with
493
+ `onRequest` applies beside them.
494
+
495
+ ```typescript sim-wafv2-anonymous-ip-list
496
+ /**
497
+ * Blocking sign-ups from a Tor exit range with the anonymous IP list.
498
+ */
499
+
500
+ import { CreateWebACLCommand } from "@aws-sdk/client-wafv2";
501
+
502
+ import { SimAws } from "@kensio/yulin";
503
+
504
+ const waf = new SimAws().wafV2();
505
+
506
+ const visibility = {
507
+ SampledRequestsEnabled: false,
508
+ CloudWatchMetricsEnabled: false,
509
+ MetricName: "pool",
510
+ };
511
+
512
+ const created = await waf.createWebAcl(
513
+ new CreateWebACLCommand({
514
+ Name: "pool-acl",
515
+ Scope: "REGIONAL",
516
+ DefaultAction: { Allow: {} },
517
+ VisibilityConfig: visibility,
518
+ Rules: [
519
+ {
520
+ Name: "anonymous-ip",
521
+ Priority: 0,
522
+ OverrideAction: { None: {} },
523
+ Statement: {
524
+ ManagedRuleGroupStatement: {
525
+ VendorName: "AWS",
526
+ Name: "AWSManagedRulesAnonymousIpList",
527
+ },
528
+ },
529
+ VisibilityConfig: { ...visibility, MetricName: "anonymous-ip" },
530
+ },
531
+ ],
532
+ }),
533
+ );
534
+
535
+ waf.managedRules().onAddress("185.220.101.0/24", {
536
+ matches: ["AnonymousIPList"],
537
+ });
538
+
539
+ const signUp = (sourceIp: string): string =>
540
+ waf.evaluateRequest({
541
+ webAclArn: created.Summary!.ARN,
542
+ request: new Request("https://myapp-login.example.com/signup", {
543
+ headers: { "user-agent": "Mozilla/5.0" },
544
+ }),
545
+ sourceIp,
546
+ }).action;
547
+
548
+ // "BLOCK"
549
+ console.log(signUp("185.220.101.7"));
550
+
551
+ // "ALLOW"
552
+ console.log(signUp("203.0.113.9"));
553
+ ```
554
+
555
+ A request over HTTP names its address in the header instead:
556
+
557
+ ```text
558
+ POST /signup HTTP/1.1
559
+ Host: myapp-login.auth.eu-west-2.amazoncognito.com
560
+ x-sim-aws-source-ip: 185.220.101.7
561
+ ```
562
+
563
+ The other kinds of statement that read the address are still held (see [Held input](#held-input)).
477
564
 
478
565
  Bot Control labels a request by the bot that sent it, and a verified bot is labelled and matches no
479
566
  rule. A test declares the bot.
@@ -865,6 +952,9 @@ and records no message.
865
952
 
866
953
  The pages are usually the point. `/signup`, `/confirm` and `/forgotPassword` are the ones that
867
954
  create an account or send an email, and a real web ACL on a user pool is usually written for them.
955
+ A request to them can name the client it comes from in the `x-sim-aws-source-ip` header, for the
956
+ anonymous IP list and the IP reputation list to decide on (see
957
+ [Where a request comes from](#where-a-request-comes-from)).
868
958
 
869
959
  ```typescript sim-wafv2-cognito-user-pool
870
960
  /**
@@ -1537,9 +1627,10 @@ where AWS blocks every address outside the list and the simulation blocks none.
1537
1627
 
1538
1628
  A rule is held for any of these statement kinds:
1539
1629
 
1540
- - `IPSetReferenceStatement`, `GeoMatchStatement` and `AsnMatchStatement`. Every request in this
1541
- simulation reports a source address of `127.0.0.1`, and a rule on where a request came from would
1542
- see one client for the whole simulation.
1630
+ - `IPSetReferenceStatement`, `GeoMatchStatement` and `AsnMatchStatement`. A request has a client
1631
+ address (see [Where a request comes from](#where-a-request-comes-from)), and these statements are
1632
+ not yet written to read it. A country or an ASN would also need an address database the simulation
1633
+ does not have.
1543
1634
  - `SqliMatchStatement` and `XssMatchStatement`. AWS publishes no description of the detection they
1544
1635
  run.
1545
1636
  - `RuleGroupReferenceStatement`. A rule group of your own is a resource in its own right, and none
@@ -1551,7 +1642,7 @@ A rule is held for any of these statement kinds:
1551
1642
 
1552
1643
  A `RateBasedStatement` is evaluated (see [Rate limiting](#rate-limiting)). A rule using two of its
1553
1644
  aggregation key types is held. `FORWARDED_IP` and `ForwardedIPConfig` read the address from a
1554
- forwarding header, which needs the source address variety an IP set is waiting on. `CUSTOM_KEYS` and
1645
+ forwarding header, and are held along with the IP set statement. `CUSTOM_KEYS` and
1555
1646
  `CustomKeys` aggregate on headers, cookies and query arguments, and are feasible and not part of
1556
1647
  this yet. `GetRateBasedStatementManagedKeys` is not simulated.
1557
1648
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kensio/yulin",
3
- "version": "1.21.34",
3
+ "version": "1.23.0",
4
4
  "description": "AWS system behaviour simulation for isolated unit testing",
5
5
  "repository": "https://github.com/KensioSoftware/yulin",
6
6
  "homepage": "https://yulinsim.dev/",
@@ -15,6 +15,7 @@
15
15
  "build": "pnpm clean && tsc -p tsconfig.build.json && pnpm oxlint:config && pnpm docs:index",
16
16
  "oxlint:config": "pnpm tsx scripts/mts/write-oxlint-config.mts",
17
17
  "docs:index": "pnpm tsx scripts/mts/write-llms-index.mts",
18
+ "cfn:read-handlers": "pnpm tsx scripts/mts/write-cfn-read-handlers.mts",
18
19
  "build:check": "tsc -p tsconfig.json --noEmit",
19
20
  "prepack": "pnpm build",
20
21
  "verify:pack": "pnpm tsx scripts/mts/verify-pack.mts",
@@ -290,16 +291,16 @@
290
291
  "conventional-changelog-conventionalcommits": "9.3.1",
291
292
  "esbuild": "^0.28.2",
292
293
  "eslint": "^10.8.0",
293
- "eslint-plugin-jsdoc": "^64.0.1",
294
+ "eslint-plugin-jsdoc": "^65.0.0",
294
295
  "eslint-plugin-no-secrets": "^2.3.3",
295
296
  "eslint-plugin-security": "^4.0.1",
296
297
  "eslint-plugin-unicorn": "^76.0.0",
297
298
  "execa": "^10.0.0",
298
299
  "fta-cli": "^3.0.0",
299
300
  "hyparquet": "^1.29.2",
300
- "hyparquet-writer": "^0.16.9",
301
+ "hyparquet-writer": "^0.18.0",
301
302
  "node-sql-parser": "^5.4.0",
302
- "oxfmt": "^0.73.0",
303
+ "oxfmt": "^0.75.0",
303
304
  "oxlint": "^1.77.0",
304
305
  "oxlint-tsgolint": "^7.0.2001",
305
306
  "semantic-release": "25.0.9",