@friggframework/devtools 2.0.0-next.45 → 2.0.0-next.47

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 (212) hide show
  1. package/infrastructure/ARCHITECTURE.md +487 -0
  2. package/infrastructure/HEALTH.md +468 -0
  3. package/infrastructure/README.md +51 -0
  4. package/infrastructure/__tests__/postgres-config.test.js +914 -0
  5. package/infrastructure/__tests__/template-generation.test.js +687 -0
  6. package/infrastructure/create-frigg-infrastructure.js +1 -1
  7. package/infrastructure/docs/POSTGRES-CONFIGURATION.md +630 -0
  8. package/infrastructure/{DEPLOYMENT-INSTRUCTIONS.md → docs/deployment-instructions.md} +3 -3
  9. package/infrastructure/{IAM-POLICY-TEMPLATES.md → docs/iam-policy-templates.md} +9 -10
  10. package/infrastructure/domains/database/aurora-builder.js +809 -0
  11. package/infrastructure/domains/database/aurora-builder.test.js +950 -0
  12. package/infrastructure/domains/database/aurora-discovery.js +87 -0
  13. package/infrastructure/domains/database/aurora-discovery.test.js +188 -0
  14. package/infrastructure/domains/database/aurora-resolver.js +210 -0
  15. package/infrastructure/domains/database/aurora-resolver.test.js +347 -0
  16. package/infrastructure/domains/database/migration-builder.js +695 -0
  17. package/infrastructure/domains/database/migration-builder.test.js +294 -0
  18. package/infrastructure/domains/database/migration-resolver.js +163 -0
  19. package/infrastructure/domains/database/migration-resolver.test.js +337 -0
  20. package/infrastructure/domains/health/application/ports/IPropertyReconciler.js +164 -0
  21. package/infrastructure/domains/health/application/ports/IResourceDetector.js +129 -0
  22. package/infrastructure/domains/health/application/ports/IResourceImporter.js +142 -0
  23. package/infrastructure/domains/health/application/ports/IStackRepository.js +131 -0
  24. package/infrastructure/domains/health/application/ports/index.js +26 -0
  25. package/infrastructure/domains/health/application/use-cases/__tests__/execute-resource-import-use-case.test.js +679 -0
  26. package/infrastructure/domains/health/application/use-cases/__tests__/mismatch-analyzer-method-name.test.js +167 -0
  27. package/infrastructure/domains/health/application/use-cases/__tests__/repair-via-import-use-case.test.js +1130 -0
  28. package/infrastructure/domains/health/application/use-cases/execute-resource-import-use-case.js +221 -0
  29. package/infrastructure/domains/health/application/use-cases/reconcile-properties-use-case.js +152 -0
  30. package/infrastructure/domains/health/application/use-cases/reconcile-properties-use-case.test.js +343 -0
  31. package/infrastructure/domains/health/application/use-cases/repair-via-import-use-case.js +535 -0
  32. package/infrastructure/domains/health/application/use-cases/repair-via-import-use-case.test.js +376 -0
  33. package/infrastructure/domains/health/application/use-cases/run-health-check-use-case.js +213 -0
  34. package/infrastructure/domains/health/application/use-cases/run-health-check-use-case.test.js +441 -0
  35. package/infrastructure/domains/health/docs/ACME-DEV-DRIFT-ANALYSIS.md +267 -0
  36. package/infrastructure/domains/health/docs/BUILD-VS-DEPLOYED-TEMPLATE-ANALYSIS.md +324 -0
  37. package/infrastructure/domains/health/docs/ORPHAN-DETECTION-ANALYSIS.md +386 -0
  38. package/infrastructure/domains/health/docs/SPEC-CLEANUP-COMMAND.md +1419 -0
  39. package/infrastructure/domains/health/docs/TDD-IMPLEMENTATION-SUMMARY.md +391 -0
  40. package/infrastructure/domains/health/docs/TEMPLATE-COMPARISON-IMPLEMENTATION.md +551 -0
  41. package/infrastructure/domains/health/domain/entities/issue.js +299 -0
  42. package/infrastructure/domains/health/domain/entities/issue.test.js +528 -0
  43. package/infrastructure/domains/health/domain/entities/property-mismatch.js +108 -0
  44. package/infrastructure/domains/health/domain/entities/property-mismatch.test.js +275 -0
  45. package/infrastructure/domains/health/domain/entities/resource.js +159 -0
  46. package/infrastructure/domains/health/domain/entities/resource.test.js +432 -0
  47. package/infrastructure/domains/health/domain/entities/stack-health-report.js +306 -0
  48. package/infrastructure/domains/health/domain/entities/stack-health-report.test.js +601 -0
  49. package/infrastructure/domains/health/domain/services/__tests__/health-score-percentage-based.test.js +380 -0
  50. package/infrastructure/domains/health/domain/services/__tests__/import-progress-monitor.test.js +971 -0
  51. package/infrastructure/domains/health/domain/services/__tests__/import-template-generator.test.js +1150 -0
  52. package/infrastructure/domains/health/domain/services/__tests__/logical-id-mapper.test.js +672 -0
  53. package/infrastructure/domains/health/domain/services/__tests__/template-parser.test.js +496 -0
  54. package/infrastructure/domains/health/domain/services/__tests__/update-progress-monitor.test.js +419 -0
  55. package/infrastructure/domains/health/domain/services/health-score-calculator.js +248 -0
  56. package/infrastructure/domains/health/domain/services/health-score-calculator.test.js +504 -0
  57. package/infrastructure/domains/health/domain/services/import-progress-monitor.js +195 -0
  58. package/infrastructure/domains/health/domain/services/import-template-generator.js +435 -0
  59. package/infrastructure/domains/health/domain/services/logical-id-mapper.js +345 -0
  60. package/infrastructure/domains/health/domain/services/mismatch-analyzer.js +234 -0
  61. package/infrastructure/domains/health/domain/services/mismatch-analyzer.test.js +431 -0
  62. package/infrastructure/domains/health/domain/services/property-mutability-config.js +382 -0
  63. package/infrastructure/domains/health/domain/services/template-parser.js +245 -0
  64. package/infrastructure/domains/health/domain/services/update-progress-monitor.js +192 -0
  65. package/infrastructure/domains/health/domain/value-objects/health-score.js +138 -0
  66. package/infrastructure/domains/health/domain/value-objects/health-score.test.js +267 -0
  67. package/infrastructure/domains/health/domain/value-objects/property-mutability.js +161 -0
  68. package/infrastructure/domains/health/domain/value-objects/property-mutability.test.js +198 -0
  69. package/infrastructure/domains/health/domain/value-objects/resource-state.js +167 -0
  70. package/infrastructure/domains/health/domain/value-objects/resource-state.test.js +196 -0
  71. package/infrastructure/domains/health/domain/value-objects/stack-identifier.js +192 -0
  72. package/infrastructure/domains/health/domain/value-objects/stack-identifier.test.js +262 -0
  73. package/infrastructure/domains/health/infrastructure/adapters/__tests__/orphan-detection-cfn-tagged.test.js +312 -0
  74. package/infrastructure/domains/health/infrastructure/adapters/__tests__/orphan-detection-multi-stack.test.js +367 -0
  75. package/infrastructure/domains/health/infrastructure/adapters/__tests__/orphan-detection-relationship-analysis.test.js +432 -0
  76. package/infrastructure/domains/health/infrastructure/adapters/aws-property-reconciler.js +784 -0
  77. package/infrastructure/domains/health/infrastructure/adapters/aws-property-reconciler.test.js +1133 -0
  78. package/infrastructure/domains/health/infrastructure/adapters/aws-resource-detector.js +565 -0
  79. package/infrastructure/domains/health/infrastructure/adapters/aws-resource-detector.test.js +554 -0
  80. package/infrastructure/domains/health/infrastructure/adapters/aws-resource-importer.js +318 -0
  81. package/infrastructure/domains/health/infrastructure/adapters/aws-resource-importer.test.js +398 -0
  82. package/infrastructure/domains/health/infrastructure/adapters/aws-stack-repository.js +777 -0
  83. package/infrastructure/domains/health/infrastructure/adapters/aws-stack-repository.test.js +580 -0
  84. package/infrastructure/domains/integration/integration-builder.js +397 -0
  85. package/infrastructure/domains/integration/integration-builder.test.js +593 -0
  86. package/infrastructure/domains/integration/integration-resolver.js +170 -0
  87. package/infrastructure/domains/integration/integration-resolver.test.js +369 -0
  88. package/infrastructure/domains/integration/websocket-builder.js +69 -0
  89. package/infrastructure/domains/integration/websocket-builder.test.js +195 -0
  90. package/infrastructure/domains/networking/vpc-builder.js +1829 -0
  91. package/infrastructure/domains/networking/vpc-builder.test.js +1262 -0
  92. package/infrastructure/domains/networking/vpc-discovery.js +177 -0
  93. package/infrastructure/domains/networking/vpc-discovery.test.js +350 -0
  94. package/infrastructure/domains/networking/vpc-resolver.js +324 -0
  95. package/infrastructure/domains/networking/vpc-resolver.test.js +501 -0
  96. package/infrastructure/domains/parameters/ssm-builder.js +79 -0
  97. package/infrastructure/domains/parameters/ssm-builder.test.js +189 -0
  98. package/infrastructure/domains/parameters/ssm-discovery.js +84 -0
  99. package/infrastructure/domains/parameters/ssm-discovery.test.js +210 -0
  100. package/infrastructure/{iam-generator.js → domains/security/iam-generator.js} +2 -2
  101. package/infrastructure/domains/security/kms-builder.js +366 -0
  102. package/infrastructure/domains/security/kms-builder.test.js +374 -0
  103. package/infrastructure/domains/security/kms-discovery.js +80 -0
  104. package/infrastructure/domains/security/kms-discovery.test.js +177 -0
  105. package/infrastructure/domains/security/kms-resolver.js +96 -0
  106. package/infrastructure/domains/security/kms-resolver.test.js +216 -0
  107. package/infrastructure/domains/shared/base-builder.js +112 -0
  108. package/infrastructure/domains/shared/base-resolver.js +186 -0
  109. package/infrastructure/domains/shared/base-resolver.test.js +305 -0
  110. package/infrastructure/domains/shared/builder-orchestrator.js +212 -0
  111. package/infrastructure/domains/shared/builder-orchestrator.test.js +213 -0
  112. package/infrastructure/domains/shared/cloudformation-discovery-v2.js +334 -0
  113. package/infrastructure/domains/shared/cloudformation-discovery.js +375 -0
  114. package/infrastructure/domains/shared/cloudformation-discovery.test.js +590 -0
  115. package/infrastructure/domains/shared/environment-builder.js +119 -0
  116. package/infrastructure/domains/shared/environment-builder.test.js +247 -0
  117. package/infrastructure/domains/shared/providers/aws-provider-adapter.js +544 -0
  118. package/infrastructure/domains/shared/providers/aws-provider-adapter.test.js +377 -0
  119. package/infrastructure/domains/shared/providers/azure-provider-adapter.stub.js +93 -0
  120. package/infrastructure/domains/shared/providers/cloud-provider-adapter.js +136 -0
  121. package/infrastructure/domains/shared/providers/gcp-provider-adapter.stub.js +82 -0
  122. package/infrastructure/domains/shared/providers/provider-factory.js +108 -0
  123. package/infrastructure/domains/shared/providers/provider-factory.test.js +170 -0
  124. package/infrastructure/domains/shared/resource-discovery.js +192 -0
  125. package/infrastructure/domains/shared/resource-discovery.test.js +552 -0
  126. package/infrastructure/domains/shared/types/app-definition.js +205 -0
  127. package/infrastructure/domains/shared/types/discovery-result.js +106 -0
  128. package/infrastructure/domains/shared/types/discovery-result.test.js +258 -0
  129. package/infrastructure/domains/shared/types/index.js +46 -0
  130. package/infrastructure/domains/shared/types/resource-ownership.js +108 -0
  131. package/infrastructure/domains/shared/types/resource-ownership.test.js +101 -0
  132. package/infrastructure/domains/shared/utilities/base-definition-factory.js +380 -0
  133. package/infrastructure/domains/shared/utilities/base-definition-factory.js.bak +338 -0
  134. package/infrastructure/domains/shared/utilities/base-definition-factory.test.js +248 -0
  135. package/infrastructure/domains/shared/utilities/handler-path-resolver.js +134 -0
  136. package/infrastructure/domains/shared/utilities/handler-path-resolver.test.js +268 -0
  137. package/infrastructure/domains/shared/utilities/prisma-layer-manager.js +55 -0
  138. package/infrastructure/domains/shared/utilities/prisma-layer-manager.test.js +138 -0
  139. package/infrastructure/{env-validator.js → domains/shared/validation/env-validator.js} +2 -1
  140. package/infrastructure/domains/shared/validation/env-validator.test.js +173 -0
  141. package/infrastructure/esbuild.config.js +53 -0
  142. package/infrastructure/infrastructure-composer.js +87 -0
  143. package/infrastructure/{serverless-template.test.js → infrastructure-composer.test.js} +115 -24
  144. package/infrastructure/scripts/build-prisma-layer.js +553 -0
  145. package/infrastructure/scripts/build-prisma-layer.test.js +102 -0
  146. package/infrastructure/{build-time-discovery.js → scripts/build-time-discovery.js} +80 -48
  147. package/infrastructure/{build-time-discovery.test.js → scripts/build-time-discovery.test.js} +5 -4
  148. package/layers/prisma/nodejs/package.json +8 -0
  149. package/management-ui/server/utils/cliIntegration.js +1 -1
  150. package/management-ui/server/utils/environment/awsParameterStore.js +29 -18
  151. package/package.json +11 -11
  152. package/frigg-cli/.eslintrc.js +0 -141
  153. package/frigg-cli/__tests__/unit/commands/build.test.js +0 -251
  154. package/frigg-cli/__tests__/unit/commands/db-setup.test.js +0 -548
  155. package/frigg-cli/__tests__/unit/commands/install.test.js +0 -400
  156. package/frigg-cli/__tests__/unit/commands/ui.test.js +0 -346
  157. package/frigg-cli/__tests__/unit/utils/database-validator.test.js +0 -366
  158. package/frigg-cli/__tests__/unit/utils/error-messages.test.js +0 -304
  159. package/frigg-cli/__tests__/unit/utils/prisma-runner.test.js +0 -486
  160. package/frigg-cli/__tests__/utils/mock-factory.js +0 -270
  161. package/frigg-cli/__tests__/utils/prisma-mock.js +0 -194
  162. package/frigg-cli/__tests__/utils/test-fixtures.js +0 -463
  163. package/frigg-cli/__tests__/utils/test-setup.js +0 -287
  164. package/frigg-cli/build-command/index.js +0 -65
  165. package/frigg-cli/db-setup-command/index.js +0 -193
  166. package/frigg-cli/deploy-command/index.js +0 -175
  167. package/frigg-cli/generate-command/__tests__/generate-command.test.js +0 -301
  168. package/frigg-cli/generate-command/azure-generator.js +0 -43
  169. package/frigg-cli/generate-command/gcp-generator.js +0 -47
  170. package/frigg-cli/generate-command/index.js +0 -332
  171. package/frigg-cli/generate-command/terraform-generator.js +0 -555
  172. package/frigg-cli/generate-iam-command.js +0 -118
  173. package/frigg-cli/index.js +0 -75
  174. package/frigg-cli/index.test.js +0 -158
  175. package/frigg-cli/init-command/backend-first-handler.js +0 -756
  176. package/frigg-cli/init-command/index.js +0 -93
  177. package/frigg-cli/init-command/template-handler.js +0 -143
  178. package/frigg-cli/install-command/backend-js.js +0 -33
  179. package/frigg-cli/install-command/commit-changes.js +0 -16
  180. package/frigg-cli/install-command/environment-variables.js +0 -127
  181. package/frigg-cli/install-command/environment-variables.test.js +0 -136
  182. package/frigg-cli/install-command/index.js +0 -54
  183. package/frigg-cli/install-command/install-package.js +0 -13
  184. package/frigg-cli/install-command/integration-file.js +0 -30
  185. package/frigg-cli/install-command/logger.js +0 -12
  186. package/frigg-cli/install-command/template.js +0 -90
  187. package/frigg-cli/install-command/validate-package.js +0 -75
  188. package/frigg-cli/jest.config.js +0 -124
  189. package/frigg-cli/package.json +0 -54
  190. package/frigg-cli/start-command/index.js +0 -149
  191. package/frigg-cli/start-command/start-command.test.js +0 -297
  192. package/frigg-cli/test/init-command.test.js +0 -180
  193. package/frigg-cli/test/npm-registry.test.js +0 -319
  194. package/frigg-cli/ui-command/index.js +0 -154
  195. package/frigg-cli/utils/app-resolver.js +0 -319
  196. package/frigg-cli/utils/backend-path.js +0 -25
  197. package/frigg-cli/utils/database-validator.js +0 -161
  198. package/frigg-cli/utils/error-messages.js +0 -257
  199. package/frigg-cli/utils/npm-registry.js +0 -167
  200. package/frigg-cli/utils/prisma-runner.js +0 -280
  201. package/frigg-cli/utils/process-manager.js +0 -199
  202. package/frigg-cli/utils/repo-detection.js +0 -405
  203. package/infrastructure/aws-discovery.js +0 -1176
  204. package/infrastructure/aws-discovery.test.js +0 -1220
  205. package/infrastructure/serverless-template.js +0 -2094
  206. /package/infrastructure/{WEBSOCKET-CONFIGURATION.md → docs/WEBSOCKET-CONFIGURATION.md} +0 -0
  207. /package/infrastructure/{GENERATE-IAM-DOCS.md → docs/generate-iam-command.md} +0 -0
  208. /package/infrastructure/{iam-generator.test.js → domains/security/iam-generator.test.js} +0 -0
  209. /package/infrastructure/{frigg-deployment-iam-stack.yaml → domains/security/templates/frigg-deployment-iam-stack.yaml} +0 -0
  210. /package/infrastructure/{iam-policy-basic.json → domains/security/templates/iam-policy-basic.json} +0 -0
  211. /package/infrastructure/{iam-policy-full.json → domains/security/templates/iam-policy-full.json} +0 -0
  212. /package/infrastructure/{run-discovery.js → scripts/run-discovery.js} +0 -0
