@kensio/yulin 1.21.26 → 1.21.28

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 (90) hide show
  1. package/dist/config/eslint/cffjs2.eslint.config.js +4 -0
  2. package/dist/service/cloudformation/parameters/sim-cfn-parameter-list-type.d.ts +7 -0
  3. package/dist/service/cloudformation/parameters/sim-cfn-parameter-list-type.js +9 -0
  4. package/dist/service/cloudformation/parameters/sim-cfn-parameter-values.d.ts +10 -0
  5. package/dist/service/cloudformation/parameters/sim-cfn-parameter-values.js +20 -0
  6. package/dist/service/cloudformation/parameters/sim-cfn-parameters.type.d.ts +7 -5
  7. package/dist/service/cloudformation/parameters/store/sim-cfn-parameter-store-value-type.js +2 -1
  8. package/dist/service/cloudformation/template/node/fn/join/sim-cfn-fn-join.d.ts +19 -14
  9. package/dist/service/cloudformation/template/node/fn/join/sim-cfn-fn-join.js +47 -28
  10. package/dist/service/cloudformation/template/parse/fn/join/sim-cfn-fn-join-parser.d.ts +7 -4
  11. package/dist/service/cloudformation/template/parse/fn/join/sim-cfn-fn-join-parser.js +11 -7
  12. package/dist/service/eventbridge/bus/sim-event-bus-name.d.ts +4 -0
  13. package/dist/service/eventbridge/bus/sim-event-bus-name.js +17 -12
  14. package/dist/service/eventbridge/cfn/bus/sim-cfn-event-bus-creator.js +8 -1
  15. package/dist/service/eventbridge/cfn/bus/sim-cfn-event-bus-properties.d.ts +5 -0
  16. package/dist/service/eventbridge/cfn/bus/sim-cfn-event-bus-properties.js +14 -1
  17. package/dist/service/eventbridge/cfn/rule/sim-cfn-event-rule-creator.js +2 -0
  18. package/dist/service/eventbridge/cfn/rule/sim-cfn-event-rule-target-delivery.d.ts +15 -0
  19. package/dist/service/eventbridge/cfn/rule/sim-cfn-event-rule-target-delivery.js +53 -0
  20. package/dist/service/eventbridge/cfn/rule/sim-cfn-event-rule-targets.d.ts +4 -0
  21. package/dist/service/eventbridge/cfn/rule/sim-cfn-event-rule-targets.js +7 -3
  22. package/dist/service/eventbridge/command/authorize/sim-event-bridge-authorizer.d.ts +5 -0
  23. package/dist/service/eventbridge/command/authorize/sim-event-bridge-authorizer.js +7 -0
  24. package/dist/service/eventbridge/command/bus/sim-event-bridge-create-event-bus.d.ts +7 -1
  25. package/dist/service/eventbridge/command/bus/sim-event-bridge-create-event-bus.js +7 -1
  26. package/dist/service/eventbridge/command/bus/sim-event-bridge-partner-bus.d.ts +24 -0
  27. package/dist/service/eventbridge/command/bus/sim-event-bridge-partner-bus.js +44 -0
  28. package/dist/service/eventbridge/command/bus/sim-event-bridge-unsimulated-bus-input.js +0 -5
  29. package/dist/service/eventbridge/command/put-events/sim-event-bridge-authorized-entries.js +8 -0
  30. package/dist/service/eventbridge/command/sim-event-bridge-command.types.d.ts +1 -0
  31. package/dist/service/eventbridge/command/sim-event-bridge-commands.d.ts +6 -0
  32. package/dist/service/eventbridge/command/sim-event-bridge-commands.js +19 -1
  33. package/dist/service/eventbridge/command/source/sim-event-bridge-describe-event-source.d.ts +35 -0
  34. package/dist/service/eventbridge/command/source/sim-event-bridge-describe-event-source.js +46 -0
  35. package/dist/service/eventbridge/command/source/sim-event-bridge-put-partner-events.d.ts +41 -0
  36. package/dist/service/eventbridge/command/source/sim-event-bridge-put-partner-events.js +82 -0
  37. package/dist/service/eventbridge/command/source/source.command.d.ts +59 -0
  38. package/dist/service/eventbridge/command/source/source.command.js +1 -0
  39. package/dist/service/eventbridge/command/target/sim-event-bridge-target-commands.js +2 -0
  40. package/dist/service/eventbridge/command/target/sim-event-bridge-unsimulated-target-input.js +2 -12
  41. package/dist/service/eventbridge/command/target/target.command.d.ts +8 -5
  42. package/dist/service/eventbridge/delivery/sim-aws-event-bridge-delivery-targets.d.ts +6 -1
  43. package/dist/service/eventbridge/delivery/sim-aws-event-bridge-delivery-targets.js +16 -0
  44. package/dist/service/eventbridge/delivery/sim-event-bridge-dead-letter-attributes.d.ts +9 -0
  45. package/dist/service/eventbridge/delivery/sim-event-bridge-dead-letter-attributes.js +47 -0
  46. package/dist/service/eventbridge/delivery/sim-event-bridge-dead-letter-queue.d.ts +24 -0
  47. package/dist/service/eventbridge/delivery/sim-event-bridge-dead-letter-queue.js +28 -0
  48. package/dist/service/eventbridge/delivery/sim-event-bridge-delivery-attempt.d.ts +41 -0
  49. package/dist/service/eventbridge/delivery/sim-event-bridge-delivery-attempt.js +97 -0
  50. package/dist/service/eventbridge/delivery/sim-event-bridge-delivery-queue.js +7 -37
  51. package/dist/service/eventbridge/delivery/sim-event-bridge-delivery-retries.test-support.d.ts +25 -0
  52. package/dist/service/eventbridge/delivery/sim-event-bridge-delivery-retries.test-support.js +60 -0
  53. package/dist/service/eventbridge/delivery/sim-event-bridge-delivery.d.ts +22 -0
  54. package/dist/service/eventbridge/delivery/sim-event-bridge-failing-targets.test-support.d.ts +23 -0
  55. package/dist/service/eventbridge/delivery/sim-event-bridge-failing-targets.test-support.js +28 -0
  56. package/dist/service/eventbridge/delivery/sim-event-bridge-no-delivery-targets.d.ts +5 -1
  57. package/dist/service/eventbridge/delivery/sim-event-bridge-no-delivery-targets.js +9 -0
  58. package/dist/service/eventbridge/delivery/sim-event-bridge-queue-message.d.ts +28 -0
  59. package/dist/service/eventbridge/delivery/sim-event-bridge-queue-message.js +45 -0
  60. package/dist/service/eventbridge/delivery/sim-event-bridge-target-delivery.d.ts +4 -1
  61. package/dist/service/eventbridge/delivery/sim-event-bridge-target-delivery.js +19 -14
  62. package/dist/service/eventbridge/index.d.ts +2 -0
  63. package/dist/service/eventbridge/index.js +2 -0
  64. package/dist/service/eventbridge/routing/sim-event-bridge-router.js +1 -0
  65. package/dist/service/eventbridge/sdk/sim-event-bridge-sdk-command-router.js +4 -0
  66. package/dist/service/eventbridge/sim-event-bridge-inspection.d.ts +10 -0
  67. package/dist/service/eventbridge/sim-event-bridge-inspection.js +9 -0
  68. package/dist/service/eventbridge/sim-event-bridge-partners.d.ts +38 -0
  69. package/dist/service/eventbridge/sim-event-bridge-partners.js +43 -0
  70. package/dist/service/eventbridge/sim-event-bridge.d.ts +12 -12
  71. package/dist/service/eventbridge/sim-event-bridge.js +14 -11
  72. package/dist/service/eventbridge/source/sim-partner-event-source-name.d.ts +28 -0
  73. package/dist/service/eventbridge/source/sim-partner-event-source-name.js +62 -0
  74. package/dist/service/eventbridge/source/sim-partner-event-source-store.d.ts +18 -0
  75. package/dist/service/eventbridge/source/sim-partner-event-source-store.js +29 -0
  76. package/dist/service/eventbridge/source/sim-partner-event-source.d.ts +46 -0
  77. package/dist/service/eventbridge/source/sim-partner-event-source.js +43 -0
  78. package/dist/service/eventbridge/target/sim-event-target-dead-letter-config.d.ts +28 -0
  79. package/dist/service/eventbridge/target/sim-event-target-dead-letter-config.js +42 -0
  80. package/dist/service/eventbridge/target/sim-event-target-retry-policy.d.ts +28 -0
  81. package/dist/service/eventbridge/target/sim-event-target-retry-policy.js +53 -0
  82. package/dist/service/eventbridge/target/sim-event-target.d.ts +17 -0
  83. package/dist/service/eventbridge/target/sim-event-target.js +19 -0
  84. package/docs/serve/README.md +1 -1
  85. package/docs/services/cloudformation/README.md +28 -5
  86. package/docs/services/dynamodb/README.md +2 -2
  87. package/docs/services/eventbridge/README.md +223 -6
  88. package/docs/services/s3/README.md +1 -1
  89. package/docs/services/sqs/README.md +1 -1
  90. package/package.json +2 -2
