@kensio/yulin 1.21.14 → 1.21.16

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 (55) hide show
  1. package/dist/service/cloudformation/changeset/sim-cfn-change-set-changes.js +1 -1
  2. package/dist/service/cloudformation/resource/factory/sim-cfn-resource-factory.type.d.ts +25 -1
  3. package/dist/service/cloudformation/resource/resolve/service/sim-cfn-resource-service-factory.d.ts +18 -0
  4. package/dist/service/cloudformation/resource/resolve/service/sim-cfn-resource-service-factory.js +30 -0
  5. package/dist/service/cloudformation/resource/sim-cfn-resource.type.d.ts +18 -0
  6. package/dist/service/cloudformation/resource/update/sim-cfn-resource-in-place-updater.d.ts +35 -0
  7. package/dist/service/cloudformation/resource/update/sim-cfn-resource-in-place-updater.js +61 -0
  8. package/dist/service/cloudformation/resource/update/sim-cfn-resource-update-error.d.ts +9 -0
  9. package/dist/service/cloudformation/resource/update/sim-cfn-resource-update-error.js +14 -0
  10. package/dist/service/cloudformation/resource/update/sim-cfn-resource-update-validator.js +5 -18
  11. package/dist/service/cloudformation/stack/sim-cfn-stack-resource-operations.d.ts +7 -2
  12. package/dist/service/cloudformation/stack/sim-cfn-stack-resource-operations.js +16 -12
  13. package/dist/service/cloudformation/stack/update/sim-cfn-stack-resource-change-pairs.d.ts +12 -0
  14. package/dist/service/cloudformation/stack/update/sim-cfn-stack-resource-change-pairs.js +18 -0
  15. package/dist/service/cloudformation/stack/update/sim-cfn-stack-resource-changes.d.ts +52 -0
  16. package/dist/service/cloudformation/stack/update/sim-cfn-stack-resource-changes.js +86 -0
  17. package/dist/service/cloudformation/stack/update/sim-cfn-stack-resource-updates.d.ts +42 -0
  18. package/dist/service/cloudformation/stack/update/sim-cfn-stack-resource-updates.js +70 -0
  19. package/dist/service/cloudformation/stack/update/sim-cfn-stack-update-plan.d.ts +25 -18
  20. package/dist/service/cloudformation/stack/update/sim-cfn-stack-update-plan.js +43 -32
  21. package/dist/service/cloudformation/stack/update/sim-cfn-stack-updater.d.ts +7 -5
  22. package/dist/service/cloudformation/stack/update/sim-cfn-stack-updater.js +12 -7
  23. package/dist/service/secretsmanager/cfn/secret/sim-cfn-secrets-manager-secret-change.d.ts +19 -0
  24. package/dist/service/secretsmanager/cfn/secret/sim-cfn-secrets-manager-secret-change.js +39 -0
  25. package/dist/service/secretsmanager/cfn/secret/sim-cfn-secrets-manager-secret-deleter.d.ts +23 -0
  26. package/dist/service/secretsmanager/cfn/secret/sim-cfn-secrets-manager-secret-deleter.js +23 -0
  27. package/dist/service/secretsmanager/cfn/secret/sim-cfn-secrets-manager-secret-template.test-support.d.ts +21 -0
  28. package/dist/service/secretsmanager/cfn/secret/sim-cfn-secrets-manager-secret-template.test-support.js +64 -0
  29. package/dist/service/secretsmanager/cfn/secret/sim-cfn-secrets-manager-secret-update.test-support.d.ts +18 -0
  30. package/dist/service/secretsmanager/cfn/secret/sim-cfn-secrets-manager-secret-update.test-support.js +39 -0
  31. package/dist/service/secretsmanager/cfn/secret/sim-cfn-secrets-manager-secret-updater.d.ts +37 -0
  32. package/dist/service/secretsmanager/cfn/secret/sim-cfn-secrets-manager-secret-updater.js +66 -0
  33. package/dist/service/secretsmanager/cfn/sim-cfn-secrets-manager-resource-factory.d.ts +20 -10
  34. package/dist/service/secretsmanager/cfn/sim-cfn-secrets-manager-resource-factory.js +41 -27
  35. package/dist/service/secretsmanager/cfn/sim-cfn-secrets-manager-resource-type.d.ts +12 -0
  36. package/dist/service/secretsmanager/cfn/sim-cfn-secrets-manager-resource-type.js +22 -0
  37. package/dist/util/background/background-clock-waits.d.ts +10 -3
  38. package/dist/util/background/background-clock-waits.js +11 -4
  39. package/dist/util/background/background-pending-tasks.d.ts +15 -9
  40. package/dist/util/background/background-pending-tasks.js +18 -12
  41. package/dist/util/background/background.d.ts +11 -3
  42. package/dist/util/background/background.js +14 -4
  43. package/dist/util/background/non-deterministic-background.d.ts +4 -0
  44. package/dist/util/background/non-deterministic-background.js +5 -1
  45. package/dist/util/clock/sim-clock.d.ts +16 -0
  46. package/dist/util/clock/sim-clock.js +4 -0
  47. package/dist/util/clock/sim-controllable-clock.d.ts +11 -0
  48. package/dist/util/clock/sim-controllable-clock.js +13 -0
  49. package/docs/services/cloudformation/README.md +21 -3
  50. package/docs/services/lambda/README.md +11 -0
  51. package/docs/services/secretsmanager/README.md +35 -1
  52. package/docs/time/README.md +7 -0
  53. package/package.json +1 -1
  54. package/dist/service/cloudformation/stack/update/sim-cfn-stack-replaced-resources.d.ts +0 -21
  55. package/dist/service/cloudformation/stack/update/sim-cfn-stack-replaced-resources.js +0 -51