@@ -0,0 +1,161 @@
1
+ /**
2
+ * PropertyMutability Value Object
3
+ *
4
+ * Enum-like immutable mutability classification for CloudFormation resource properties
5
+ *
6
+ * Types:
7
+ * - MUTABLE: Property can be changed without replacing the resource (Update requires: No interruption)
8
+ * - IMMUTABLE: Property cannot be changed - requires resource replacement (Update requires: Replacement)
9
+ * - CONDITIONAL: Property mutability depends on other properties or conditions (Update requires: Some interruptions)
10
+ */
11
+
12
+ class PropertyMutability {
13
+ /**
14
+ * Valid mutability types
15
+ * @type {string[]}
16
+ */
17
+ static VALID_TYPES = [
18
+ 'MUTABLE',
19
+ 'IMMUTABLE',
20
+ 'CONDITIONAL',
21
+ ];
22
+
23
+ /**
24
+ * Create a new PropertyMutability
25
+ *
26
+ * @param {string} value - Mutability type
27
+ */
28
+ constructor(value) {
29
+ if (value === undefined || value === null) {
30
+ throw new Error('Property mutability is required');
31
+ }
32
+
33
+ if (!PropertyMutability.VALID_TYPES.includes(value)) {
34
+ throw new Error(`Invalid property mutability: ${value}`);
35
+ }
36
+
37
+ this._value = value;
38
+
39
+ // Make immutable
40
+ Object.freeze(this);
41
+ }
42
+
43
+ /**
44
+ * Get mutability value
45
+ * @returns {string}
46
+ */
47
+ get value() {
48
+ return this._value;
49
+ }
50
+
51
+ /**
52
+ * Prevent modification of value
53
+ * @throws {TypeError}
54
+ */
55
+ set value(newValue) {
56
+ throw new TypeError('Cannot modify immutable property value');
57
+ }
58
+
59
+ /**
60
+ * Check if property is mutable
61
+ * @returns {boolean}
62
+ */
63
+ isMutable() {
64
+ return this._value === 'MUTABLE';
65
+ }
66
+
67
+ /**
68
+ * Check if property is immutable
69
+ * @returns {boolean}
70
+ */
71
+ isImmutable() {
72
+ return this._value === 'IMMUTABLE';
73
+ }
74
+
75
+ /**
76
+ * Check if property mutability is conditional
77
+ * @returns {boolean}
78
+ */
79
+ isConditional() {
80
+ return this._value === 'CONDITIONAL';
81
+ }
82
+
83
+ /**
84
+ * Check if property can be changed
85
+ * @returns {boolean}
86
+ */
87
+ canChange() {
88
+ return this._value === 'MUTABLE';
89
+ }
90
+
91
+ /**
92
+ * Check if changing property requires resource replacement
93
+ * @returns {boolean}
94
+ */
95
+ requiresReplacement() {
96
+ return this._value === 'IMMUTABLE';
97
+ }
98
+
99
+ /**
100
+ * Get description of mutability type
101
+ * @returns {string}
102
+ */
103
+ getDescription() {
104
+ const descriptions = {
105
+ MUTABLE: 'Property can be changed without replacing the resource',
106
+ IMMUTABLE: 'Property cannot be changed - requires resource replacement',
107
+ CONDITIONAL: 'Property mutability depends on other property values or conditions',
108
+ };
109
+
110
+ return descriptions[this._value];
111
+ }
112
+
113
+ /**
114
+ * Check equality with another PropertyMutability
115
+ *
116
+ * @param {PropertyMutability} other
117
+ * @returns {boolean}
118
+ */
119
+ equals(other) {
120
+ if (!(other instanceof PropertyMutability)) {
121
+ return false;
122
+ }
123
+
124
+ return this._value === other._value;
125
+ }
126
+
127
+ /**
128
+ * Get string representation
129
+ *
130
+ * @returns {string}
131
+ */
132
+ toString() {
133
+ return this._value;
134
+ }
135
+
136
+ /**
137
+ * Predefined mutability: MUTABLE
138
+ * @type {PropertyMutability}
139
+ */
140
+ static get MUTABLE() {
141
+ return new PropertyMutability('MUTABLE');
142
+ }
143
+
144
+ /**
145
+ * Predefined mutability: IMMUTABLE
146
+ * @type {PropertyMutability}
147
+ */
148
+ static get IMMUTABLE() {
149
+ return new PropertyMutability('IMMUTABLE');
150
+ }
151
+
152
+ /**
153
+ * Predefined mutability: CONDITIONAL
154
+ * @type {PropertyMutability}
155
+ */
156
+ static get CONDITIONAL() {
157
+ return new PropertyMutability('CONDITIONAL');
158
+ }
159
+ }
160
+
161
+ module.exports = PropertyMutability;
@@ -0,0 +1,198 @@
1
+ /**
2
+ * Tests for PropertyMutability Value Object
3
+ */
4
+
5
+ const PropertyMutability = require('./property-mutability');
6
+
7
+ describe('PropertyMutability', () => {
8
+ describe('valid mutability types', () => {
9
+ it('should accept MUTABLE', () => {
10
+ const mutability = new PropertyMutability('MUTABLE');
11
+
12
+ expect(mutability.value).toBe('MUTABLE');
13
+ });
14
+
15
+ it('should accept IMMUTABLE', () => {
16
+ const mutability = new PropertyMutability('IMMUTABLE');
17
+
18
+ expect(mutability.value).toBe('IMMUTABLE');
19
+ });
20
+
21
+ it('should accept CONDITIONAL', () => {
22
+ const mutability = new PropertyMutability('CONDITIONAL');
23
+
24
+ expect(mutability.value).toBe('CONDITIONAL');
25
+ });
26
+ });
27
+
28
+ describe('invalid mutability types', () => {
29
+ it('should reject invalid mutability', () => {
30
+ expect(() => {
31
+ new PropertyMutability('INVALID');
32
+ }).toThrow('Invalid property mutability: INVALID');
33
+ });
34
+
35
+ it('should reject lowercase mutability', () => {
36
+ expect(() => {
37
+ new PropertyMutability('mutable');
38
+ }).toThrow('Invalid property mutability: mutable');
39
+ });
40
+
41
+ it('should reject null', () => {
42
+ expect(() => {
43
+ new PropertyMutability(null);
44
+ }).toThrow('Property mutability is required');
45
+ });
46
+
47
+ it('should reject undefined', () => {
48
+ expect(() => {
49
+ new PropertyMutability(undefined);
50
+ }).toThrow('Property mutability is required');
51
+ });
52
+ });
53
+
54
+ describe('mutability checks', () => {
55
+ it('should check if property is mutable', () => {
56
+ const mutability = new PropertyMutability('MUTABLE');
57
+
58
+ expect(mutability.isMutable()).toBe(true);
59
+ expect(mutability.isImmutable()).toBe(false);
60
+ expect(mutability.isConditional()).toBe(false);
61
+ });
62
+
63
+ it('should check if property is immutable', () => {
64
+ const mutability = new PropertyMutability('IMMUTABLE');
65
+
66
+ expect(mutability.isMutable()).toBe(false);
67
+ expect(mutability.isImmutable()).toBe(true);
68
+ expect(mutability.isConditional()).toBe(false);
69
+ });
70
+
71
+ it('should check if property is conditionally mutable', () => {
72
+ const mutability = new PropertyMutability('CONDITIONAL');
73
+
74
+ expect(mutability.isMutable()).toBe(false);
75
+ expect(mutability.isImmutable()).toBe(false);
76
+ expect(mutability.isConditional()).toBe(true);
77
+ });
78
+ });
79
+
80
+ describe('changeability checks', () => {
81
+ it('should allow changes for mutable properties', () => {
82
+ const mutability = new PropertyMutability('MUTABLE');
83
+
84
+ expect(mutability.canChange()).toBe(true);
85
+ expect(mutability.requiresReplacement()).toBe(false);
86
+ });
87
+
88
+ it('should not allow changes for immutable properties', () => {
89
+ const mutability = new PropertyMutability('IMMUTABLE');
90
+
91
+ expect(mutability.canChange()).toBe(false);
92
+ expect(mutability.requiresReplacement()).toBe(true);
93
+ });
94
+
95
+ it('should conditionally allow changes', () => {
96
+ const mutability = new PropertyMutability('CONDITIONAL');
97
+
98
+ // Conditional means it depends on other factors
99
+ expect(mutability.canChange()).toBe(false); // Can't change without conditions met
100
+ expect(mutability.requiresReplacement()).toBe(false); // Doesn't always require replacement
101
+ });
102
+ });
103
+
104
+ describe('equality', () => {
105
+ it('should be equal to same mutability', () => {
106
+ const m1 = new PropertyMutability('MUTABLE');
107
+ const m2 = new PropertyMutability('MUTABLE');
108
+
109
+ expect(m1.equals(m2)).toBe(true);
110
+ });
111
+
112
+ it('should not be equal to different mutability', () => {
113
+ const m1 = new PropertyMutability('MUTABLE');
114
+ const m2 = new PropertyMutability('IMMUTABLE');
115
+
116
+ expect(m1.equals(m2)).toBe(false);
117
+ });
118
+
119
+ it('should not be equal to non-PropertyMutability', () => {
120
+ const mutability = new PropertyMutability('MUTABLE');
121
+
122
+ expect(mutability.equals('MUTABLE')).toBe(false);
123
+ expect(mutability.equals(null)).toBe(false);
124
+ });
125
+ });
126
+
127
+ describe('toString', () => {
128
+ it('should return string representation', () => {
129
+ const mutability = new PropertyMutability('MUTABLE');
130
+
131
+ expect(mutability.toString()).toBe('MUTABLE');
132
+ });
133
+ });
134
+
135
+ describe('static constants', () => {
136
+ it('should provide MUTABLE constant', () => {
137
+ expect(PropertyMutability.MUTABLE.value).toBe('MUTABLE');
138
+ });
139
+
140
+ it('should provide IMMUTABLE constant', () => {
141
+ expect(PropertyMutability.IMMUTABLE.value).toBe('IMMUTABLE');
142
+ });
143
+
144
+ it('should provide CONDITIONAL constant', () => {
145
+ expect(PropertyMutability.CONDITIONAL.value).toBe('CONDITIONAL');
146
+ });
147
+
148
+ it('should provide VALID_TYPES array', () => {
149
+ expect(PropertyMutability.VALID_TYPES).toEqual([
150
+ 'MUTABLE',
151
+ 'IMMUTABLE',
152
+ 'CONDITIONAL',
153
+ ]);
154
+ });
155
+ });
156
+
157
+ describe('immutability', () => {
158
+ it('should not allow modification of value', () => {
159
+ const mutability = new PropertyMutability('MUTABLE');
160
+
161
+ expect(() => {
162
+ mutability.value = 'IMMUTABLE';
163
+ }).toThrow();
164
+ });
165
+
166
+ it('should be frozen', () => {
167
+ const mutability = new PropertyMutability('MUTABLE');
168
+
169
+ expect(Object.isFrozen(mutability)).toBe(true);
170
+ });
171
+ });
172
+
173
+ describe('description', () => {
174
+ it('should provide description for MUTABLE', () => {
175
+ const mutability = new PropertyMutability('MUTABLE');
176
+
177
+ expect(mutability.getDescription()).toBe(
178
+ 'Property can be changed without replacing the resource'
179
+ );
180
+ });
181
+
182
+ it('should provide description for IMMUTABLE', () => {
183
+ const mutability = new PropertyMutability('IMMUTABLE');
184
+
185
+ expect(mutability.getDescription()).toBe(
186
+ 'Property cannot be changed - requires resource replacement'
187
+ );
188
+ });
189
+
190
+ it('should provide description for CONDITIONAL', () => {
191
+ const mutability = new PropertyMutability('CONDITIONAL');
192
+
193
+ expect(mutability.getDescription()).toBe(
194
+ 'Property mutability depends on other property values or conditions'
195
+ );
196
+ });
197
+ });
198
+ });
@@ -0,0 +1,167 @@
1
+ /**
2
+ * ResourceState Value Object
3
+ *
4
+ * Enum-like immutable state for CloudFormation resources
5
+ *
6
+ * States:
7
+ * - IN_STACK: Resource exists in CloudFormation stack and matches expected definition
8
+ * - ORPHANED: Resource exists in AWS but not in CloudFormation stack
9
+ * - MISSING: Resource is in CloudFormation stack but not in AWS
10
+ * - DRIFTED: Resource exists in both but properties differ
11
+ * - EXTERNAL: Resource is intentionally external to stack
12
+ */
13
+
14
+ class ResourceState {
15
+ /**
16
+ * Valid resource states
17
+ * @type {string[]}
18
+ */
19
+ static VALID_STATES = [
20
+ 'IN_STACK',
21
+ 'ORPHANED',
22
+ 'MISSING',
23
+ 'DRIFTED',
24
+ 'EXTERNAL',
25
+ ];
26
+
27
+ /**
28
+ * Create a new ResourceState
29
+ *
30
+ * @param {string} value - State value
31
+ */
32
+ constructor(value) {
33
+ if (value === undefined || value === null) {
34
+ throw new Error('Resource state is required');
35
+ }
36
+
37
+ if (!ResourceState.VALID_STATES.includes(value)) {
38
+ throw new Error(`Invalid resource state: ${value}`);
39
+ }
40
+
41
+ this._value = value;
42
+
43
+ // Make immutable
44
+ Object.freeze(this);
45
+ }
46
+
47
+ /**
48
+ * Get state value
49
+ * @returns {string}
50
+ */
51
+ get value() {
52
+ return this._value;
53
+ }
54
+
55
+ /**
56
+ * Prevent modification of value
57
+ * @throws {TypeError}
58
+ */
59
+ set value(newValue) {
60
+ throw new TypeError('Cannot modify immutable property value');
61
+ }
62
+
63
+ /**
64
+ * Check if resource is in stack
65
+ * @returns {boolean}
66
+ */
67
+ isInStack() {
68
+ return this._value === 'IN_STACK';
69
+ }
70
+
71
+ /**
72
+ * Check if resource is orphaned
73
+ * @returns {boolean}
74
+ */
75
+ isOrphaned() {
76
+ return this._value === 'ORPHANED';
77
+ }
78
+
79
+ /**
80
+ * Check if resource is missing
81
+ * @returns {boolean}
82
+ */
83
+ isMissing() {
84
+ return this._value === 'MISSING';
85
+ }
86
+
87
+ /**
88
+ * Check if resource has drifted
89
+ * @returns {boolean}
90
+ */
91
+ isDrifted() {
92
+ return this._value === 'DRIFTED';
93
+ }
94
+
95
+ /**
96
+ * Check if resource is external
97
+ * @returns {boolean}
98
+ */
99
+ isExternal() {
100
+ return this._value === 'EXTERNAL';
101
+ }
102
+
103
+ /**
104
+ * Check equality with another ResourceState
105
+ *
106
+ * @param {ResourceState} other
107
+ * @returns {boolean}
108
+ */
109
+ equals(other) {
110
+ if (!(other instanceof ResourceState)) {
111
+ return false;
112
+ }
113
+
114
+ return this._value === other._value;
115
+ }
116
+
117
+ /**
118
+ * Get string representation
119
+ *
120
+ * @returns {string}
121
+ */
122
+ toString() {
123
+ return this._value;
124
+ }
125
+
126
+ /**
127
+ * Predefined state: IN_STACK
128
+ * @type {ResourceState}
129
+ */
130
+ static get IN_STACK() {
131
+ return new ResourceState('IN_STACK');
132
+ }
133
+
134
+ /**
135
+ * Predefined state: ORPHANED
136
+ * @type {ResourceState}
137
+ */
138
+ static get ORPHANED() {
139
+ return new ResourceState('ORPHANED');
140
+ }
141
+
142
+ /**
143
+ * Predefined state: MISSING
144
+ * @type {ResourceState}
145
+ */
146
+ static get MISSING() {
147
+ return new ResourceState('MISSING');
148
+ }
149
+
150
+ /**
151
+ * Predefined state: DRIFTED
152
+ * @type {ResourceState}
153
+ */
154
+ static get DRIFTED() {
155
+ return new ResourceState('DRIFTED');
156
+ }
157
+
158
+ /**
159
+ * Predefined state: EXTERNAL
160
+ * @type {ResourceState}
161
+ */
162
+ static get EXTERNAL() {
163
+ return new ResourceState('EXTERNAL');
164
+ }
165
+ }
166
+
167
+ module.exports = ResourceState;