@@ -1,4 +1,6 @@
1
+ import { assertDefined } from "../../../util/type-guard/defined.js";
1
2
  import { SimEventBridgeDeliveryFunction } from "./sim-event-bridge-delivery-function.js";
3
+ import { SimEventBridgeDeadLetterQueue } from "./sim-event-bridge-dead-letter-queue.js";
2
4
  import { SimEventBridgeDeliveryQueue } from "./sim-event-bridge-delivery-queue.js";
3
5
  import { SimEventBridgeDeliveryTask } from "./sim-event-bridge-delivery-task.js";
4
6
  import { SimEventBridgeDeliveryTopic } from "./sim-event-bridge-delivery-topic.js";
@@ -26,6 +28,20 @@ export class SimAwsEventBridgeDeliveryTargets {
26
28
  const scope = this.simAws.accountRegionScope(arn.accountId, arn.regionName);
27
29
  await this.deliverTo(arn.service, { request, scope });
28
30
  }
31
+ /**
32
+ * Send an event EventBridge gave up on to the dead-letter queue its target
33
+ * names, in the queue's own Account and Region.
34
+ */
35
+ async deadLetter(request) {
36
+ const config = request.delivery.target.deadLetterConfig;
37
+ assertDefined(config, "A dead letter requires a dead-letter queue");
38
+ const { accountId, regionName, name } = config.queue;
39
+ const scope = this.simAws.accountRegionScope(accountId, regionName);
40
+ await new SimEventBridgeDeadLetterQueue({ scope }).deliver(request, {
41
+ name,
42
+ arn: config.arn,
43
+ });
44
+ }
29
45
  /**
30
46
  * Hand the delivery to the one destination that knows this service.
31
47
  */
