@go-to-k/cdkd 0.219.9 → 0.220.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.
@@ -3383,6 +3383,60 @@ var S3StateBackend = class {
3383
3383
  }
3384
3384
  }
3385
3385
  /**
3386
+ * Raw sidecar-object write under the state bucket. Used for non-state
3387
+ * auxiliary files that share the bucket + region-resolution plumbing
3388
+ * (e.g. deployment-event JSONL streams + their `index.json`, issue
3389
+ * #808) without going through the state-schema validation that
3390
+ * `saveState` applies. No optimistic locking — callers own their key
3391
+ * uniqueness / last-writer-wins semantics.
3392
+ */
3393
+ async putRawObject(key, body, contentType = "application/json") {
3394
+ await this.ensureClientForBucket();
3395
+ await this.s3Client.send(new PutObjectCommand({
3396
+ Bucket: this.config.bucket,
3397
+ Key: key,
3398
+ Body: body,
3399
+ ContentLength: Buffer.byteLength(body),
3400
+ ContentType: contentType
3401
+ }));
3402
+ }
3403
+ /**
3404
+ * Raw sidecar-object read under the state bucket. Returns `null` when
3405
+ * the key does not exist; other errors propagate.
3406
+ */
3407
+ async getRawObject(key) {
3408
+ await this.ensureClientForBucket();
3409
+ try {
3410
+ return await (await this.s3Client.send(new GetObjectCommand({
3411
+ Bucket: this.config.bucket,
3412
+ Key: key
3413
+ }))).Body?.transformToString() ?? null;
3414
+ } catch (error) {
3415
+ if (isNoSuchKey(error) || error.name === "NotFound") return null;
3416
+ throw error;
3417
+ }
3418
+ }
3419
+ /**
3420
+ * Raw key listing under an arbitrary key prefix in the state bucket
3421
+ * (paginated). Used by `cdkd events` to discover regions / runs under
3422
+ * `{prefix}/{stackName}/.../deployments/`.
3423
+ */
3424
+ async listRawKeys(keyPrefix) {
3425
+ await this.ensureClientForBucket();
3426
+ const keys = [];
3427
+ let continuationToken;
3428
+ do {
3429
+ const response = await this.s3Client.send(new ListObjectsV2Command({
3430
+ Bucket: this.config.bucket,
3431
+ Prefix: keyPrefix,
3432
+ ...continuationToken && { ContinuationToken: continuationToken }
3433
+ }));
3434
+ for (const obj of response.Contents ?? []) if (obj.Key) keys.push(obj.Key);
3435
+ continuationToken = response.IsTruncated ? response.NextContinuationToken : void 0;
3436
+ } while (continuationToken);
3437
+ return keys;
3438
+ }
3439
+ /**
3386
3440
  * HeadObject probe — returns true on 200, false on NotFound. Other errors
3387
3441
  * propagate so we don't accidentally swallow IAM denials.
3388
3442
  */
@@ -11049,6 +11103,37 @@ var DagExecutor = class {
11049
11103
  }
11050
11104
  };
11051
11105
 
11106
+ //#endregion
11107
+ //#region src/types/deployment-events.ts
11108
+ /**
11109
+ * Walk an error's `.cause` chain (bounded) and extract metadata for a
11110
+ * deployment event. The outermost error supplies `name` / `message`
11111
+ * (matching what the user sees in the log); the innermost AWS-SDK-shaped
11112
+ * error (the one carrying `$metadata`) supplies `awsErrorCode` /
11113
+ * `requestId` when present.
11114
+ */
11115
+ function extractDeploymentEventError(err) {
11116
+ if (!(err instanceof Error)) return {
11117
+ name: "UnknownError",
11118
+ message: String(err)
11119
+ };
11120
+ const result = {
11121
+ name: err.name || "Error",
11122
+ message: err.message
11123
+ };
11124
+ let current = err;
11125
+ for (let depth = 0; depth < 10 && current instanceof Error; depth++) {
11126
+ const maybeAws = current;
11127
+ if (maybeAws.$metadata !== void 0) {
11128
+ const code = maybeAws.Code ?? maybeAws.name;
11129
+ if (code) result.awsErrorCode = code;
11130
+ if (maybeAws.$metadata.requestId) result.requestId = maybeAws.$metadata.requestId;
11131
+ }
11132
+ current = current.cause;
11133
+ }
11134
+ return result;
11135
+ }
11136
+
11052
11137
  //#endregion
11053
11138
  //#region src/analyzer/implicit-delete-deps.ts
11054
11139
  /**
@@ -11925,25 +12010,33 @@ var DeployEngine = class {
11925
12010
  * information from state, then processes UPDATE/DELETE rollbacks, and finally
11926
12011
  * processes sorted CREATE rollback deletions.
11927
12012
  */