@@ -1,5 +1,5 @@
1
1
  import { makeSimCfnStackResourceMap } from "../stack/resource-map/sim-cfn-stack-resource-map.js";
2
- import { simCfnStackReplacedLogicalIds } from "../stack/update/sim-cfn-stack-replaced-resources.js";
2
+ import { simCfnStackReplacedLogicalIds } from "../stack/update/sim-cfn-stack-resource-changes.js";
3
3
  /**
4
4
  * What executing a change set would do to the Stack it is held against.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  import type { SimCfnResource, SimCloudFormationResourceCreateContext, SimCloudFormationResourceDeleteContext } from "../sim-cfn-resource.js";
2
- import type { SimCloudFormationResourceUpdateContext } from "../sim-cfn-resource.type.js";
2
+ import type { SimCloudFormationResourceInPlaceUpdateContext, SimCloudFormationResourceUpdateContext } from "../sim-cfn-resource.type.js";
3
3
  export interface SimCfnServiceResourceFactory {
4
4
  create(resourceTypeName: string, resource: SimCfnResource, context: SimCloudFormationResourceCreateContext): Promise<object | undefined>;
5
5
  /**
@@ -24,6 +24,30 @@ export interface SimCfnServiceResourceFactory {
24
24
  * create request cannot check.
25
25
  */
26
26
  assertUpdateAllowed?(resourceTypeName: string, current: SimCfnResource, updated: SimCfnResource, context: SimCloudFormationResourceUpdateContext): Promise<void> | void;
27
+ /**
28
+ * Whether a change between two definitions of this Resource can be applied
29
+ * to the deployed Resource, leaving it where it is.
30
+ *
31
+ * Sim CloudFormation replaces a changed Resource unless a service says
32
+ * otherwise, which is the safe reading for one with no way to change a
33
+ * deployed Resource. A service answers true for the changes real
34
+ * CloudFormation applies with no interruption, and carries them out in
35
+ * `updateInPlace`.
36
+ *
37
+ * Answering true keeps the Resource's physical name, so nothing naming it is
38
+ * replaced either.
39
+ */
40
+ updatesInPlace?(resourceTypeName: string, current: SimCfnResource, updated: SimCfnResource): boolean;
41
+ /**
42
+ * Apply a change to the deployed Resource.
43
+ *
44
+ * Called only for a change `updatesInPlace` claimed, and before the update
45
+ * deletes or creates anything. The deployed simulated AWS object is on
46
+ * `current`, and `updated` carries the new definition. What it returns
47
+ * becomes the Resource's simulated AWS object, which is usually the deployed
48
+ * one it was given.
49
+ */
50
+ updateInPlace?(resourceTypeName: string, current: SimCfnResource, updated: SimCfnResource, context: SimCloudFormationResourceInPlaceUpdateContext): Promise<object | undefined>;
27
51
  }