@@ -0,0 +1,9 @@
1
+ import type { SimEventBridgeDeadLetterRequest } from "./sim-event-bridge-delivery.js";
2
+ import type { SimEventBridgeStringAttribute } from "./sim-event-bridge-queue-message.js";
3
+ /**
4
+ * The message attributes EventBridge documents for a dead letter.
5
+ *
6
+ * `EXHAUSTED_RETRY_CONDITION` is there only for a failure that was retried
7
+ * until a limit ran out.
8
+ */
9
+ export declare function simEventBridgeDeadLetterAttributes(request: SimEventBridgeDeadLetterRequest): Readonly<Record<string, SimEventBridgeStringAttribute>>;
@@ -0,0 +1,47 @@
1
+ import { SimEventBridgeDeliveryNotPermitted, SimEventBridgeTargetNotFound, } from "../error/sim-event-bridge-delivery.error.js";
2
+ /**
3
+ * The `ERROR_CODE` a dead letter carries for a failure.
4
+ *
5
+ * EventBridge documents a list of codes without saying which failure gets
6
+ * which. A target that refuses EventBridge is `NO_PERMISSIONS`, a target that
7
+ * is not there is `NO_RESOURCE`, and anything else the target answered with
8
+ * is `ERROR_FROM_TARGET`.
9
+ */
10
+ function errorCode(error) {
11
+ if (error instanceof SimEventBridgeDeliveryNotPermitted) {
12
+ return "NO_PERMISSIONS";
13
+ }
14
+ if (error instanceof SimEventBridgeTargetNotFound) {
15
+ return "NO_RESOURCE";
16
+ }
17
+ return "ERROR_FROM_TARGET";
18
+ }
19
+ function errorMessage(error) {
20
+ if (error instanceof Error) {
21
+ return error.message;
22
+ }
23
+ return String(error);
24
+ }
25
+ /**
26
+ * The message attributes EventBridge documents for a dead letter.
27
+ *
28
+ * `EXHAUSTED_RETRY_CONDITION` is there only for a failure that was retried
29
+ * until a limit ran out.
30
+ */
31
+ export function simEventBridgeDeadLetterAttributes(request) {
32
+ const { delivery, error, retryAttempts, exhaustedCondition } = request;
33
+ const attributes = {
34
+ RULE_ARN: delivery.ruleArn,
35
+ TARGET_ARN: delivery.target.arn.value,
36
+ ERROR_CODE: errorCode(error),
37
+ ERROR_MESSAGE: errorMessage(error),
38
+ RETRY_ATTEMPTS: String(retryAttempts),
39
+ ...(exhaustedCondition !== undefined && {
40
+ EXHAUSTED_RETRY_CONDITION: exhaustedCondition,
41
+ }),
42
+ };
43
+ return Object.fromEntries(Object.entries(attributes).map(([name, value]) => [
44
+ name,
45
+ { DataType: "String", StringValue: value },
46
+ ]));
47
+ }
@@ -0,0 +1,24 @@
1
+ import type { SimAwsAccountRegionContainer } from "../../aws/sim-aws-account-region-scope.js";
2
+ import { type SimEventBridgeDeadLetterRequest } from "./sim-event-bridge-delivery.js";
3
+ interface SimEventBridgeDeadLetterQueueProperties {
4
+ readonly scope: SimAwsAccountRegionContainer;
5
+ }
6
+ /**
7
+ * A target's dead-letter queue, in the Account and Region its ARN names.
8
+ *
9
+ * The message body is the event, as JSON, whatever `Input` the target had.
10
+ * The message attributes say which rule and target gave up on it, and why.
11
+ */
12
+ export declare class SimEventBridgeDeadLetterQueue {
13
+ private readonly scope;
14
+ constructor(properties: SimEventBridgeDeadLetterQueueProperties);
15
+ /**
16
+ * Put an event EventBridge gave up on onto the queue, if it admits
17
+ * EventBridge for this rule.
18
+ */
19
+ deliver(request: SimEventBridgeDeadLetterRequest, queue: {
20
+ readonly name: string;
21
+ readonly arn: string;
22
+ }): Promise<void>;
23
+ }
24
+ export {};
@@ -0,0 +1,28 @@
1
+ import { simEventBridgeDeadLetterAttributes } from "./sim-event-bridge-dead-letter-attributes.js";
2
+ import { simEventBridgeDeliverySource, } from "./sim-event-bridge-delivery.js";
3
+ import { sendEventBridgeQueueMessage } from "./sim-event-bridge-queue-message.js";
4
+ /**
5
+ * A target's dead-letter queue, in the Account and Region its ARN names.
6
+ *
7
+ * The message body is the event, as JSON, whatever `Input` the target had.
8
+ * The message attributes say which rule and target gave up on it, and why.
9
+ */
10
+ export class SimEventBridgeDeadLetterQueue {
11
+ scope;
12
+ constructor(properties) {
13
+ this.scope = properties.scope;
14
+ }
15
+ /**
16
+ * Put an event EventBridge gave up on onto the queue, if it admits
17
+ * EventBridge for this rule.
18
+ */
19
+ async deliver(request, queue) {
20
+ await sendEventBridgeQueueMessage(this.scope, {
21
+ queueName: queue.name,
22
+ queueArn: queue.arn,
23
+ body: JSON.stringify(request.delivery.event.toEnvelope()),
24
+ attributes: simEventBridgeDeadLetterAttributes(request),
25
+ source: simEventBridgeDeliverySource(request.delivery),
26
+ });
27
+ }
28
+ }
@@ -0,0 +1,41 @@
1
+ import type { BackgroundScheduler } from "../../../util/background/background.js";
2
+ import type { SimEventBridgeDeliveryRequest, SimEventBridgeDeliveryTargets } from "./sim-event-bridge-delivery.js";
3
+ interface SimEventBridgeDeliveryAttemptProperties {
4
+ readonly request: SimEventBridgeDeliveryRequest;
5
+ readonly endpoints: SimEventBridgeDeliveryTargets;
6
+ readonly background: BackgroundScheduler;
7
+ readonly record: (error: unknown) => void;
8
+ }
9
+ /**
10
+ * One event's delivery to one target, across its first attempt and any
11
+ * retries.
12
+ *
13
+ * A target with a `RetryPolicy` has a failed attempt retried on the
14
+ * simulation's clock, one second later, then two, four and so on. Retries stop
15
+ * at the policy's retry limit, or at its event age counted from the first
16
+ * attempt. A missing target or a refusal is never retried, because the same
17
+ * request gets the same answer, and EventBridge sends those straight to the
18
+ * dead-letter queue.
19
+ *
20
+ * An event given up on goes to the target's dead-letter queue where it names
21
+ * one. A failure is recorded where it names none, or where the dead-letter
22
+ * queue refuses it in turn.
23
+ */
24
+ export declare class SimEventBridgeDeliveryAttempt {
25
+ private readonly properties;
26
+ private readonly firstAttemptAt;
27
+ private retryAttempts;
28
+ constructor(properties: SimEventBridgeDeliveryAttemptProperties);
29
+ /**
30
+ * Make the first attempt.
31
+ */
32
+ start(): Promise<void>;
33
+ private attempt;
34
+ private failed;
35
+ /**
36
+ * The first retry waits one second, and every following wait doubles.
37
+ */
38
+ private retryDueTime;
39
+ private abandon;
40
+ }
41
+ export {};
@@ -0,0 +1,97 @@
1
+ import { SimEventBridgeDeliveryNotPermitted, SimEventBridgeTargetNotFound, } from "../error/sim-event-bridge-delivery.error.js";
2
+ const millisecondsPerSecond = 1000;
3
+ /**
4
+ * One event's delivery to one target, across its first attempt and any
5
+ * retries.
6
+ *
7
+ * A target with a `RetryPolicy` has a failed attempt retried on the
8
+ * simulation's clock, one second later, then two, four and so on. Retries stop
9
+ * at the policy's retry limit, or at its event age counted from the first
10
+ * attempt. A missing target or a refusal is never retried, because the same
11
+ * request gets the same answer, and EventBridge sends those straight to the
12
+ * dead-letter queue.
13
+ *
14
+ * An event given up on goes to the target's dead-letter queue where it names
15
+ * one. A failure is recorded where it names none, or where the dead-letter
16
+ * queue refuses it in turn.
17
+ */
18
+ export class SimEventBridgeDeliveryAttempt {
19
+ properties;
20
+ firstAttemptAt;
21
+ retryAttempts = 0;
22
+ constructor(properties) {
23
+ this.properties = properties;
24
+ this.firstAttemptAt = properties.background.now();
25
+ }
26
+ /**
27
+ * Make the first attempt.
28
+ */
29
+ async start() {
30
+ await this.attempt();
31
+ }
32
+ async attempt() {
33
+ try {
34
+ await this.properties.endpoints.deliver(this.properties.request);
35
+ }
36
+ catch (error) {
37
+ await this.failed(error);
38
+ }
39
+ }
40
+ async failed(error) {
41
+ const policy = this.properties.request.target.retryPolicy;
42
+ if (policy === undefined || isPermanent(error)) {
43
+ await this.abandon(error, undefined);
44
+ return;
45
+ }
46
+ if (this.retryAttempts >= policy.maximumRetryAttempts) {
47
+ await this.abandon(error, "MaximumRetryAttempts");
48
+ return;
49
+ }
50
+ const retryDue = this.retryDueTime();
51
+ const expiresAt = new Date(this.firstAttemptAt.getTime() +
52
+ policy.maximumEventAgeInSeconds * millisecondsPerSecond);
53
+ if (retryDue > expiresAt) {
54
+ this.properties.background.scheduleAt(expiresAt, async () => {
55
+ await this.abandon(error, "MaximumEventAgeInSeconds");
56
+ });
57
+ return;
58
+ }
59
+ this.properties.background.scheduleAt(retryDue, async () => {
60
+ this.retryAttempts += 1;
61
+ await this.attempt();
62
+ });
63
+ }
64
+ /**
65
+ * The first retry waits one second, and every following wait doubles.
66
+ */
67
+ retryDueTime() {
68
+ const delaySeconds = 2 ** this.retryAttempts;
69
+ return new Date(this.properties.background.now().getTime() +
70
+ delaySeconds * millisecondsPerSecond);
71
+ }
72
+ async abandon(error, exhaustedCondition) {
73
+ const { request, endpoints, record } = this.properties;
74
+ if (request.target.deadLetterConfig === undefined) {
75
+ record(error);
76
+ return;
77
+ }
78
+ try {
79
+ await endpoints.deadLetter({
80
+ delivery: request,
81
+ error,
82
+ retryAttempts: this.retryAttempts,
83
+ exhaustedCondition,
84
+ });
85
+ }
86
+ catch (deadLetterError) {
87
+ record(deadLetterError);
88
+ }
89
+ }
90
+ }
91
+ /**
92
+ * Whether a failure would come back the same however often it was retried.
93
+ */
94
+ function isPermanent(error) {
95
+ return (error instanceof SimEventBridgeDeliveryNotPermitted ||
96
+ error instanceof SimEventBridgeTargetNotFound);
97
+ }
@@ -1,7 +1,5 @@
1
- import { SimSqsServiceSendAuthorizer } from "../../sqs/command/authorize/sim-sqs-service-send-authorizer.js";
2
- import { SimEventBridgeDeliveryNotPermitted, SimEventBridgeTargetNotFound, } from "../error/sim-event-bridge-delivery.error.js";
3
- import { simEventBridgeDeliveryJson, simEventBridgeDeliverySource, simEventBridgeServicePrincipal, } from "./sim-event-bridge-delivery.js";
4
- import { simScopeIamAuthZ } from "../../iam/authorize/sim-iam-region-auth-z.js";
1
+ import { simEventBridgeDeliveryJson, simEventBridgeDeliverySource, } from "./sim-event-bridge-delivery.js";
2
+ import { sendEventBridgeQueueMessage } from "./sim-event-bridge-queue-message.js";
5
3
  /**
6
4
  * A queue a simulated rule is sending an event to, in the Account and Region
7
5
  * the target ARN names.
@@ -19,39 +17,11 @@ export class SimEventBridgeDeliveryQueue {
19
17
  * Put the event on the queue, if it admits EventBridge for this rule.
20
18
  */
