@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
@@ -13,6 +13,10 @@ export default defineConfig({
13
13
  files: ["**/*.cff.js"],
14
14
  extends: [tseslint.configs.disableTypeChecked],
15
15
  languageOptions: {
16
+ // The typescript-eslint rules below read scope information only its own
17
+ // parser produces. Under ESLint's default parser, `no-unused-vars` takes
18
+ // every reference for a type-only one and reports each variable.
19
+ parser: tseslint.parser,
16
20
  parserOptions: {
17
21
  projectService: false,
18
22
  },
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Whether a declared Parameter `Type` holds a list.
3
+ *
4
+ * `CommaDelimitedList` and every `List<...>` type do, whatever the member
5
+ * type. A `Ref` to one resolves to a list of strings, `List<Number>` included.
6
+ */
7
+ export declare function isSimCfnParameterListType(type: string | undefined): boolean;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Whether a declared Parameter `Type` holds a list.
3
+ *
4
+ * `CommaDelimitedList` and every `List<...>` type do, whatever the member
5
+ * type. A `Ref` to one resolves to a list of strings, `List<Number>` included.
6
+ */
7
+ export function isSimCfnParameterListType(type) {
8
+ return type === "CommaDelimitedList" || (type?.startsWith("List<") ?? false);
9
+ }
@@ -13,6 +13,9 @@ interface SimCfnParameterValuesProperties {
13
13
  * the command wins over a template `Default`, and a Parameter declared as a
14
14
  * Parameter Store value type reads whichever of the two it ended up with as the
15
15
  * name to read.
16
+ *
17
+ * A `CommaDelimitedList` or `List<...>` Parameter is given its value as one
18
+ * comma-separated string, and holds it as the list of members.
16
19
  */
17
20
  export declare class SimCfnParameterValues {
18
21
  private readonly values;
@@ -29,5 +32,12 @@ export declare class SimCfnParameterValues {
29
32
  * `Default`.
30
33
  */
31
34
  private recordDefaults;
35
+ /**
36
+ * Split the value of each list-typed Parameter on its commas.
37
+ *
38
+ * Each member is trimmed of the spaces around it, as CloudFormation trims
39
+ * them. A value with no commas is a list of one.
40
+ */
41
+ private splitLists;
32
42
  }
33
43
  export {};
@@ -1,3 +1,4 @@
1
+ import { isSimCfnParameterListType } from "./sim-cfn-parameter-list-type.js";
1
2
  import { SimCfnParameterStoreValues } from "./store/sim-cfn-parameter-store-values.js";
2
3
  /**
3
4
  * The runtime value of every Parameter a Stack was created with.
@@ -6,6 +7,9 @@ import { SimCfnParameterStoreValues } from "./store/sim-cfn-parameter-store-valu
6
7
  * the command wins over a template `Default`, and a Parameter declared as a
7
8
  * Parameter Store value type reads whichever of the two it ended up with as the
8
9
  * name to read.
10
+ *
11
+ * A `CommaDelimitedList` or `List<...>` Parameter is given its value as one
12
+ * comma-separated string, and holds it as the list of members.
9
13
  */
10
14
  export class SimCfnParameterValues {
11
15
  values;
@@ -15,6 +19,7 @@ export class SimCfnParameterValues {
15
19
  this.storeValues = new SimCfnParameterStoreValues({ store });
16
20
  this.values = new Map(Object.entries(supplied));
17
21
  this.recordDefaults(definitions);
22
+ this.splitLists(definitions);
18
23
  this.storeValues.applyTo(this.values, definitions);
19
24
  }
20
25
  /** Every Parameter that resolved to a stand-in value. */
@@ -39,4 +44,19 @@ export class SimCfnParameterValues {
39
44
  }
40
45
  }
41
46
  }
47
+ /**
48
+ * Split the value of each list-typed Parameter on its commas.
49
+ *
50
+ * Each member is trimmed of the spaces around it, as CloudFormation trims
51
+ * them. A value with no commas is a list of one.
52
+ */
53
+ splitLists(definitions) {
54
+ for (const [parameterName, definition] of definitions) {
55
+ const value = this.values.get(parameterName);
56
+ if (typeof value === "string" &&
57
+ isSimCfnParameterListType(definition.Type)) {
58
+ this.values.set(parameterName, value.split(",").map((member) => member.trim()));
59
+ }
60
+ }
61
+ }
42
62
  }
@@ -5,9 +5,10 @@ import type { SimCfnParameterStoreReader } from "./store/sim-cfn-parameter-store
5
5
  * defaults and Parameter Store reads have been applied.
6
6
  *
7
7
  * A Parameter is given its value as a string, because AWS CloudFormation
8
- * command inputs provide ParameterValue as one. A Parameter declared as
9
- * `AWS::SSM::Parameter::Value<List<String>>` is given a parameter name and
10
- * resolves to the stored value split into a list, so a resolved value can be
8
+ * command inputs provide ParameterValue as one. A `CommaDelimitedList` or
9
+ * `List<...>` Parameter resolves to that string split into a list. So does an
10
+ * `AWS::SSM::Parameter::Value<List<String>>` Parameter, which is given a
11
+ * parameter name and splits the value stored under it. A resolved value can be
11
12
  * either.
12
13
  */
13
14
  export type SimCloudFormationParameterValue = string | string[];
@@ -61,8 +62,9 @@ export type SimCfnParametersContext = Pick<SimCfnParametersProperties, "definiti
61
62
  *
62
63
  * Most fields are recorded for structural typing/documentation. Runtime
63
64
  * behavior applies string Default values when a command does not provide an
64
- * explicit Parameter value, and reads an `AWS::SSM::Parameter::Value<...>`
65
- * Type from simulated Parameter Store.
65
+ * explicit Parameter value, splits the value of a `CommaDelimitedList` or
66
+ * `List<...>` Type, and reads an `AWS::SSM::Parameter::Value<...>` Type from
67
+ * simulated Parameter Store.
66
68
  */
67
69
  export interface SimCfnParameterDefinition {
68
70
  readonly Type?: string | undefined;
@@ -1,3 +1,4 @@
1
+ import { isSimCfnParameterListType } from "../sim-cfn-parameter-list-type.js";
1
2
  /** The Parameter type that holds a Parameter Store name. */
2
3
  const parameterStoreValuePrefix = "AWS::SSM::Parameter::Value<";
3
4
  /**
@@ -14,6 +15,6 @@ export function simCfnParameterStoreValueType(declaredType) {
14
15
  }
15
16
  const innerType = declaredType.slice(parameterStoreValuePrefix.length, -1);
16
17
  return {
17
- list: innerType === "CommaDelimitedList" || innerType.startsWith("List<"),
18
+ list: isSimCfnParameterListType(innerType),
18
19
  };
19
20
  }
@@ -9,19 +9,25 @@ import type { SimCfnResolveContext } from "../../../resolve/sim-cfn-resolve-cont
9
9
  * {
10
10
  * "Fn::Join": ["-", ["a", "b", { "Ref": "Name" }]]
11
11
  * }
12
+ *
13
+ * The values may also come from one function that returns the whole list:
14
+ *
15
+ * {
16
+ * "Fn::Join": [",", { "Fn::GetAtt": ["Zone", "NameServers"] }]
17
+ * }
12
18
  */
13
19
  export declare class SimCfnFnJoin extends SimCfnNode {
14
20
  private readonly delimiter;
15
21
  private readonly values;
16
- constructor(delimiter: string, values: readonly SimCfnNode[]);
22
+ constructor(delimiter: string, values: SimCfnNode);
17
23
  /**
18
- * Resolve each value and join them with the delimiter.
24
+ * Resolve the values and join them with the delimiter.
19
25
  *
20
- * Joining happens only when every value has resolved to a string. If any
21
- * value is still an unresolved expression (for example a Resource `Ref`
22
- * during the up-front pass before Resources exist), this node re-emits
23
- * itself in template form. A later resolution pass can finish it once the
24
- * referenced Resources are available.
26
+ * Joining happens only when the values have resolved to a list of strings.
27
+ * If the list, or any value in it, is still an unresolved expression (for
28
+ * example a Resource `Ref` during the up-front pass before Resources exist),
29
+ * this node re-emits itself in template form. A later resolution pass can
30
+ * finish it once the referenced Resources are available.
25
31
  *
26
32
  * A dynamic reference can be assembled out of the joined values, as CDK
27
33
  * writes one whenever the secret it reads sits in the same Stack: the
@@ -32,16 +38,15 @@ export declare class SimCfnFnJoin extends SimCfnNode {
32
38
  */
33
39
  resolve(context: SimCfnResolveContext): SimCfnTemplateValue;
34
40
  /**
35
- * Collect referenced names from every joined value.
41
+ * Collect referenced names from the values.
36
42
  */
37
43
  referencedNames(): string[];
38
- private resolveValue;
39
44
  /**
40
- * Whether a resolved value is still an unresolved intrinsic expression.
45
+ * Refuse a value that can never become a string.
41
46
  *
42
- * Only object-shaped values (a preserved `Ref` or nested function object)
43
- * are treated as deferred. Other non-string primitives such as numbers are
44
- * genuine type errors.
47
+ * A string joins now, and an unresolved expression may join on a later
48
+ * pass. Anything else (a number, a nested list, a plain object) is a type
49
+ * error, as CloudFormation joins a list of strings only.
45
50
  */
46
- private isDeferredExpression;
51
+ private assertJoinable;
47
52
  }
@@ -1,4 +1,5 @@
1
1
  import { SimCfnNode } from "../../sim-cfn-node.js";
2
+ import { isSimCfnUnresolvedExpression } from "../../../value/sim-cfn-unresolved-expression.js";
2
3
  /**
3
4
  * Simulated CloudFormation `Fn::Join` intrinsic function.
4
5
  *
@@ -7,6 +8,12 @@ import { SimCfnNode } from "../../sim-cfn-node.js";
7
8
  * {
8
9
  * "Fn::Join": ["-", ["a", "b", { "Ref": "Name" }]]
9
10
  * }
11
+ *
12
+ * The values may also come from one function that returns the whole list:
13
+ *
14
+ * {
15
+ * "Fn::Join": [",", { "Fn::GetAtt": ["Zone", "NameServers"] }]
16
+ * }
10
17
  */
11
18
  export class SimCfnFnJoin extends SimCfnNode {
12
19
  delimiter;
@@ -17,13 +24,13 @@ export class SimCfnFnJoin extends SimCfnNode {
17
24
  this.values = values;
18
25
  }
19
26
  /**
20
- * Resolve each value and join them with the delimiter.
27
+ * Resolve the values and join them with the delimiter.
21
28
  *
22
- * Joining happens only when every value has resolved to a string. If any
23
- * value is still an unresolved expression (for example a Resource `Ref`
24
- * during the up-front pass before Resources exist), this node re-emits
25
- * itself in template form. A later resolution pass can finish it once the
26
- * referenced Resources are available.
29
+ * Joining happens only when the values have resolved to a list of strings.
30
+ * If the list, or any value in it, is still an unresolved expression (for
31
+ * example a Resource `Ref` during the up-front pass before Resources exist),
32
+ * this node re-emits itself in template form. A later resolution pass can
33
+ * finish it once the referenced Resources are available.
27
34
  *
28
35
  * A dynamic reference can be assembled out of the joined values, as CDK
29
36
  * writes one whenever the secret it reads sits in the same Stack: the
@@ -33,37 +40,49 @@ export class SimCfnFnJoin extends SimCfnNode {
33
40
  * out of its variables.
34
41
  */
35
42
  resolve(context) {
36
- const resolved = this.values.map((value) => this.resolveValue(value, context));
37
- if (resolved.every((value) => typeof value === "string")) {
38
- const joined = resolved.join(this.delimiter);
43
+ const values = this.values.resolve(context);
44
+ if (isSimCfnUnresolvedExpression(values)) {
45
+ return { "Fn::Join": [this.delimiter, values] };
46
+ }
47
+ if (!Array.isArray(values)) {
48
+ throw new TypeError(`Sim CloudFormation Fn::Join values must resolve to a list, got ${describe(values)}`);
49
+ }
50
+ for (const value of values) {
51
+ this.assertJoinable(value);
52
+ }
53
+ if (values.every((value) => typeof value === "string")) {
54
+ const joined = values.join(this.delimiter);
39
55
  return context.dynamicReferences?.substitute(joined) ?? joined;
40
56
  }
41
- return { "Fn::Join": [this.delimiter, resolved] };
57
+ return { "Fn::Join": [this.delimiter, values] };
42
58
  }
43
59
  /**
44
- * Collect referenced names from every joined value.
60
+ * Collect referenced names from the values.
45
61
  */
46
62
  referencedNames() {
47
- return this.values.flatMap((value) => value.referencedNames());
48
- }
49
- resolveValue(value, context) {
50
- const resolved = value.resolve(context);
51
- if (typeof resolved === "string") {
52
- return resolved;
53
- }
54
- if (this.isDeferredExpression(resolved)) {
55
- return resolved;
56
- }
57
- throw new TypeError(`Sim CloudFormation Fn::Join values must each resolve to a string, got ${typeof resolved}`);
63
+ return this.values.referencedNames();
58
64
  }
59
65
  /**
60
- * Whether a resolved value is still an unresolved intrinsic expression.
66
+ * Refuse a value that can never become a string.
61
67
  *
62
- * Only object-shaped values (a preserved `Ref` or nested function object)
63
- * are treated as deferred. Other non-string primitives such as numbers are
64
- * genuine type errors.
68
+ * A string joins now, and an unresolved expression may join on a later
69
+ * pass. Anything else (a number, a nested list, a plain object) is a type
70
+ * error, as CloudFormation joins a list of strings only.
65
71
  */
66
- isDeferredExpression(value) {
67
- return typeof value === "object" && value !== null;
72
+ assertJoinable(value) {
73
+ if (typeof value === "string" || isSimCfnUnresolvedExpression(value)) {
74
+ return;
75
+ }
76
+ throw new TypeError(`Sim CloudFormation Fn::Join values must each resolve to a string, got ${describe(value)}`);
77
+ }
78
+ }
79
+ /**
80
+ * Name a resolved value's type for an error message, telling a list and a
81
+ * null apart from an object.
82
+ */
83
+ function describe(value) {
84
+ if (Array.isArray(value)) {
85
+ return "list";
68
86
  }
87
+ return value === null ? "null" : typeof value;
69
88
  }
@@ -10,10 +10,13 @@ import type { SimCfnValueParser } from "../../value/sim-cfn-value-parser.type.js
10
10
  * "Fn::Join": ["-", ["a", { "Ref": "Name" }]]
11
11
  * }
12
12
  *
13
- * The parser validates only the Fn::Join envelope: a string delimiter and an
14
- * array of values. Each joined value is parsed through the recursive value
15
- * parser, because join entries may themselves be literals, Refs, other
16
- * intrinsic functions, arrays, or plain objects.
13
+ * The values argument goes through the recursive value parser. It may be a
14
+ * literal list, whose entries may themselves be literals, Refs or other
15
+ * intrinsic functions, or a function that returns a whole list (such as
16
+ * `Fn::GetAtt` on a hosted zone's `NameServers`, or `Fn::Split`). Whether it
17
+ * resolves to a list of strings is a resolution-time question, so it belongs
18
+ * to the node. A string, number or boolean in its place can never become a
19
+ * list, and is refused here.
17
20
  */
18
21
  export declare class SimCfnFnJoinParser {
19
22
  private readonly valueParser;
@@ -8,10 +8,13 @@ import { SimCfnFnJoin as SimCfnFunctionJoin } from "../../../node/fn/join/sim-cf
8
8
  * "Fn::Join": ["-", ["a", { "Ref": "Name" }]]
9
9
  * }
10
10
  *
11
- * The parser validates only the Fn::Join envelope: a string delimiter and an
12
- * array of values. Each joined value is parsed through the recursive value
13
- * parser, because join entries may themselves be literals, Refs, other
14
- * intrinsic functions, arrays, or plain objects.
11
+ * The values argument goes through the recursive value parser. It may be a
12
+ * literal list, whose entries may themselves be literals, Refs or other
13
+ * intrinsic functions, or a function that returns a whole list (such as
14
+ * `Fn::GetAtt` on a hosted zone's `NameServers`, or `Fn::Split`). Whether it
15
+ * resolves to a list of strings is a resolution-time question, so it belongs
16
+ * to the node. A string, number or boolean in its place can never become a
17
+ * list, and is refused here.
15
18
  */
16
19
  export class SimCfnFnJoinParser {
17
20
  valueParser;
@@ -29,9 +32,10 @@ export class SimCfnFnJoinParser {
29
32
  if (typeof delimiter !== "string") {
30
33
  throw new TypeError("Sim CloudFormation Fn::Join delimiter must be a string");
31
34
  }
32
- if (!Array.isArray(values)) {
33
- throw new TypeError("Sim CloudFormation Fn::Join values must be an array");
35
+ if (typeof values !== "object" || values === null) {
36
+ throw new TypeError("Sim CloudFormation Fn::Join values must be a list or a function " +
37
+ "that returns one");
34
38
  }
35
- return new SimCfnFunctionJoin(delimiter, values.map((item) => this.valueParser.parse(item)));
39
+ return new SimCfnFunctionJoin(delimiter, this.valueParser.parse(values));
36
40
  }
37
41
  }
@@ -29,6 +29,10 @@ export declare class SimEventBusName {
29
29
  * refuse.
30
30
  */
31
31
  static of(value: string): SimEventBusName;
32
+ /**
33
+ * Whether this is the name of a partner event bus.
34
+ */
35
+ get isPartner(): boolean;
32
36
  /**
33
37
  * Whether this is the name of the default event bus.
34
38
  */
@@ -1,4 +1,5 @@
1
- import { SimEventBridgeUnsimulatedInputException, SimEventBridgeValidationException, } from "../error/sim-event-bridge.error.js";
1
+ import { SimEventBridgeValidationException } from "../error/sim-event-bridge.error.js";
2
+ import { isPartnerEventSourceName } from "../source/sim-partner-event-source-name.js";
2
3
  /**
3
4
  * The name of the event bus every Account has without creating one.
4
5
  *
@@ -10,7 +11,8 @@ export const defaultEventBusName = "default";
10
11
  /**
11
12
  * Real EventBridge allows alphanumerics, full stops, hyphens and underscores,
12
13
  * up to 256 characters. The `/` character is in the API's own pattern but only
13
- * for partner event bus names, which take the form `aws.partner/...`.
14
+ * for partner event bus names, which take the name of their partner event
15
+ * source, `aws.partner/<partner>/<name>`.
14
16
  */
15
17
  const eventBusNamePattern = /^[.\-_A-Za-z0-9]{1,256}$/;
16
18
  const partnerNameSeparator = "/";
@@ -46,17 +48,20 @@ export class SimEventBusName {
46
48
  * refuse.
47
49
  */
48
50
  static of(value) {
49
- if (value.includes(partnerNameSeparator)) {
50
- throw new SimEventBridgeUnsimulatedInputException(`Event bus name '${value}' carries a '/', which only a partner event ` +
51
- `bus name does. Partner event buses are not simulated.`);
51
+ if (eventBusNamePattern.test(value) || isPartnerEventSourceName(value)) {
52
+ return new this(value);
52
53
  }
53
- if (!eventBusNamePattern.test(value)) {
54
- throw new SimEventBridgeValidationException(`Invalid parameter: Name Reason: '${value}' is not a valid event bus ` +
55
- `name. Event bus names are made up of only letters, numbers, full ` +
56
- `stops, hyphens and underscores, and are between 1 and 256 ` +
57
- `characters long.`);
58
- }
59
- return new this(value);
54
+ throw new SimEventBridgeValidationException(`Invalid parameter: Name Reason: '${value}' is not a valid event bus ` +
55
+ `name. Event bus names are made up of only letters, numbers, full ` +
56
+ `stops, hyphens and underscores, and are between 1 and 256 ` +
57
+ `characters long. Only a partner event bus name carries a '/', and ` +
58
+ `it takes the form aws.partner/<partner>/<name>.`);
59
+ }
60
+ /**
61
+ * Whether this is the name of a partner event bus.
62
+ */
63
+ get isPartner() {
64
+ return this.value.includes(partnerNameSeparator);
60
65
  }
61
66
  /**
62
67
  * Whether this is the name of the default event bus.
@@ -27,8 +27,15 @@ export class SimCfnEventBusCreator {
27
27
  busProperties.recordIgnoredProperties();
28
28
  const name = busProperties.name();
29
29
  const description = busProperties.description();
30
+ const eventSourceName = busProperties.eventSourceName();
30
31
  return await simCfnEventBridgeResourceCreation(eventBusResourceType, resource.logicalId, async () => {
31
- await this.eventBridge.createEventBus({ input: { Name: name, Description: description } }, options);
32
+ await this.eventBridge.createEventBus({
33
+ input: {
34
+ Name: name,
35
+ Description: description,
36
+ EventSourceName: eventSourceName,
37
+ },
38
+ }, options);
32
39
  const bus = this.eventBridge.findEventBus(name);
33
40
  assertDefined(bus, `sim EventBridge bus ${name} after CloudFormation creation`);
34
41
  return bus;
@@ -23,6 +23,11 @@ export declare class SimCfnEventBusProperties {
23
23
  * The bus description, which a template may leave out.
24
24
  */
25
25
  description(): string | undefined;
26
+ /**
27
+ * The partner event source a partner event bus is matched with, which a
28
+ * custom bus leaves out.
29
+ */
30
+ eventSourceName(): string | undefined;
26
31
  /**
27
32
  * Refuse what this simulation does not model, naming the property.
28
33
  */
@@ -16,7 +16,6 @@ const unsimulatedProperties = [
16
16
  "DeadLetterConfig",
17
17
  "an undeliverable event is recorded rather than sent on",
18
18
  ],
19
- ["EventSourceName", "partner event buses are not simulated"],
20
19
  ["KmsKeyIdentifier", "events are not encrypted with a customer managed key"],
21
20
  ["LogConfig", "event bus logging is not simulated"],
22
21
  ];
@@ -73,6 +72,20 @@ export class SimCfnEventBusProperties {
73
72
  }
74
73
  return description;
75
74
  }
75
+ /**
76
+ * The partner event source a partner event bus is matched with, which a
77
+ * custom bus leaves out.
78
+ */
79
+ eventSourceName() {
80
+ const eventSourceName = this.properties.get("EventSourceName");
81
+ if (eventSourceName === undefined) {
82
+ return undefined;
83
+ }
84
+ if (typeof eventSourceName !== "string") {
85
+ throw this.propertyError("EventSourceName must be a string");
86
+ }
87
+ return eventSourceName;
88
+ }
76
89
  /**
77
90
  * Refuse what this simulation does not model, naming the property.
78
91
  */
@@ -65,6 +65,8 @@ export class SimCfnEventRuleCreator {
65
65
  Id: target.Id,
66
66
  Arn: target.Arn,
67
67
  Input: target.Input,
68
+ DeadLetterConfig: target.DeadLetterConfig,
69
+ RetryPolicy: target.RetryPolicy,
68
70
  })),
69
71
  },
70
72
  }, options);
@@ -0,0 +1,15 @@
1
+ import type { SimEventTargetDeadLetterConfigInput } from "../../target/sim-event-target-dead-letter-config.js";
2
+ import type { SimEventTargetRetryPolicyInput } from "../../target/sim-event-target-retry-policy.js";
3
+ /**
4
+ * What a rule's target says about retrying a failed delivery, and where an
5
+ * undelivered event goes.
6
+ */
7
+ interface SimCfnEventRuleTargetDelivery {
8
+ readonly DeadLetterConfig: SimEventTargetDeadLetterConfigInput | undefined;
9
+ readonly RetryPolicy: SimEventTargetRetryPolicyInput | undefined;
10
+ }
11
+ /**
12
+ * Read a target's `DeadLetterConfig` and `RetryPolicy` from a template.
13
+ */
14
+ export declare function simCfnEventRuleTargetDelivery(target: Record<string, unknown>, refuse: (reason: string) => Error): SimCfnEventRuleTargetDelivery;
15
+ export {};
@@ -0,0 +1,53 @@
1
+ import { isRecord } from "../../../../util/type-guard/record.js";
2
+ /**
3
+ * Read a target's `DeadLetterConfig` and `RetryPolicy` from a template.
4
+ */
5
+ export function simCfnEventRuleTargetDelivery(target, refuse) {
6
+ return {
7
+ DeadLetterConfig: readDeadLetterConfig(target["DeadLetterConfig"], refuse),
8
+ RetryPolicy: readRetryPolicy(target["RetryPolicy"], refuse),
9
+ };
10
+ }
11
+ /**
12
+ * Read a target's `DeadLetterConfig`, whose `Arn` is a queue ARN.
13
+ *
14
+ * Whether the ARN names a standard queue is left to PutTargets, so a template
15
+ * and an SDK caller are refused the same way.
16
+ */
17
+ function readDeadLetterConfig(config, refuse) {
18
+ if (config === undefined) {
19
+ return undefined;
20
+ }
21
+ if (!isRecord(config) || typeof config["Arn"] !== "string") {
22
+ throw refuse("DeadLetterConfig needs an Arn");
23
+ }
24
+ return { Arn: config["Arn"] };
25
+ }
26
+ /**
27
+ * Read a target's `RetryPolicy`, whose two limits are numbers.
28
+ *
29
+ * Their ranges are left to PutTargets, so a template and an SDK caller are
30
+ * refused the same way.
31
+ */
32
+ function readRetryPolicy(policy, refuse) {
33
+ if (policy === undefined) {
34
+ return undefined;
35
+ }
36
+ if (!isRecord(policy)) {
37
+ throw refuse("RetryPolicy is an object");
38
+ }
39
+ return {
40
+ MaximumEventAgeInSeconds: readLimit(policy, "MaximumEventAgeInSeconds", refuse),
41
+ MaximumRetryAttempts: readLimit(policy, "MaximumRetryAttempts", refuse),
42
+ };
43
+ }
44
+ /**
45
+ * Read one numeric limit of a retry policy, which may be left out.
46
+ */
47
+ function readLimit(policy, name, refuse) {
48
+ const value = new Map(Object.entries(policy)).get(name);
49
+ if (value === undefined || typeof value === "number") {
50
+ return value;
51
+ }
52
+ throw refuse(`RetryPolicy ${name} must be a number`);
53
+ }
@@ -1,4 +1,6 @@
1
1
  import type { SimCfnTemplateValue } from "../../../cloudformation/template/value/sim-cfn-template-value.js";
2
+ import type { SimEventTargetDeadLetterConfigInput } from "../../target/sim-event-target-dead-letter-config.js";
3
+ import type { SimEventTargetRetryPolicyInput } from "../../target/sim-event-target-retry-policy.js";
2
4
  /**
3
5
  * One target of a rule, as this simulation takes it.
4
6
  */
@@ -6,6 +8,8 @@ export interface SimCfnEventRuleTarget {
6
8
  readonly Id: string;
7
9
  readonly Arn: string;
8
10
  readonly Input?: string | undefined;
11
+ readonly DeadLetterConfig?: SimEventTargetDeadLetterConfigInput | undefined;
12
+ readonly RetryPolicy?: SimEventTargetRetryPolicyInput | undefined;
9
13
  }
10
14
  /**
11
15
  * Read a rule's inline `Targets` list.
@@ -1,4 +1,5 @@
1
1
  import { isRecord } from "../../../../util/type-guard/record.js";
2
+ import { simCfnEventRuleTargetDelivery } from "./sim-cfn-event-rule-target-delivery.js";
2
3
  /**
3
4
  * What a template can say about a target that this simulation does not model.
4
5
  *
@@ -14,8 +15,6 @@ const unsimulatedTargetProperties = [
14
15
  "a rule reaches its target as the EventBridge service principal, and an " +
15
16
  "ECS target, which does carry a role, is written with PutTargets",
16
17
  ],
17
- ["DeadLetterConfig", "a failed delivery is recorded rather than sent on"],
18
- ["RetryPolicy", "a delivery is attempted once"],
19
18
  ["SqsParameters", "a FIFO queue target is not simulated"],
20
19
  ["KinesisParameters", "Kinesis is not a simulated target"],
21
20
  [
@@ -62,7 +61,12 @@ function readTarget(target, refuse) {
62
61
  if (input !== undefined && typeof input !== "string") {
63
62
  throw refuse(`target ${id} Input must be a string of JSON`);
64
63
  }
65
- return { Id: id, Arn: arn, Input: input };
64
+ return {
65
+ Id: id,
66
+ Arn: arn,
67
+ Input: input,
68
+ ...simCfnEventRuleTargetDelivery(target, (reason) => refuse(`target ${id} ${reason}`)),
69
+ };
66
70
  }
67
71
  /**
68
72
  * Read a rule's inline `Targets` list.
@@ -45,6 +45,11 @@ export declare class SimEventBridgeAuthorizer {
45
45
  * may well not be there.
46
46
  */
47
47
  authorizeBus(action: string, busArn: string, options?: SimEventBridgeRequestOptions): SimAwsResolvedCaller;
48
+ /**
49
+ * Ensure the caller may perform an action on a partner event source, named
50
+ * by its ARN.
51
+ */
52
+ authorizeEventSource(action: string, sourceArn: string, options?: SimEventBridgeRequestOptions): SimAwsResolvedCaller;
48
53
  /**
49
54
  * Ensure the caller may perform an action that names no particular bus.
50
55
  */
@@ -62,6 +62,13 @@ export class SimEventBridgeAuthorizer {
62
62
  authorizeBus(action, busArn, options) {
63
63
  return this.authorizeResource(action, busArn, options);
64
64
  }
65
+ /**
66
+ * Ensure the caller may perform an action on a partner event source, named
67
+ * by its ARN.
68
+ */
69
+ authorizeEventSource(action, sourceArn, options) {
70
+ return this.authorizeResource(action, sourceArn, options);
71
+ }
65
72
  /**
66
73
  * Ensure the caller may perform an action that names no particular bus.
67
74
  */