codify-plugin-lib 1.0.36 → 1.0.38
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/entities/change-set.d.ts +17 -9
- package/dist/entities/change-set.js +91 -36
- package/dist/entities/plan-types.d.ts +11 -0
- package/dist/entities/plan-types.js +1 -0
- package/dist/entities/plan.d.ts +10 -6
- package/dist/entities/plan.js +67 -8
- package/dist/entities/plugin.d.ts +1 -0
- package/dist/entities/plugin.js +23 -12
- package/dist/entities/resource-types.d.ts +24 -0
- package/dist/entities/resource-types.js +1 -0
- package/dist/entities/resource.d.ts +20 -15
- package/dist/entities/resource.js +129 -63
- package/dist/entities/stateful-parameter.d.ts +22 -8
- package/dist/entities/stateful-parameter.js +33 -0
- package/dist/entities/test.d.ts +1 -0
- package/dist/entities/test.js +22 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2 -0
- package/dist/utils/common-types.d.ts +3 -0
- package/dist/utils/common-types.js +1 -0
- package/dist/utils/utils.d.ts +6 -0
- package/dist/utils/utils.js +15 -0
- package/package.json +2 -2
- package/src/entities/change-set.test.ts +24 -36
- package/src/entities/change-set.ts +138 -47
- package/src/entities/plan-types.ts +26 -0
- package/src/entities/plan.ts +106 -13
- package/src/entities/plugin.ts +29 -13
- package/src/entities/resource-types.ts +47 -0
- package/src/entities/resource.test.ts +124 -179
- package/src/entities/resource.ts +192 -92
- package/src/entities/stateful-parameter.ts +56 -8
- package/src/index.ts +3 -0
- package/src/utils/utils.ts +21 -0
package/src/entities/resource.ts
CHANGED
|
@@ -1,134 +1,234 @@
|
|
|
1
|
-
import { ParameterOperation, ResourceConfig, ResourceOperation } from 'codify-schemas';
|
|
2
|
-
import {
|
|
1
|
+
import { ParameterOperation, ResourceConfig, ResourceOperation, StringIndexedObject, } from 'codify-schemas';
|
|
2
|
+
import { ParameterChange } from './change-set.js';
|
|
3
3
|
import { Plan } from './plan.js';
|
|
4
4
|
import { StatefulParameter } from './stateful-parameter.js';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
5
|
+
import { ResourceConfiguration, ValidationResult } from './resource-types.js';
|
|
6
|
+
import { setsEqual, splitUserConfig } from '../utils/utils.js';
|
|
7
|
+
import { ParameterConfiguration, PlanConfiguration } from './plan-types.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Description of resource here
|
|
11
|
+
* Two main functions:
|
|
12
|
+
* - Plan
|
|
13
|
+
* - Apply
|
|
14
|
+
*
|
|
15
|
+
*/
|
|
16
|
+
export abstract class Resource<T extends StringIndexedObject> {
|
|
17
|
+
|
|
18
|
+
readonly typeId: string;
|
|
19
|
+
readonly statefulParameters: Map<keyof T, StatefulParameter<T, T[keyof T]>>;
|
|
20
|
+
readonly dependencies: Resource<any>[]; // TODO: Change this to a string
|
|
21
|
+
readonly parameterConfigurations: Record<string, ParameterConfiguration>
|
|
22
|
+
|
|
23
|
+
private readonly options: ResourceConfiguration<T>;
|
|
24
|
+
|
|
25
|
+
protected constructor(configuration: ResourceConfiguration<T>) {
|
|
26
|
+
this.validateResourceConfiguration(configuration);
|
|
27
|
+
|
|
28
|
+
this.typeId = configuration.type;
|
|
29
|
+
this.statefulParameters = new Map(configuration.statefulParameters?.map((sp) => [sp.name, sp]));
|
|
30
|
+
this.parameterConfigurations = this.generateParameterConfigurations(configuration);
|
|
31
|
+
|
|
32
|
+
this.dependencies = configuration.dependencies ?? [];
|
|
33
|
+
this.options = configuration;
|
|
34
|
+
}
|
|
17
35
|
|
|
18
36
|
getDependencyTypeIds(): string[] {
|
|
19
|
-
return this.dependencies.map((d) => d.
|
|
37
|
+
return this.dependencies.map((d) => d.typeId)
|
|
20
38
|
}
|
|
21
39
|
|
|
22
40
|
async onInitialize(): Promise<void> {}
|
|
23
41
|
|
|
24
42
|
// TODO: Add state in later.
|
|
25
|
-
//
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
43
|
+
// Currently only calculating how to add things to reach desired state. Can't delete resources.
|
|
44
|
+
// Add previousConfig as a parameter for plan(desired, previous);
|
|
45
|
+
async plan(desiredConfig: Partial<T> & ResourceConfig): Promise<Plan<T>> {
|
|
46
|
+
|
|
47
|
+
// Explanation: these are settings for how the plan will be generated
|
|
48
|
+
const planConfiguration: PlanConfiguration = {
|
|
49
|
+
statefulMode: false,
|
|
50
|
+
parameterConfigurations: this.parameterConfigurations,
|
|
30
51
|
}
|
|
31
52
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
53
|
+
const { resourceMetadata, parameters: desiredParameters } = splitUserConfig(desiredConfig);
|
|
54
|
+
|
|
55
|
+
// Refresh resource parameters
|
|
56
|
+
// This refreshes the parameters that configure the resource itself
|
|
57
|
+
|
|
58
|
+
const resourceParameters = Object.fromEntries([
|
|
59
|
+
...Object.entries(desiredParameters).filter(([key]) => !this.statefulParameters.has(key)),
|
|
60
|
+
]) as Partial<T>;
|
|
61
|
+
|
|
62
|
+
const keysToRefresh = new Set(Object.keys(resourceParameters));
|
|
63
|
+
const currentParameters = await this.refresh(keysToRefresh);
|
|
64
|
+
if (!currentParameters) {
|
|
65
|
+
return Plan.create(desiredConfig, null, planConfiguration);
|
|
40
66
|
}
|
|
41
67
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
//
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
.
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
return ChangeSet.combineResourceOperations(operation, newOperation);
|
|
56
|
-
}, ResourceOperation.NOOP);
|
|
68
|
+
this.validateRefreshResults(currentParameters, keysToRefresh);
|
|
69
|
+
|
|
70
|
+
// Refresh stateful parameters
|
|
71
|
+
// This refreshes parameters that are stateful (they can be added, deleted separately from the resource)
|
|
72
|
+
|
|
73
|
+
const statefulParameters = [...this.statefulParameters.values()]
|
|
74
|
+
.filter((sp) => desiredParameters[sp.name] !== undefined) // Checking for undefined is fine here because JSONs can only have null.
|
|
75
|
+
|
|
76
|
+
for(const statefulParameter of statefulParameters) {
|
|
77
|
+
currentParameters[statefulParameter.name] = await statefulParameter.refresh(
|
|
78
|
+
desiredParameters[statefulParameter.name] ?? null
|
|
79
|
+
) ?? undefined;
|
|
80
|
+
}
|
|
57
81
|
|
|
58
82
|
return Plan.create(
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
83
|
+
desiredConfig,
|
|
84
|
+
{ ...currentParameters, ...resourceMetadata } as Partial<T> & ResourceConfig,
|
|
85
|
+
planConfiguration,
|
|
86
|
+
)
|
|
62
87
|
}
|
|
63
88
|
|
|
64
89
|
async apply(plan: Plan<T>): Promise<void> {
|
|
65
|
-
if (plan.getResourceType() !== this.
|
|
66
|
-
throw new Error(`Internal error: Plan set to wrong resource during apply. Expected ${this.
|
|
90
|
+
if (plan.getResourceType() !== this.typeId) {
|
|
91
|
+
throw new Error(`Internal error: Plan set to wrong resource during apply. Expected ${this.typeId} but got: ${plan.getResourceType()}`);
|
|
67
92
|
}
|
|
68
93
|
|
|
69
94
|
switch (plan.changeSet.operation) {
|
|
95
|
+
case ResourceOperation.CREATE: {
|
|
96
|
+
return this._applyCreate(plan); // TODO: Add new parameters value so that apply
|
|
97
|
+
}
|
|
70
98
|
case ResourceOperation.MODIFY: {
|
|
71
|
-
|
|
72
|
-
|
|
99
|
+
return this._applyModify(plan);
|
|
100
|
+
}
|
|
101
|
+
case ResourceOperation.RECREATE: {
|
|
102
|
+
await this._applyDestroy(plan);
|
|
103
|
+
return this._applyCreate(plan);
|
|
104
|
+
}
|
|
105
|
+
case ResourceOperation.DESTROY: {
|
|
106
|
+
return this._applyDestroy(plan);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
73
110
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
await this.applyModify(plan);
|
|
77
|
-
}
|
|
111
|
+
private async _applyCreate(plan: Plan<T>): Promise<void> {
|
|
112
|
+
await this.applyCreate(plan);
|
|
78
113
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
114
|
+
const statefulParameterChanges = plan.changeSet.parameterChanges
|
|
115
|
+
.filter((pc: ParameterChange<T>) => this.statefulParameters.has(pc.name))
|
|
116
|
+
for (const parameterChange of statefulParameterChanges) {
|
|
117
|
+
const statefulParameter = this.statefulParameters.get(parameterChange.name)!;
|
|
118
|
+
await statefulParameter.applyAdd(parameterChange.newValue, plan);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
private async _applyModify(plan: Plan<T>): Promise<void> {
|
|
123
|
+
const parameterChanges = plan
|
|
124
|
+
.changeSet
|
|
125
|
+
.parameterChanges
|
|
126
|
+
.filter((c: ParameterChange<T>) => c.operation !== ParameterOperation.NOOP);
|
|
127
|
+
|
|
128
|
+
const statelessParameterChanges = parameterChanges
|
|
129
|
+
.filter((pc: ParameterChange<T>) => !this.statefulParameters.has(pc.name))
|
|
130
|
+
for (const pc of statelessParameterChanges) {
|
|
131
|
+
// TODO: When stateful mode is added in the future. Dynamically choose if deletes are allowed
|
|
132
|
+
await this.applyModify(pc.name, pc.newValue, pc.previousValue, false, plan);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const statefulParameterChanges = parameterChanges
|
|
136
|
+
.filter((pc: ParameterChange<T>) => this.statefulParameters.has(pc.name))
|
|
137
|
+
for (const parameterChange of statefulParameterChanges) {
|
|
138
|
+
const statefulParameter = this.statefulParameters.get(parameterChange.name)!;
|
|
139
|
+
|
|
140
|
+
switch (parameterChange.operation) {
|
|
141
|
+
case ParameterOperation.ADD: {
|
|
142
|
+
await statefulParameter.applyAdd(parameterChange.newValue, plan);
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
case ParameterOperation.MODIFY: {
|
|
146
|
+
// TODO: When stateful mode is added in the future. Dynamically choose if deletes are allowed
|
|
147
|
+
await statefulParameter.applyModify(parameterChange.newValue, parameterChange.previousValue, false, plan);
|
|
148
|
+
break;
|
|
96
149
|
}
|
|
150
|
+
case ParameterOperation.REMOVE: {
|
|
151
|
+
await statefulParameter.applyRemove(parameterChange.previousValue, plan);
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
97
157
|
|
|
98
|
-
|
|
158
|
+
private async _applyDestroy(plan: Plan<T>): Promise<void> {
|
|
159
|
+
// If this option is set (defaults to false), then stateful parameters need to be destroyed
|
|
160
|
+
// as well. This means that the stateful parameter wouldn't have been normally destroyed with applyDestroy()
|
|
161
|
+
if (this.options.callStatefulParameterRemoveOnDestroy) {
|
|
162
|
+
const statefulParameterChanges = plan.changeSet.parameterChanges
|
|
163
|
+
.filter((pc: ParameterChange<T>) => this.statefulParameters.has(pc.name))
|
|
164
|
+
for (const parameterChange of statefulParameterChanges) {
|
|
165
|
+
const statefulParameter = this.statefulParameters.get(parameterChange.name)!;
|
|
166
|
+
await statefulParameter.applyRemove(parameterChange.previousValue, plan);
|
|
99
167
|
}
|
|
100
|
-
|
|
101
|
-
await this.applyCreate(plan);
|
|
102
|
-
const statefulParameterChanges = plan.changeSet.parameterChanges
|
|
103
|
-
.filter((pc: ParameterChange) => this.statefulParameters.has(pc.name))
|
|
168
|
+
}
|
|
104
169
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
170
|
+
await this.applyDestroy(plan);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
private generateParameterConfigurations(
|
|
174
|
+
resourceConfiguration: ResourceConfiguration<T>
|
|
175
|
+
): Record<string, ParameterConfiguration> {
|
|
176
|
+
const resourceParameters: Record<string, ParameterConfiguration> = Object.fromEntries(
|
|
177
|
+
Object.entries(resourceConfiguration.parameterConfigurations ?? {})
|
|
178
|
+
?.map(([name, value]) => ([name, { ...value, isStatefulParameter: false }]))
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
const statefulParameters: Record<string, ParameterConfiguration> = resourceConfiguration.statefulParameters
|
|
182
|
+
?.reduce((obj, sp) => {
|
|
183
|
+
return {
|
|
184
|
+
...obj,
|
|
185
|
+
[sp.name]: {
|
|
186
|
+
...sp.configuration,
|
|
187
|
+
isStatefulParameter: true,
|
|
188
|
+
}
|
|
108
189
|
}
|
|
190
|
+
}, {}) ?? {}
|
|
109
191
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
case ResourceOperation.DESTROY: return this.applyDestroy(plan);
|
|
192
|
+
return {
|
|
193
|
+
...resourceParameters,
|
|
194
|
+
...statefulParameters,
|
|
114
195
|
}
|
|
196
|
+
|
|
115
197
|
}
|
|
116
198
|
|
|
117
|
-
|
|
118
|
-
|
|
199
|
+
private validateResourceConfiguration(data: ResourceConfiguration<T>) {
|
|
200
|
+
// A parameter cannot be both stateful and stateless
|
|
201
|
+
if (data.parameterConfigurations && data.statefulParameters) {
|
|
202
|
+
const parameters = [...Object.keys(data.parameterConfigurations)];
|
|
203
|
+
const statefulParameterSet = new Set(Object.keys(data.statefulParameters));
|
|
204
|
+
|
|
205
|
+
const intersection = parameters.some((p) => statefulParameterSet.has(p));
|
|
206
|
+
if (intersection) {
|
|
207
|
+
throw new Error(`Resource ${this.typeId} cannot declare a parameter as both stateful and non-stateful`);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
119
210
|
}
|
|
120
211
|
|
|
121
|
-
|
|
212
|
+
private validateRefreshResults(refresh: Partial<T>, desiredKeys: Set<keyof T>) {
|
|
213
|
+
const refreshKeys = new Set(Object.keys(refresh)) as Set<keyof T>;
|
|
214
|
+
|
|
215
|
+
if (!setsEqual(desiredKeys, refreshKeys)) {
|
|
216
|
+
throw new Error(
|
|
217
|
+
`Resource ${this.options.type}
|
|
218
|
+
refresh() must return back exactly the keys that were provided
|
|
219
|
+
Missing: ${[...desiredKeys].filter((k) => !refreshKeys.has(k))};
|
|
220
|
+
Additional: ${[...refreshKeys].filter(k => !desiredKeys.has(k))};`
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
122
224
|
|
|
123
|
-
abstract
|
|
225
|
+
abstract validate(config: unknown): Promise<ValidationResult>;
|
|
124
226
|
|
|
125
|
-
abstract
|
|
227
|
+
abstract refresh(keys: Set<keyof T>): Promise<Partial<T> | null>;
|
|
126
228
|
|
|
127
229
|
abstract applyCreate(plan: Plan<T>): Promise<void>;
|
|
128
230
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
abstract applyRecreate(plan: Plan<T>): Promise<void>;
|
|
231
|
+
async applyModify(parameterName: keyof T, newValue: unknown, previousValue: unknown, allowDeletes: boolean, plan: Plan<T>): Promise<void> {};
|
|
132
232
|
|
|
133
|
-
abstract applyDestroy(plan:Plan<T>): Promise<void>;
|
|
233
|
+
abstract applyDestroy(plan: Plan<T>): Promise<void>;
|
|
134
234
|
}
|
|
@@ -1,13 +1,61 @@
|
|
|
1
|
-
import { ParameterChange } from './change-set.js';
|
|
2
1
|
import { Plan } from './plan.js';
|
|
3
|
-
import {
|
|
2
|
+
import { StringIndexedObject } from 'codify-schemas';
|
|
4
3
|
|
|
5
|
-
export
|
|
6
|
-
|
|
4
|
+
export interface StatefulParameterConfiguration<T> {
|
|
5
|
+
name: keyof T;
|
|
6
|
+
isEqual?: (a: any, b: any) => boolean;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export abstract class StatefulParameter<T extends StringIndexedObject, V extends T[keyof T]> {
|
|
10
|
+
readonly name: keyof T;
|
|
11
|
+
readonly configuration: StatefulParameterConfiguration<T>;
|
|
12
|
+
|
|
13
|
+
protected constructor(configuration: StatefulParameterConfiguration<T>) {
|
|
14
|
+
this.name = configuration.name;
|
|
15
|
+
this.configuration = configuration
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
abstract refresh(previousValue: V | null): Promise<V | null>;
|
|
19
|
+
|
|
20
|
+
// TODO: Add an additional parameter here for what has actually changed.
|
|
21
|
+
abstract applyAdd(valueToAdd: V, plan: Plan<T>): Promise<void>;
|
|
22
|
+
abstract applyModify(newValue: V, previousValue: V, allowDeletes: boolean, plan: Plan<T>): Promise<void>;
|
|
23
|
+
abstract applyRemove(valueToRemove: V, plan: Plan<T>): Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export abstract class ArrayStatefulParameter<T extends StringIndexedObject, V> extends StatefulParameter<T, any>{
|
|
27
|
+
protected constructor(configuration: StatefulParameterConfiguration<T>) {
|
|
28
|
+
super(configuration);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async applyAdd(valuesToAdd: V[], plan: Plan<T>): Promise<void> {
|
|
32
|
+
for (const value of valuesToAdd) {
|
|
33
|
+
await this.applyAddItem(value, plan);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async applyModify(newValues: V[], previousValues: V[], allowDeletes: boolean, plan: Plan<T>): Promise<void> {
|
|
38
|
+
const valuesToAdd = newValues.filter((n) => !previousValues.includes(n));
|
|
39
|
+
const valuesToRemove = previousValues.filter((n) => !newValues.includes(n));
|
|
40
|
+
|
|
41
|
+
for (const value of valuesToAdd) {
|
|
42
|
+
await this.applyAddItem(value, plan)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (allowDeletes) {
|
|
46
|
+
for (const value of valuesToRemove) {
|
|
47
|
+
await this.applyRemoveItem(value, plan)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
7
51
|
|
|
8
|
-
|
|
52
|
+
async applyRemove(valuesToRemove: V[], plan: Plan<T>): Promise<void> {
|
|
53
|
+
for (const value of valuesToRemove) {
|
|
54
|
+
await this.applyRemoveItem(value as V, plan);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
9
57
|
|
|
10
|
-
abstract
|
|
11
|
-
abstract
|
|
12
|
-
abstract
|
|
58
|
+
abstract refresh(previousValue: V[] | null): Promise<V[] | null>;
|
|
59
|
+
abstract applyAddItem(item: V, plan: Plan<T>): Promise<void>;
|
|
60
|
+
abstract applyRemoveItem(item: V, plan: Plan<T>): Promise<void>;
|
|
13
61
|
}
|
package/src/index.ts
CHANGED
|
@@ -2,9 +2,11 @@ import { Plugin } from './entities/plugin.js';
|
|
|
2
2
|
import { MessageHandler } from './messages/handlers.js';
|
|
3
3
|
|
|
4
4
|
export * from './entities/resource.js'
|
|
5
|
+
export * from './entities/resource-types.js'
|
|
5
6
|
export * from './entities/plugin.js'
|
|
6
7
|
export * from './entities/change-set.js'
|
|
7
8
|
export * from './entities/plan.js'
|
|
9
|
+
export * from './entities/plan-types.js'
|
|
8
10
|
export * from './entities/stateful-parameter.js'
|
|
9
11
|
|
|
10
12
|
export * from './utils/test-utils.js'
|
|
@@ -14,3 +16,4 @@ export async function runPlugin(plugin: Plugin) {
|
|
|
14
16
|
const messageHandler = new MessageHandler(plugin);
|
|
15
17
|
process.on('message', (message) => messageHandler.onMessage(message))
|
|
16
18
|
}
|
|
19
|
+
export { ErrorMessage } from './entities/resource-types.js';
|
package/src/utils/utils.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import promiseSpawn from '@npmcli/promise-spawn';
|
|
2
2
|
import { SpawnOptions } from 'child_process';
|
|
3
|
+
import { ResourceConfig, StringIndexedObject } from 'codify-schemas';
|
|
3
4
|
|
|
4
5
|
export enum SpawnStatus {
|
|
5
6
|
SUCCESS = 'success',
|
|
@@ -78,3 +79,23 @@ export async function codifySpawn(
|
|
|
78
79
|
export function isDebug(): boolean {
|
|
79
80
|
return process.env.DEBUG != null && process.env.DEBUG.includes('codify'); // TODO: replace with debug library
|
|
80
81
|
}
|
|
82
|
+
|
|
83
|
+
export function splitUserConfig<T extends StringIndexedObject>(
|
|
84
|
+
config: T & ResourceConfig
|
|
85
|
+
): { parameters: T; resourceMetadata: ResourceConfig} {
|
|
86
|
+
const resourceMetadata = {
|
|
87
|
+
type: config.type,
|
|
88
|
+
...(config.name && { name: config.name }),
|
|
89
|
+
...(config.dependsOn && { dependsOn: config.dependsOn }),
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const { type, name, dependsOn, ...parameters } = config;
|
|
93
|
+
return {
|
|
94
|
+
parameters: parameters as T,
|
|
95
|
+
resourceMetadata,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function setsEqual(set1: Set<unknown>, set2: Set<unknown>): boolean {
|
|
100
|
+
return set1.size === set2.size && [...set1].every((v) => set2.has(v));
|
|
101
|
+
}
|