21
19
  async deliver(request) {
22
- const source = simEventBridgeDeliverySource(request);
23
- const targetArn = request.target.arn;
24
- const queue = this.scope.sqs().findQueue(targetArn.resource);
25
- if (queue === undefined) {
26
- throw new SimEventBridgeTargetNotFound(`${targetArn.value} is not a simulated SQS queue.`);
27
- }
28
- const decision = new SimSqsServiceSendAuthorizer({
29
- iam: simScopeIamAuthZ(this.scope),
30
- }).authorize({
31
- queue,
32
- servicePrincipal: simEventBridgeServicePrincipal,
33
- ...source,
34
- });
35
- if (decision.isDenied) {
36
- throw new SimEventBridgeDeliveryNotPermitted(`The queue policy of ${targetArn.value} does not allow ` +
37
- `${simEventBridgeServicePrincipal} to send to it for ` +
38
- `${source.sourceArn}. Grant sqs:SendMessage with the queue's ` +
39
- `Policy attribute.`);
40
- }
41
- // Sent through the ordinary SendMessage path, so a delivered event is the
42
- // same thing an SDK caller would have sent, and is authorized again on the
43
- // way in.
44
- await this.scope.sqs().sendMessage({
45
- input: {
46
- QueueUrl: queue.arn.url,
47
- MessageBody: simEventBridgeDeliveryJson(request),
48
- },
49
- }, {
50
- caller: {
51
- kind: "service",
52
- service: simEventBridgeServicePrincipal,
53
- },
54
- ...source,
20
+ await sendEventBridgeQueueMessage(this.scope, {
21
+ queueName: request.target.arn.resource,
22
+ queueArn: request.target.arn.value,
23
+ body: simEventBridgeDeliveryJson(request),
24
+ source: simEventBridgeDeliverySource(request),
55
25
  });
56
26
  }
57
27
  }
@@ -0,0 +1,25 @@
1
+ import { SimClockControl } from "../../../util/clock/sim-clock-control.js";
2
+ import { SimEventBridge } from "../sim-event-bridge.js";
3
+ import type { SimEventTargetRetryPolicyInput } from "../target/sim-event-target-retry-policy.js";
4
+ import type { SimEventBridgeDeadLetterRequest } from "./sim-event-bridge-delivery.js";
5
+ import { type SimEventBridgeFailingTargetsOptions } from "./sim-event-bridge-failing-targets.test-support.js";
6
+ export interface RetrySimulation {
7
+ readonly eventBridge: SimEventBridge;
8
+ readonly clock: SimClockControl;
9
+ readonly attempts: Date[];
10
+ readonly deadLetters: SimEventBridgeDeadLetterRequest[];
11
+ }
12
+ /**
13
+ * A simulated EventBridge whose targets fail as the options say.
14
+ */
15
+ export declare function retrySimulation(options: SimEventBridgeFailingTargetsOptions): RetrySimulation;
16
+ /**
17
+ * Add a rule with one target carrying the given retry policy and a
18
+ * dead-letter queue, and put one matching event.
19
+ */
20
+ export declare function putOrder(simulation: RetrySimulation, target: {
21
+ readonly RetryPolicy?: SimEventTargetRetryPolicyInput;
22
+ readonly DeadLetterConfig?: {
23
+ readonly Arn: string;
24
+ };
25
+ }): Promise<void>;
@@ -0,0 +1,60 @@
1
+ import { BackgroundTasks } from "../../../util/background/background.js";
2
+ import { SimClockControl } from "../../../util/clock/sim-clock-control.js";
3
+ import { SimControllableClock } from "../../../util/clock/sim-controllable-clock.js";
4
+ import { SimFixedClock } from "../../../util/clock/sim-clock.js";
5
+ import { SimEventBridge } from "../sim-event-bridge.js";
6
+ import { SimEventBridgeFailingTargets, } from "./sim-event-bridge-failing-targets.test-support.js";
7
+ const startedAt = new Date("2026-10-07T09:00:00.000Z");
8
+ /**
9
+ * A simulated EventBridge whose targets fail as the options say.
10
+ */
11
+ export function retrySimulation(options) {
12
+ const clock = new SimControllableClock({
13
+ base: new SimFixedClock(startedAt),
14
+ });
15
+ const background = new BackgroundTasks({ clock });
16
+ const targets = new SimEventBridgeFailingTargets(clock, options);
17
+ return {
18
+ eventBridge: new SimEventBridge({ background, deliveryTargets: targets }),
19
+ clock: new SimClockControl({ clock, background }),
20
+ attempts: targets.attempts,
21
+ deadLetters: targets.deadLetters,
22
+ };
23
+ }
24
+ /**
25
+ * Add a rule with one target carrying the given retry policy and a
26
+ * dead-letter queue, and put one matching event.
27
+ */
28
+ export async function putOrder(simulation, target) {
29
+ const { eventBridge } = simulation;
30
+ await eventBridge.putRule({
31
+ input: {
32
+ Name: "orders",
33
+ EventPattern: JSON.stringify({ source: ["orders.service"] }),
34
+ },
35
+ });
36
+ await eventBridge.putTargets({
37
+ input: {
38
+ Rule: "orders",
39
+ Targets: [
40
+ {
41
+ Id: "fulfilment",
42
+ Arn: "arn:aws:sqs:us-east-1:888888888888:fulfilment",
43
+ ...target,
44
+ },
45
+ ],
46
+ },
47
+ });
48
+ await eventBridge.putEvents({
49
+ input: {
50
+ Entries: [
51
+ {
52
+ Source: "orders.service",
53
+ DetailType: "OrderPlaced",
54
+ Detail: JSON.stringify({ orderId: "order-1" }),
55
+ },
56
+ ],
57
+ },
58
+ });
59
+ await simulation.clock.advanceBy({ milliseconds: 0 });
60
+ }
@@ -37,6 +37,28 @@ export interface SimEventBridgeDeliveryTargets {
37
37
  * EventBridge.
38
38
  */
39
39
  deliver(request: SimEventBridgeDeliveryRequest): Promise<void>;
40
+ /**
41
+ * Send an event EventBridge gave up delivering to its target's dead-letter
42
+ * queue, refusing if the queue does not admit EventBridge.
43
+ */
44
+ deadLetter(request: SimEventBridgeDeadLetterRequest): Promise<void>;
45
+ }
46
+ /**
47
+ * Which of a target's retry limits ran out, as the dead-letter message's
48
+ * `EXHAUSTED_RETRY_CONDITION` names it.
49
+ */
50
+ export type SimEventBridgeExhaustedRetryCondition = "MaximumEventAgeInSeconds" | "MaximumRetryAttempts";
51
+ /**
52
+ * One event on its way to a target's dead-letter queue, and why.
53
+ *
54
+ * A failure EventBridge does not retry, such as a target that is missing or
55
+ * refuses EventBridge, carries no exhausted condition.
56
+ */
57
+ export interface SimEventBridgeDeadLetterRequest {
58
+ readonly delivery: SimEventBridgeDeliveryRequest;
59
+ readonly error: unknown;
60
+ readonly retryAttempts: number;
61
+ readonly exhaustedCondition: SimEventBridgeExhaustedRetryCondition | undefined;
40
62
  }
41
63
  /**
42
64
  * What a target's policy is asked about, as IAM condition values.
@@ -0,0 +1,23 @@
1
+ import type { SimClock } from "../../../util/clock/sim-clock.js";
2
+ import type { SimEventBridgeDeadLetterRequest, SimEventBridgeDeliveryTargets } from "./sim-event-bridge-delivery.js";
3
+ /**
4
+ * How a set of failing targets behaves.
5
+ */
6
+ export interface SimEventBridgeFailingTargetsOptions {
7
+ readonly succeedsOnAttempt?: number;
8
+ readonly error?: Error;
9
+ readonly deadLetterRefused?: boolean;
10
+ }
11
+ /**
12
+ * Targets that fail with an error until a given attempt, and whose
13
+ * dead-letter queue takes or refuses what it is sent, keeping both.
14
+ */
15
+ export declare class SimEventBridgeFailingTargets implements SimEventBridgeDeliveryTargets {
16
+ private readonly clock;
17
+ private readonly options;
18
+ readonly attempts: Date[];
19
+ readonly deadLetters: SimEventBridgeDeadLetterRequest[];
20
+ constructor(clock: SimClock, options: SimEventBridgeFailingTargetsOptions);
21
+ deliver(): Promise<void>;
22
+ deadLetter(request: SimEventBridgeDeadLetterRequest): Promise<void>;
23
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Targets that fail with an error until a given attempt, and whose
3
+ * dead-letter queue takes or refuses what it is sent, keeping both.
4
+ */
5
+ export class SimEventBridgeFailingTargets {
6
+ clock;
7
+ options;
8
+ attempts = [];
9
+ deadLetters = [];
10
+ constructor(clock, options) {
11
+ this.clock = clock;
12
+ this.options = options;
13
+ }
14
+ deliver() {
15
+ this.attempts.push(this.clock.now());
16
+ if (this.attempts.length === this.options.succeedsOnAttempt) {
17
+ return Promise.resolve();
18
+ }
19
+ return Promise.reject(this.options.error ?? new Error("fulfilment is unavailable"));
20
+ }
21
+ deadLetter(request) {
22
+ if (this.options.deadLetterRefused === true) {
23
+ return Promise.reject(new Error("dead-letter queue refused"));
24
+ }
25
+ this.deadLetters.push(request);
26
+ return Promise.resolve();
27
+ }
28
+ }
@@ -1,4 +1,4 @@
1
- import type { SimEventBridgeDeliveryRequest, SimEventBridgeDeliveryTargets } from "./sim-event-bridge-delivery.js";
1
+ import type { SimEventBridgeDeadLetterRequest, SimEventBridgeDeliveryRequest, SimEventBridgeDeliveryTargets } from "./sim-event-bridge-delivery.js";
2
2
  /**
3
3
  * The targets of a simulated EventBridge built on its own, which are none.
4
4
  *
@@ -13,4 +13,8 @@ export declare class SimEventBridgeNoDeliveryTargets implements SimEventBridgeDe
13
13
  * Refuse the delivery, saying why there was nowhere to make it.
14
14
  */
15
15
  deliver(request: SimEventBridgeDeliveryRequest): Promise<void>;
16
+ /**
17
+ * Refuse the dead letter, saying why there was nowhere to send it.
18
+ */
19
+ deadLetter(request: SimEventBridgeDeadLetterRequest): Promise<void>;
16
20
  }
@@ -17,4 +17,13 @@ export class SimEventBridgeNoDeliveryTargets {
17
17
  `${request.target.arn.value} was not reached. Reach EventBridge ` +
18
18
  `through SimAws for a rule that delivers.`));
