@go-to-k/cdkd 0.235.0 → 0.235.1

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.
@@ -1,830 +1,6 @@
1
- import { AsyncLocalStorage } from "node:async_hooks";
2
- import { createHash } from "node:crypto";
1
+ import { n as getLogger } from "./logger-BzO-joNR.js";
3
2
  import { ModifyInstanceAttributeCommand } from "@aws-sdk/client-ec2";
4
3
 
5
- //#region src/provisioning/resource-name.ts
6
- /**
7
- * Per-async-context stack name. Resource-name generation reads this so that
8
- * concurrent deploys (`cdkd deploy --all` runs stacks in parallel up to
9
- * `--stack-concurrency`) don't fight over a single shared variable.
10
- *
11
- * History: this was `let currentStackName: string | undefined` until
12
- * 2026-05-01. Two parallel `deploy()` calls would each call
13
- * `setCurrentStackName(...)` and the second would overwrite the first;
14
- * any IAM Role / SQS Queue / etc. created by the first stack while the
15
- * second was active would get the second stack's prefix in its physical
16
- * name, then the second stack's own create attempt for the same logical
17
- * id would collide ("Role with name X already exists"). Switching to
18
- * `AsyncLocalStorage` scopes the value to each deploy's async chain.
19
- */
20
- const stackNameStore = new AsyncLocalStorage();
21
- function withStackName(stackName, fn) {
22
- return stackNameStore.run(stackName, fn);
23
- }
24
- /**
25
- * Read the current async context's stack name, if any.
26
- *
27
- * Returns `undefined` outside any `withStackName` / `setCurrentStackName`
28
- * scope. Used by the live renderer to scope per-stack in-flight task
29
- * entries so concurrent deploys don't clobber each other's tasks (same
30
- * `logicalId` in two stacks would collide on the singleton renderer's
31
- * task Map without this).
32
- */
33
- function getCurrentStackName() {
34
- return stackNameStore.getStore();
35
- }
36
- /**
37
- * Per-async-context "skip the stack-name prefix on user-supplied physical
38
- * names" flag. Read by `generateResourceName` when its caller passes
39
- * `userSupplied: true`; auto-generated-name paths
40
- * (`generateResourceName(logicalId, ...)`) ignore this flag.
41
- *
42
- * Scoped via AsyncLocalStorage so that `--stack-concurrency > 1` runs
43
- * cannot cross-contaminate — each deploy's body is wrapped in its own
44
- * `withSkipPrefix(...)` scope (the deploy CLI plumbs the resolved
45
- * `--no-prefix-user-supplied-names` value through here). Default
46
- * `false` preserves pre-PR behavior when the flag is not set.
47
- */
48
- const skipPrefixStore = new AsyncLocalStorage();
49
- function withSkipPrefix(skip, fn) {
50
- return skipPrefixStore.run(skip, fn);
51
- }
52
- /**
53
- * Read the current async context's skip-prefix flag. Defaults to
54
- * `false` when no `withSkipPrefix` scope is active.
55
- *
56
- * Public for unit tests; `generateResourceName` consumes this
57
- * internally.
58
- */
59
- function getCurrentSkipPrefix() {
60
- return skipPrefixStore.getStore() ?? false;
61
- }
62
- /**
63
- * Resource types whose pre-PR #297 code path ran user-supplied
64
- * physical names through `generateResourceName` (= got the stack-name
65
- * prefix). These are the only types affected by
66
- * `--no-prefix-user-supplied-names`; flipping the flag against an
67
- * existing stack proposes REPLACEMENT on every state resource of
68
- * one of these types whose `physicalId` is still prefixed.
69
- *
70
- * Pattern A providers (Lambda, S3, SNS, SQS, DynamoDB, Logs LogGroup,
71
- * Events Rule, etc.) historically short-circuited user-supplied names
72
- * **out** of `generateResourceName` entirely — those types never got
73
- * the prefix regardless of the flag, so they are NOT included here.
74
- *
75
- * Used by the deploy-time pre-flight migration check in
76
- * `src/cli/commands/prefix-migration-check.ts`. Keep in sync with the
77
- * Pattern B provider call sites that consume
78
- * `generateResourceNameWithFallback(...)`.
79
- */
80
- const PATTERN_B_RESOURCE_TYPES = [
81
- "AWS::IAM::Role",
82
- "AWS::IAM::User",
83
- "AWS::IAM::Group",
84
- "AWS::IAM::InstanceProfile",
85
- "AWS::IAM::ManagedPolicy",
86
- "AWS::ElasticLoadBalancingV2::LoadBalancer",
87
- "AWS::ElasticLoadBalancingV2::TargetGroup"
88
- ];
89
- /**
90
- * For each Pattern B resource type, the CFn template `Properties` field
91
- * the user sets to supply a physical name (`new iam.Role(this, 'X',
92
- * { roleName: 'my-role' })` → `Properties.RoleName: 'my-role'`).
93
- *
94
- * Used by the prefix-migration check to distinguish user-supplied
95
- * physical names (which the v0.94.0 default flip would actually
96
- * REPLACE on next deploy) from auto-generated logical-id-fallback
97
- * names (which keep the prefix in BOTH old and new default — no
98
- * REPLACE pending). Without this discriminator, the migration
99
- * check naively flags every prefix-style physicalId regardless of
100
- * its origin, surfacing a false-positive WARNING on auto-generated
101
- * names that won't actually be touched.
102
- *
103
- * Keep in sync with `PATTERN_B_RESOURCE_TYPES` and with each
104
- * provider's `Properties[<NameField>]` lookup in
105
- * `src/provisioning/providers/`.
106
- */
107
- const PATTERN_B_NAME_PROPERTIES = {
108
- "AWS::IAM::Role": "RoleName",
109
- "AWS::IAM::User": "UserName",
110
- "AWS::IAM::Group": "GroupName",
111
- "AWS::IAM::InstanceProfile": "InstanceProfileName",
112
- "AWS::IAM::ManagedPolicy": "ManagedPolicyName",
113
- "AWS::ElasticLoadBalancingV2::LoadBalancer": "Name",
114
- "AWS::ElasticLoadBalancingV2::TargetGroup": "Name"
115
- };
116
- /**
117
- * For each Pattern B resource type, the `generateResourceName` options each
118
- * provider passes at its name call site (maxLength + any lowercase /
119
- * allowedPattern). Keep in sync with the provider call sites
120
- * (`generateResourceNameWithFallback(...)` in
121
- * `src/provisioning/providers/`).
122
- *
123
- * Used by the prefix-migration check to recompute BOTH the legacy
124
- * (prefixed) and the post-v0.94 (unprefixed) physical name for a recorded
125
- * user-supplied name through the SAME sanitize / truncate pipeline the
126
- * provider used — so a name that needed sanitization (`my_role` ->
127
- * `my-role`) or hash-suffix truncation (> maxLength) is compared exactly,
128
- * not via a naive prefix concat / strip.
129
- */
130
- const PATTERN_B_NAME_OPTIONS = {
131
- "AWS::IAM::Role": { maxLength: 64 },
132
- "AWS::IAM::User": { maxLength: 64 },
133
- "AWS::IAM::Group": { maxLength: 128 },
134
- "AWS::IAM::InstanceProfile": { maxLength: 128 },
135
- "AWS::IAM::ManagedPolicy": { maxLength: 128 },
136
- "AWS::ElasticLoadBalancingV2::LoadBalancer": { maxLength: 32 },
137
- "AWS::ElasticLoadBalancingV2::TargetGroup": { maxLength: 32 }
138
- };
139
- /**
140
- * Generate a unique resource name from the logical ID.
141
- *
142
- * Generates names in CloudFormation-compatible format:
143
- * `{StackName}-{LogicalId}-{Hash}` (truncated to maxLength).
144
- *
145
- * @param name The raw name (from properties or logicalId fallback)
146
- * @param options Length and character constraints
147
- * @returns A sanitized, truncated name that fits the constraints
148
- */
149
- function generateResourceName(name, options) {
150
- const { maxLength, lowercase = false, allowedPattern = /[^a-zA-Z0-9-]/g, userSupplied = false } = options;
151
- const currentStackName = stackNameStore.getStore();
152
- const fullName = currentStackName && !(userSupplied && getCurrentSkipPrefix()) ? `${currentStackName}-${name}` : name;
153
- let sanitized = lowercase ? fullName.toLowerCase() : fullName;
154
- sanitized = sanitized.replace(allowedPattern, "-");
155
- sanitized = sanitized.replace(/-{2,}/g, "-").replace(/^-+|-+$/g, "");
156
- if (sanitized.length <= maxLength) return sanitized;
157
- const hash = createHash("sha256").update(fullName).digest("hex").substring(0, 8);
158
- const maxPrefixLength = maxLength - hash.length - 1;
159
- return `${sanitized.substring(0, maxPrefixLength).replace(/-+$/, "")}-${hash}`;
160
- }
161
- /**
162
- * Generate a resource name from a user-declared physical name OR
163
- * fall back to the logical id.
164
- *
165
- * Wraps {@link generateResourceName} to express the Pattern B call-site
166
- * shape (`generateResourceName((properties['Name'] as string | undefined)
167
- * || logicalId, opts)`) as a single typed helper. The user-supplied
168
- * branch passes `userSupplied: true`, which makes the per-deploy
169
- * `withSkipPrefix(true)` flag drop the stack-name prefix on that name.
170
- * The fallback (logical-id) branch is `userSupplied: false` and keeps
171
- * the prefix regardless of the flag — auto-generated names rely on
172
- * the prefix for cross-stack uniqueness.
173
- *
174
- * Use at every Pattern B provider call site (currently IAM Role, IAM
175
- * User, IAM Group, IAM InstanceProfile, ELBv2 LoadBalancer, ELBv2
176
- * TargetGroup) so the `--no-prefix-user-supplied-names` flag controls
177
- * those types consistently. Pattern A providers (Lambda, S3, SNS,
178
- * SQS, DynamoDB, etc.) do NOT need this helper — they already
179
- * short-circuit the user-supplied name out of the
180
- * `generateResourceName` call entirely, so the prefix is never
181
- * applied to user-supplied names regardless of the flag.
182
- */
183
- function generateResourceNameWithFallback(userSuppliedName, logicalId, options) {
184
- if (userSuppliedName !== void 0 && userSuppliedName !== "") return generateResourceName(userSuppliedName, {
185
- ...options,
186
- userSupplied: true
187
- });
188
- return generateResourceName(logicalId, {
189
- ...options,
190
- userSupplied: false
191
- });
192
- }
193
- /**
194
- * Default name generation rules for CC API fallback.
195
- *
196
- * When an SDK provider falls back to CC API, the resource may need a
197
- * default name that the SDK provider would have generated. This map
198
- * defines the name property and generation options for each resource type.
199
- *
200
- * Format: resourceType → { nameProperty, options, postProcess? }
201
- */
202
- const FALLBACK_NAME_RULES = {
203
- "AWS::S3::Bucket": {
204
- nameProperty: "BucketName",
205
- options: {
206
- maxLength: 63,
207
- lowercase: true
208
- }
209
- },
210
- "AWS::SQS::Queue": {
211
- nameProperty: "QueueName",
212
- options: { maxLength: 80 }
213
- },
214
- "AWS::SNS::Topic": {
215
- nameProperty: "TopicName",
216
- options: { maxLength: 256 }
217
- },
218
- "AWS::Lambda::Function": {
219
- nameProperty: "FunctionName",
220
- options: { maxLength: 64 }
221
- },
222
- "AWS::Lambda::LayerVersion": {
223
- nameProperty: "LayerName",
224
- options: { maxLength: 64 }
225
- },
226
- "AWS::IAM::Role": {
227
- nameProperty: "RoleName",
228
- options: { maxLength: 64 }
229
- },
230
- "AWS::IAM::Policy": {
231
- nameProperty: "PolicyName",
232
- options: { maxLength: 64 }
233
- },
234
- "AWS::IAM::ManagedPolicy": {
235
- nameProperty: "ManagedPolicyName",
236
- options: { maxLength: 128 }
237
- },
238
- "AWS::IAM::User": {
239
- nameProperty: "UserName",
240
- options: { maxLength: 64 }
241
- },
242
- "AWS::IAM::Group": {
243
- nameProperty: "GroupName",
244
- options: { maxLength: 128 }
245
- },
246
- "AWS::IAM::InstanceProfile": {
247
- nameProperty: "InstanceProfileName",
248
- options: { maxLength: 128 }
249
- },
250
- "AWS::DynamoDB::Table": {
251
- nameProperty: "TableName",
252
- options: { maxLength: 255 }
253
- },
254
- "AWS::ECR::Repository": {
255
- nameProperty: "RepositoryName",
256
- options: {
257
- maxLength: 256,
258
- lowercase: true
259
- }
260
- },
261
- "AWS::ECS::Cluster": {
262
- nameProperty: "ClusterName",
263
- options: { maxLength: 255 }
264
- },
265
- "AWS::ECS::Service": {
266
- nameProperty: "ServiceName",
267
- options: { maxLength: 255 }
268
- },
269
- "AWS::Logs::LogGroup": {
270
- nameProperty: "LogGroupName",
271
- options: { maxLength: 512 }
272
- },
273
- "AWS::CloudWatch::Alarm": {
274
- nameProperty: "AlarmName",
275
- options: { maxLength: 256 }
276
- },
277
- "AWS::Events::Rule": {
278
- nameProperty: "Name",
279
- options: { maxLength: 64 }
280
- },
281
- "AWS::Events::EventBus": {
282
- nameProperty: "Name",
283
- options: { maxLength: 256 }
284
- },
285
- "AWS::Kinesis::Stream": {
286
- nameProperty: "Name",
287
- options: { maxLength: 128 }
288
- },
289
- "AWS::StepFunctions::StateMachine": {
290
- nameProperty: "StateMachineName",
291
- options: { maxLength: 80 }
292
- },
293
- "AWS::SecretsManager::Secret": {
294
- nameProperty: "Name",
295
- options: {
296
- maxLength: 512,
297
- allowedPattern: /[^a-zA-Z0-9-/_]/g
298
- }
299
- },
300
- "AWS::SSM::Parameter": {
301
- nameProperty: "Name",
302
- options: { maxLength: 2048 }
303
- },
304
- "AWS::Cognito::UserPool": {
305
- nameProperty: "UserPoolName",
306
- options: { maxLength: 128 }
307
- },
308
- "AWS::ElastiCache::SubnetGroup": {
309
- nameProperty: "CacheSubnetGroupName",
310
- options: {
311
- maxLength: 255,
312
- lowercase: true
313
- }
314
- },
315
- "AWS::ElastiCache::CacheCluster": {
316
- nameProperty: "ClusterName",
317
- options: {
318
- maxLength: 40,
319
- lowercase: true
320
- }
321
- },
322
- "AWS::RDS::DBSubnetGroup": {
323
- nameProperty: "DBSubnetGroupName",
324
- options: {
325
- maxLength: 255,
326
- lowercase: true
327
- }
328
- },
329
- "AWS::RDS::DBCluster": {
330
- nameProperty: "DBClusterIdentifier",
331
- options: {
332
- maxLength: 63,
333
- lowercase: true
334
- }
335
- },
336
- "AWS::RDS::DBInstance": {
337
- nameProperty: "DBInstanceIdentifier",
338
- options: {
339
- maxLength: 63,
340
- lowercase: true
341
- }
342
- },
343
- "AWS::DocDB::DBSubnetGroup": {
344
- nameProperty: "DBSubnetGroupName",
345
- options: {
346
- maxLength: 255,
347
- lowercase: true
348
- }
349
- },
350
- "AWS::DocDB::DBCluster": {
351
- nameProperty: "DBClusterIdentifier",
352
- options: {
353
- maxLength: 63,
354
- lowercase: true
355
- }
356
- },
357
- "AWS::DocDB::DBInstance": {
358
- nameProperty: "DBInstanceIdentifier",
359
- options: {
360
- maxLength: 63,
361
- lowercase: true
362
- }
363
- },
364
- "AWS::Neptune::DBSubnetGroup": {
365
- nameProperty: "DBSubnetGroupName",
366
- options: {
367
- maxLength: 255,
368
- lowercase: true
369
- }
370
- },
371
- "AWS::Neptune::DBCluster": {
372
- nameProperty: "DBClusterIdentifier",
373
- options: {
374
- maxLength: 63,
375
- lowercase: true
376
- }
377
- },
378
- "AWS::Neptune::DBInstance": {
379
- nameProperty: "DBInstanceIdentifier",
380
- options: {
381
- maxLength: 63,
382
- lowercase: true
383
- }
384
- },
385
- "AWS::ElasticLoadBalancingV2::LoadBalancer": {
386
- nameProperty: "Name",
387
- options: { maxLength: 32 }
388
- },
389
- "AWS::ElasticLoadBalancingV2::TargetGroup": {
390
- nameProperty: "Name",
391
- options: { maxLength: 32 }
392
- },
393
- "AWS::WAFv2::WebACL": {
394
- nameProperty: "Name",
395
- options: { maxLength: 128 }
396
- },
397
- "AWS::CodeBuild::Project": {
398
- nameProperty: "Name",
399
- options: { maxLength: 255 }
400
- },
401
- "AWS::S3Express::DirectoryBucket": {
402
- nameProperty: "BucketName",
403
- options: {
404
- maxLength: 63,
405
- lowercase: true
406
- }
407
- }
408
- };
409
- /**
410
- * Apply default name generation for CC API fallback.
411
- *
412
- * When a resource doesn't have an explicit name property set,
413
- * generates the same default name that the SDK provider would have created.
414
- * This ensures consistent naming regardless of whether SDK or CC API handles the resource.
415
- *
416
- * @param logicalId Logical ID from the template
417
- * @param resourceType CloudFormation resource type
418
- * @param properties Resource properties (will not be mutated)
419
- * @returns Properties with default name applied if needed, or original properties if no rule exists
420
- */
421
- function applyDefaultNameForFallback(logicalId, resourceType, properties) {
422
- const rule = FALLBACK_NAME_RULES[resourceType];
423
- if (!rule) return properties;
424
- if (properties[rule.nameProperty]) return properties;
425
- const generatedName = generateResourceName(logicalId, rule.options);
426
- return {
427
- ...properties,
428
- [rule.nameProperty]: generatedName
429
- };
430
- }
431
-
432
- //#endregion
433
- //#region src/utils/live-renderer.ts
434
- /**
435
- * Live multi-line progress renderer for the bottom of the terminal.
436
- *
437
- * Maintains a "live area" listing in-flight tasks (Creating MyBucket...),
438
- * redrawn on a spinner timer. Other log output is routed through
439
- * {@link LiveRenderer.printAbove} so it appears above the live area without
440
- * disturbing the currently-displayed in-flight tasks.
441
- *
442
- * Design notes:
443
- * - Multiple resources can be in flight concurrently (cdkd uses parallel DAG
444
- * dispatch), so a single in-place line overwrite is not enough — each
445
- * in-flight resource is its own line in the live area.
446
- * - On non-TTY (CI/log-collection), the renderer stays inactive and
447
- * {@link LiveRenderer.printAbove} falls through to a direct write, so output
448
- * matches the previous append-only behavior.
449
- * - In verbose mode (debug level) the caller should not start the renderer:
450
- * debug logs would interleave too aggressively with the live area.
451
- */
452
- const SPINNER_FRAMES = [
453
- "⠋",
454
- "⠙",
455
- "⠹",
456
- "⠸",
457
- "⠼",
458
- "⠴",
459
- "⠦",
460
- "⠧",
461
- "⠇",
462
- "⠏"
463
- ];
464
- const FRAME_INTERVAL_MS = 80;
465
- const ESC = "\x1B[";
466
- /**
467
- * Scope a task `id` to its calling stack so two stacks running in
468
- * parallel — `cdkd deploy --all` with `--stack-concurrency > 1` — don't
469
- * collide on the same `logicalId` in the renderer's task Map. Without
470
- * this, stack B's `addTask('MyQueue', ...)` would overwrite stack A's
471
- * entry, and stack A's later `removeTask('MyQueue')` would erase
472
- * stack B's.
473
- */
474
- function scopedKey(id, stackName) {
475
- return stackName ? `${stackName}:${id}` : id;
476
- }
477
- var LiveRenderer = class {
478
- tasks = /* @__PURE__ */ new Map();
479
- active = false;
480
- spinnerIndex = 0;
481
- interval = null;
482
- linesDrawn = 0;
483
- cursorHidden = false;
484
- exitListener = null;
485
- stream;
486
- constructor(stream = process.stdout) {
487
- this.stream = stream;
488
- }
489
- isActive() {
490
- return this.active;
491
- }
492
- /**
493
- * Enable the live renderer. No-op if stdout is not a TTY or if
494
- * `CDKD_NO_LIVE=1`. Returns true if successfully enabled.
495
- */
496
- start() {
497
- if (this.active) return true;
498
- if (!this.stream.isTTY) return false;
499
- if (process.env["CDKD_NO_LIVE"] === "1") return false;
500
- this.active = true;
501
- this.hideCursor();
502
- if (!this.exitListener) {
503
- this.exitListener = () => this.showCursor();
504
- process.on("exit", this.exitListener);
505
- }
506
- this.interval = setInterval(() => this.draw(), FRAME_INTERVAL_MS);
507
- if (typeof this.interval.unref === "function") this.interval.unref();
508
- return true;
509
- }
510
- stop() {
511
- if (!this.active) return;
512
- if (this.interval) {
513
- clearInterval(this.interval);
514
- this.interval = null;
515
- }
516
- this.clear();
517
- this.showCursor();
518
- if (this.exitListener) {
519
- process.removeListener("exit", this.exitListener);
520
- this.exitListener = null;
521
- }
522
- this.tasks.clear();
523
- this.active = false;
524
- }
525
- addTask(id, label) {
526
- const stackName = getCurrentStackName();
527
- this.tasks.set(scopedKey(id, stackName), {
528
- label,
529
- startedAt: Date.now(),
530
- stackName
531
- });
532
- if (this.active) this.draw();
533
- }
534
- removeTask(id) {
535
- const stackName = getCurrentStackName();
536
- if (!this.tasks.delete(scopedKey(id, stackName))) return;
537
- if (this.active) this.draw();
538
- }
539
- /**
540
- * Replace the label of a previously-added task in place. No-op if the
541
- * task is not currently tracked (e.g. it already finished). Used by the
542
- * per-resource deadline wrapper to surface a "[taking longer than
543
- * expected, Nm+]" suffix without disturbing the elapsed-time counter
544
- * the renderer tracks via `startedAt`.
545
- */
546
- updateTaskLabel(id, label) {
547
- const stackName = getCurrentStackName();
548
- const task = this.tasks.get(scopedKey(id, stackName));
549
- if (!task) return;
550
- task.label = label;
551
- if (this.active) this.draw();
552
- }
553
- /**
554
- * Print content above the live area. Clears the live area, runs the writer,
555
- * then redraws the live area. When the renderer is inactive, the writer
556
- * runs directly so callers can use this unconditionally.
557
- */
558
- printAbove(write) {
559
- if (!this.active) {
560
- write();
561
- return;
562
- }
563
- this.clear();
564
- write();
565
- this.draw();
566
- }
567
- clear() {
568
- if (this.linesDrawn === 0) return;
569
- this.stream.write("\r");
570
- for (let i = 0; i < this.linesDrawn; i++) this.stream.write(`${ESC}1A${ESC}2K`);
571
- this.linesDrawn = 0;
572
- }
573
- draw() {
574
- if (!this.active) return;
575
- this.clear();
576
- if (this.tasks.size === 0) return;
577
- const frame = SPINNER_FRAMES[this.spinnerIndex % SPINNER_FRAMES.length];
578
- this.spinnerIndex++;
579
- const distinctStacks = /* @__PURE__ */ new Set();
580
- for (const task of this.tasks.values()) distinctStacks.add(task.stackName);
581
- const showStackPrefix = distinctStacks.size > 1;
582
- const cols = this.stream.columns ?? 80;
583
- const lines = [];
584
- for (const task of this.tasks.values()) {
585
- const elapsed = ((Date.now() - task.startedAt) / 1e3).toFixed(1);
586
- const raw = ` ${frame} ${showStackPrefix && task.stackName ? `[${task.stackName}] ` : ""}${task.label} (${elapsed}s)`;
587
- lines.push(this.truncate(raw, cols));
588
- }
589
- this.stream.write(lines.join("\n") + "\n");
590
- this.linesDrawn = lines.length;
591
- }
592
- truncate(s, maxLen) {
593
- if (s.length <= maxLen) return s;
594
- if (maxLen <= 1) return "…";
595
- return s.substring(0, maxLen - 1) + "…";
596
- }
597
- hideCursor() {
598
- if (this.cursorHidden) return;
599
- this.stream.write(`${ESC}?25l`);
600
- this.cursorHidden = true;
601
- }
602
- showCursor() {
603
- if (!this.cursorHidden) return;
604
- this.stream.write(`${ESC}?25h`);
605
- this.cursorHidden = false;
606
- }
607
- };
608
- let globalRenderer = null;
609
- function getLiveRenderer() {
610
- if (!globalRenderer) globalRenderer = new LiveRenderer();
611
- return globalRenderer;
612
- }
613
-
614
- //#endregion
615
- //#region src/utils/stack-context.ts
616
- const outputBufferStore = new AsyncLocalStorage();
617
- /**
618
- * Run `fn` with a fresh log buffer scoped to its async chain. Any
619
- * `logger.info / debug / warn / error` calls inside `fn` (and any
620
- * `await`s) push into the buffer instead of writing to stdout/stderr.
621
- * Returns the buffered lines (and either `result` or `error`) so the
622
- * caller can flush them in one block.
623
- */
624
- async function runStackBuffered(fn) {
625
- const buffer = { lines: [] };
626
- return outputBufferStore.run(buffer, async () => {
627
- try {
628
- return {
629
- ok: true,
630
- result: await fn(),
631
- lines: buffer.lines
632
- };
633
- } catch (error) {
634
- return {
635
- ok: false,
636
- error,
637
- lines: buffer.lines
638
- };
639
- }
640
- });
641
- }
642
- /**
643
- * Get the current async context's stack output buffer, or `undefined`
644
- * if no `runStackBuffered` is active. The logger consults this on every
645
- * call: present → push to buffer; absent → fall through to live
646
- * renderer / console.
647
- */
648
- function getCurrentStackOutputBuffer() {
649
- return outputBufferStore.getStore();
650
- }
651
-
652
- //#endregion
653
- //#region src/utils/logger.ts
654
- /**
655
- * ANSI color codes
656
- *
657
- * Kept internal — `ConsoleLogger.formatMessage` references these for the
658
- * verbose/compact mode level prefixes. For inline color wrapping in
659
- * production code, import from `./colors.js` instead (which lives in a
660
- * separate module so unit tests that mock `logger.ts` don't strip color
661
- * helpers as a side effect).
662
- */
663
- const colors = {
664
- reset: "\x1B[0m",
665
- bright: "\x1B[1m",
666
- dim: "\x1B[2m",
667
- red: "\x1B[31m",
668
- green: "\x1B[32m",
669
- yellow: "\x1B[33m",
670
- blue: "\x1B[34m",
671
- cyan: "\x1B[36m",
672
- gray: "\x1B[90m"
673
- };
674
- /**
675
- * Format timestamp
676
- */
677
- function formatTimestamp() {
678
- return (/* @__PURE__ */ new Date()).toISOString();
679
- }
680
- /**
681
- * Console logger implementation
682
- *
683
- * Supports two output modes:
684
- * - verbose (debug level): timestamps, module prefixes, all details
685
- * - compact (info level): clean output without timestamps or prefixes
686
- */
687
- var ConsoleLogger = class {
688
- level;
689
- useColors;
690
- constructor(level = "info", useColors = true) {
691
- this.level = level;
692
- this.useColors = useColors;
693
- }
694
- shouldLog(level) {
695
- const levels = [
696
- "debug",
697
- "info",
698
- "warn",
699
- "error"
700
- ];
701
- const currentLevelIndex = levels.indexOf(this.level);
702
- return levels.indexOf(level) >= currentLevelIndex;
703
- }
704
- formatMessage(level, message, ...args) {
705
- const formattedArgs = args.length > 0 ? " " + args.map((a) => JSON.stringify(a)).join(" ") : "";
706
- if (this.level === "debug") {
707
- const timestamp = formatTimestamp();
708
- const levelStr = level.toUpperCase().padEnd(5);
709
- if (this.useColors) {
710
- const levelColor = {
711
- debug: colors.gray,
712
- info: colors.blue,
713
- warn: colors.yellow,
714
- error: colors.red
715
- }[level];
716
- return `${colors.dim}${timestamp}${colors.reset} ${levelColor}${levelStr}${colors.reset} ${message}${formattedArgs}`;
717
- }
718
- return `${timestamp} ${levelStr} ${message}${formattedArgs}`;
719
- }
720
- if (this.useColors) {
721
- if (level === "error") return `${colors.red}${message}${formattedArgs}${colors.reset}`;
722
- if (level === "warn") return `${colors.yellow}${message}${formattedArgs}${colors.reset}`;
723
- return `${message}${formattedArgs}`;
724
- }
725
- return `${message}${formattedArgs}`;
726
- }
727
- /**
728
- * Route a formatted log line. When a per-stack output buffer is active in
729
- * the current async context (parallel multi-stack deploy), capture the
730
- * line into the buffer so it can be flushed as one atomic block when the
731
- * stack finishes. Otherwise fall through to the live renderer / console
732
- * as before.
733
- */
734
- emit(level, formatted) {
735
- const buffer = getCurrentStackOutputBuffer();
736
- if (buffer) {
737
- buffer.lines.push(formatted);
738
- return;
739
- }
740
- getLiveRenderer().printAbove(() => {
741
- if (level === "error") console.error(formatted);
742
- else if (level === "warn") console.warn(formatted);
743
- else if (level === "info") console.info(formatted);
744
- else console.debug(formatted);
745
- });
746
- }
747
- debug(message, ...args) {
748
- if (this.shouldLog("debug")) this.emit("debug", this.formatMessage("debug", message, ...args));
749
- }
750
- info(message, ...args) {
751
- if (this.shouldLog("info")) this.emit("info", this.formatMessage("info", message, ...args));
752
- }
753
- warn(message, ...args) {
754
- if (this.shouldLog("warn")) this.emit("warn", this.formatMessage("warn", message, ...args));
755
- }
756
- error(message, ...args) {
757
- if (this.shouldLog("error")) this.emit("error", this.formatMessage("error", message, ...args));
758
- }
759
- /**
760
- * Set log level
761
- */
762
- setLevel(level) {
763
- this.level = level;
764
- }
765
- getLevel() {
766
- return this.level;
767
- }
768
- /**
769
- * Create a child logger with a prefix
770
- *
771
- * In verbose mode, prefix is shown as [Prefix]. In compact mode, prefix is hidden.
772
- */
773
- child(prefix) {
774
- return new ChildLogger(prefix, this.useColors);
775
- }
776
- };
777
- /**
778
- * Child logger that always syncs level from global logger
779
- */
780
- var ChildLogger = class extends ConsoleLogger {
781
- prefix;
782
- constructor(prefix, useColors) {
783
- super("info", useColors);
784
- this.prefix = prefix;
785
- }
786
- syncLevel() {
787
- if (globalLogger) this.setLevel(globalLogger.getLevel());
788
- }
789
- debug(message, ...args) {
790
- this.syncLevel();
791
- super.debug(`[${this.prefix}] ${message}`, ...args);
792
- }
793
- info(message, ...args) {
794
- this.syncLevel();
795
- const msg = this.getLevel() === "debug" ? `[${this.prefix}] ${message}` : message;
796
- super.info(msg, ...args);
797
- }
798
- warn(message, ...args) {
799
- this.syncLevel();
800
- const msg = this.getLevel() === "debug" ? `[${this.prefix}] ${message}` : message;
801
- super.warn(msg, ...args);
802
- }
803
- error(message, ...args) {
804
- this.syncLevel();
805
- const msg = this.getLevel() === "debug" ? `[${this.prefix}] ${message}` : message;
806
- super.error(msg, ...args);
807
- }
808
- };
809
- /**
810
- * Global logger instance
811
- */
812
- let globalLogger = null;
813
- /**
814
- * Get or create global logger
815
- */
816
- function getLogger() {
817
- if (!globalLogger) globalLogger = new ConsoleLogger();
818
- return globalLogger;
819
- }
820
- /**
821
- * Set global logger instance
822
- */
823
- function setLogger(logger) {
824
- globalLogger = logger;
825
- }
826
-
827
- //#endregion
828
4
  //#region src/utils/error-handler.ts
