codify-plugin-lib 1.0.178 → 1.0.179

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 (43) hide show
  1. package/dist/entities/change-set.d.ts +24 -0
  2. package/dist/entities/change-set.js +152 -0
  3. package/dist/entities/errors.d.ts +4 -0
  4. package/dist/entities/errors.js +7 -0
  5. package/dist/entities/plan-types.d.ts +25 -0
  6. package/dist/entities/plan-types.js +1 -0
  7. package/dist/entities/plan.d.ts +15 -0
  8. package/dist/entities/plan.js +127 -0
  9. package/dist/entities/plugin.d.ts +16 -0
  10. package/dist/entities/plugin.js +80 -0
  11. package/dist/entities/resource-options.d.ts +31 -0
  12. package/dist/entities/resource-options.js +76 -0
  13. package/dist/entities/resource-types.d.ts +11 -0
  14. package/dist/entities/resource-types.js +1 -0
  15. package/dist/entities/resource.d.ts +42 -0
  16. package/dist/entities/resource.js +303 -0
  17. package/dist/entities/stateful-parameter.d.ts +29 -0
  18. package/dist/entities/stateful-parameter.js +46 -0
  19. package/dist/entities/transform-parameter.d.ts +4 -0
  20. package/dist/entities/transform-parameter.js +2 -0
  21. package/dist/plan/change-set.d.ts +8 -3
  22. package/dist/plan/change-set.js +10 -3
  23. package/dist/plan/plan.js +7 -4
  24. package/dist/plugin/plugin.d.ts +1 -1
  25. package/dist/plugin/plugin.js +8 -1
  26. package/dist/pty/vitest.config.d.ts +2 -0
  27. package/dist/pty/vitest.config.js +11 -0
  28. package/dist/resource/resource-settings.d.ts +6 -0
  29. package/dist/resource/stateful-parameter.d.ts +165 -0
  30. package/dist/resource/stateful-parameter.js +94 -0
  31. package/dist/utils/spawn-2.d.ts +5 -0
  32. package/dist/utils/spawn-2.js +7 -0
  33. package/dist/utils/spawn.d.ts +29 -0
  34. package/dist/utils/spawn.js +124 -0
  35. package/package.json +2 -2
  36. package/src/plan/change-set.test.ts +1 -1
  37. package/src/plan/change-set.ts +16 -3
  38. package/src/plan/plan.ts +7 -4
  39. package/src/plugin/plugin.ts +9 -1
  40. package/src/resource/resource-controller-stateful-mode.test.ts +15 -7
  41. package/src/resource/resource-controller.test.ts +4 -2
  42. package/src/resource/resource-settings.test.ts +2 -0
  43. package/src/resource/resource-settings.ts +8 -1