11928
- async performRollback(completedOperations, stateResources, _stackName) {
12013
+ async performRollback(completedOperations, stateResources, stackName) {
11929
12014
  if (completedOperations.length === 0) {
11930
12015
  this.logger.info("No completed operations to roll back.");
11931
12016
  return;
11932
12017
  }
11933
12018
  this.logger.info(`Rolling back ${completedOperations.length} completed operation(s)...`);
12019
+ this.recordEvent({
12020
+ eventType: "ROLLBACK_STARTED",
12021
+ stackName
12022
+ });
11934
12023
  const createOps = [];
11935
12024
  const otherOps = [];
11936
12025
  for (const op of completedOperations) if (op.changeType === "CREATE") createOps.push(op);
11937
12026
  else otherOps.push(op);
11938
12027
  for (let i = otherOps.length - 1; i >= 0; i--) {
11939
12028
  const op = otherOps[i];
11940
- await this.performSingleRollback(op, stateResources);
12029
+ await this.performSingleRollback(op, stateResources, stackName);
11941
12030
  }
11942
12031
  if (createOps.length > 0) {
11943
12032
  const sortedCreateOps = this.sortRollbackCreates(createOps, stateResources);
11944
- for (const op of sortedCreateOps) await this.performSingleRollback(op, stateResources);
12033
+ for (const op of sortedCreateOps) await this.performSingleRollback(op, stateResources, stackName);
11945
12034
  }
11946
12035
  this.logger.info("Rollback completed. Some resources may remain if deletion failed.");
12036
+ this.recordEvent({
12037
+ eventType: "ROLLBACK_FINISHED",
12038
+ stackName
12039
+ });
11947
12040
  }
11948
12041
  /**
11949
12042
  * Sort CREATE rollback operations so that resources depending on others
@@ -11997,7 +12090,7 @@ var DeployEngine = class {
11997
12090
  /**
11998
12091
  * Perform a single rollback operation (extracted for reuse)
11999
12092
  */
12000
- async performSingleRollback(op, stateResources) {
12093
+ async performSingleRollback(op, stateResources, stackName) {
12001
12094
  try {
12002
12095
  switch (op.changeType) {
12003
12096
  case "CREATE": {
@@ -12013,6 +12106,14 @@ var DeployEngine = class {
12013
12106
  await provider.delete(op.logicalId, op.physicalId, op.resourceType, op.properties, { expectedRegion: this.stackRegion });
12014
12107
  delete stateResources[op.logicalId];
12015
12108
  this.logger.info(` Rollback: ${op.logicalId} deleted successfully`);
12109
+ this.recordEvent({
12110
+ eventType: "ROLLBACK_RESOURCE_SUCCEEDED",
12111
+ stackName,
12112
+ operation: "CREATE",
12113
+ logicalId: op.logicalId,
12114
+ resourceType: op.resourceType,
12115
+ ...op.provisionedBy && { provisionedBy: op.provisionedBy }
12116
+ });
12016
12117
  break;
12017
12118
  }
12018
12119
  case "UPDATE": {
@@ -12033,6 +12134,14 @@ var DeployEngine = class {
12033
12134
  await provider.update(op.logicalId, currentResource.physicalId, op.resourceType, op.previousState.properties, currentResource.properties);
12034
12135
  stateResources[op.logicalId] = op.previousState;
12035
12136
  this.logger.info(` Rollback: ${op.logicalId} restored successfully`);
12137
+ this.recordEvent({
12138
+ eventType: "ROLLBACK_RESOURCE_SUCCEEDED",
12139
+ stackName,
12140
+ operation: "UPDATE",
12141
+ logicalId: op.logicalId,
12142
+ resourceType: op.resourceType,
12143
+ ...op.provisionedBy && { provisionedBy: op.provisionedBy }
12144
+ });
12036
12145
  break;
12037
12146
  }
12038
12147
  case "DELETE":
@@ -12042,6 +12151,15 @@ var DeployEngine = class {
12042
12151
  } catch (rollbackError) {
12043
12152
  this.logger.warn(` Rollback failed for ${op.logicalId} (${op.changeType}): ${rollbackError instanceof Error ? rollbackError.message : String(rollbackError)}`);
12044
12153
  this.logger.warn(" Continuing with remaining rollback operations...");
12154
+ this.recordEvent({
12155
+ eventType: "ROLLBACK_RESOURCE_FAILED",
12156
+ stackName,
12157
+ operation: op.changeType,
12158
+ logicalId: op.logicalId,
12159
+ resourceType: op.resourceType,
12160
+ ...op.provisionedBy && { provisionedBy: op.provisionedBy },
12161
+ error: extractDeploymentEventError(rollbackError)
12162
+ });
12045
12163
  }
12046
12164
  }
12047
12165
  /**
@@ -12051,13 +12169,25 @@ var DeployEngine = class {
12051
12169
  const resourceType = change.resourceType;
12052
12170
  const renderer = getLiveRenderer();
12053
12171
  const needsReplacement = change.changeType === "UPDATE" && (change.propertyChanges?.some((pc) => pc.requiresReplacement) ?? false);
12054
- const baseLabel = `${change.changeType === "CREATE" ? "Creating" : change.changeType === "DELETE" ? "Deleting" : needsReplacement ? "Replacing" : "Updating"} ${logicalId} (${resourceType})${this.peekRoutingForLabel(change, stateResources[logicalId]) === "cc-api" ? " [CC API]" : ""}`;
12172
+ const verb = change.changeType === "CREATE" ? "Creating" : change.changeType === "DELETE" ? "Deleting" : needsReplacement ? "Replacing" : "Updating";
12173
+ const labelRouting = this.peekRoutingForLabel(change, stateResources[logicalId]);
12174
+ const baseLabel = `${verb} ${logicalId} (${resourceType})${labelRouting === "cc-api" ? " [CC API]" : ""}`;
12055
12175
  renderer.addTask(logicalId, baseLabel);
12056
12176
  const operationKind = change.changeType === "CREATE" ? "CREATE" : change.changeType === "DELETE" ? "DELETE" : "UPDATE";
12057
12177
  const providerMinTimeoutMs = this.providerRegistry.getProvider(resourceType).getMinResourceTimeoutMs?.() ?? 0;
12058
12178
  const warnAfterMs = this.options.resourceWarnAfterByType?.[resourceType] ?? this.options.resourceWarnAfterMs ?? 3e5;
12059
12179
  const globalTimeoutMs = this.options.resourceTimeoutMs ?? 18e5;
12060
12180
  const timeoutMs = this.options.resourceTimeoutByType?.[resourceType] ?? Math.max(providerMinTimeoutMs, globalTimeoutMs);
12181
+ const eventOp = operationKind;
12182
+ const resourceStartedAt = Date.now();
12183
+ this.recordEvent({
12184
+ eventType: "RESOURCE_STARTED",
12185
+ stackName,
12186
+ operation: eventOp,
12187
+ logicalId,
12188
+ resourceType,
12189
+ ...labelRouting && { provisionedBy: labelRouting }
12190
+ });
12061
12191
  try {
12062
12192
  await withResourceDeadline(async () => {
12063
12193
  await this.provisionResourceBody(logicalId, change, stateResources, stackName, template, parameterValues, conditions, counts, progress);
@@ -12074,10 +12204,30 @@ var DeployEngine = class {
12074
12204
  },
12075
12205
  onTimeout: (elapsedMs) => new ResourceTimeoutError(logicalId, resourceType, this.stackRegion, elapsedMs, operationKind, timeoutMs)
12076
12206
  });
12207
+ this.recordEvent({
12208
+ eventType: "RESOURCE_SUCCEEDED",
12209
+ stackName,
12210
+ operation: eventOp,
12211
+ logicalId,
12212
+ resourceType,
12213
+ ...stateResources[logicalId]?.provisionedBy ? { provisionedBy: stateResources[logicalId]?.provisionedBy } : labelRouting && { provisionedBy: labelRouting },
12214
+ ...stateResources[logicalId]?.physicalId && { physicalId: stateResources[logicalId]?.physicalId },
12215
+ durationMs: Date.now() - resourceStartedAt
12216
+ });
12077
12217
  } catch (error) {
12078
12218
  renderer.removeTask(logicalId);
12079
12219
  const message = error instanceof Error ? error.message : String(error);
12080
12220
  this.logger.error(`Failed to ${change.changeType.toLowerCase()} ${logicalId}: ${message}`);
12221
+ this.recordEvent({
12222
+ eventType: "RESOURCE_FAILED",
12223
+ stackName,
12224
+ operation: eventOp,
12225
+ logicalId,
12226
+ resourceType,
12227
+ ...stateResources[logicalId]?.provisionedBy ? { provisionedBy: stateResources[logicalId]?.provisionedBy } : labelRouting && { provisionedBy: labelRouting },
12228
+ durationMs: Date.now() - resourceStartedAt,
12229
+ error: extractDeploymentEventError(error)
12230
+ });
12081
12231
  throw new ProvisioningError(`Failed to ${change.changeType.toLowerCase()} resource ${logicalId}`, resourceType, logicalId, stateResources[logicalId]?.physicalId, error instanceof Error ? error : void 0);
12082
12232
  } finally {
12083
12233
  renderer.removeTask(logicalId);
@@ -12087,6 +12237,18 @@ var DeployEngine = class {
12087
12237
  return deriveLabelRouting(change, existingState, this.providerRegistry);
12088
12238
  }
12089
12239
  /**
12240
+ * #808 — forward one structured deployment event to the optional
12241
+ * recorder. No-op when no recorder was supplied. `record()` is
12242
+ * contractually synchronous and never-throwing, but we still guard
12243
+ * with a try/catch so an event emission can NEVER abort a deploy.
12244
+ */
12245
+ recordEvent(event) {
12246
+ if (!this.options.eventRecorder) return;
12247
+ try {
12248
+ this.options.eventRecorder.record(event);
12249
+ } catch {}
12250
+ }
12251
+ /**
12090
12252
  * Inner body of provisionResource, extracted so the outer wrapper can
12091
12253
  * apply the per-resource deadline (`withResourceDeadline`) without
12092
12254
  * having the timeout / warn timer code dwarf the real provisioning
@@ -12510,5 +12672,5 @@ var DeployEngine = class {
12510
12672
  };
12511
12673
 
12512
12674
  //#endregion
12513
- export { WorkGraph as A, resolveCaptureObservedState as B, DagBuilder as C, shouldRetainResource as D, S3StateBackend as E, runDockerStreaming as F, CFN_TEMPLATE_BODY_LIMIT as G, resolveStateBucketWithDefault as H, Synthesizer as I, findLargeInlineResources as J, CFN_TEMPLATE_URL_LIMIT as K, getDefaultStateBucketName as L, formatDockerLoginError as M, getDockerCmd as N, AssetPublisher as O, runDockerForeground as P, resolveBucketRegion as Q, getLegacyStateBucketName as R, DiffCalculator as S, LockManager as T, resolveStateBucketWithDefaultAndSource as U, resolveSkipPrefix as V, warnDeprecatedNoPrefixCliFlag as W, AssemblyReader as X, uploadCfnTemplate as Y, clearBucketRegionCache as Z, ProviderRegistry as _, withRetry as a, IntrinsicFunctionResolver as b, formatResourceLine as c, gray as d, green as f, collectInlinePolicyNamesManagedBySiblings as g, IAMRoleProvider as h, withResourceDeadline as i, buildDockerImage as j, stringifyValue as k, bold as l, yellow as m, DEFAULT_RESOURCE_WARN_AFTER_MS as n, isRetryableTransientError as o, red as p, MIGRATE_TMP_PREFIX as q, DeployEngine as r, IMPLICIT_DELETE_DEPENDENCIES as s, DEFAULT_RESOURCE_TIMEOUT_MS as t, cyan as u, findActionableSilentDrops as v, TemplateParser as w, applyRoleArnIfSet as x, CloudControlProvider as y, resolveApp as z };
12514
- //# sourceMappingURL=deploy-engine-DVP4vDXG.js.map
12675
+ export { resolveBucketRegion as $, stringifyValue as A, resolveApp as B, DiffCalculator as C, S3StateBackend as D, LockManager as E, runDockerForeground as F, warnDeprecatedNoPrefixCliFlag as G, resolveSkipPrefix as H, runDockerStreaming as I, MIGRATE_TMP_PREFIX as J, CFN_TEMPLATE_BODY_LIMIT as K, Synthesizer as L, buildDockerImage as M, formatDockerLoginError as N, shouldRetainResource as O, getDockerCmd as P, clearBucketRegionCache as Q, getDefaultStateBucketName as R, applyRoleArnIfSet as S, TemplateParser as T, resolveStateBucketWithDefault as U, resolveCaptureObservedState as V, resolveStateBucketWithDefaultAndSource as W, uploadCfnTemplate as X, findLargeInlineResources as Y, AssemblyReader as Z, collectInlinePolicyNamesManagedBySiblings as _, withRetry as a, CloudControlProvider as b, extractDeploymentEventError as c, cyan as d, gray as f, IAMRoleProvider as g, yellow as h, withResourceDeadline as i, WorkGraph as j, AssetPublisher as k, formatResourceLine as l, red as m, DEFAULT_RESOURCE_WARN_AFTER_MS as n, isRetryableTransientError as o, green as p, CFN_TEMPLATE_URL_LIMIT as q, DeployEngine as r, IMPLICIT_DELETE_DEPENDENCIES as s, DEFAULT_RESOURCE_TIMEOUT_MS as t, bold as u, ProviderRegistry as v, DagBuilder as w, IntrinsicFunctionResolver as x, findActionableSilentDrops as y, getLegacyStateBucketName as z };
12676
+ //# sourceMappingURL=deploy-engine-CFaK71E9.js.map