aws-cdk-conf-n-tags 0.1.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.
package/LICENSE ADDED
@@ -0,0 +1,29 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2021-2026, Eduard Moskvin <ed@moskvin.ca>
4
+ All rights reserved.
5
+
6
+ Redistribution and use in source and binary forms, with or without
7
+ modification, are permitted provided that the following conditions are met:
8
+
9
+ 1. Redistributions of source code must retain the above copyright notice, this
10
+ list of conditions and the following disclaimer.
11
+
12
+ 2. Redistributions in binary form must reproduce the above copyright notice,
13
+ this list of conditions and the following disclaimer in the documentation
14
+ and/or other materials provided with the distribution.
15
+
16
+ 3. Neither the name of the copyright holder nor the names of its
17
+ contributors may be used to endorse or promote products derived from
18
+ this software without specific prior written permission.
19
+
20
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package/README.md ADDED
@@ -0,0 +1,297 @@
1
+ # aws-cdk-conf-n-tags
2
+
3
+ Per-environment configuration loading and stack tagging for **AWS CDK** apps.
4
+
5
+ - **One YAML file per environment**, selected with `--context env=<name>`.
6
+ - **Schema-validated** with [zod](https://zod.dev) — typos and bad values fail loudly
7
+ at synth time, and the validated config is fully typed.
8
+ - **Account/region guard** — refuses to synth an environment against the wrong AWS
9
+ account or region.
10
+ - **Immutable config** — the loaded values are frozen; this is a config object, not a
11
+ place to stash resource references.
12
+ - **Centralized tagging** — apply arbitrary tags plus auto `CreatedBy` / `WorkDir`
13
+ tags to every resource in a stack.
14
+
15
+ ## Install
16
+
17
+ This package is used inside an AWS CDK app. If you don't have one yet, scaffold it first
18
+ with the CDK CLI in an empty directory:
19
+
20
+ ```sh
21
+ mkdir my-infra && cd my-infra
22
+ cdk init app --language typescript
23
+ ```
24
+
25
+ Then, **from inside that CDK app directory** (where `package.json` and `cdk.json` live),
26
+ install this package:
27
+
28
+ ```sh
29
+ npm install aws-cdk-conf-n-tags zod
30
+ ```
31
+
32
+ > This both adds the entries to your `package.json` `dependencies` and installs them, in
33
+ > one step (modern npm writes to `package.json` by default — no `--save` needed). If you
34
+ > prefer, you can instead add the dependencies to `package.json` by hand and then run
35
+ > `npm install` with no arguments.
36
+
37
+ `aws-cdk-lib` (`^2`), `constructs` (`^10`), and `zod` (`^3`) are **peer dependencies**.
38
+ Your CDK app already has the first two; install `zod` yourself — you import it to declare
39
+ your schema, and a single shared `zod` instance is required for validation to work
40
+ (a second copy would break the internal schema checks).
41
+
42
+ ## Quick start
43
+
44
+ ### 1. Declare your schema
45
+
46
+ Extend `BaseConfigSchema` (which already covers `awsAccount`, `awsRegion`, `tags`, and
47
+ the tag-name overrides) with the parameters your stack needs:
48
+
49
+ ```ts
50
+ // config-schema.ts
51
+ import { BaseConfigSchema } from 'aws-cdk-conf-n-tags';
52
+ import { z } from 'zod';
53
+
54
+ export const ConfigSchema = BaseConfigSchema.extend({
55
+ vpcId: z.string(),
56
+ asgMinCapacity: z.number().int().default(1),
57
+ asgMaxCapacity: z.number().int().default(1),
58
+ ec2InstanceType: z.string().default('t4g.micro'),
59
+ auroraEngineVersion: z.enum(['5.7', '8.0']).default('5.7'),
60
+ serverlessMinCapacity: z.number().min(0.5).default(1),
61
+ certificateArns: z.array(z.string()).optional(),
62
+ });
63
+ ```
64
+
65
+ ### 2. Load and use it in your stack
66
+
67
+ ```ts
68
+ import { Stack, type StackProps } from 'aws-cdk-lib';
69
+ import { Vpc } from 'aws-cdk-lib/aws-ec2';
70
+ import type { Construct } from 'constructs';
71
+ import { Config } from 'aws-cdk-conf-n-tags';
72
+ import { ConfigSchema } from './config-schema.js';
73
+ import { MyAsg } from './my-asg.js'; // your own construct
74
+
75
+ export class MyStack extends Stack {
76
+ constructor(scope: Construct, id: string, props?: StackProps) {
77
+ super(scope, id, props);
78
+
79
+ // Resolves env from `--context env=...`, reads config/<env>.yaml,
80
+ // validates it, and checks the account/region. `conf.values` is typed + frozen.
81
+ const conf = Config.load(this, ConfigSchema);
82
+
83
+ // Tag every resource in the stack (config tags + auto CreatedBy/WorkDir).
84
+ conf.tagStack(this);
85
+
86
+ // Pass config values to constructs explicitly.
87
+ const vpc = Vpc.fromLookup(this, 'vpc', { vpcId: conf.values.vpcId });
88
+ new MyAsg(this, 'asg', { vpc, min: conf.values.asgMinCapacity });
89
+ }
90
+ }
91
+ ```
92
+
93
+ ### 3. Add a config file per environment
94
+
95
+ ```yaml
96
+ # config/production.yaml
97
+ awsAccount: "012345678901" # quote it — preserves leading zeros
98
+ awsRegion: us-west-2
99
+
100
+ vpcId: vpc-0abc123def4567890
101
+ asgMinCapacity: 2
102
+ auroraEngineVersion: "8.0"
103
+
104
+ tags:
105
+ Product: my-product
106
+ Cluster: web-001
107
+ Env: Production
108
+ RetentionDays: 28 # non-string values are coerced to strings when tagging
109
+ ```
110
+
111
+ Deploy with:
112
+
113
+ ```sh
114
+ cdk deploy --context env=production
115
+ ```
116
+
117
+ ## Using from JavaScript
118
+
119
+ The package ships both ESM and CommonJS builds, so it works in JavaScript CDK apps
120
+ (`cdk init app --language javascript`) just as well as TypeScript ones. The API is
121
+ identical — you only give up the *compile-time* layer (`z.infer` types and `tsc`
122
+ checking). **Runtime validation still catches typos, bad types, and out-of-range values
123
+ and applies defaults**, because that is zod behavior, not TypeScript.
124
+
125
+ ESM JavaScript (a project with `"type": "module"` in its `package.json`):
126
+
127
+ ```js
128
+ import { Stack } from 'aws-cdk-lib';
129
+ import { Config, BaseConfigSchema } from 'aws-cdk-conf-n-tags';
130
+ import { z } from 'zod';
131
+
132
+ const ConfigSchema = BaseConfigSchema.extend({
133
+ vpcId: z.string(),
134
+ asgMinCapacity: z.number().int().default(1),
135
+ });
136
+
137
+ export class MyStack extends Stack {
138
+ constructor(scope, id, props) {
139
+ super(scope, id, props);
140
+
141
+ const conf = Config.load(this, ConfigSchema);
142
+ conf.tagStack(this);
143
+ // conf.values.vpcId, conf.values.asgMinCapacity, ...
144
+ }
145
+ }
146
+ ```
147
+
148
+ CommonJS JavaScript (the default `cdk init --language javascript` template) is the same,
149
+ with `require`:
150
+
151
+ ```js
152
+ const { Config, BaseConfigSchema } = require('aws-cdk-conf-n-tags');
153
+ const { z } = require('zod');
154
+ ```
155
+
156
+ > Tip: even in `.js` files, editors like VS Code read the package's bundled type
157
+ > declarations and offer autocomplete and hover docs on `conf.values`.
158
+
159
+ ## How it behaves
160
+
161
+ - **Environment selection** — `Config.load` reads the env name from CDK context
162
+ (`--context env=<name>`; the key is configurable). It then loads
163
+ `config/<env>.yaml` relative to the current working directory.
164
+ - **Validation** — unknown keys are **rejected** by default (`unknownKeys: 'strict'`),
165
+ so a mistyped key like `asgMinCapcity` errors instead of silently using a default.
166
+ Types, enums, numeric ranges, and `required`/`default` are all enforced by your
167
+ schema. Switch to `'strip'` or `'passthrough'` if you need leniency.
168
+ - **Account/region guard** — `awsAccount` / `awsRegion` in the file are checked against
169
+ `CDK_DEFAULT_ACCOUNT` / `CDK_DEFAULT_REGION` (override via options, or disable with
170
+ `verifyAccountRegion: false`).
171
+ - **Immutability** — `conf.values` is `Object.freeze`'d, so it can't double as a mutable bag for created resources. Share those via explicit args
172
+ or the [`StackResources`](#sharing-resources-between-constructs) helper instead.
173
+
174
+ ## Sharing resources between constructs
175
+
176
+ Because `conf.values` is frozen, it can't accumulate created resources the way a single
177
+ mutable config object used to. To hand resources (VPC, database, …) from one construct to
178
+ the next, you have two options.
179
+
180
+ **Explicit props (most type-safe).** Give each construct exactly what it needs:
181
+
182
+ ```js
183
+ const vpc = Vpc.fromLookup(this, 'vpc', { vpcId: conf.values.vpcId });
184
+ const db = new MyDb(this, 'db', { vpc, conf });
185
+ new MyBackup(this, 'backup', { dbCluster: db.cluster });
186
+ ```
187
+
188
+ **`StackResources` (one bag, fail-loud).** If you prefer threading a single object
189
+ through every construct, use the exported `StackResources` container. Unlike a plain
190
+ object, reading a resource that hasn't been created yet throws a clear error instead of
191
+ yielding `undefined` — valuable in JavaScript stacks where there's no compiler to catch it:
192
+
193
+ ```js
194
+ import { Config, StackResources } from 'aws-cdk-conf-n-tags';
195
+
196
+ const res = new StackResources();
197
+ res.set('vpc', Vpc.fromLookup(this, 'vpc', { vpcId: conf.values.vpcId }));
198
+ const db = res.set('db', new MyDb(this, 'db', { conf, res })); // set() returns the value
199
+
200
+ new MyBackup(this, 'backup', { conf, res }); // internally reads res.get('db').cluster
201
+
202
+ // res.get('cache') before the cache is created throws:
203
+ // Resource "cache" was requested before it was created. Check the order ...
204
+ ```
205
+
206
+ `set(key, value)` stores and returns the value; `get(key)` returns it or throws;
207
+ `has(key)` checks; `keys()` lists what's been set. In TypeScript, parameterize it for
208
+ typed keys and values: `new StackResources<{ vpc: IVpc; db: MyDb }>()`.
209
+
210
+ ## API
211
+
212
+ ### `Config.load(scope, schema, options?)`
213
+
214
+ Returns a `Config` with:
215
+
216
+ | Member | Description |
217
+ |---|---|
218
+ | `conf.values` | The validated, frozen config (typed as `z.infer<typeof schema>`). |
219
+ | `conf.environment` | The resolved environment name. |
220
+ | `conf.path` | Absolute path of the file that was loaded. |
221
+ | `conf.tagStack(scope, options?)` | Tag a stack using this config's `tags` + auto-tags. |
222
+
223
+ `options` (all optional):
224
+
225
+ | Option | Default | Description |
226
+ |---|---|---|
227
+ | `contextKey` | `'env'` | CDK context key holding the environment name. |
228
+ | `environment` | — | Provide the env name explicitly (skips context). |
229
+ | `configDir` | `'config'` | Directory holding `<env>.yaml`, relative to `cwd`. |
230
+ | `configPath` | — | Explicit file path (overrides `configDir`). |
231
+ | `cwd` | `process.cwd()` | Base directory for relative paths. |
232
+ | `unknownKeys` | `'strict'` | `'strict'` \| `'strip'` \| `'passthrough'`. |
233
+ | `verifyAccountRegion` | `true` | Cross-check account/region. |
234
+ | `account` / `region` | `CDK_DEFAULT_*` | Expected account/region. |
235
+
236
+ ### `tagStack(scope, options?)`
237
+
238
+ Standalone tagging (no `Config` needed). Applies the working-directory tag, the
239
+ created-by tag, then the explicit `tags` map, to every taggable resource in `scope`.
240
+
241
+ ```ts
242
+ import { tagStack } from 'aws-cdk-conf-n-tags';
243
+
244
+ tagStack(stack, {
245
+ tags: { Product: 'my-product', Env: 'Production' },
246
+ auto: { workDir: false }, // disable individual auto-tags
247
+ tagNames: { createdBy: 'Blame' }, // rename auto-tag keys
248
+ providers: { createdBy: () => 'ci-bot' }, // override how a value is computed
249
+ });
250
+ ```
251
+
252
+ By default `CreatedBy` is derived from `git config user.name` / `user.email` (falling
253
+ back to `$USER`), and `WorkDir` is `process.cwd()`.
254
+
255
+ ### `StackResources<T>`
256
+
257
+ A fail-loud container for resources created during synthesis — see
258
+ [Sharing resources between constructs](#sharing-resources-between-constructs).
259
+
260
+ | Method | Description |
261
+ |---|---|
262
+ | `set(key, value)` | Store a resource and return it. |
263
+ | `get(key)` | Return it, or throw if it was never set. |
264
+ | `has(key)` | Whether a resource has been set. |
265
+ | `keys()` | The keys set so far. |
266
+
267
+ ### `loadConfig(schema, options)`
268
+
269
+ The validation core without any CDK scope — useful for tests or non-CDK tooling.
270
+ Returns `{ values, path }`. See `LoadOptions` for the option shape.
271
+
272
+ ## Migrating from the original `Config` class
273
+
274
+ If you used the original JavaScript `Config` (which flattened every YAML key onto the
275
+ instance and doubled as a mutable dependency bag):
276
+
277
+ - Read config via `conf.values.<key>` instead of `conf.<key>`.
278
+ - Declare a zod schema instead of relying on scattered `|| default` fallbacks.
279
+ - Stop writing resources back onto the config object (`conf.vpc = vpc`); pass them to
280
+ constructs explicitly.
281
+ - `setTags(stack)` → `conf.tagStack(stack)`.
282
+
283
+ ## Development
284
+
285
+ ```sh
286
+ npm install
287
+ npm run build # tsup → dist (ESM + CJS + d.ts/d.cts)
288
+ npm test # vitest
289
+ npm run typecheck # tsc --noEmit
290
+ npm run check:exports # publint + attw
291
+ ```
292
+
293
+ Run a single test: `npx vitest run test/loader.test.ts` (or `-t "<name>"`).
294
+
295
+ ## License
296
+
297
+ BSD-3-Clause © Eduard Moskvin
package/dist/index.cjs ADDED
@@ -0,0 +1,275 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ BaseConfigSchema: () => BaseConfigSchema,
34
+ Config: () => Config,
35
+ StackResources: () => StackResources,
36
+ gitCreatedBy: () => gitCreatedBy,
37
+ loadConfig: () => loadConfig,
38
+ resolveConfigPath: () => resolveConfigPath,
39
+ tagStack: () => tagStack,
40
+ tagValueSchema: () => tagValueSchema
41
+ });
42
+ module.exports = __toCommonJS(index_exports);
43
+
44
+ // src/config.ts
45
+ var import_zod2 = require("zod");
46
+
47
+ // src/loader.ts
48
+ var import_node_fs = require("fs");
49
+ var import_node_path = require("path");
50
+ var import_yaml = __toESM(require("yaml"), 1);
51
+ var import_zod = require("zod");
52
+ function resolveConfigPath(opts) {
53
+ const cwd = opts.cwd ?? process.cwd();
54
+ if (opts.configPath) {
55
+ return (0, import_node_path.isAbsolute)(opts.configPath) ? opts.configPath : (0, import_node_path.join)(cwd, opts.configPath);
56
+ }
57
+ return (0, import_node_path.join)(cwd, opts.configDir ?? "config", `${opts.environment}.yaml`);
58
+ }
59
+ function withUnknownKeys(schema, policy) {
60
+ if (policy === "strip") return schema;
61
+ if (schema instanceof import_zod.z.ZodObject) {
62
+ return policy === "strict" ? schema.strict() : schema.passthrough();
63
+ }
64
+ if (schema instanceof import_zod.z.ZodEffects) {
65
+ const inner = withUnknownKeys(schema.innerType(), policy);
66
+ return new import_zod.z.ZodEffects({ ...schema._def, schema: inner });
67
+ }
68
+ return schema;
69
+ }
70
+ function verifyAccountRegion(values, options, path) {
71
+ const account = options.account ?? process.env.CDK_DEFAULT_ACCOUNT;
72
+ const region = options.region ?? process.env.CDK_DEFAULT_REGION;
73
+ if (account && typeof values.awsAccount === "string" && values.awsAccount !== account) {
74
+ throw new Error(
75
+ `AWS account mismatch for environment "${options.environment}": ${path} expects "${values.awsAccount}" but the active credentials resolve to "${account}". Check ${path} and your AWS CLI settings.`
76
+ );
77
+ }
78
+ if (region && typeof values.awsRegion === "string" && values.awsRegion !== region) {
79
+ throw new Error(
80
+ `AWS region mismatch for environment "${options.environment}": ${path} expects "${values.awsRegion}" but the active credentials resolve to "${region}". Check ${path} and your AWS CLI settings.`
81
+ );
82
+ }
83
+ }
84
+ function loadConfig(schema, options) {
85
+ const path = resolveConfigPath(options);
86
+ let raw;
87
+ try {
88
+ raw = (0, import_node_fs.readFileSync)(path, "utf8");
89
+ } catch (err) {
90
+ throw new Error(
91
+ `Could not read config file for environment "${options.environment}" at ${path}: ${err.message}`
92
+ );
93
+ }
94
+ let parsed;
95
+ try {
96
+ parsed = import_yaml.default.parse(raw) ?? {};
97
+ } catch (err) {
98
+ throw new Error(
99
+ `Could not parse YAML config for environment "${options.environment}" at ${path}: ${err.message}`
100
+ );
101
+ }
102
+ const result = withUnknownKeys(schema, options.unknownKeys ?? "strict").safeParse(parsed);
103
+ if (!result.success) {
104
+ const details = result.error.issues.map((issue) => ` - ${issue.path.join(".") || "(root)"}: ${issue.message}`).join("\n");
105
+ throw new Error(`Invalid configuration in ${path}:
106
+ ${details}`);
107
+ }
108
+ if (options.verifyAccountRegion !== false) {
109
+ verifyAccountRegion(result.data, options, path);
110
+ }
111
+ return { values: result.data, path };
112
+ }
113
+
114
+ // src/tags.ts
115
+ var import_node_child_process = require("child_process");
116
+ var import_aws_cdk_lib = require("aws-cdk-lib");
117
+ var DEFAULT_CREATED_BY_TAG = "CreatedBy";
118
+ var DEFAULT_WORK_DIR_TAG = "WorkDir";
119
+ function gitCreatedBy() {
120
+ let name = "";
121
+ let email = "";
122
+ try {
123
+ name = (0, import_node_child_process.execSync)("git config user.name").toString().trim();
124
+ email = (0, import_node_child_process.execSync)("git config user.email").toString().trim();
125
+ } catch {
126
+ }
127
+ const joined = name && email ? `${name} - ${email}` : name || email;
128
+ return joined || process.env.USER || void 0;
129
+ }
130
+ function tagStack(scope, options = {}) {
131
+ const { tags, auto = {}, tagNames = {}, providers = {} } = options;
132
+ const collected = [];
133
+ if (auto.workDir !== false) {
134
+ const value = (providers.workDir ?? (() => process.cwd()))();
135
+ if (value) collected.push({ key: tagNames.workDir ?? DEFAULT_WORK_DIR_TAG, value });
136
+ }
137
+ if (auto.createdBy !== false) {
138
+ const value = (providers.createdBy ?? gitCreatedBy)();
139
+ if (value) collected.push({ key: tagNames.createdBy ?? DEFAULT_CREATED_BY_TAG, value });
140
+ }
141
+ if (tags && typeof tags === "object" && !Array.isArray(tags)) {
142
+ for (const [key, value] of Object.entries(tags)) {
143
+ if (value === void 0 || value === null) continue;
144
+ collected.push({ key, value: String(value) });
145
+ }
146
+ }
147
+ for (const { key, value } of collected) {
148
+ import_aws_cdk_lib.Tags.of(scope).add(key, value);
149
+ }
150
+ }
151
+
152
+ // src/config.ts
153
+ function deepFreeze(value) {
154
+ if (value && typeof value === "object" && !Object.isFrozen(value)) {
155
+ Object.freeze(value);
156
+ for (const nested of Object.values(value)) deepFreeze(nested);
157
+ }
158
+ return value;
159
+ }
160
+ function resolveEnvironment(scope, options) {
161
+ if (options.environment) return options.environment;
162
+ const key = options.contextKey ?? "env";
163
+ const fromContext = scope.node.tryGetContext(key);
164
+ if (typeof fromContext !== "string" || fromContext.length === 0) {
165
+ throw new Error(
166
+ `Deployment environment not set. Pass it with \`--context ${key}=<environment>\` or via the \`environment\` option.`
167
+ );
168
+ }
169
+ return fromContext;
170
+ }
171
+ var Config = class _Config {
172
+ /** The environment name (e.g. from `--context env=...`). */
173
+ environment;
174
+ /** The validated, frozen config values. */
175
+ values;
176
+ /** Absolute path of the config file that was loaded. */
177
+ path;
178
+ constructor(environment, values, path) {
179
+ this.environment = environment;
180
+ this.values = deepFreeze(values);
181
+ this.path = path;
182
+ }
183
+ /**
184
+ * Resolve the environment from `scope`'s context, then load and validate its
185
+ * config against `schema` (which should extend {@link BaseConfigSchema}).
186
+ */
187
+ static load(scope, schema, options = {}) {
188
+ const environment = resolveEnvironment(scope, options);
189
+ const { values, path } = loadConfig(schema, {
190
+ environment,
191
+ configDir: options.configDir,
192
+ configPath: options.configPath,
193
+ cwd: options.cwd,
194
+ unknownKeys: options.unknownKeys,
195
+ verifyAccountRegion: options.verifyAccountRegion,
196
+ account: options.account,
197
+ region: options.region
198
+ });
199
+ return new _Config(environment, values, path);
200
+ }
201
+ /**
202
+ * Tag every taggable resource in `scope` (the stack) using this config's `tags`
203
+ * and the configured auto-tag key names. Pass `options` to toggle auto-tags or
204
+ * override providers.
205
+ */
206
+ tagStack(scope, options = {}) {
207
+ const base = this.values;
208
+ tagStack(scope, {
209
+ ...options,
210
+ // Merge per-key so a partial override (e.g. only `tags.Extra` or only
211
+ // `tagNames.createdBy`) layers on top of the config instead of replacing
212
+ // the whole map and silently dropping the config's other values.
213
+ tags: { ...base.tags, ...options.tags },
214
+ tagNames: {
215
+ createdBy: options.tagNames?.createdBy ?? base.createdByTagName,
216
+ workDir: options.tagNames?.workDir ?? base.cwdTagName
217
+ }
218
+ });
219
+ }
220
+ };
221
+
222
+ // src/resources.ts
223
+ var StackResources = class {
224
+ #refs = /* @__PURE__ */ new Map();
225
+ /** Store a resource and return it, so it can be captured inline. */
226
+ set(key, value) {
227
+ this.#refs.set(key, value);
228
+ return value;
229
+ }
230
+ /** Retrieve a resource, throwing a clear error if it was never set. */
231
+ get(key) {
232
+ if (!this.#refs.has(key)) {
233
+ throw new Error(
234
+ `Resource "${String(key)}" was requested before it was created. Check the order constructs are created in the stack.`
235
+ );
236
+ }
237
+ return this.#refs.get(key);
238
+ }
239
+ /** Whether a resource has been set. */
240
+ has(key) {
241
+ return this.#refs.has(key);
242
+ }
243
+ /** The keys set so far. */
244
+ keys() {
245
+ return [...this.#refs.keys()];
246
+ }
247
+ };
248
+
249
+ // src/schema.ts
250
+ var import_zod3 = require("zod");
251
+ var tagValueSchema = import_zod3.z.union([import_zod3.z.string(), import_zod3.z.number(), import_zod3.z.boolean()]);
252
+ var BaseConfigSchema = import_zod3.z.object({
253
+ /** Target AWS account ID. Quote it in YAML (`"012345678901"`) to preserve leading zeros. */
254
+ awsAccount: import_zod3.z.string().min(1),
255
+ /** Target AWS region, e.g. `us-east-1`. */
256
+ awsRegion: import_zod3.z.string().min(1),
257
+ /** Arbitrary tags applied to every resource in the stack. */
258
+ tags: import_zod3.z.record(import_zod3.z.string(), tagValueSchema).optional(),
259
+ /** Tag key used for the auto-generated "created by" tag. */
260
+ createdByTagName: import_zod3.z.string().default("CreatedBy"),
261
+ /** Tag key used for the auto-generated "working directory" tag. */
262
+ cwdTagName: import_zod3.z.string().default("WorkDir")
263
+ });
264
+ // Annotate the CommonJS export names for ESM import in node:
265
+ 0 && (module.exports = {
266
+ BaseConfigSchema,
267
+ Config,
268
+ StackResources,
269
+ gitCreatedBy,
270
+ loadConfig,
271
+ resolveConfigPath,
272
+ tagStack,
273
+ tagValueSchema
274
+ });
275
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/config.ts","../src/loader.ts","../src/tags.ts","../src/resources.ts","../src/schema.ts"],"sourcesContent":["export { Config, type ConfigOptions } from './config.js';\nexport { StackResources } from './resources.js';\nexport { BaseConfigSchema, tagValueSchema, type BaseConfig } from './schema.js';\nexport {\n loadConfig,\n resolveConfigPath,\n type LoadOptions,\n type UnknownKeys,\n} from './loader.js';\nexport {\n tagStack,\n gitCreatedBy,\n type TagStackOptions,\n type TagValue,\n} from './tags.js';\n","import { z } from 'zod';\nimport type { IConstruct } from 'constructs';\nimport { loadConfig, type UnknownKeys } from './loader.js';\nimport { tagStack as applyStackTags, type TagStackOptions } from './tags.js';\nimport type { BaseConfig } from './schema.js';\n\n/** Options for {@link Config.load}. */\nexport interface ConfigOptions {\n /** CDK context key holding the environment name. Default: `env`. */\n contextKey?: string;\n /** Provide the environment name explicitly instead of reading it from context. */\n environment?: string;\n /** Directory holding `<environment>.yaml`, relative to `cwd`. Default: `config`. */\n configDir?: string;\n /** Explicit config file path (absolute, or relative to `cwd`). Overrides `configDir`. */\n configPath?: string;\n /** Base directory for relative paths. Default: `process.cwd()`. */\n cwd?: string;\n /** Unknown-key policy. Default: `strict` (reject mistyped keys). */\n unknownKeys?: UnknownKeys;\n /** Cross-check `awsAccount`/`awsRegion` against the active credentials. Default: `true`. */\n verifyAccountRegion?: boolean;\n /** Expected account. Default: `process.env.CDK_DEFAULT_ACCOUNT`. */\n account?: string;\n /** Expected region. Default: `process.env.CDK_DEFAULT_REGION`. */\n region?: string;\n}\n\n/** Recursively freeze an object and everything reachable from it. */\nfunction deepFreeze<T>(value: T): T {\n if (value && typeof value === 'object' && !Object.isFrozen(value)) {\n Object.freeze(value);\n for (const nested of Object.values(value)) deepFreeze(nested);\n }\n return value;\n}\n\nfunction resolveEnvironment(scope: IConstruct, options: ConfigOptions): string {\n if (options.environment) return options.environment;\n const key = options.contextKey ?? 'env';\n const fromContext: unknown = scope.node.tryGetContext(key);\n if (typeof fromContext !== 'string' || fromContext.length === 0) {\n throw new Error(\n `Deployment environment not set. Pass it with \\`--context ${key}=<environment>\\` ` +\n `or via the \\`environment\\` option.`,\n );\n }\n return fromContext;\n}\n\n/**\n * Loaded, validated, immutable per-environment configuration.\n *\n * Build it with {@link Config.load}, which resolves the environment from CDK\n * context, reads and validates `config/<env>.yaml`, and (by default) verifies the\n * target account/region. The validated values are frozen — this object is **not**\n * a place to stash resource references; pass those between constructs explicitly.\n */\nexport class Config<V extends BaseConfig> {\n /** The environment name (e.g. from `--context env=...`). */\n readonly environment: string;\n /** The validated, frozen config values. */\n readonly values: Readonly<V>;\n /** Absolute path of the config file that was loaded. */\n readonly path: string;\n\n private constructor(environment: string, values: V, path: string) {\n this.environment = environment;\n this.values = deepFreeze(values);\n this.path = path;\n }\n\n /**\n * Resolve the environment from `scope`'s context, then load and validate its\n * config against `schema` (which should extend {@link BaseConfigSchema}).\n */\n static load<S extends z.ZodTypeAny>(\n scope: IConstruct,\n schema: S,\n options: ConfigOptions = {},\n ): Config<z.infer<S> & BaseConfig> {\n const environment = resolveEnvironment(scope, options);\n const { values, path } = loadConfig(schema, {\n environment,\n configDir: options.configDir,\n configPath: options.configPath,\n cwd: options.cwd,\n unknownKeys: options.unknownKeys,\n verifyAccountRegion: options.verifyAccountRegion,\n account: options.account,\n region: options.region,\n });\n return new Config(environment, values as z.infer<S> & BaseConfig, path);\n }\n\n /**\n * Tag every taggable resource in `scope` (the stack) using this config's `tags`\n * and the configured auto-tag key names. Pass `options` to toggle auto-tags or\n * override providers.\n */\n tagStack(scope: IConstruct, options: TagStackOptions = {}): void {\n const base = this.values as BaseConfig;\n applyStackTags(scope, {\n ...options,\n // Merge per-key so a partial override (e.g. only `tags.Extra` or only\n // `tagNames.createdBy`) layers on top of the config instead of replacing\n // the whole map and silently dropping the config's other values.\n tags: { ...base.tags, ...options.tags },\n tagNames: {\n createdBy: options.tagNames?.createdBy ?? base.createdByTagName,\n workDir: options.tagNames?.workDir ?? base.cwdTagName,\n },\n });\n }\n}\n","import { readFileSync } from 'node:fs';\nimport { isAbsolute, join } from 'node:path';\nimport YAML from 'yaml';\nimport { z } from 'zod';\n\n/**\n * How unknown (typically mistyped) keys in the config file are handled:\n * - `strict` — reject them with an error (default; catches typos loudly);\n * - `strip` — silently drop them (zod's default object behavior);\n * - `passthrough` — keep them on the result without validation.\n */\nexport type UnknownKeys = 'strict' | 'strip' | 'passthrough';\n\n/** Inputs for {@link loadConfig}. */\nexport interface LoadOptions {\n /** Environment name, used to resolve the config file and for error messages. */\n environment: string;\n /** Directory holding `<environment>.yaml`, relative to `cwd`. Default: `config`. */\n configDir?: string;\n /** Explicit config file path (absolute, or relative to `cwd`). Overrides `configDir`. */\n configPath?: string;\n /** Base directory for relative paths. Default: `process.cwd()`. */\n cwd?: string;\n /** Unknown-key policy. Default: `strict`. */\n unknownKeys?: UnknownKeys;\n /** Cross-check `awsAccount`/`awsRegion` against the active credentials. Default: `true`. */\n verifyAccountRegion?: boolean;\n /** Expected account. Default: `process.env.CDK_DEFAULT_ACCOUNT`. */\n account?: string;\n /** Expected region. Default: `process.env.CDK_DEFAULT_REGION`. */\n region?: string;\n}\n\n/** Resolve the absolute path of the config file for an environment. */\nexport function resolveConfigPath(opts: {\n environment: string;\n configDir?: string;\n configPath?: string;\n cwd?: string;\n}): string {\n const cwd = opts.cwd ?? process.cwd();\n if (opts.configPath) {\n return isAbsolute(opts.configPath) ? opts.configPath : join(cwd, opts.configPath);\n }\n return join(cwd, opts.configDir ?? 'config', `${opts.environment}.yaml`);\n}\n\nfunction withUnknownKeys<S extends z.ZodTypeAny>(schema: S, policy: UnknownKeys): S {\n if (policy === 'strip') return schema; // zod's default object behavior\n if (schema instanceof z.ZodObject) {\n return (policy === 'strict' ? schema.strict() : schema.passthrough()) as unknown as S;\n }\n // Unwrap `.refine()` / `.superRefine()` / `.transform()` so the policy still\n // reaches the underlying object schema. Without this, a refined schema would\n // silently fall through to zod's default strip and never reject typos.\n if (schema instanceof z.ZodEffects) {\n const inner = withUnknownKeys(schema.innerType(), policy);\n return new z.ZodEffects({ ...schema._def, schema: inner }) as unknown as S;\n }\n return schema;\n}\n\nfunction verifyAccountRegion(\n values: { awsAccount?: unknown; awsRegion?: unknown },\n options: LoadOptions,\n path: string,\n): void {\n const account = options.account ?? process.env.CDK_DEFAULT_ACCOUNT;\n const region = options.region ?? process.env.CDK_DEFAULT_REGION;\n\n // Only compare when there is a reference to compare against; an unset\n // CDK_DEFAULT_ACCOUNT/REGION (common in `cdk synth` and unit tests) means we\n // cannot verify, not that the config is wrong.\n if (account && typeof values.awsAccount === 'string' && values.awsAccount !== account) {\n throw new Error(\n `AWS account mismatch for environment \"${options.environment}\": ${path} expects ` +\n `\"${values.awsAccount}\" but the active credentials resolve to \"${account}\". ` +\n `Check ${path} and your AWS CLI settings.`,\n );\n }\n if (region && typeof values.awsRegion === 'string' && values.awsRegion !== region) {\n throw new Error(\n `AWS region mismatch for environment \"${options.environment}\": ${path} expects ` +\n `\"${values.awsRegion}\" but the active credentials resolve to \"${region}\". ` +\n `Check ${path} and your AWS CLI settings.`,\n );\n }\n}\n\n/**\n * Read, parse, and validate the YAML config for an environment.\n *\n * Standalone (no CDK scope needed) so it can be unit-tested and reused outside a\n * stack. {@link Config.load} wraps this with environment resolution from context.\n */\nexport function loadConfig<S extends z.ZodTypeAny>(\n schema: S,\n options: LoadOptions,\n): { values: z.infer<S>; path: string } {\n const path = resolveConfigPath(options);\n\n let raw: string;\n try {\n raw = readFileSync(path, 'utf8');\n } catch (err) {\n throw new Error(\n `Could not read config file for environment \"${options.environment}\" at ${path}: ` +\n `${(err as Error).message}`,\n );\n }\n\n let parsed: unknown;\n try {\n parsed = YAML.parse(raw) ?? {};\n } catch (err) {\n throw new Error(\n `Could not parse YAML config for environment \"${options.environment}\" at ${path}: ` +\n `${(err as Error).message}`,\n );\n }\n\n const result = withUnknownKeys(schema, options.unknownKeys ?? 'strict').safeParse(parsed);\n if (!result.success) {\n const details = result.error.issues\n .map((issue) => ` - ${issue.path.join('.') || '(root)'}: ${issue.message}`)\n .join('\\n');\n throw new Error(`Invalid configuration in ${path}:\\n${details}`);\n }\n\n if (options.verifyAccountRegion !== false) {\n verifyAccountRegion(result.data as Record<string, unknown>, options, path);\n }\n\n return { values: result.data as z.infer<S>, path };\n}\n","import { execSync } from 'node:child_process';\nimport { Tags } from 'aws-cdk-lib';\nimport type { IConstruct } from 'constructs';\n\n/** A tag value as authored in config; coerced to a string when applied. */\nexport type TagValue = string | number | boolean;\n\n/** Options for {@link tagStack}. */\nexport interface TagStackOptions {\n /** Arbitrary key/value tags to apply (e.g. from config `tags:`). */\n tags?: Record<string, TagValue>;\n /** Toggle the auto-generated tags. Both default to `true`. */\n auto?: {\n /** Add a \"created by\" tag derived from the git/OS user. */\n createdBy?: boolean;\n /** Add a \"working directory\" tag with the current working directory. */\n workDir?: boolean;\n };\n /** Override the keys used for the auto-generated tags. */\n tagNames?: {\n createdBy?: string;\n workDir?: string;\n };\n /**\n * Override how the auto-tag values are computed. Injectable so tests don't\n * shell out to git and stacks can supply their own provenance.\n */\n providers?: {\n createdBy?: () => string | undefined;\n workDir?: () => string | undefined;\n };\n}\n\nconst DEFAULT_CREATED_BY_TAG = 'CreatedBy';\nconst DEFAULT_WORK_DIR_TAG = 'WorkDir';\n\n/**\n * Default \"created by\" provider: the git `user.name - user.email` identity,\n * falling back to `$USER`. Returns `undefined` if nothing is available.\n */\nexport function gitCreatedBy(): string | undefined {\n let name = '';\n let email = '';\n try {\n name = execSync('git config user.name').toString().trim();\n email = execSync('git config user.email').toString().trim();\n } catch {\n // No git identity available; fall back below.\n }\n const joined = name && email ? `${name} - ${email}` : name || email;\n return joined || process.env.USER || undefined;\n}\n\n/**\n * Apply tags to every taggable resource in `scope` (typically a Stack) via CDK's\n * tag aspect. Merges, in order: the working-directory tag, the created-by tag,\n * then the explicit `tags` map. Non-string tag values are coerced with `String()`.\n */\nexport function tagStack(scope: IConstruct, options: TagStackOptions = {}): void {\n const { tags, auto = {}, tagNames = {}, providers = {} } = options;\n const collected: Array<{ key: string; value: string }> = [];\n\n if (auto.workDir !== false) {\n const value = (providers.workDir ?? (() => process.cwd()))();\n if (value) collected.push({ key: tagNames.workDir ?? DEFAULT_WORK_DIR_TAG, value });\n }\n\n if (auto.createdBy !== false) {\n const value = (providers.createdBy ?? gitCreatedBy)();\n if (value) collected.push({ key: tagNames.createdBy ?? DEFAULT_CREATED_BY_TAG, value });\n }\n\n if (tags && typeof tags === 'object' && !Array.isArray(tags)) {\n for (const [key, value] of Object.entries(tags)) {\n if (value === undefined || value === null) continue;\n collected.push({ key, value: String(value) });\n }\n }\n\n for (const { key, value } of collected) {\n Tags.of(scope).add(key, value);\n }\n}\n","/**\n * A small, mutable container for resources created during stack synthesis, kept\n * separate from the frozen {@link Config} values.\n *\n * It lets you thread a single object through your constructs (the convenience of a\n * shared dependency bag) without the classic footgun: reading a resource that has\n * not been created yet throws a clear error instead of silently returning\n * `undefined`. That matters most in JavaScript stacks, where there is no compiler to\n * catch a missing reference.\n *\n * ```ts\n * const res = new StackResources<{ vpc: IVpc; db: MyDb }>();\n * res.set('vpc', Vpc.fromLookup(this, 'vpc', { vpcId: conf.values.vpcId }));\n * const db = res.set('db', new MyDb(this, 'db', { conf, res }));\n * // later: res.get('db').cluster — throws if 'db' was never set\n * ```\n */\nexport class StackResources<T extends Record<string, unknown> = Record<string, unknown>> {\n #refs = new Map<keyof T, T[keyof T]>();\n\n /** Store a resource and return it, so it can be captured inline. */\n set<K extends keyof T>(key: K, value: T[K]): T[K] {\n this.#refs.set(key, value);\n return value;\n }\n\n /** Retrieve a resource, throwing a clear error if it was never set. */\n get<K extends keyof T>(key: K): T[K] {\n if (!this.#refs.has(key)) {\n throw new Error(\n `Resource \"${String(key)}\" was requested before it was created. ` +\n `Check the order constructs are created in the stack.`,\n );\n }\n return this.#refs.get(key) as T[K];\n }\n\n /** Whether a resource has been set. */\n has<K extends keyof T>(key: K): boolean {\n return this.#refs.has(key);\n }\n\n /** The keys set so far. */\n keys(): Array<keyof T> {\n return [...this.#refs.keys()];\n }\n}\n","import { z } from 'zod';\n\n/**\n * A single tag value as it may appear in YAML. CloudFormation tag values must\n * ultimately be strings; numbers and booleans are coerced at tag-apply time\n * (see {@link tagStack}) so config authors can write `Env: 1` or `Managed: true`.\n */\nexport const tagValueSchema = z.union([z.string(), z.number(), z.boolean()]);\n\n/**\n * The keys every environment config shares. Consumers build their own schema by\n * extending this with the parameters their stack needs:\n *\n * ```ts\n * const ConfigSchema = BaseConfigSchema.extend({\n * vpcId: z.string(),\n * asgMinCapacity: z.number().int().default(1),\n * });\n * ```\n */\nexport const BaseConfigSchema = z.object({\n /** Target AWS account ID. Quote it in YAML (`\"012345678901\"`) to preserve leading zeros. */\n awsAccount: z.string().min(1),\n /** Target AWS region, e.g. `us-east-1`. */\n awsRegion: z.string().min(1),\n /** Arbitrary tags applied to every resource in the stack. */\n tags: z.record(z.string(), tagValueSchema).optional(),\n /** Tag key used for the auto-generated \"created by\" tag. */\n createdByTagName: z.string().default('CreatedBy'),\n /** Tag key used for the auto-generated \"working directory\" tag. */\n cwdTagName: z.string().default('WorkDir'),\n});\n\n/** The validated shape produced by {@link BaseConfigSchema}. */\nexport type BaseConfig = z.infer<typeof BaseConfigSchema>;\n"],"mappings":"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","names":["import_zod","YAML","import_zod"]}