@composurecdk/cloudformation 0.6.0 → 0.8.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 (77) hide show
  1. package/dist/commonjs/apply-builder-tags.d.ts +32 -0
  2. package/dist/commonjs/apply-builder-tags.d.ts.map +1 -0
  3. package/dist/commonjs/apply-builder-tags.js +69 -0
  4. package/dist/commonjs/apply-builder-tags.js.map +1 -0
  5. package/dist/commonjs/index.d.ts +8 -0
  6. package/dist/commonjs/index.d.ts.map +1 -0
  7. package/dist/commonjs/index.js +20 -0
  8. package/dist/commonjs/index.js.map +1 -0
  9. package/dist/commonjs/outputs.d.ts.map +1 -0
  10. package/dist/commonjs/outputs.js +77 -0
  11. package/dist/commonjs/outputs.js.map +1 -0
  12. package/dist/commonjs/package.json +3 -0
  13. package/dist/{stack-builder.d.ts → commonjs/stack-builder.d.ts} +14 -18
  14. package/dist/commonjs/stack-builder.d.ts.map +1 -0
  15. package/dist/commonjs/stack-builder.js +47 -0
  16. package/dist/commonjs/stack-builder.js.map +1 -0
  17. package/dist/commonjs/strategies.d.ts.map +1 -0
  18. package/dist/commonjs/strategies.js +80 -0
  19. package/dist/commonjs/strategies.js.map +1 -0
  20. package/dist/commonjs/tag-validator.d.ts +22 -0
  21. package/dist/commonjs/tag-validator.d.ts.map +1 -0
  22. package/dist/commonjs/tag-validator.js +67 -0
  23. package/dist/commonjs/tag-validator.js.map +1 -0
  24. package/dist/commonjs/tagged-builder.d.ts +112 -0
  25. package/dist/commonjs/tagged-builder.d.ts.map +1 -0
  26. package/dist/commonjs/tagged-builder.js +121 -0
  27. package/dist/commonjs/tagged-builder.js.map +1 -0
  28. package/dist/commonjs/tags.d.ts +91 -0
  29. package/dist/commonjs/tags.d.ts.map +1 -0
  30. package/dist/commonjs/tags.js +86 -0
  31. package/dist/commonjs/tags.js.map +1 -0
  32. package/dist/esm/apply-builder-tags.d.ts +32 -0
  33. package/dist/esm/apply-builder-tags.d.ts.map +1 -0
  34. package/dist/esm/apply-builder-tags.js +65 -0
  35. package/dist/esm/apply-builder-tags.js.map +1 -0
  36. package/dist/esm/index.d.ts +8 -0
  37. package/dist/esm/index.d.ts.map +1 -0
  38. package/dist/esm/index.js +8 -0
  39. package/dist/esm/index.js.map +1 -0
  40. package/dist/esm/outputs.d.ts +84 -0
  41. package/dist/esm/outputs.d.ts.map +1 -0
  42. package/dist/esm/outputs.js.map +1 -0
  43. package/dist/esm/package.json +3 -0
  44. package/dist/esm/stack-builder.d.ts +79 -0
  45. package/dist/esm/stack-builder.d.ts.map +1 -0
  46. package/dist/{stack-builder.js → esm/stack-builder.js} +9 -19
  47. package/dist/esm/stack-builder.js.map +1 -0
  48. package/dist/esm/strategies.d.ts +70 -0
  49. package/dist/esm/strategies.d.ts.map +1 -0
  50. package/dist/esm/strategies.js.map +1 -0
  51. package/dist/esm/tag-validator.d.ts +22 -0
  52. package/dist/esm/tag-validator.d.ts.map +1 -0
  53. package/dist/esm/tag-validator.js +63 -0
  54. package/dist/esm/tag-validator.js.map +1 -0
  55. package/dist/esm/tagged-builder.d.ts +112 -0
  56. package/dist/esm/tagged-builder.d.ts.map +1 -0
  57. package/dist/esm/tagged-builder.js +117 -0
  58. package/dist/esm/tagged-builder.js.map +1 -0
  59. package/dist/esm/tags.d.ts +91 -0
  60. package/dist/esm/tags.d.ts.map +1 -0
  61. package/dist/esm/tags.js +83 -0
  62. package/dist/esm/tags.js.map +1 -0
  63. package/package.json +33 -15
  64. package/dist/index.d.ts +0 -4
  65. package/dist/index.d.ts.map +0 -1
  66. package/dist/index.js +0 -4
  67. package/dist/index.js.map +0 -1
  68. package/dist/outputs.d.ts.map +0 -1
  69. package/dist/outputs.js.map +0 -1
  70. package/dist/stack-builder.d.ts.map +0 -1
  71. package/dist/stack-builder.js.map +0 -1
  72. package/dist/strategies.d.ts.map +0 -1
  73. package/dist/strategies.js.map +0 -1
  74. /package/dist/{outputs.d.ts → commonjs/outputs.d.ts} +0 -0
  75. /package/dist/{strategies.d.ts → commonjs/strategies.d.ts} +0 -0
  76. /package/dist/{outputs.js → esm/outputs.js} +0 -0
  77. /package/dist/{strategies.js → esm/strategies.js} +0 -0