19
19
  }
20
+ /**
21
+ * Refuse the dead letter, saying why there was nowhere to send it.
22
+ */
23
+ deadLetter(request) {
24
+ return Promise.reject(new SimEventBridgeTargetNotFound(`This simulated EventBridge has no queues to send dead letters to, ` +
25
+ `so the dead-letter queue of target ${request.delivery.target.id} ` +
26
+ `was not reached. Reach EventBridge through SimAws for a ` +
27
+ `dead-letter queue that receives.`));
28
+ }
20
29
  }
@@ -0,0 +1,28 @@
1
+ import type { SimAwsAccountRegionContainer } from "../../aws/sim-aws-account-region-scope.js";
2
+ import { type SimEventBridgeDeliverySource } from "./sim-event-bridge-delivery.js";
3
+ /**
4
+ * One SQS message attribute, as SendMessage takes it.
5
+ */
6
+ export interface SimEventBridgeStringAttribute {
7
+ readonly DataType: string;
8
+ readonly StringValue: string;
9
+ }
10
+ /**
11
+ * One message EventBridge sends to a queue, and the queue it is for.
12
+ */
13
+ export interface SimEventBridgeQueueMessage {
14
+ readonly queueName: string;
15
+ readonly queueArn: string;
16
+ readonly body: string;
17
+ readonly attributes?: Readonly<Record<string, SimEventBridgeStringAttribute>>;
18
+ readonly source: SimEventBridgeDeliverySource;
19
+ }
20
+ /**
21
+ * Send a message to a queue as EventBridge, if the queue admits it for the
22
+ * rule.
23
+ *
24
+ * A target queue and a dead-letter queue are reached the same way. Both are
25
+ * sent to as `events.amazonaws.com`, and both need the queue policy to admit
26
+ * that principal for the rule.
27
+ */
28
+ export declare function sendEventBridgeQueueMessage(scope: SimAwsAccountRegionContainer, message: SimEventBridgeQueueMessage): Promise<void>;
@@ -0,0 +1,45 @@
1
+ import { simScopeIamAuthZ } from "../../iam/authorize/sim-iam-region-auth-z.js";
2
+ import { SimSqsServiceSendAuthorizer } from "../../sqs/command/authorize/sim-sqs-service-send-authorizer.js";
3
+ import { SimEventBridgeDeliveryNotPermitted, SimEventBridgeTargetNotFound, } from "../error/sim-event-bridge-delivery.error.js";
4
+ import { simEventBridgeServicePrincipal, } from "./sim-event-bridge-delivery.js";
5
+ /**
6
+ * Send a message to a queue as EventBridge, if the queue admits it for the
7
+ * rule.
8
+ *
9
+ * A target queue and a dead-letter queue are reached the same way. Both are
10
+ * sent to as `events.amazonaws.com`, and both need the queue policy to admit
11
+ * that principal for the rule.
12
+ */
13
+ export async function sendEventBridgeQueueMessage(scope, message) {
14
+ const { queueArn, source } = message;
15
+ const queue = scope.sqs().findQueue(message.queueName);
16
+ if (queue === undefined) {
17
+ throw new SimEventBridgeTargetNotFound(`${queueArn} is not a simulated SQS queue.`);
18
+ }
19
+ const decision = new SimSqsServiceSendAuthorizer({
20
+ iam: simScopeIamAuthZ(scope),
21
+ }).authorize({
22
+ queue,
23
+ servicePrincipal: simEventBridgeServicePrincipal,
24
+ ...source,
25
+ });
26
+ if (decision.isDenied) {
27
+ throw new SimEventBridgeDeliveryNotPermitted(`The queue policy of ${queueArn} does not allow ` +
28
+ `${simEventBridgeServicePrincipal} to send to it for ` +
29
+ `${source.sourceArn}. Grant sqs:SendMessage with the queue's ` +
30
+ `Policy attribute.`);
31
+ }
32
+ // Sent through the ordinary SendMessage path, so a delivered event is the
33
+ // same thing an SDK caller would have sent, and is authorized again on the
34
+ // way in.
35
+ await scope.sqs().sendMessage({
36
+ input: {
37
+ QueueUrl: queue.arn.url,
38
+ MessageBody: message.body,
39
+ MessageAttributes: message.attributes,
40
+ },
41
+ }, {
42
+ caller: { kind: "service", service: simEventBridgeServicePrincipal },
43
+ ...source,
44
+ });
45
+ }
@@ -1,10 +1,12 @@
1
+ import type { BackgroundScheduler } from "../../../util/background/background.js";
1
2
  import type { SimEventBridgeDeliveryRequest, SimEventBridgeDeliveryTargets } from "./sim-event-bridge-delivery.js";
2
3
  import type { SimEventBridgeDeliveryFailure } from "./sim-event-bridge-delivery-failures.js";
3
4
  interface SimEventBridgeTargetDeliveryProperties {
4
5
  readonly endpoints: SimEventBridgeDeliveryTargets;
6
+ readonly background: BackgroundScheduler;
5
7
  }
6
8
  /**
7
- * Makes one delivery and keeps whatever went wrong.
9
+ * Makes one delivery, with its retries, and keeps whatever went wrong.
8
10
  *
9
11
  * A failure is recorded rather than thrown, because these run as background
10
12
  * tasks and one left rejected would fail an unrelated
@@ -14,6 +16,7 @@ interface SimEventBridgeTargetDeliveryProperties {
14
16
  */
15
17
  export declare class SimEventBridgeTargetDelivery {
16
18
  private readonly endpoints;
19
+ private readonly background;
17
20
  private readonly failures;
18
21
  constructor(properties: SimEventBridgeTargetDeliveryProperties);
19
22
  /**