28
52
  export interface SimCloudFormationParsedResourceType {
29
53
  readonly providerName: string;
@@ -0,0 +1,18 @@
1
+ import type { SimAws } from "../../../../aws/sim-aws.js";
2
+ import type { SimCfnServiceResourceFactory } from "../../factory/sim-cfn-resource-factory.type.js";
3
+ import type { SimCfnResource } from "../../sim-cfn-resource.js";
4
+ /**
5
+ * A Resource's service factory, and the type name that factory knows it by.
6
+ */
7
+ export interface SimCfnResourceServiceFactory {
8
+ readonly factory: SimCfnServiceResourceFactory;
9
+ readonly resourceTypeName: string;
10
+ }
11
+ /**
12
+ * The service factory owning a Resource, where a service owns it.
13
+ *
14
+ * An untyped Resource and one whose type no service simulates both come back
15
+ * undefined. Update work asks this before a Stack is changed, and a Resource
16
+ * nothing can create is a Resource nothing can update either.
17
+ */
18
+ export declare function simCfnResourceServiceFactory(simAws: SimAws, resource: SimCfnResource): SimCfnResourceServiceFactory | undefined;
@@ -0,0 +1,30 @@
1
+ import { parseSimCloudFormationResourceType } from "../../parser/sim-cfn-resource-parser.js";
2
+ import { isSimCfnUnsupportedResourceError } from "../../unsupported/sim-cfn-unsupported-resource.js";
3
+ import { resolveSimCloudFormationServiceResourceFactory } from "./sim-cfn-service-resolver.js";
4
+ /**
5
+ * The service factory owning a Resource, where a service owns it.
6
+ *
7
+ * An untyped Resource and one whose type no service simulates both come back
8
+ * undefined. Update work asks this before a Stack is changed, and a Resource
9
+ * nothing can create is a Resource nothing can update either.
10
+ */
11
+ export function simCfnResourceServiceFactory(simAws, resource) {
12
+ const { type } = resource;
13
+ if (type === undefined) {
14
+ return undefined;
15
+ }
16
+ const resourceType = parseSimCloudFormationResourceType(type);
17
+ try {
18
+ return {
19
+ factory: resolveSimCloudFormationServiceResourceFactory(simAws, resource.accountRegionScope, resourceType),
20
+ resourceTypeName: resourceType.resourceTypeName,
21
+ };
22
+ }
23
+ catch (error) {
24
+ if (isSimCfnUnsupportedResourceError(error)) {
25
+ return undefined;
26
+ }
27
+ /* v8 ignore next -- defensive: the resolver refuses with nothing else */
28
+ throw error;
29
+ }
30
+ }
@@ -94,6 +94,24 @@ export interface SimCloudFormationResourceDeleteContext {
94
94
  */
95
95
  readonly retention?: SimCfnResourceRetention | undefined;
96
96
  }
97
+ /**
98
+ * What a service is given to apply a change to a Resource it created, without
99
+ * replacing it.
100
+ *
101
+ * Both property sets are resolved against the Resources the Stack holds now,
102
+ * so a Ref reaches the physical name of a deployed Resource. The old set is
103
+ * there because a service often has to know which property changed. Secrets
104
+ * Manager writes a new secret version for a changed value and leaves the
105
+ * version alone for a changed description.
106
+ */
107
+ export interface SimCloudFormationResourceInPlaceUpdateContext {
108
+ readonly simAws: SimAws;
109
+ readonly resources: ReadonlyMap<string, SimCfnResource>;
110
+ readonly currentResolvedProperties: SimCfnTemplateValueRecord;
111
+ readonly updatedResolvedProperties: SimCfnTemplateValueRecord;
112
+ /** The principal the update runs as, as creation carries it. */
113
+ readonly caller?: SimAwsCaller | undefined;
114
+ }
97
115
  /**
98
116
  * The two resolved Resource definitions available to update validation.
99
117
  */
@@ -0,0 +1,35 @@
1
+ import type { SimAws } from "../../../aws/sim-aws.js";
2
+ import type { SimAwsCaller } from "../../../aws/caller/sim-aws-caller.js";
3
+ import type { SimCfnResource } from "../sim-cfn-resource.js";
4
+ interface SimCfnResourceInPlaceUpdaterProperties {
5
+ readonly current: SimCfnResource;
6
+ readonly updated: SimCfnResource;
7
+ }
8
+ interface ApplySimCfnResourceInPlaceUpdateProperties {
9
+ readonly simAws: SimAws;
10
+ readonly resources: ReadonlyMap<string, SimCfnResource>;
11
+ readonly caller?: SimAwsCaller | undefined;
12
+ }
13
+ /**
14
+ * Applies one Resource change to the deployed Resource, without replacing it.
15
+ *
16
+ * The new Resource record takes the deployed simulated AWS object on from the
17
+ * one it replaces in the Stack's map. That is what carries the bucket's
18
+ * contents or the secret's versions across an update, and what leaves the
19
+ * Resource reporting CREATE_COMPLETE rather than starting again as pending.
20
+ *
21
+ * Both property sets are resolved against the Resources the Stack holds now.
22
+ * Nothing has been deleted or created at this point, so a Ref reaches a
23
+ * deployed Resource's physical name from either side.
24
+ */
25
+ export declare class SimCfnResourceInPlaceUpdater {
26
+ private readonly current;
27
+ private readonly updated;
28
+ constructor(properties: SimCfnResourceInPlaceUpdaterProperties);
29
+ /**
30
+ * Apply the change, and move the new Resource record on to what it left in
31
+ * simulated AWS.
32
+ */
33
+ apply(properties: ApplySimCfnResourceInPlaceUpdateProperties): Promise<void>;
34
+ }
35
+ export {};
@@ -0,0 +1,61 @@
1
+ import { assertDefined } from "../../../../util/type-guard/defined.js";
2
+ import { simCfnResourceServiceFactory } from "../resolve/service/sim-cfn-resource-service-factory.js";
3
+ import { simCfnResourceUpdateError } from "./sim-cfn-resource-update-error.js";
4
+ /**
5
+ * Applies one Resource change to the deployed Resource, without replacing it.
6
+ *
7
+ * The new Resource record takes the deployed simulated AWS object on from the
8
+ * one it replaces in the Stack's map. That is what carries the bucket's
9
+ * contents or the secret's versions across an update, and what leaves the
10
+ * Resource reporting CREATE_COMPLETE rather than starting again as pending.
11
+ *
12
+ * Both property sets are resolved against the Resources the Stack holds now.
13
+ * Nothing has been deleted or created at this point, so a Ref reaches a
14
+ * deployed Resource's physical name from either side.
15
+ */
16
+ export class SimCfnResourceInPlaceUpdater {
17
+ current;
18
+ updated;
19
+ constructor(properties) {
20
+ this.current = properties.current;
21
+ this.updated = properties.updated;
22
+ }
23
+ /**
24
+ * Apply the change, and move the new Resource record on to what it left in
25
+ * simulated AWS.
26
+ */
27
+ async apply(properties) {
28
+ const { current, updated } = this;
29
+ const { simAws, resources, caller } = properties;
30
+ const service = simCfnResourceServiceFactory(simAws, updated);
31
+ // The plan only lists a Resource whose service has both update hooks.
32
+ assertDefined(service, `sim CloudFormation service for Resource ${updated.logicalId} to update`);
33
+ const { factory, resourceTypeName } = service;
34
+ const updateInPlace = factory.updateInPlace?.bind(factory);
35
+ assertDefined(updateInPlace, `sim CloudFormation in-place update for Resource ${updated.logicalId}`);
36
+ updated.markCreateInProgress();
37
+ try {
38
+ const simResource = await updateInPlace(resourceTypeName, current, updated, {
39
+ simAws,
40
+ resources,
41
+ currentResolvedProperties: await current.resolvedProperties({
42
+ simAws,
43
+ resources,
44
+ caller,
45
+ }),
46
+ updatedResolvedProperties: await updated.resolvedProperties({
47
+ simAws,
48
+ resources,
49
+ caller,
50
+ }),
51
+ caller,
52
+ });
53
+ updated.markCreateComplete(simResource);
54
+ }
55
+ catch (error) {
56
+ const updateError = simCfnResourceUpdateError(updated, error);
57
+ updated.markCreateFailed(updateError);
58
+ throw updateError;
59
+ }
60
+ }
61
+ }
@@ -0,0 +1,9 @@
1
+ import type { SimCfnResource } from "../sim-cfn-resource.js";
2
+ /**
3
+ * The error a failed in-place update reports, naming the Resource it stopped
4
+ * on.
5
+ *
6
+ * An Error is given the prefix and passed on, so a service's own error type and
7
+ * its stack survive to the Stack that reads it.
8
+ */
9
+ export declare function simCfnResourceUpdateError(resource: SimCfnResource, error: unknown): Error;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * The error a failed in-place update reports, naming the Resource it stopped
3
+ * on.
4
+ *
5
+ * An Error is given the prefix and passed on, so a service's own error type and
6
+ * its stack survive to the Stack that reads it.
7
+ */
8
+ export function simCfnResourceUpdateError(resource, error) {
9
+ const messagePrefix = `Sim CloudFormation Resource ${resource.logicalId} update failed`;
10
+ /* v8 ignore next -- defensive: a service refuses with an Error */
11
+ const refusal = error instanceof Error ? error : new Error(String(error));
12
+ refusal.message = `${messagePrefix}: ${refusal.message}`;
13
+ return refusal;
14
+ }
@@ -1,6 +1,4 @@
1
- import { parseSimCloudFormationResourceType } from "../parser/sim-cfn-resource-parser.js";
2
- import { resolveSimCloudFormationServiceResourceFactory } from "../resolve/service/sim-cfn-service-resolver.js";
3
- import { isSimCfnUnsupportedResourceError } from "../unsupported/sim-cfn-unsupported-resource.js";
1
+ import { simCfnResourceServiceFactory } from "../resolve/service/sim-cfn-resource-service-factory.js";
4
2
  /**
5
3
  * Runs service-specific validation for one Resource replacement.
6
4
  */
@@ -17,25 +15,14 @@ export class SimCfnResourceUpdateValidator {
17
15
  async assertAllowed(properties) {
18
16
  const { current, updated } = this;
19
17
  const { simAws, currentResources, updatedResources, caller } = properties;
20
- const { type } = updated;
21
- if (type === undefined || current.type !== type) {
18
+ if (updated.type === undefined || current.type !== updated.type) {
22
19
  return;
23
20
  }
24
- const resourceType = parseSimCloudFormationResourceType(type);
25
- let factory;
26
- try {
27
- factory = resolveSimCloudFormationServiceResourceFactory(simAws, updated.accountRegionScope, resourceType);
28
- }
29
- catch (error) {
30
- if (isSimCfnUnsupportedResourceError(error)) {
31
- return;
32
- }
33
- throw error;
34
- }
35
- if (factory.assertUpdateAllowed === undefined) {
21
+ const service = simCfnResourceServiceFactory(simAws, updated);
22
+ if (service?.factory.assertUpdateAllowed === undefined) {
36
23
  return;
37
24
  }
38
- await factory.assertUpdateAllowed(resourceType.resourceTypeName, current, updated, {
25
+ await service.factory.assertUpdateAllowed(service.resourceTypeName, current, updated, {
39
26
  simAws,
40
27
  currentResources,
41
28
  updatedResources,
@@ -10,7 +10,7 @@ import { SimCfnResourceRetention } from "../resource/delete/sim-cfn-resource-ret
10
10
  import type { SimCfnTemplate } from "../template/sim-cfn-template.js";
11
11
  import type { SimCfnResourceOrder } from "./deploy/sim-cfn-resource-order.js";
12
12
  import { SimCfnStackUpdater } from "./update/sim-cfn-stack-updater.js";
13
- import type { SimCfnStackResourceReplacement } from "./update/sim-cfn-stack-update-plan.js";
13
+ import type { SimCfnStackResourceChange } from "./update/sim-cfn-stack-resource-change-pairs.js";
14
14
  import type { SimCloudFormationStackName } from "./sim-cfn-stack.js";
15
15
  interface SimCfnStackUpdateProperties {
16
16
  readonly background: BackgroundScheduler;
@@ -104,7 +104,11 @@ export declare class SimCfnStackResourceOperations {
104
104
  /**
105
105
  * Validate every Resource replacement before the update changes the Stack.
106
106
  */
107
- assertUpdatesAllowed(currentResources: ReadonlyMap<string, SimCfnResource>, updatedResources: ReadonlyMap<string, SimCfnResource>, replacements: readonly SimCfnStackResourceReplacement[]): Promise<void>;
107
+ assertUpdatesAllowed(currentResources: ReadonlyMap<string, SimCfnResource>, updatedResources: ReadonlyMap<string, SimCfnResource>, replacements: readonly SimCfnStackResourceChange[]): Promise<void>;
108
+ /** Whether the service owning a Resource can apply this change to it. */
109
+ updatesInPlace(current: SimCfnResource, updated: SimCfnResource): boolean;
110
+ /** Apply the given Resource changes to the deployed Resources. */
111
+ updateInPlace(resources: ReadonlyMap<string, SimCfnResource>, updates: readonly SimCfnStackResourceChange[]): Promise<void>;
108
112
  /**
109
113
  * An update of this Stack from a changed template, in the same scope.
110
114
  */
@@ -130,6 +134,7 @@ export declare class SimCfnStackResourceOperations {
130
134
  */
131
135
  private teardownRetention;
132
136
  private publishAssets;
137
+ private resourceUpdates;
133
138
  private creator;
134
139
  private deleter;
135
140
  }
@@ -5,7 +5,7 @@ import { simCfnStackRetainedLogicalIds } from "./teardown/sim-cfn-stack-retained
5
5
  import { SimCfnStackResourceCreator } from "./deploy/sim-cfn-stack-resource-creator.js";
6
6
  import { SimCfnStackResourceDeleter } from "./teardown/sim-cfn-stack-resource-deleter.js";
7
7
  import { SimCfnStackUpdater } from "./update/sim-cfn-stack-updater.js";
8
- import { SimCfnResourceUpdateValidator } from "../resource/update/sim-cfn-resource-update-validator.js";
8
+ import { SimCfnStackResourceUpdates } from "./update/sim-cfn-stack-resource-updates.js";
9
9
  /**
10
10
  * What a Stack can ask simulated AWS to do with its Resources.
11
11
  *
@@ -98,17 +98,15 @@ export class SimCfnStackResourceOperations {
98
98
  * Validate every Resource replacement before the update changes the Stack.
99
99
  */
100
100
  async assertUpdatesAllowed(currentResources, updatedResources, replacements) {
101
- await Promise.all(replacements.map(async ({ current, updated }) => {
102
- await new SimCfnResourceUpdateValidator({
103
- current,
104
- updated,
105
- }).assertAllowed({
106
- simAws: this.simAws,
107
- currentResources,
108
- updatedResources,
109
- caller: this.context.caller,
110
- });
111
- }));
101
+ await this.resourceUpdates().assertAllowed(currentResources, updatedResources, replacements);
102
+ }
103
+ /** Whether the service owning a Resource can apply this change to it. */
104
+ updatesInPlace(current, updated) {
105
+ return this.resourceUpdates().claimed(current, updated);
106
+ }
107
+ /** Apply the given Resource changes to the deployed Resources. */
108
+ async updateInPlace(resources, updates) {
109
+ await this.resourceUpdates().apply(resources, updates);
112
110
  }
113
111
  /**
114
112
  * An update of this Stack from a changed template, in the same scope.
@@ -163,6 +161,12 @@ export class SimCfnStackResourceOperations {
163
161
  assetsCaller: this.context.publishingCaller(),
164
162
  }).publish();
165
163
  }
164
+ resourceUpdates() {
165
+ return new SimCfnStackResourceUpdates({
166
+ simAws: this.simAws,
167
+ caller: this.context.caller,
168
+ });
169
+ }
166
170
  creator(resources) {
167
171
  return new SimCfnStackResourceCreator({
168
172
  simAws: this.simAws,
@@ -0,0 +1,12 @@
1
+ import type { SimCfnResource } from "../../resource/sim-cfn-resource.js";
2
+ /**
3
+ * The deployed and new halves of one Resource an update changes.
4
+ */
5
+ export interface SimCfnStackResourceChange {
6
+ readonly current: SimCfnResource;
7
+ readonly updated: SimCfnResource;
8
+ }
9
+ /**
10
+ * The deployed and new halves of each of the given logical IDs.
11
+ */
12
+ export declare function simCfnStackResourceChangePairs(current: ReadonlyMap<string, SimCfnResource>, updated: ReadonlyMap<string, SimCfnResource>, logicalIds: ReadonlySet<string>): readonly SimCfnStackResourceChange[];
@@ -0,0 +1,18 @@
1
+ /**
2
+ * The deployed and new halves of each of the given logical IDs.
3
+ */
4
+ export function simCfnStackResourceChangePairs(current, updated, logicalIds) {
5
+ return logicalIds
6
+ .values()
7
+ .map((logicalId) => {
8
+ const currentResource = current.get(logicalId);
9
+ const updatedResource = updated.get(logicalId);
10
+ /* v8 ignore next 5 -- defensive: a changed logical ID is one both maps
11
+ hold */
12
+ if (currentResource === undefined || updatedResource === undefined) {
13
+ throw new Error(`CloudFormation change ${logicalId} is missing one of its Resource definitions`);
14
+ }
15
+ return { current: currentResource, updated: updatedResource };
16
+ })
17
+ .toArray();
18
+ }
@@ -0,0 +1,52 @@
1
+ import type { SimCfnResource } from "../../resource/sim-cfn-resource.js";
2
+ /**
3
+ * Who decides whether a change can be applied to the deployed Resource.
4
+ * Answering false replaces the Resource.
5
+ */
6
+ export interface SimCfnStackInPlaceUpdates {
7
+ updatesInPlace(current: SimCfnResource, updated: SimCfnResource): boolean;
8
+ }
9
+ interface SimCfnStackResourceChangesProperties {
10
+ readonly current: ReadonlyMap<string, SimCfnResource>;
11
+ readonly updated: ReadonlyMap<string, SimCfnResource>;
12
+ /**
13
+ * Which changes the owning service can apply to the deployed Resource.
14
+ * Omitted, every changed Resource is replaced.
15
+ */
16
+ readonly inPlaceUpdates?: SimCfnStackInPlaceUpdates | undefined;
17
+ }
18
+ /**
19
+ * What an update does to each Resource a Stack and its new template share.
20
+ */
21
+ export interface SimCfnStackResourceChanges {
22
+ /** The logical IDs to delete and create again from the new template. */
23
+ readonly replaced: ReadonlySet<string>;
24
+ /** The logical IDs to change where they are. */
25
+ readonly updatedInPlace: ReadonlySet<string>;
26
+ }
27
+ /**
28
+ * How an update reconciles each changed Resource, given the Stack's deployed
29
+ * Resources and the ones its new template describes.
30
+ *
31
+ * A Resource whose resolved template entry changed is replaced, which means
32
+ * deleting it and creating it again from the new template. The service owning
33
+ * the Resource can claim the change instead, for the properties real
34
+ * CloudFormation applies with no interruption.
35
+ *
36
+ * A Resource that names a replaced Resource is replaced too, all the way up the
37
+ * dependency chain. Real CloudFormation hands the dependent the new physical
38
+ * name instead, but nothing here can rewrite an already created simulated
39
+ * Resource, and leaving the dependent alone would leave it pointing at a
40
+ * Resource that has gone. That reaches a claimed Resource as well, and takes
41
+ * the claim back. An in-place update never starts a spread of its own, since
42
+ * the Resource keeps the physical name its dependents already hold.
43
+ */
44
+ export declare function simCfnStackResourceChanges(properties: SimCfnStackResourceChangesProperties): SimCfnStackResourceChanges;
45
+ /**
46
+ * The logical IDs an update has to replace.
47
+ *
48
+ * Kept for the change set, which reports what an update would do without
49
+ * knowing which service owns each Resource.
50
+ */
51
+ export declare function simCfnStackReplacedLogicalIds(properties: SimCfnStackResourceChangesProperties): ReadonlySet<string>;
52
+ export {};
@@ -0,0 +1,86 @@
1
+ import { simCfnTemplateSignature } from "./sim-cfn-template-signature.js";
2
+ /**
3
+ * How an update reconciles each changed Resource, given the Stack's deployed
4
+ * Resources and the ones its new template describes.
5
+ *
6
+ * A Resource whose resolved template entry changed is replaced, which means
7
+ * deleting it and creating it again from the new template. The service owning
8
+ * the Resource can claim the change instead, for the properties real
9
+ * CloudFormation applies with no interruption.
10
+ *
11
+ * A Resource that names a replaced Resource is replaced too, all the way up the
12
+ * dependency chain. Real CloudFormation hands the dependent the new physical
13
+ * name instead, but nothing here can rewrite an already created simulated
14
+ * Resource, and leaving the dependent alone would leave it pointing at a
15
+ * Resource that has gone. That reaches a claimed Resource as well, and takes
16
+ * the claim back. An in-place update never starts a spread of its own, since
17
+ * the Resource keeps the physical name its dependents already hold.
18
+ */
19
+ export function simCfnStackResourceChanges(properties) {
20
+ const { current, updated, inPlaceUpdates } = properties;
21
+ const changed = changedLogicalIds({ current, updated });
22
+ const updatedInPlace = new Set(changed.values().filter((logicalId) => {
23
+ const currentResource = current.get(logicalId);
24
+ const updatedResource = updated.get(logicalId);
25
+ return (currentResource !== undefined &&
26
+ updatedResource !== undefined &&
27
+ inPlaceUpdates?.updatesInPlace(currentResource, updatedResource) ===
28
+ true);
29
+ }));
30
+ const replaced = new Set(changed.values().filter((logicalId) => !updatedInPlace.has(logicalId)));
31
+ spreadToDependents({ updated, current, replaced });
32
+ // A claimed Resource the spread reached is replaced after all. Its
33
+ // dependency is being deleted and created again, and an update applied
34
+ // before that would have read the deployed Resource on its way out.
35
+ for (const logicalId of replaced) {
36
+ updatedInPlace.delete(logicalId);
37
+ }
38
+ return { replaced, updatedInPlace };
39
+ }
40
+ /**
41
+ * The logical IDs an update has to replace.
42
+ *
43
+ * Kept for the change set, which reports what an update would do without
44
+ * knowing which service owns each Resource.
45
+ */
46
+ export function simCfnStackReplacedLogicalIds(properties) {
47
+ return simCfnStackResourceChanges(properties).replaced;
48
+ }
49
+ /**
50
+ * Replace every Resource naming a replaced one, and every Resource naming
51
+ * those.
52
+ */
53
+ function spreadToDependents(properties) {
54
+ const { current, updated, replaced } = properties;
55
+ // Each pass replaces the Resources naming one already being replaced, until a
56
+ // pass finds none, which is the end of the dependency chain.
57
+ let spreading = true;
58
+ while (spreading) {
59
+ spreading = false;
60
+ for (const [logicalId, resource] of updated) {
61
+ const spreads = current.has(logicalId) &&
62
+ !replaced.has(logicalId) &&
63
+ resource.dependencies().some((dependency) => replaced.has(dependency));
64
+ if (spreads) {
65
+ replaced.add(logicalId);
66
+ spreading = true;
67
+ }
68
+ }
69
+ }
70
+ }
71
+ /**
72
+ * The logical IDs whose resolved template entry differs from the deployed one.
73
+ */
74
+ function changedLogicalIds(properties) {
75
+ const { current, updated } = properties;
76
+ const changed = new Set();
77
+ for (const [logicalId, resource] of updated) {
78
+ const deployed = current.get(logicalId);
79
+ if (deployed !== undefined &&
80
+ simCfnTemplateSignature(deployed.template) !==
81
+ simCfnTemplateSignature(resource.template)) {
82
+ changed.add(logicalId);
83
+ }
84
+ }
85
+ return changed;
86
+ }
@@ -0,0 +1,42 @@
1
+ import type { SimAws } from "../../../aws/sim-aws.js";
2
+ import type { SimAwsCaller } from "../../../aws/caller/sim-aws-caller.js";
3
+ import type { SimCfnResource } from "../../resource/sim-cfn-resource.js";
4
+ import type { SimCfnStackResourceChange } from "./sim-cfn-stack-resource-change-pairs.js";
5
+ interface SimCfnStackResourceUpdatesProperties {
6
+ readonly simAws: SimAws;
7
+ /** The principal the update runs as, as creation carries it. */
8
+ readonly caller?: SimAwsCaller | undefined;
9
+ }
10
+ /**
11
+ * The Resource changes a Stack update applies to the deployed Resources,
12
+ * rather than by replacing them.
13
+ *
14
+ * Which changes those are is the owning service's answer, given before the
15
+ * update touches anything. A service with no way to change a deployed Resource
16
+ * answers nothing, and every change to its Resources is a replacement.
17
+ */
18
+ export declare class SimCfnStackResourceUpdates {
19
+ private readonly simAws;
20
+ private readonly caller;
21
+ constructor(properties: SimCfnStackResourceUpdatesProperties);
22
+ /**
23
+ * Whether the service owning a Resource can apply this change to the
24
+ * deployed Resource.
25
+ *
26
+ * A Resource type no service simulates cannot be updated where it is. Its
27
+ * creation was skipped, so there is nothing deployed to change.
28
+ */
29
+ claimed(current: SimCfnResource, updated: SimCfnResource): boolean;
30
+ /**
31
+ * Validate every Resource replacement before the update changes the Stack.
32
+ */
33
+ assertAllowed(currentResources: ReadonlyMap<string, SimCfnResource>, updatedResources: ReadonlyMap<string, SimCfnResource>, replacements: readonly SimCfnStackResourceChange[]): Promise<void>;
34
+ /**
35
+ * Apply the given changes to the deployed Resources.
36
+ *
37
+ * Each one is independent of the others, since none of them creates or
38
+ * deletes anything another could be waiting on.
39
+ */
40
+ apply(resources: ReadonlyMap<string, SimCfnResource>, updates: readonly SimCfnStackResourceChange[]): Promise<void>;
41
+ }
42
+ export {};
@@ -0,0 +1,70 @@
1
+ import { SimCfnResourceInPlaceUpdater } from "../../resource/update/sim-cfn-resource-in-place-updater.js";
2
+ import { simCfnResourceServiceFactory } from "../../resource/resolve/service/sim-cfn-resource-service-factory.js";
3
+ import { SimCfnResourceUpdateValidator } from "../../resource/update/sim-cfn-resource-update-validator.js";
4
+ /**
5
+ * The Resource changes a Stack update applies to the deployed Resources,
6
+ * rather than by replacing them.
7
+ *
8
+ * Which changes those are is the owning service's answer, given before the
9
+ * update touches anything. A service with no way to change a deployed Resource
10
+ * answers nothing, and every change to its Resources is a replacement.
11
+ */
12
+ export class SimCfnStackResourceUpdates {
13
+ simAws;
14
+ caller;
15
+ constructor(properties) {
16
+ this.simAws = properties.simAws;
17
+ this.caller = properties.caller;
18
+ }
19
+ /**
20
+ * Whether the service owning a Resource can apply this change to the
21
+ * deployed Resource.
22
+ *
23
+ * A Resource type no service simulates cannot be updated where it is. Its
24
+ * creation was skipped, so there is nothing deployed to change.
25
+ */
26
+ claimed(current, updated) {
27
+ if (updated.type === undefined || current.type !== updated.type) {
28
+ return false;
29
+ }
30
+ const service = simCfnResourceServiceFactory(this.simAws, updated);
31
+ // Both halves or neither. A service that can recognise a change it can
32
+ // apply but cannot apply it would leave the update with nothing to run.
33
+ if (service?.factory.updatesInPlace === undefined ||
34
+ service.factory.updateInPlace === undefined) {
35
+ return false;
36
+ }
37
+ return service.factory.updatesInPlace(service.resourceTypeName, current, updated);
38
+ }
39
+ /**
40
+ * Validate every Resource replacement before the update changes the Stack.
41
+ */
42
+ async assertAllowed(currentResources, updatedResources, replacements) {
43
+ await Promise.all(replacements.map(async ({ current, updated }) => {
44
+ await new SimCfnResourceUpdateValidator({
45
+ current,
46
+ updated,
47
+ }).assertAllowed({
48
+ simAws: this.simAws,
49
+ currentResources,
50
+ updatedResources,
51
+ caller: this.caller,
52
+ });
53
+ }));
54
+ }
55
+ /**
56
+ * Apply the given changes to the deployed Resources.
57
+ *
58
+ * Each one is independent of the others, since none of them creates or
59
+ * deletes anything another could be waiting on.
60
+ */
61
+ async apply(resources, updates) {
62
+ await Promise.all(updates.map(async ({ current, updated }) => {
63
+ await new SimCfnResourceInPlaceUpdater({ current, updated }).apply({
64
+ simAws: this.simAws,
65
+ resources,
66
+ caller: this.caller,
67
+ });
68
+ }));
69
+ }
70
+ }