829
5
  /**
830
6
  * Base error class for cdkd
@@ -1503,5 +679,5 @@ function isTerminationProtectionPropagationError(message) {
1503
679
  }
1504
680
 
1505
681
  //#endregion
1506
- export { withErrorHandling as A, generateResourceName as B, StackHasActiveImportsError as C, formatError as D, SynthesisError as E, getLiveRenderer as F, withSkipPrefix as H, PATTERN_B_NAME_OPTIONS as I, PATTERN_B_NAME_PROPERTIES as L, getLogger as M, setLogger as N, isCdkdError as O, runStackBuffered as P, PATTERN_B_RESOURCE_TYPES as R, ResourceUpdateNotSupportedError as S, StateError as T, withStackName as U, generateResourceNameWithFallback as V, MissingCdkCliError as _, normalizeAwsTagsToCfn as a, ProvisioningError as b, AssetError as c, DependencyError as d, LocalInvokeBuildError as f, MacroExpansionError as g, LockError as h, matchesCdkPath as i, ConsoleLogger as j, normalizeAwsError as k, CdkdError as l, LocalStartServiceError as m, isTerminationProtectionPropagationError as n, resolveExplicitPhysicalId as o, LocalMigrateError as p, CDK_PATH_TAG as r, assertRegionMatch as s, disableInstanceApiTermination as t, ConfigError as u, NestedStackChildDirectDestroyError as v, StackTerminationProtectionError as w, ResourceTimeoutError as x, PartialFailureError as y, applyDefaultNameForFallback as z };
1507
- //# sourceMappingURL=ec2-termination-protection-uvVnteGl.js.map
682
+ export { withErrorHandling as A, StackHasActiveImportsError as C, formatError as D, SynthesisError as E, isCdkdError as O, ResourceUpdateNotSupportedError as S, StateError as T, MissingCdkCliError as _, normalizeAwsTagsToCfn as a, ProvisioningError as b, AssetError as c, DependencyError as d, LocalInvokeBuildError as f, MacroExpansionError as g, LockError as h, matchesCdkPath as i, normalizeAwsError as k, CdkdError as l, LocalStartServiceError as m, isTerminationProtectionPropagationError as n, resolveExplicitPhysicalId as o, LocalMigrateError as p, CDK_PATH_TAG as r, assertRegionMatch as s, disableInstanceApiTermination as t, ConfigError as u, NestedStackChildDirectDestroyError as v, StackTerminationProtectionError as w, ResourceTimeoutError as x, PartialFailureError as y };
683
+ //# sourceMappingURL=ec2-termination-protection-q-WGWgxa.js.map