@@ -0,0 +1,165 @@
1
+ import { StringIndexedObject } from 'codify-schemas';
2
+ import { Plan } from '../plan/plan.js';
3
+ import { ArrayParameterSetting, ParameterSetting } from './resource-settings.js';
4
+ /**
5
+ * A stateful parameter represents a parameter that holds state on the system (can be created, destroyed) but
6
+ * is still tied to the overall lifecycle of a resource.
7
+ *
8
+ * **Examples include:**
9
+ * 1. Homebrew formulas are stateful parameters. They can be installed and uninstalled but they are still tied to the
10
+ * overall lifecycle of homebrew
11
+ * 2. Nvm installed node versions are stateful parameters. Nvm can install and uninstall different versions of Node but
12
+ * these versions are tied to the lifecycle of nvm. If nvm is uninstalled then so are the Node versions.
13
+ */
14
+ export declare abstract class StatefulParameter<T extends StringIndexedObject, V extends T[keyof T]> {
15
+ /**
16
+ * Parameter settings for the stateful parameter. Stateful parameters share the same parameter settings as
17
+ * regular parameters except that they cannot be of type 'stateful'. See {@link ParameterSetting} for more
18
+ * information on available settings.
19
+ *
20
+ * @return The parameter settings
21
+ */
22
+ getSettings(): ParameterSetting;
23
+ /**
24
+ * Refresh the status of the stateful parameter on the system. This method works similarly to {@link Resource.refresh}.
25
+ * Return the value of the stateful parameter or null if not found.
26
+ *
27
+ * @param desired The desired value of the user.
28
+ * @param config The desired config
29
+ *
30
+ * @return The value of the stateful parameter currently on the system or null if not found
31
+ */
32
+ abstract refresh(desired: V | null, config: Partial<T>): Promise<V | null>;
33
+ /**
34
+ * Create the stateful parameter on the system. This method is similar {@link Resource.create} except that its only
35
+ * applicable to the stateful parameter. For resource `CREATE` operations, this method will be called after the
36
+ * resource is successfully created. The add method is called when a ParameterChange is ADD in a plan. The add
37
+ * method is only called when the stateful parameter does not currently exist.
38
+ *
39
+ * **Example (Homebrew formula):**
40
+ * 1. Add is called with a value of:
41
+ * ```
42
+ * ['jq', 'jenv']
43
+ * ```
44
+ * 2. Add handles the request by calling `brew install --formulae jq jenv`
45
+ *
46
+ * @param valueToAdd The desired value of the stateful parameter.
47
+ * @param plan The overall plan that contains the ADD
48
+ */
49
+ abstract add(valueToAdd: V, plan: Plan<T>): Promise<void>;
50
+ /**
51
+ * Modify the state of a stateful parameter on the system. This method is similar to {@link Resource.modify} except that its only
52
+ * applicable to the stateful parameter.
53
+ *
54
+ * **Example (Git email parameter):**
55
+ * 1. Add is called with a value of:
56
+ * ```
57
+ * newValue: 'email+new@gmail.com', previousValue: 'email+old@gmail.com'
58
+ * ```
59
+ * 2. Modify handles the request by calling `git config --global user.email email+new@gmail.com`
60
+ *
61
+ * @param newValue The desired value of the stateful parameter
62
+ * @param previousValue The current value of the stateful parameter
63
+ * @param plan The overall plan
64
+ */
65
+ abstract modify(newValue: V, previousValue: V, plan: Plan<T>): Promise<void>;
66
+ /**
67
+ * Create the stateful parameter on the system. This method is similar {@link Resource.destroy} except that its only
68
+ * applicable to the stateful parameter. The remove method is only called when the stateful parameter already currently exist.
69
+ * This method corresponds to REMOVE parameter operations in a plan.
70
+ * For resource `DESTORY`, this method is only called if the {@link ResourceSettings.removeStatefulParametersBeforeDestroy}
71
+ * is set to true. This method will be called before the resource is destroyed.
72
+ *
73
+ * **Example (Homebrew formula):**
74
+ * 1. Remove is called with a value of:
75
+ * ```
76
+ * ['jq', 'jenv']
77
+ * ```
78
+ * 2. Remove handles the request by calling `brew uninstall --formulae jq jenv`
79
+ *
80
+ * @param valueToRemove The value to remove from the stateful parameter.
81
+ * @param plan The overall plan that contains the REMOVE
82
+ */
83
+ abstract remove(valueToRemove: V, plan: Plan<T>): Promise<void>;
84
+ }
85
+ /**
86
+ * A specialized version of {@link StatefulParameter } that is used for stateful parameters which are arrays.
87
+ * A stateful parameter represents a parameter that holds state on the system (can be created, destroyed) but
88
+ * is still tied to the overall lifecycle of a resource.
89
+ *
90
+ * **Examples:**
91
+ * - Homebrew formulas are arrays
92
+ * - Pyenv python versions are arrays
93
+ */
94
+ export declare abstract class ArrayStatefulParameter<T extends StringIndexedObject, V> extends StatefulParameter<T, any> {
95
+ /**
96
+ * Parameter level settings. Type must be 'array'.
97
+ */
98
+ getSettings(): ArrayParameterSetting;
99
+ /**
100
+ * It is not recommended to override the `add` method. A addItem helper method is available to operate on
101
+ * individual elements of the desired array. See {@link StatefulParameter.add} for more info.
102
+ *
103
+ * @param valuesToAdd The array of values to add
104
+ * @param plan The overall plan
105
+ *
106
+ */
107
+ add(valuesToAdd: V[], plan: Plan<T>): Promise<void>;
108
+ /**
109
+ * It is not recommended to override the `modify` method. `addItem` and `removeItem` will be called accordingly based
110
+ * on the modifications. See {@link StatefulParameter.modify} for more info.
111
+ *
112
+ * @param newValues The new array value
113
+ * @param previousValues The previous array value
114
+ * @param plan The overall plan
115
+ */
116
+ modify(newValues: V[], previousValues: V[], plan: Plan<T>): Promise<void>;
117
+ /**
118
+ * It is not recommended to override the `remove` method. A removeItem helper method is available to operate on
119
+ * individual elements of the desired array. See {@link StatefulParameter.remove} for more info.
120
+ *
121
+ * @param valuesToAdd The array of values to add
122
+ * @param plan The overall plan
123
+ *
124
+ */
125
+ remove(valuesToRemove: V[], plan: Plan<T>): Promise<void>;
126
+ /**
127
+ * See {@link StatefulParameter.refresh} for more info.
128
+ *
129
+ * @param desired The desired value to refresh
130
+ * @return The current value on the system or null if not found.
131
+ */
132
+ abstract refresh(desired: V[] | null, config: Partial<T>): Promise<V[] | null>;
133
+ /**
134
+ * Helper method that gets called when individual elements of the array need to be added. See {@link StatefulParameter.add}
135
+ * for more information.
136
+ *
137
+ * Example (Homebrew formula):
138
+ * 1. The stateful parameter receives an input of:
139
+ * ```
140
+ * ['jq', 'jenv', 'docker']
141
+ * ```
142
+ * 2. Internally the stateful parameter will iterate the array and call `addItem` for each element
143
+ * 3. Override addItem and install each formula using `brew install --formula jq`
144
+ *
145
+ * @param item The item to add (install)
146
+ * @param plan The overall plan
147
+ */
148
+ abstract addItem(item: V, plan: Plan<T>): Promise<void>;
149
+ /**
150
+ * Helper method that gets called when individual elements of the array need to be removed. See {@link StatefulParameter.remove}
151
+ * for more information.
152
+ *
153
+ * Example (Homebrew formula):
154
+ * 1. The stateful parameter receives an input of:
155
+ * ```
156
+ * ['jq', 'jenv', 'docker']
157
+ * ```
158
+ * 2. Internally the stateful parameter will iterate the array and call `removeItem` for each element
159
+ * 3. Override removeItem and uninstall each formula using `brew uninstall --formula jq`
160
+ *
161
+ * @param item The item to remove (uninstall)
162
+ * @param plan The overall plan
163
+ */
164
+ abstract removeItem(item: V, plan: Plan<T>): Promise<void>;
165
+ }
@@ -0,0 +1,94 @@
1
+ /**
2
+ * A stateful parameter represents a parameter that holds state on the system (can be created, destroyed) but
3
+ * is still tied to the overall lifecycle of a resource.
4
+ *
5
+ * **Examples include:**
6
+ * 1. Homebrew formulas are stateful parameters. They can be installed and uninstalled but they are still tied to the
7
+ * overall lifecycle of homebrew
8
+ * 2. Nvm installed node versions are stateful parameters. Nvm can install and uninstall different versions of Node but
9
+ * these versions are tied to the lifecycle of nvm. If nvm is uninstalled then so are the Node versions.
10
+ */
11
+ export class StatefulParameter {
12
+ /**
13
+ * Parameter settings for the stateful parameter. Stateful parameters share the same parameter settings as
14
+ * regular parameters except that they cannot be of type 'stateful'. See {@link ParameterSetting} for more
15
+ * information on available settings.
16
+ *
17
+ * @return The parameter settings
18
+ */
19
+ getSettings() {
20
+ return {};
21
+ }
22
+ }
23
+ /**
24
+ * A specialized version of {@link StatefulParameter } that is used for stateful parameters which are arrays.
25
+ * A stateful parameter represents a parameter that holds state on the system (can be created, destroyed) but
26
+ * is still tied to the overall lifecycle of a resource.
27
+ *
28
+ * **Examples:**
29
+ * - Homebrew formulas are arrays
30
+ * - Pyenv python versions are arrays
31
+ */
32
+ export class ArrayStatefulParameter extends StatefulParameter {
33
+ /**
34
+ * Parameter level settings. Type must be 'array'.
35
+ */
36
+ getSettings() {
37
+ return { type: 'array' };
38
+ }
39
+ /**
40
+ * It is not recommended to override the `add` method. A addItem helper method is available to operate on
41
+ * individual elements of the desired array. See {@link StatefulParameter.add} for more info.
42
+ *
43
+ * @param valuesToAdd The array of values to add
44
+ * @param plan The overall plan
45
+ *
46
+ */
47
+ async add(valuesToAdd, plan) {
48
+ for (const value of valuesToAdd) {
49
+ await this.addItem(value, plan);
50
+ }
51
+ }
52
+ /**
53
+ * It is not recommended to override the `modify` method. `addItem` and `removeItem` will be called accordingly based
54
+ * on the modifications. See {@link StatefulParameter.modify} for more info.
55
+ *
56
+ * @param newValues The new array value
57
+ * @param previousValues The previous array value
58
+ * @param plan The overall plan
59
+ */
60
+ async modify(newValues, previousValues, plan) {
61
+ // TODO: I don't think this works with duplicate elements. Solve at another time
62
+ const valuesToAdd = newValues.filter((n) => !previousValues.some((p) => {
63
+ if (this.getSettings()?.isElementEqual) {
64
+ return this.getSettings().isElementEqual(n, p);
65
+ }
66
+ return n === p;
67
+ }));
68
+ const valuesToRemove = previousValues.filter((p) => !newValues.some((n) => {
69
+ if (this.getSettings().isElementEqual) {
70
+ return this.getSettings().isElementEqual(n, p);
71
+ }
72
+ return n === p;
73
+ }));
74
+ for (const value of valuesToAdd) {
75
+ await this.addItem(value, plan);
76
+ }
77
+ for (const value of valuesToRemove) {
78
+ await this.removeItem(value, plan);
79
+ }
80
+ }
81
+ /**
82
+ * It is not recommended to override the `remove` method. A removeItem helper method is available to operate on
83
+ * individual elements of the desired array. See {@link StatefulParameter.remove} for more info.
84
+ *
85
+ * @param valuesToAdd The array of values to add
86
+ * @param plan The overall plan
87
+ *
88
+ */
89
+ async remove(valuesToRemove, plan) {
90
+ for (const value of valuesToRemove) {
91
+ await this.removeItem(value, plan);
92
+ }
93
+ }
94
+ }
@@ -0,0 +1,5 @@
1
+ import { Shell } from 'zx';
2
+ export declare class ShellContext implements Shell {
3
+ zx: Shell;
4
+ static create(): ShellContext;
5
+ }
@@ -0,0 +1,7 @@
1
+ import { $ } from 'zx';
2
+ export class ShellContext {
3
+ zx = $({ shell: true });
4
+ static create() {
5
+ return new ShellContext();
6
+ }
7
+ }
@@ -0,0 +1,29 @@
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ import { SpawnOptions } from 'node:child_process';
3
+ export declare enum SpawnStatus {
4
+ SUCCESS = "success",
5
+ ERROR = "error"
6
+ }
7
+ export interface SpawnResult {
8
+ status: SpawnStatus;
9
+ data: string;
10
+ }
11
+ type CodifySpawnOptions = {
12
+ cwd?: string;
13
+ throws?: boolean;
14
+ requiresRoot?: boolean;
15
+ } & Omit<SpawnOptions, 'detached' | 'shell' | 'stdio'>;
16
+ /**
17
+ *
18
+ * @param cmd Command to run. Ex: `rm -rf`
19
+ * @param opts Standard options for node spawn. Additional argument:
20
+ * throws determines if a shell will throw a JS error. Defaults to true
21
+ *
22
+ * @see promiseSpawn
23
+ * @see spawn
24
+ *
25
+ * @returns SpawnResult { status: SUCCESS | ERROR; data: string }
26
+ */
27
+ export declare function $(cmd: string, opts?: CodifySpawnOptions): Promise<SpawnResult>;
28
+ export declare function isDebug(): boolean;
29
+ export {};
@@ -0,0 +1,124 @@
1
+ import { Ajv } from 'ajv';
2
+ import { MessageCmd, SudoRequestResponseDataSchema } from 'codify-schemas';
3
+ import { spawn } from 'node:child_process';
4
+ import { SudoError } from '../errors.js';
5
+ const ajv = new Ajv({
6
+ strict: true,
7
+ });
8
+ const validateSudoRequestResponse = ajv.compile(SudoRequestResponseDataSchema);
9
+ export var SpawnStatus;
10
+ (function (SpawnStatus) {
11
+ SpawnStatus["SUCCESS"] = "success";
12
+ SpawnStatus["ERROR"] = "error";
13
+ })(SpawnStatus || (SpawnStatus = {}));
14
+ /**
15
+ *
16
+ * @param cmd Command to run. Ex: `rm -rf`
17
+ * @param opts Standard options for node spawn. Additional argument:
18
+ * throws determines if a shell will throw a JS error. Defaults to true
19
+ *
20
+ * @see promiseSpawn
21
+ * @see spawn
22
+ *
23
+ * @returns SpawnResult { status: SUCCESS | ERROR; data: string }
24
+ */
25
+ export async function $(cmd, opts) {
26
+ const throws = opts?.throws ?? true;
27
+ console.log(`Running command: ${cmd}`);
28
+ try {
29
+ // TODO: Need to benchmark the effects of using sh vs zsh for shell.
30
+ // Seems like zsh shells run slower
31
+ let result;
32
+ if (!opts?.requiresRoot) {
33
+ result = await internalSpawn(cmd, opts ?? {});
34
+ }
35
+ else {
36
+ result = await externalSpawnWithSudo(cmd, opts);
37
+ }
38
+ if (result.status !== SpawnStatus.SUCCESS) {
39
+ throw new Error(result.data);
40
+ }
41
+ return result;
42
+ }
43
+ catch (error) {
44
+ if (isDebug()) {
45
+ console.error(`CodifySpawn error for command ${cmd}`, error);
46
+ }
47
+ if (error.message?.startsWith('sudo:')) {
48
+ throw new SudoError(cmd);
49
+ }
50
+ if (throws) {
51
+ throw error;
52
+ }
53
+ if (error instanceof Error) {
54
+ return {
55
+ status: SpawnStatus.ERROR,
56
+ data: error.message,
57
+ };
58
+ }
59
+ return {
60
+ status: SpawnStatus.ERROR,
61
+ data: error + '',
62
+ };
63
+ }
64
+ }
65
+ async function internalSpawn(cmd, opts) {
66
+ return new Promise((resolve, reject) => {
67
+ const output = [];
68
+ // Source start up shells to emulate a users environment vs. a non-interactive non-login shell script
69
+ // Ignore all stdin
70
+ const _process = spawn(`source ~/.zshrc; ${cmd}`, [], {
71
+ ...opts,
72
+ stdio: ['ignore', 'pipe', 'pipe'],
73
+ shell: 'zsh',
74
+ });
75
+ const { stdout, stderr, stdin } = _process;
76
+ stdout.setEncoding('utf8');
77
+ stderr.setEncoding('utf8');
78
+ stdout.on('data', (data) => {
79
+ output.push(data.toString());
80
+ });
81
+ stderr.on('data', (data) => {
82
+ output.push(data.toString());
83
+ });
84
+ _process.on('error', (data) => {
85
+ });
86
+ // please node that this is not a full replacement for 'inherit'
87
+ // the child process can and will detect if stdout is a pty and change output based on it
88
+ // the terminal context is lost & ansi information (coloring) etc will be lost
89
+ if (stdout && stderr) {
90
+ stdout.pipe(process.stdout);
91
+ stderr.pipe(process.stderr);
92
+ }
93
+ _process.on('close', (code) => {
94
+ resolve({
95
+ status: code === 0 ? SpawnStatus.SUCCESS : SpawnStatus.ERROR,
96
+ data: output.join('\n'),
97
+ });
98
+ });
99
+ });
100
+ }
101
+ async function externalSpawnWithSudo(cmd, opts) {
102
+ return await new Promise((resolve) => {
103
+ const listener = (data) => {
104
+ if (data.cmd === MessageCmd.SUDO_REQUEST + '_Response') {
105
+ process.removeListener('message', listener);
106
+ if (!validateSudoRequestResponse(data.data)) {
107
+ throw new Error(`Invalid response for sudo request: ${JSON.stringify(validateSudoRequestResponse.errors, null, 2)}`);
108
+ }
109
+ resolve(data.data);
110
+ }
111
+ };
112
+ process.on('message', listener);
113
+ process.send({
114
+ cmd: MessageCmd.SUDO_REQUEST,
115
+ data: {
116
+ command: cmd,
117
+ options: opts ?? {},
118
+ }
119
+ });
120
+ });
121
+ }
122
+ export function isDebug() {
123
+ return process.env.DEBUG != null && process.env.DEBUG.includes('codify'); // TODO: replace with debug library
124
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codify-plugin-lib",
3
- "version": "1.0.178",
3
+ "version": "1.0.179",
4
4
  "description": "Library plugin library",
5
5
  "main": "dist/index.js",
6
6
  "typings": "dist/index.d.ts",
@@ -16,7 +16,7 @@
16
16
  "dependencies": {
17
17
  "ajv": "^8.12.0",
18
18
  "ajv-formats": "^2.1.1",
19
- "codify-schemas": "1.0.77",
19
+ "codify-schemas": "1.0.83",
20
20
  "@npmcli/promise-spawn": "^7.0.1",
21
21
  "@homebridge/node-pty-prebuilt-multiarch": "^0.12.0-beta.5",
22
22
  "uuid": "^10.0.0",
@@ -87,7 +87,7 @@ describe('Change set tests', () => {
87
87
  it('Correctly diffs two resource configs (destory)', () => {
88
88
  const cs = ChangeSet.destroy({
89
89
  propA: 'prop',
90
- propB: 'propB'
90
+ propB: 'propB',
91
91
  });
92
92
 
93
93
  expect(cs.parameterChanges.length).to.eq(2);
@@ -1,6 +1,7 @@
1
1
  import { ParameterOperation, ResourceOperation, StringIndexedObject } from 'codify-schemas';
2
2
 
3
3
  import { ParsedParameterSetting } from '../resource/parsed-resource-settings.js';
4
+ import { ResourceSettings } from '../resource/resource-settings.js';
4
5
 
5
6
  /**
6
7
  * A parameter change describes a parameter level change to a resource.
@@ -25,6 +26,11 @@ export interface ParameterChange<T extends StringIndexedObject> {
25
26
  * The new value of the resource (the desired value)
26
27
  */
27
28
  newValue: any | null;
29
+
30
+ /**
31
+ * Whether the parameter is sensitive
32
+ */
33
+ isSensitive: boolean;
28
34
  }
29
35
 
30
36
  // Change set will coerce undefined values to null because undefined is not valid JSON
@@ -60,37 +66,40 @@ export class ChangeSet<T extends StringIndexedObject> {
60
66
  return new ChangeSet<T>(ResourceOperation.NOOP, []);
61
67
  }
62
68
 
63
- static create<T extends StringIndexedObject>(desired: Partial<T>): ChangeSet<T> {
69
+ static create<T extends StringIndexedObject>(desired: Partial<T>, settings?: ResourceSettings<T>): ChangeSet<T> {
64
70
  const parameterChanges = Object.entries(desired)
65
71
  .map(([k, v]) => ({
66
72
  name: k,
67
73
  operation: ParameterOperation.ADD,
68
74
  previousValue: null,
69
75
  newValue: v ?? null,
76
+ isSensitive: settings?.parameterSettings?.[k]?.isSensitive ?? false,
70
77
  }))
71
78
 
72
79
  return new ChangeSet(ResourceOperation.CREATE, parameterChanges);
73
80
  }
74
81
 
75
- static noop<T extends StringIndexedObject>(parameters: Partial<T>): ChangeSet<T> {
82
+ static noop<T extends StringIndexedObject>(parameters: Partial<T>, settings?: ResourceSettings<T>): ChangeSet<T> {
76
83
  const parameterChanges = Object.entries(parameters)
77
84
  .map(([k, v]) => ({
78
85
  name: k,
79
86
  operation: ParameterOperation.NOOP,
80
87
  previousValue: v ?? null,
81
88
  newValue: v ?? null,
89
+ isSensitive: settings?.parameterSettings?.[k]?.isSensitive ?? false,
82
90
  }))
83
91
 
84
92
  return new ChangeSet(ResourceOperation.NOOP, parameterChanges);
85
93
  }
86
94
 
87
- static destroy<T extends StringIndexedObject>(current: Partial<T>): ChangeSet<T> {
95
+ static destroy<T extends StringIndexedObject>(current: Partial<T>, settings?: ResourceSettings<T>): ChangeSet<T> {
88
96
  const parameterChanges = Object.entries(current)
89
97
  .map(([k, v]) => ({
90
98
  name: k,
91
99
  operation: ParameterOperation.REMOVE,
92
100
  previousValue: v ?? null,
93
101
  newValue: null,
102
+ isSensitive: settings?.parameterSettings?.[k]?.isSensitive ?? false,
94
103
  }))
95
104
 
96
105
  return new ChangeSet(ResourceOperation.DESTROY, parameterChanges);
@@ -160,6 +169,7 @@ export class ChangeSet<T extends StringIndexedObject> {
160
169
  previousValue: current[k] ?? null,
161
170
  newValue: desired[k] ?? null,
162
171
  operation: ParameterOperation.NOOP,
172
+ isSensitive: parameterOptions?.[k]?.isSensitive ?? false,
163
173
  })
164
174
 
165
175
  continue;
@@ -171,6 +181,7 @@ export class ChangeSet<T extends StringIndexedObject> {
171
181
  previousValue: current[k] ?? null,
172
182
  newValue: null,
173
183
  operation: ParameterOperation.REMOVE,
184
+ isSensitive: parameterOptions?.[k]?.isSensitive ?? false,
174
185
  })
175
186
 
176
187
  continue;
@@ -182,6 +193,7 @@ export class ChangeSet<T extends StringIndexedObject> {
182
193
  previousValue: null,
183
194
  newValue: desired[k] ?? null,
184
195
  operation: ParameterOperation.ADD,
196
+ isSensitive: parameterOptions?.[k]?.isSensitive ?? false,
185
197
  })
186
198
 
187
199
  continue;
@@ -192,6 +204,7 @@ export class ChangeSet<T extends StringIndexedObject> {
192
204
  previousValue: current[k] ?? null,
193
205
  newValue: desired[k] ?? null,
194
206
  operation: ParameterOperation.MODIFY,
207
+ isSensitive: parameterOptions?.[k]?.isSensitive ?? false,
195
208
  })
196
209
  }
197
210
 
package/src/plan/plan.ts CHANGED
@@ -118,7 +118,7 @@ export class Plan<T extends StringIndexedObject> {
118
118
  if (!filteredCurrentParameters && desired) {
119
119
  return new Plan(
120
120
  uuidV4(),
121
- ChangeSet.create(desired),
121
+ ChangeSet.create(desired, settings),
122
122
  core,
123
123
  isStateful,
124
124
  )
@@ -130,7 +130,7 @@ export class Plan<T extends StringIndexedObject> {
130
130
  if (!settings.canDestroy) {
131
131
  return new Plan(
132
132
  uuidV4(),
133
- ChangeSet.noop(filteredCurrentParameters),
133
+ ChangeSet.noop(filteredCurrentParameters, settings),
134
134
  core,
135
135
  isStateful,
136
136
  )
@@ -138,7 +138,7 @@ export class Plan<T extends StringIndexedObject> {
138
138
 
139
139
  return new Plan(
140
140
  uuidV4(),
141
- ChangeSet.destroy(filteredCurrentParameters),
141
+ ChangeSet.destroy(filteredCurrentParameters, settings),
142
142
  core,
143
143
  isStateful,
144
144
  )
@@ -171,7 +171,10 @@ export class Plan<T extends StringIndexedObject> {
171
171
  uuidV4(),
172
172
  new ChangeSet<T>(
173
173
  data.operation,
174
- data.parameters
174
+ data.parameters.map((p) => ({
175
+ ...p,
176
+ isSensitive: p.isSensitive ?? false,
177
+ })),
175
178
  ),
176
179
  {
177
180
  type: data.resourceType,
@@ -62,11 +62,14 @@ export class Plugin {
62
62
  .map((r) => ({
63
63
  dependencies: r.dependencies,
64
64
  type: r.typeId,
65
+ sensitiveParameters: Object.entries(r.settings.parameterSettings ?? {})
66
+ .filter(([, v]) => v?.isSensitive)
67
+ .map(([k]) => k),
65
68
  }))
66
69
  }
67
70
  }
68
71
 
69
- async getResourceInfo(data: GetResourceInfoRequestData): Promise<GetResourceInfoResponseData> {
72
+ getResourceInfo(data: GetResourceInfoRequestData): GetResourceInfoResponseData {
70
73
  if (!this.resourceControllers.has(data.type)) {
71
74
  throw new Error(`Cannot get info for resource ${data.type}, resource doesn't exist`);
72
75
  }
@@ -84,6 +87,10 @@ export class Plugin {
84
87
  const allowMultiple = resource.settings.allowMultiple !== undefined
85
88
  && resource.settings.allowMultiple !== false;
86
89
 
90
+ const sensitiveParameters = Object.entries(resource.settings.parameterSettings ?? {})
91
+ .filter(([, v]) => v?.isSensitive)
92
+ .map(([k]) => k);
93
+
87
94
  return {
88
95
  plugin: this.name,
89
96
  type: data.type,
@@ -96,6 +103,7 @@ export class Plugin {
96
103
  import: {
97
104
  requiredParameters: requiredPropertyNames,
98
105
  },
106
+ sensitiveParameters,
99
107
  allowMultiple
100
108
  }
101
109
  }
@@ -142,19 +142,23 @@ describe('Resource tests for stateful plans', () => {
142
142
  name: "propA",
143
143
  newValue: "propA",
144
144
  previousValue: "propA",
145
- operation: ParameterOperation.NOOP
145
+ operation: ParameterOperation.NOOP,
146
+ isSensitive: false,
147
+
146
148
  },
147
149
  {
148
150
  name: "propB",
149
151
  newValue: 10,
150
152
  previousValue: null,
151
- operation: ParameterOperation.ADD
153
+ operation: ParameterOperation.ADD,
154
+ isSensitive: false,
152
155
  },
153
156
  {
154
157
  name: "propC",
155
158
  newValue: 'propC',
156
159
  previousValue: 'propC',
157
- operation: ParameterOperation.NOOP
160
+ operation: ParameterOperation.NOOP,
161
+ isSensitive: false,
158
162
  },
159
163
  ])
160
164
  },
@@ -214,25 +218,29 @@ describe('Resource tests for stateful plans', () => {
214
218
  name: "propA",
215
219
  newValue: "propA",
216
220
  previousValue: "propA",
217
- operation: ParameterOperation.NOOP
221
+ operation: ParameterOperation.NOOP,
222
+ isSensitive: false,
218
223
  },
219
224
  {
220
225
  name: "propB",
221
226
  newValue: 10,
222
227
  previousValue: null,
223
- operation: ParameterOperation.ADD
228
+ operation: ParameterOperation.ADD,
229
+ isSensitive: false,
224
230
  },
225
231
  {
226
232
  name: "propC",
227
233
  newValue: 'propC',
228
234
  previousValue: 'propC',
229
- operation: ParameterOperation.NOOP
235
+ operation: ParameterOperation.NOOP,
236
+ isSensitive: false,
230
237
  },
231
238
  {
232
239
  name: "propD",
233
240
  newValue: 'propD',
234
241
  previousValue: null,
235
- operation: ParameterOperation.ADD
242
+ operation: ParameterOperation.ADD,
243
+ isSensitive: false,
236
244
  },
237
245
  ])
238
246
  },