@@ -0,0 +1,112 @@
1
+ /**
2
+ * The `name` attached to the `process.emitWarning` call when `.tag(k, v)` /
3
+ * `.tags({...})` overwrites an existing key. Exported so callers with
4
+ * layered configuration (a base builder plus per-environment refinements
5
+ * that intentionally override) can filter via
6
+ * `process.on("warning", w => { if (w.name !== TAG_OVERRIDE_WARNING_NAME) ... })`
7
+ * or the Node 21.3+ `--disable-warning=ComposureCDKTagOverride` CLI flag.
8
+ */
9
+ export declare const TAG_OVERRIDE_WARNING_NAME = "ComposureCDKTagOverride";
10
+ type Constructor<T> = new () => T;
11
+ interface ObjectWithProps<Props extends object> {
12
+ props: Partial<Props>;
13
+ }
14
+ /**
15
+ * A fluent builder extended with tag-accumulating methods. Returned by
16
+ * {@link taggedBuilder}.
17
+ *
18
+ * Mirrors `IBuilder<Props, T>` from `@composurecdk/core` but rewrites every
19
+ * chainable return type to `ITaggedBuilder<Props, T>` so the augmenting
20
+ * `.tag()` / `.tags()` methods stay reachable after any prop setter or
21
+ * chainable method on `T`. {@link ITaggedBuilder.copy | `.copy()`} also
22
+ * returns a tagged builder so the chain survives variant authoring.
23
+ *
24
+ * Tags accumulated via {@link ITaggedBuilder.tag | .tag()} and
25
+ * {@link ITaggedBuilder.tags | .tags()} are applied to every construct in
26
+ * the build result — see {@link applyBuilderTags} for the exact walk.
27
+ *
28
+ * @typeParam Props - The configurable properties.
29
+ * @typeParam T - The target class the builder wraps.
30
+ */
31
+ export type ITaggedBuilder<Props extends object, T> = {
32
+ [K in keyof Props]-?: ((arg: Props[K]) => ITaggedBuilder<Props, T>) & (() => Props[K]);
33
+ } & {
34
+ [K in keyof T]: T[K] extends (...args: infer A) => T ? (...args: A) => ITaggedBuilder<Props, T> : T[K];
35
+ } & {
36
+ /**
37
+ * Adds a single tag. Validates the key/value at call time and throws on
38
+ * AWS-rejected inputs (empty key, `aws:` prefix, oversize, disallowed
39
+ * characters).
40
+ *
41
+ * Repeated keys overwrite earlier values and emit a process warning so the
42
+ * override is visible at the call site.
43
+ *
44
+ * @param key - The tag key.
45
+ * @param value - The tag value.
46
+ * @returns The builder for chaining.
47
+ */
48
+ tag(key: string, value: string): ITaggedBuilder<Props, T>;
49
+ /**
50
+ * Adds many tags at once. Each entry is validated independently as if
51
+ * passed to {@link ITaggedBuilder.tag | .tag()}. Existing keys are
52
+ * overwritten with a process warning.
53
+ *
54
+ * @param values - A record of tag keys to values.
55
+ * @returns The builder for chaining.
56
+ */
57
+ tags(values: Record<string, string>): ITaggedBuilder<Props, T>;
58
+ /**
59
+ * Returns an independent tagged builder with the same configured props
60
+ * and a deep clone of the accumulated tags.
61
+ *
62
+ * Mirrors {@link IBuilder.copy} from `@composurecdk/core` but preserves
63
+ * the tagging surface. Mutations to the returned builder's tags do not
64
+ * affect the original, and vice versa.
65
+ */
66
+ copy(): ITaggedBuilder<Props, T>;
67
+ };
68
+ /**
69
+ * Wraps {@link Builder} to add the {@link ITaggedBuilder.tag | .tag()} and
70
+ * {@link ITaggedBuilder.tags | .tags()} accumulators and apply the
71
+ * collected tags to every construct in the build result.
72
+ *
73
+ * The wrapper maintains its own outer Proxy around the inner builder Proxy
74
+ * created by `@composurecdk/core`. The outer Proxy:
75
+ *
76
+ * 1. Intercepts `.tag(k, v)` and `.tags({...})` to validate inputs and
77
+ * accumulate them in an insertion-ordered map. Repeated keys overwrite
78
+ * earlier values and emit `process.emitWarning` so the override is
79
+ * visible at the configuring call site.
80
+ * 2. Intercepts `build()` to call {@link applyBuilderTags} on the result
81
+ * after the inner build completes.
82
+ * 3. Intercepts `copy()` so the returned builder is itself a tagged builder
83
+ * with an independent clone of the accumulator. Without this, `.copy()`
84
+ * on a tagged builder would surface the bare inner Proxy and silently
85
+ * drop both the tag methods and the build-time walker.
86
+ * 4. Passes every other access through to the inner Proxy unchanged. Inner
87
+ * methods that return the inner Proxy (chainable setters) are
88
+ * re-wrapped so the chain returns the outer Proxy and the new tag
89
+ * methods stay reachable.
90
+ *
91
+ * Each builder factory in the library opts in by calling `taggedBuilder()`
92
+ * instead of `Builder()`. Custom builders authored outside the library can
93
+ * use plain `Builder()` and forgo tagging.
94
+ *
95
+ * @typeParam Props - The configurable properties.
96
+ * @typeParam T - The target class the builder wraps.
97
+ *
98
+ * @example
99
+ * ```ts
100
+ * export function createBucketBuilder(): IBucketBuilder {
101
+ * return taggedBuilder<BucketBuilderProps, BucketBuilder>(BucketBuilder);
102
+ * }
103
+ *
104
+ * createBucketBuilder()
105
+ * .tag("Project", "claude-rig")
106
+ * .tags({ Owner: "platform", Environment: "prod" })
107
+ * .build(stack, "Bucket");
108
+ * ```
109
+ */
110
+ export declare function taggedBuilder<Props extends object, T extends ObjectWithProps<Props>>(constructor: Constructor<T>): ITaggedBuilder<Props, T>;
111
+ export {};
112
+ //# sourceMappingURL=tagged-builder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tagged-builder.d.ts","sourceRoot":"","sources":["../../src/tagged-builder.ts"],"names":[],"mappings":"AAIA;;;;;;;GAOG;AACH,eAAO,MAAM,yBAAyB,4BAA4B,CAAC;AAEnE,KAAK,WAAW,CAAC,CAAC,IAAI,UAAU,CAAC,CAAC;AAElC,UAAU,eAAe,CAAC,KAAK,SAAS,MAAM;IAC5C,KAAK,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,cAAc,CAAC,KAAK,SAAS,MAAM,EAAE,CAAC,IAAI;KACnD,CAAC,IAAI,MAAM,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,KAAK,cAAc,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC;CACvF,GAAG;KACD,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,GAChD,CAAC,GAAG,IAAI,EAAE,CAAC,KAAK,cAAc,CAAC,KAAK,EAAE,CAAC,CAAC,GACxC,CAAC,CAAC,CAAC,CAAC;CACT,GAAG;IACF;;;;;;;;;;;OAWG;IACH,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,cAAc,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IAE1D;;;;;;;OAOG;IACH,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,cAAc,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IAE/D;;;;;;;OAOG;IACH,IAAI,IAAI,cAAc,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;CAClC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,wBAAgB,aAAa,CAAC,KAAK,SAAS,MAAM,EAAE,CAAC,SAAS,eAAe,CAAC,KAAK,CAAC,EAClF,WAAW,EAAE,WAAW,CAAC,CAAC,CAAC,GAC1B,cAAc,CAAC,KAAK,EAAE,CAAC,CAAC,CAG1B"}
@@ -0,0 +1,121 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TAG_OVERRIDE_WARNING_NAME = void 0;
4
+ exports.taggedBuilder = taggedBuilder;
5
+ const core_1 = require("@composurecdk/core");
6
+ const apply_builder_tags_js_1 = require("./apply-builder-tags.js");
7
+ const tag_validator_js_1 = require("./tag-validator.js");
8
+ /**
9
+ * The `name` attached to the `process.emitWarning` call when `.tag(k, v)` /
10
+ * `.tags({...})` overwrites an existing key. Exported so callers with
11
+ * layered configuration (a base builder plus per-environment refinements
12
+ * that intentionally override) can filter via
13
+ * `process.on("warning", w => { if (w.name !== TAG_OVERRIDE_WARNING_NAME) ... })`
14
+ * or the Node 21.3+ `--disable-warning=ComposureCDKTagOverride` CLI flag.
15
+ */
16
+ exports.TAG_OVERRIDE_WARNING_NAME = "ComposureCDKTagOverride";
17
+ /**
18
+ * Wraps {@link Builder} to add the {@link ITaggedBuilder.tag | .tag()} and
19
+ * {@link ITaggedBuilder.tags | .tags()} accumulators and apply the
20
+ * collected tags to every construct in the build result.
21
+ *
22
+ * The wrapper maintains its own outer Proxy around the inner builder Proxy
23
+ * created by `@composurecdk/core`. The outer Proxy:
24
+ *
25
+ * 1. Intercepts `.tag(k, v)` and `.tags({...})` to validate inputs and
26
+ * accumulate them in an insertion-ordered map. Repeated keys overwrite
27
+ * earlier values and emit `process.emitWarning` so the override is
28
+ * visible at the configuring call site.
29
+ * 2. Intercepts `build()` to call {@link applyBuilderTags} on the result
30
+ * after the inner build completes.
31
+ * 3. Intercepts `copy()` so the returned builder is itself a tagged builder
32
+ * with an independent clone of the accumulator. Without this, `.copy()`
33
+ * on a tagged builder would surface the bare inner Proxy and silently
34
+ * drop both the tag methods and the build-time walker.
35
+ * 4. Passes every other access through to the inner Proxy unchanged. Inner
36
+ * methods that return the inner Proxy (chainable setters) are
37
+ * re-wrapped so the chain returns the outer Proxy and the new tag
38
+ * methods stay reachable.
39
+ *
40
+ * Each builder factory in the library opts in by calling `taggedBuilder()`
41
+ * instead of `Builder()`. Custom builders authored outside the library can
42
+ * use plain `Builder()` and forgo tagging.
43
+ *
44
+ * @typeParam Props - The configurable properties.
45
+ * @typeParam T - The target class the builder wraps.
46
+ *
47
+ * @example
48
+ * ```ts
49
+ * export function createBucketBuilder(): IBucketBuilder {
50
+ * return taggedBuilder<BucketBuilderProps, BucketBuilder>(BucketBuilder);
51
+ * }
52
+ *
53
+ * createBucketBuilder()
54
+ * .tag("Project", "claude-rig")
55
+ * .tags({ Owner: "platform", Environment: "prod" })
56
+ * .build(stack, "Bucket");
57
+ * ```
58
+ */
59
+ function taggedBuilder(constructor) {
60
+ const inner = (0, core_1.Builder)(constructor);
61
+ return wrapTagged(inner, new Map());
62
+ }
63
+ function wrapTagged(inner, accumulator) {
64
+ const recordTag = (key, value) => {
65
+ if (accumulator.has(key)) {
66
+ const previous = accumulator.get(key);
67
+ process.emitWarning(`Tag "${key}" was already set to "${previous ?? ""}" and is being overwritten with "${value}". ` +
68
+ "Last write wins; remove the duplicate to silence this warning.", { type: exports.TAG_OVERRIDE_WARNING_NAME });
69
+ }
70
+ accumulator.set(key, value);
71
+ };
72
+ const buildFn = (...args) => {
73
+ const target = inner;
74
+ const result = target.build(...args);
75
+ (0, apply_builder_tags_js_1.applyBuilderTags)(result, accumulator);
76
+ return result;
77
+ };
78
+ const copyFn = () => {
79
+ const innerCopy = inner;
80
+ return wrapTagged(innerCopy.copy(), new Map(accumulator));
81
+ };
82
+ // `tagFn` / `tagsFn` and `outer` form a cycle: the interceptors return
83
+ // `outer` for chaining, and `outer`'s `get` trap returns the interceptors.
84
+ // Resolved by Proxy laziness: the `get` trap closes over the names but
85
+ // does not read them until a property is accessed, by which point the
86
+ // `const`s below have initialised.
87
+ const outer = new Proxy(inner, {
88
+ get(target, prop, receiver) {
89
+ if (prop === "tag")
90
+ return tagFn;
91
+ if (prop === "tags")
92
+ return tagsFn;
93
+ if (prop === "build")
94
+ return buildFn;
95
+ if (prop === "copy")
96
+ return copyFn;
97
+ const value = Reflect.get(target, prop, receiver);
98
+ if (typeof value === "function") {
99
+ return (...args) => {
100
+ const ret = value.apply(target, args);
101
+ return ret === inner ? outer : ret;
102
+ };
103
+ }
104
+ return value;
105
+ },
106
+ });
107
+ const tagFn = (key, value) => {
108
+ (0, tag_validator_js_1.validateTag)(key, value);
109
+ recordTag(key, value);
110
+ return outer;
111
+ };
112
+ const tagsFn = (values) => {
113
+ (0, tag_validator_js_1.validateTagRecord)(values);
114
+ for (const [key, value] of Object.entries(values)) {
115
+ recordTag(key, value);
116
+ }
117
+ return outer;
118
+ };
119
+ return outer;
120
+ }
121
+ //# sourceMappingURL=tagged-builder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tagged-builder.js","sourceRoot":"","sources":["../../src/tagged-builder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,91 @@
1
+ import { type AfterBuildHook } from "@composurecdk/core";
2
+ /**
3
+ * Configures cross-cutting tag application against a composed system.
4
+ *
5
+ * - `system` tags reach every construct under the top-level scope passed
6
+ * to `build(scope, id)` — useful for ownership, environment, and cost
7
+ * allocation tags that should apply to every resource the system creates.
8
+ * - `byComponent` tags reach only the scope a named component was built
9
+ * into. Under {@link ComposedSystem.withStacks} or
10
+ * {@link ComposedSystem.withStackStrategy}, components may live in
11
+ * different stacks — `byComponent` routes the tag to whichever scope
12
+ * that component received.
13
+ *
14
+ * Component keys in `byComponent` are statically typed against the
15
+ * composed system's component keys, matching the pattern `outputs()` uses
16
+ * for its `scope` field, so a typo on a component name is a compile-time
17
+ * error.
18
+ *
19
+ * Builder-level tags applied via `.tag()` / `.tags()` always take
20
+ * precedence on key conflict because they target a closer scope; CDK's
21
+ * native tag priority resolves the collision automatically.
22
+ *
23
+ * @typeParam T - The composed system's build result type. Inferred from
24
+ * the surrounding `compose(...).afterBuild(tags(...))` chain.
25
+ */
26
+ export interface TagDefinitions<T extends object = object> {
27
+ /**
28
+ * Tags applied to every construct under the top-level scope. Each
29
+ * key/value pair is validated against the AWS tag character set; invalid
30
+ * inputs throw at configuration time.
31
+ */
32
+ system?: Record<string, string>;
33
+ /**
34
+ * Tags applied only to constructs under a specific component's scope.
35
+ * Component keys are statically checked against the composed system's
36
+ * component keys. Each key/value pair is validated identically to
37
+ * `system`.
38
+ */
39
+ byComponent?: Partial<Record<keyof T & string, Record<string, string>>>;
40
+ }
41
+ /**
42
+ * Returns an {@link AfterBuildHook} that applies cross-cutting tags to a
43
+ * composed system. Modelled on {@link outputs} — both share the same
44
+ * `(scope, id, results, componentScopes)` shape and live alongside the
45
+ * other CloudFormation-flavoured composition helpers in this package.
46
+ *
47
+ * The hook walks the supplied {@link TagDefinitions} once per build:
48
+ *
49
+ * - `system` entries are applied to the top-level `scope` via
50
+ * `Tags.of(scope).add(...)`. CDK's tag aspect propagates each tag
51
+ * through the construct subtree to every taggable descendant.
52
+ * - `byComponent` entries are applied to `componentScopes[key]` —
53
+ * each component's own scope, which under
54
+ * {@link ComposedSystem.withStacks} or
55
+ * {@link ComposedSystem.withStackStrategy} may be a per-component
56
+ * stack rather than the top-level scope.
57
+ *
58
+ * Tag keys and values are validated synchronously inside the hook before
59
+ * any `Tags.of(...).add(...)` call. Invalid tags throw and surface at the
60
+ * `compose(...).afterBuild(tags({...}))` site rather than at deploy time.
61
+ *
62
+ * Builder-level tags (set via `.tag()` / `.tags()` on individual builders)
63
+ * land on closer-scoped constructs and therefore win on key collision —
64
+ * CDK's tag priority resolves the collision automatically. Use builder
65
+ * tags for selector tags that must match exactly one resource type;
66
+ * use this helper for system-wide concerns like ownership, environment,
67
+ * and cost-allocation dimensions.
68
+ *
69
+ * @param defs - System-wide and per-component tag definitions.
70
+ * @returns An `AfterBuildHook` to pass to {@link ComposedSystem.afterBuild}.
71
+ *
72
+ * @example
73
+ * ```ts
74
+ * import { compose } from "@composurecdk/core";
75
+ * import { tags } from "@composurecdk/cloudformation";
76
+ *
77
+ * compose(
78
+ * { agent: createInstanceBuilder(), bucket: createBucketBuilder() },
79
+ * { agent: [], bucket: [] },
80
+ * )
81
+ * .afterBuild(
82
+ * tags({
83
+ * system: { Owner: "platform", Environment: "prod" },
84
+ * byComponent: { agent: { Project: "claude-rig" } },
85
+ * }),
86
+ * )
87
+ * .build(stack, "MySystem");
88
+ * ```
89
+ */
90
+ export declare function tags<T extends object = object>(defs: TagDefinitions<T>): AfterBuildHook<T>;
91
+ //# sourceMappingURL=tags.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tags.d.ts","sourceRoot":"","sources":["../../src/tags.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAIzD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,WAAW,cAAc,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM;IACvD;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEhC;;;;;OAKG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;CACzE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,wBAAgB,IAAI,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,EAAE,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC,GAAG,cAAc,CAAC,CAAC,CAAC,CA+B1F"}
@@ -0,0 +1,86 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.tags = tags;
4
+ const apply_builder_tags_js_1 = require("./apply-builder-tags.js");
5
+ const tag_validator_js_1 = require("./tag-validator.js");
6
+ /**
7
+ * Returns an {@link AfterBuildHook} that applies cross-cutting tags to a
8
+ * composed system. Modelled on {@link outputs} — both share the same
9
+ * `(scope, id, results, componentScopes)` shape and live alongside the
10
+ * other CloudFormation-flavoured composition helpers in this package.
11
+ *
12
+ * The hook walks the supplied {@link TagDefinitions} once per build:
13
+ *
14
+ * - `system` entries are applied to the top-level `scope` via
15
+ * `Tags.of(scope).add(...)`. CDK's tag aspect propagates each tag
16
+ * through the construct subtree to every taggable descendant.
17
+ * - `byComponent` entries are applied to `componentScopes[key]` —
18
+ * each component's own scope, which under
19
+ * {@link ComposedSystem.withStacks} or
20
+ * {@link ComposedSystem.withStackStrategy} may be a per-component
21
+ * stack rather than the top-level scope.
22
+ *
23
+ * Tag keys and values are validated synchronously inside the hook before
24
+ * any `Tags.of(...).add(...)` call. Invalid tags throw and surface at the
25
+ * `compose(...).afterBuild(tags({...}))` site rather than at deploy time.
26
+ *
27
+ * Builder-level tags (set via `.tag()` / `.tags()` on individual builders)
28
+ * land on closer-scoped constructs and therefore win on key collision —
29
+ * CDK's tag priority resolves the collision automatically. Use builder
30
+ * tags for selector tags that must match exactly one resource type;
31
+ * use this helper for system-wide concerns like ownership, environment,
32
+ * and cost-allocation dimensions.
33
+ *
34
+ * @param defs - System-wide and per-component tag definitions.
35
+ * @returns An `AfterBuildHook` to pass to {@link ComposedSystem.afterBuild}.
36
+ *
37
+ * @example
38
+ * ```ts
39
+ * import { compose } from "@composurecdk/core";
40
+ * import { tags } from "@composurecdk/cloudformation";
41
+ *
42
+ * compose(
43
+ * { agent: createInstanceBuilder(), bucket: createBucketBuilder() },
44
+ * { agent: [], bucket: [] },
45
+ * )
46
+ * .afterBuild(
47
+ * tags({
48
+ * system: { Owner: "platform", Environment: "prod" },
49
+ * byComponent: { agent: { Project: "claude-rig" } },
50
+ * }),
51
+ * )
52
+ * .build(stack, "MySystem");
53
+ * ```
54
+ */
55
+ function tags(defs) {
56
+ // Validate eagerly so configuration errors surface at the call site.
57
+ if (defs.system) {
58
+ (0, tag_validator_js_1.validateTagRecord)(defs.system);
59
+ }
60
+ const byComponent = defs.byComponent;
61
+ if (byComponent) {
62
+ for (const componentTags of Object.values(byComponent)) {
63
+ if (componentTags === undefined)
64
+ continue;
65
+ (0, tag_validator_js_1.validateTagRecord)(componentTags);
66
+ }
67
+ }
68
+ return (scope, _id, _results, componentScopes) => {
69
+ if (defs.system) {
70
+ (0, apply_builder_tags_js_1.applyTagsToConstruct)(scope, Object.entries(defs.system));
71
+ }
72
+ if (byComponent) {
73
+ const scopesByKey = componentScopes;
74
+ for (const [componentKey, componentTags] of Object.entries(byComponent)) {
75
+ if (componentTags === undefined)
76
+ continue;
77
+ const target = scopesByKey[componentKey];
78
+ if (target === undefined) {
79
+ throw new Error(`tags(): byComponent entry "${componentKey}" is not a known component.`);
80
+ }
81
+ (0, apply_builder_tags_js_1.applyTagsToConstruct)(target, Object.entries(componentTags));
82
+ }
83
+ }
84
+ };
85
+ }
86
+ //# sourceMappingURL=tags.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tags.js","sourceRoot":"","sources":["../../src/tags.ts"],"names":[],"mappings":";;AA+FA,oBA+BC;AA5HD,mEAA+D;AAC/D,yDAAuD;AA2CvD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,SAAgB,IAAI,CAA4B,IAAuB;IACrE,qEAAqE;IACrE,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;QAChB,IAAA,oCAAiB,EAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACjC,CAAC;IACD,MAAM,WAAW,GAAG,IAAI,CAAC,WAEZ,CAAC;IACd,IAAI,WAAW,EAAE,CAAC;QAChB,KAAK,MAAM,aAAa,IAAI,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC;YACvD,IAAI,aAAa,KAAK,SAAS;gBAAE,SAAS;YAC1C,IAAA,oCAAiB,EAAC,aAAa,CAAC,CAAC;QACnC,CAAC;IACH,CAAC;IAED,OAAO,CAAC,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,eAAe,EAAE,EAAE;QAC/C,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,IAAA,4CAAoB,EAAC,KAAK,EAAE,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;QAC3D,CAAC;QACD,IAAI,WAAW,EAAE,CAAC;YAChB,MAAM,WAAW,GAAG,eAAmE,CAAC;YACxF,KAAK,MAAM,CAAC,YAAY,EAAE,aAAa,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC;gBACxE,IAAI,aAAa,KAAK,SAAS;oBAAE,SAAS;gBAC1C,MAAM,MAAM,GAAG,WAAW,CAAC,YAAY,CAAC,CAAC;gBACzC,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;oBACzB,MAAM,IAAI,KAAK,CAAC,8BAA8B,YAAY,6BAA6B,CAAC,CAAC;gBAC3F,CAAC;gBACD,IAAA,4CAAoB,EAAC,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC;YAC9D,CAAC;QACH,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,32 @@
1
+ import { type IConstruct } from "constructs";
2
+ /**
3
+ * Applies every accumulated tag to every {@link IConstruct} reachable in a
4
+ * builder result.
5
+ *
6
+ * Recursively descends through plain-object literals, tagging every
7
+ * `IConstruct` it finds. Stops at:
8
+ *
9
+ * - **Constructs** — tagged via `Tags.of(...).add(...)`. The CDK Aspect
10
+ * schedules tag application across the construct's subtree at
11
+ * synth-prepare time, so the walker does not recurse into the construct's
12
+ * internals.
13
+ * - **Class instances that aren't constructs** (e.g. `PolicyDocument`) —
14
+ * skipped. Plain-object detection requires `Object.prototype` as the
15
+ * prototype, so class instances are opaque to the walker.
16
+ * - **Arrays and primitives** — skipped.
17
+ *
18
+ * The contract this implements: every construct exposed in a builder's
19
+ * result type is a tag target. Wrapper shapes such as
20
+ * `Record<string, { construct: ..., metadata: ... }>` are unwrapped
21
+ * naturally — the walker descends through the plain-object value and tags
22
+ * the construct field. Authors do not need an opt-in marker; if a construct
23
+ * appears in the result, it is tagged.
24
+ */
25
+ export declare function applyBuilderTags(result: object, tags: ReadonlyMap<string, string>): void;
26
+ /**
27
+ * Applies every entry of `tags` to `target` via `Tags.of(target).add(...)`.
28
+ * Accepts any iterable of `[key, value]` pairs so callers can pass `Map`,
29
+ * `Object.entries(record)`, or other compatible sources without copying.
30
+ */
31
+ export declare function applyTagsToConstruct(target: IConstruct, tags: Iterable<[string, string]>): void;
32
+ //# sourceMappingURL=apply-builder-tags.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apply-builder-tags.d.ts","sourceRoot":"","sources":["../../src/apply-builder-tags.ts"],"names":[],"mappings":"AACA,OAAO,EAAa,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAMxD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAGxF;AAcD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,IAAI,CAK/F"}
@@ -0,0 +1,65 @@
1
+ import { Tags } from "aws-cdk-lib";
2
+ import { Construct } from "constructs";
3
+ function isConstruct(value) {
4
+ return value !== null && typeof value === "object" && Construct.isConstruct(value);
5
+ }
6
+ /**
7
+ * Applies every accumulated tag to every {@link IConstruct} reachable in a
8
+ * builder result.
9
+ *
10
+ * Recursively descends through plain-object literals, tagging every
11
+ * `IConstruct` it finds. Stops at:
12
+ *
13
+ * - **Constructs** — tagged via `Tags.of(...).add(...)`. The CDK Aspect
14
+ * schedules tag application across the construct's subtree at
15
+ * synth-prepare time, so the walker does not recurse into the construct's
16
+ * internals.
17
+ * - **Class instances that aren't constructs** (e.g. `PolicyDocument`) —
18
+ * skipped. Plain-object detection requires `Object.prototype` as the
19
+ * prototype, so class instances are opaque to the walker.
20
+ * - **Arrays and primitives** — skipped.
21
+ *
22
+ * The contract this implements: every construct exposed in a builder's
23
+ * result type is a tag target. Wrapper shapes such as
24
+ * `Record<string, { construct: ..., metadata: ... }>` are unwrapped
25
+ * naturally — the walker descends through the plain-object value and tags
26
+ * the construct field. Authors do not need an opt-in marker; if a construct
27
+ * appears in the result, it is tagged.
28
+ */
29
+ export function applyBuilderTags(result, tags) {
30
+ if (tags.size === 0)
31
+ return;
32
+ walkAndTag(result, tags);
33
+ }
34
+ function walkAndTag(value, tags) {
35
+ if (isConstruct(value)) {
36
+ applyTagsToConstruct(value, tags);
37
+ return;
38
+ }
39
+ if (isPlainObject(value)) {
40
+ for (const inner of Object.values(value)) {
41
+ walkAndTag(inner, tags);
42
+ }
43
+ }
44
+ }
45
+ /**
46
+ * Applies every entry of `tags` to `target` via `Tags.of(target).add(...)`.
47
+ * Accepts any iterable of `[key, value]` pairs so callers can pass `Map`,
48
+ * `Object.entries(record)`, or other compatible sources without copying.
49
+ */
50
+ export function applyTagsToConstruct(target, tags) {
51
+ const t = Tags.of(target);
52
+ for (const [key, value] of tags) {
53
+ t.add(key, value);
54
+ }
55
+ }
56
+ function isPlainObject(value) {
57
+ if (value === null || typeof value !== "object" || Array.isArray(value))
58
+ return false;
59
+ // Accept both `{...}` literals and `Object.create(null)` dictionaries — the
60
+ // latter is a common idiom for prototype-pollution-safe key/value maps and
61
+ // would otherwise be silently skipped here.
62
+ const proto = Object.getPrototypeOf(value);
63
+ return proto === null || proto === Object.prototype;
64
+ }
65
+ //# sourceMappingURL=apply-builder-tags.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apply-builder-tags.js","sourceRoot":"","sources":["../../src/apply-builder-tags.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACnC,OAAO,EAAE,SAAS,EAAmB,MAAM,YAAY,CAAC;AAExD,SAAS,WAAW,CAAC,KAAc;IACjC,OAAO,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,SAAS,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;AACrF,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAc,EAAE,IAAiC;IAChF,IAAI,IAAI,CAAC,IAAI,KAAK,CAAC;QAAE,OAAO;IAC5B,UAAU,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;AAC3B,CAAC;AAED,SAAS,UAAU,CAAC,KAAc,EAAE,IAAiC;IACnE,IAAI,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC;QACvB,oBAAoB,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QAClC,OAAO;IACT,CAAC;IACD,IAAI,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;YACzC,UAAU,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAAC,MAAkB,EAAE,IAAgC;IACvF,MAAM,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC;IAC1B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC;QAChC,CAAC,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IACpB,CAAC;AACH,CAAC;AAED,SAAS,aAAa,CAAC,KAAc;IACnC,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACtF,4EAA4E;IAC5E,2EAA2E;IAC3E,4CAA4C;IAC5C,MAAM,KAAK,GAAG,MAAM,CAAC,cAAc,CAAC,KAAK,CAAkB,CAAC;IAC5D,OAAO,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,MAAM,CAAC,SAAS,CAAC;AACtD,CAAC"}
@@ -0,0 +1,8 @@
1
+ export { createStackBuilder, type IStackBuilder, type StackBuilderResult, } from "./stack-builder.js";
2
+ export { singleStack, groupedStacks } from "./strategies.js";
3
+ export { outputs, type OutputDefinition, type OutputDefinitions } from "./outputs.js";
4
+ export { taggedBuilder, type ITaggedBuilder, TAG_OVERRIDE_WARNING_NAME } from "./tagged-builder.js";
5
+ export { applyBuilderTags } from "./apply-builder-tags.js";
6
+ export { validateTag } from "./tag-validator.js";
7
+ export { tags, type TagDefinitions } from "./tags.js";
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,KAAK,aAAa,EAClB,KAAK,kBAAkB,GACxB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EAAE,OAAO,EAAE,KAAK,gBAAgB,EAAE,KAAK,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACtF,OAAO,EAAE,aAAa,EAAE,KAAK,cAAc,EAAE,yBAAyB,EAAE,MAAM,qBAAqB,CAAC;AACpG,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,IAAI,EAAE,KAAK,cAAc,EAAE,MAAM,WAAW,CAAC"}
@@ -0,0 +1,8 @@
1
+ export { createStackBuilder, } from "./stack-builder.js";
2
+ export { singleStack, groupedStacks } from "./strategies.js";
3
+ export { outputs } from "./outputs.js";
4
+ export { taggedBuilder, TAG_OVERRIDE_WARNING_NAME } from "./tagged-builder.js";
5
+ export { applyBuilderTags } from "./apply-builder-tags.js";
6
+ export { validateTag } from "./tag-validator.js";
7
+ export { tags } from "./tags.js";
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,GAGnB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EAAE,OAAO,EAAiD,MAAM,cAAc,CAAC;AACtF,OAAO,EAAE,aAAa,EAAuB,yBAAyB,EAAE,MAAM,qBAAqB,CAAC;AACpG,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,IAAI,EAAuB,MAAM,WAAW,CAAC"}
@@ -0,0 +1,84 @@
1
+ import { type IConstruct } from "constructs";
2
+ import { type AfterBuildHook, type Resolvable } from "@composurecdk/core";
3
+ /**
4
+ * Defines a CloudFormation stack output with a value that can be a
5
+ * {@link Resolvable} — either a concrete string or a {@link Ref} that
6
+ * resolves against the system's build results at build time.
7
+ *
8
+ * @typeParam T - The composed system's build result type. When `outputs()`
9
+ * is passed to {@link ComposedSystem.afterBuild | .afterBuild()} the type
10
+ * is inferred, making `scope` component keys statically checked.
11
+ */
12
+ export interface OutputDefinition<T extends object = object> {
13
+ /** The output value, or a Ref that resolves to one. */
14
+ value: Resolvable<string>;
15
+ /** A description of the output. */
16
+ description?: string;
17
+ /**
18
+ * The name under which the output is exported for cross-stack references.
19
+ * When set, this creates a CloudFormation Export.
20
+ */
21
+ exportName?: string;
22
+ /**
23
+ * The scope to attach this output to. Either an `IConstruct` (typically a
24
+ * Stack reference held by the caller — useful with
25
+ * {@link ComposedSystem.withStacks | .withStacks()}) or the string key of
26
+ * a component in the composed system, in which case the output lands in
27
+ * whichever scope that component was built into (useful with
28
+ * {@link ComposedSystem.withStackStrategy | .withStackStrategy()}).
29
+ *
30
+ * When omitted, the output falls back to the top-level scope passed to
31
+ * `build()` — the same scope the `AfterBuildHook` receives.
32
+ */
33
+ scope?: IConstruct | (keyof T & string);
34
+ }
35
+ /**
36
+ * A record of output definitions keyed by logical output name.
37
+ */
38
+ export type OutputDefinitions<T extends object = object> = Record<string, OutputDefinition<T>>;
39
+ /**
40
+ * Returns an {@link AfterBuildHook} that creates CloudFormation stack outputs
41
+ * from the composed system's build results.
42
+ *
43
+ * Each output definition's `value` can be a concrete string or a {@link Ref}
44
+ * that is resolved against the build results. An optional `scope` routes
45
+ * individual outputs to specific stacks — either as a direct `IConstruct`
46
+ * or as a component key string (statically typed against the composed
47
+ * system's component keys).
48
+ *
49
+ * Intended for use with {@link ComposedSystem.afterBuild}.
50
+ *
51
+ * @param defs - A record of output definitions keyed by logical name.
52
+ * @returns An {@link AfterBuildHook} that creates `CfnOutput` constructs.
53
+ *
54
+ * @example
55
+ * ```ts
56
+ * import { compose, ref } from "@composurecdk/core";
57
+ * import { outputs } from "@composurecdk/cloudformation";
58
+ *
59
+ * compose(
60
+ * { site: createBucketBuilder(), cdn: createDistributionBuilder(), dns: createZoneBuilder() },
61
+ * { site: [], cdn: ["site"], dns: [] },
62
+ * )
63
+ * .withStacks({ site: siteStack, cdn: siteStack, dns: dnsStack })
64
+ * .afterBuild(outputs({
65
+ * DistributionUrl: {
66
+ * value: ref("cdn", (r: DistributionBuilderResult) =>
67
+ * `https://${r.distribution.distributionDomainName}`),
68
+ * scope: "cdn",
69
+ * },
70
+ * BucketName: {
71
+ * value: ref("site", (r: BucketBuilderResult) => r.bucket.bucketName),
72
+ * scope: siteStack,
73
+ * },
74
+ * NameServers: {
75
+ * value: ref("dns", (r: ZoneBuilderResult) =>
76
+ * Fn.join(",", r.zone.hostedZoneNameServers!)),
77
+ * scope: "dns",
78
+ * },
79
+ * }))
80
+ * .build(app, "StaticWebsite");
81
+ * ```
82
+ */
83
+ export declare function outputs<T extends object = object>(defs: OutputDefinitions<T>): AfterBuildHook<T>;
84
+ //# sourceMappingURL=outputs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outputs.d.ts","sourceRoot":"","sources":["../../src/outputs.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,KAAK,cAAc,EAAE,KAAK,UAAU,EAAW,MAAM,oBAAoB,CAAC;AAEnF;;;;;;;;GAQG;AACH,MAAM,WAAW,gBAAgB,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM;IACzD,uDAAuD;IACvD,KAAK,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;IAE1B,mCAAmC;IACnC,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;;;;;;;;OAUG;IACH,KAAK,CAAC,EAAE,UAAU,GAAG,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC;CACzC;AAED;;GAEG;AACH,MAAM,MAAM,iBAAiB,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC;AAE/F;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,wBAAgB,OAAO,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,EAAE,IAAI,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG,cAAc,CAAC,CAAC,CAAC,CA0BhG"}