codify-plugin-lib 1.0.131 → 1.0.133

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.
@@ -1,9 +1,15 @@
1
+ import { JSONSchemaType } from 'ajv';
1
2
  import { StringIndexedObject } from 'codify-schemas';
2
3
  import isObjectsEqual from 'lodash.isequal'
3
4
  import path from 'node:path';
4
5
 
5
6
  import { ArrayStatefulParameter, StatefulParameter } from '../stateful-parameter/stateful-parameter.js';
6
- import { areArraysEqual, untildify } from '../utils/utils.js';
7
+ import { areArraysEqual, tildify, untildify } from '../utils/utils.js';
8
+
9
+ export interface InputTransformation {
10
+ to: (input: any) => Promise<any> | any;
11
+ from: (current: any) => Promise<any> | any;
12
+ }
7
13
 
8
14
  /**
9
15
  * The configuration and settings for a resource.
@@ -18,7 +24,7 @@ export interface ResourceSettings<T extends StringIndexedObject> {
18
24
  /**
19
25
  * Schema to validate user configs with. Must be in the format JSON Schema draft07
20
26
  */
21
- schema?: unknown;
27
+ schema?: JSONSchemaType<T | any>;
22
28
 
23
29
  /**
24
30
  * Allow multiple of the same resource to unique. Set truthy if
@@ -27,6 +33,17 @@ export interface ResourceSettings<T extends StringIndexedObject> {
27
33
  */
28
34
  allowMultiple?: {
29
35
 
36
+ /**
37
+ * A set of parameters that uniquely identifies a resource. The value of these parameters is used to determine which
38
+ * resource is which when multiple can exist at the same time. Defaults to the required parameters inside the json
39
+ * schema.
40
+ *
41
+ * For example:
42
+ * If paramA is required, then if resource1.paramA === resource2.paramA then are the same resource.
43
+ * If resource1.paramA !== resource1.paramA, then they are different.
44
+ */
45
+ requiredParameters?: string[]
46
+
30
47
  /**
31
48
  * If multiple copies are allowed then a matcher must be defined to match the desired
32
49
  * config with one of the resources currently existing on the system. Return null if there is no match.
@@ -36,8 +53,8 @@ export interface ResourceSettings<T extends StringIndexedObject> {
36
53
  *
37
54
  * @return The matched resource.
38
55
  */
39
- matcher: (desired: Partial<T>, current: Partial<T>[],) => Partial<T>
40
- }
56
+ matcher?: (desired: Partial<T>, current: Partial<T>[],) => Partial<T>
57
+ } | boolean
41
58
 
42
59
  /**
43
60
  * If true, {@link StatefulParameter} remove() will be called before resource destruction. This is useful
@@ -67,9 +84,9 @@ export interface ResourceSettings<T extends StringIndexedObject> {
67
84
  inputTransformation?: (desired: Partial<T>) => Promise<unknown> | unknown;
68
85
 
69
86
  /**
70
- * Customize the import behavior of the resource. By default, <code>codify import</code> will call `refresh()` with
71
- * every parameter set to null and return the result of the refresh as the imported config. It looks for required parameters
72
- * in the schema and will prompt the user for these values before performing the import.
87
+ * Customize the import and destory behavior of the resource. By default, <code>codify import</code> and <code>codify destroy</code> will call
88
+ * `refresh()` with every parameter set to null and return the result of the refresh as the imported config. It looks for required parameters
89
+ * in the schema and will prompt the user for these values before performing the import or destroy.
73
90
  *
74
91
  * <b>Example:</b><br>
75
92
  * Resource `alias` with parameters
@@ -85,7 +102,7 @@ export interface ResourceSettings<T extends StringIndexedObject> {
85
102
  * { type: 'alias', alias: 'user-input', value: 'git push' }
86
103
  * ```
87
104
  */
88
- import?: {
105
+ importAndDestroy?: {
89
106
 
90
107
  /**
91
108
  * Customize the required parameters needed to import this resource. By default, the `requiredParameters` are taken
@@ -96,7 +113,7 @@ export interface ResourceSettings<T extends StringIndexedObject> {
96
113
  * the required parameters change the behaviour of the refresh (for example for the `alias` resource, the `alias` parmaeter
97
114
  * chooses which alias the resource is managing).
98
115
  *
99
- * See {@link import} for more information on how importing works.
116
+ * See {@link importAndDestroy} for more information on how importing works.
100
117
  */
101
118
  requiredParameters?: Array<Partial<keyof T>>;
102
119
 
@@ -107,14 +124,14 @@ export interface ResourceSettings<T extends StringIndexedObject> {
107
124
  * By default all parameters (except for {@link requiredParameters }) are passed in with the value `null`. The passed
108
125
  * in value can be customized using {@link defaultRefreshValues}
109
126
  *
110
- * See {@link import} for more information on how importing works.
127
+ * See {@link importAndDestroy} for more information on how importing works.
111
128
  */
112
129
  refreshKeys?: Array<Partial<keyof T>>;
113
130
 
114
131
  /**
115
132
  * Customize the value that is passed into refresh when importing. This must only contain keys found in {@link refreshKeys}.
116
133
  *
117
- * See {@link import} for more information on how importing works.
134
+ * See {@link importAndDestroy} for more information on how importing works.
118
135
  */
119
136
  defaultRefreshValues?: Partial<T>
120
137
  }
@@ -166,12 +183,13 @@ export interface DefaultParameterSetting {
166
183
  default?: unknown;
167
184
 
168
185
  /**
169
- * A transformation of the input value for this parameter. This transformation is only applied to the desired parameter
170
- * value supplied by the user.
186
+ * A transformation of the input value for this parameter. Two transformations need to be provided: to (from desired to
187
+ * the internal type), and from (from the internal type back to desired). All transformations need to be bi-directional
188
+ * to support imports properly
171
189
  *
172
190
  * @param input The original parameter value from the desired config.
173
191
  */
174
- inputTransformation?: (input: any) => Promise<any> | any;
192
+ inputTransformation?: InputTransformation;
175
193
 
176
194
  /**
177
195
  * Customize the equality comparison for a parameter. This is used in the diffing algorithm for generating the plan.
@@ -233,6 +251,12 @@ export interface ArrayParameterSetting extends DefaultParameterSetting {
233
251
  * Defaults to true.
234
252
  */
235
253
  filterInStatelessMode?: ((desired: any[], current: any[]) => any[]) | boolean,
254
+
255
+ /**
256
+ * The type of the array item. See {@link ParameterSettingType} for the available options. This value
257
+ * is mainly used to determine the equality method when performing diffing.
258
+ */
259
+ itemType?: ParameterSettingType,
236
260
  }
237
261
 
238
262
  /**
@@ -273,10 +297,7 @@ export function resolveEqualsFn(parameter: ParameterSetting): (desired: unknown,
273
297
  const isEqual = resolveFnFromEqualsFnOrString(parameter.isEqual);
274
298
 
275
299
  if (parameter.type === 'array') {
276
- const arrayParameter = parameter as ArrayParameterSetting;
277
- const isElementEqual = resolveFnFromEqualsFnOrString(arrayParameter.isElementEqual);
278
-
279
- return isEqual ?? areArraysEqual.bind(areArraysEqual, isElementEqual)
300
+ return isEqual ?? areArraysEqual.bind(areArraysEqual, resolveElementEqualsFn(parameter as ArrayParameterSetting))
280
301
  }
281
302
 
282
303
  if (parameter.type === 'stateful') {
@@ -286,6 +307,21 @@ export function resolveEqualsFn(parameter: ParameterSetting): (desired: unknown,
286
307
  return isEqual ?? ParameterEqualsDefaults[parameter.type as ParameterSettingType] ?? (((a, b) => a === b));
287
308
  }
288
309
 
310
+ export function resolveElementEqualsFn(parameter: ArrayParameterSetting): (desired: unknown, current: unknown) => boolean {
311
+ if (parameter.isElementEqual) {
312
+ const elementEq = resolveFnFromEqualsFnOrString(parameter.isElementEqual);
313
+ if (elementEq) {
314
+ return elementEq;
315
+ }
316
+ }
317
+
318
+ if (parameter.itemType && ParameterEqualsDefaults[parameter.itemType]) {
319
+ return ParameterEqualsDefaults[parameter.itemType]!
320
+ }
321
+
322
+ return (a, b) => a === b;
323
+ }
324
+
289
325
  // This resolves the fn if it is a string.
290
326
  // A string can be specified to use a default equals method
291
327
  export function resolveFnFromEqualsFnOrString(
@@ -303,19 +339,47 @@ export function resolveFnFromEqualsFnOrString(
303
339
  return fnOrString as ((a: unknown, b: unknown) => boolean) | undefined;
304
340
  }
305
341
 
306
- const ParameterTransformationDefaults: Partial<Record<ParameterSettingType, (input: any, parameter: ParameterSetting) => Promise<any> | any>> = {
307
- 'directory': (a: unknown) => path.resolve(untildify(String(a))),
308
- 'stateful': (a: unknown, b: ParameterSetting) => {
309
- const sp = b as StatefulParameterSetting;
310
- return (sp.definition?.getSettings()?.inputTransformation)
311
- ? (sp.definition.getSettings().inputTransformation!(a))
312
- : a;
342
+ const ParameterTransformationDefaults: Partial<Record<ParameterSettingType, InputTransformation>> = {
343
+ 'directory': {
344
+ to: (a: unknown) => path.resolve(untildify(String(a))),
345
+ from: (a: unknown) => tildify(String(a)),
313
346
  },
314
- 'string': String,
347
+ 'string': {
348
+ to: String,
349
+ from: String,
350
+ }
315
351
  }
316
352
 
317
353
  export function resolveParameterTransformFn(
318
354
  parameter: ParameterSetting
319
- ): ((input: any, parameter: ParameterSetting) => Promise<any> | any) | undefined {
355
+ ): InputTransformation | undefined {
356
+
357
+ if (parameter.type === 'stateful' && !parameter.inputTransformation) {
358
+ const sp = (parameter as StatefulParameterSetting).definition.getSettings();
359
+ if (sp.inputTransformation) {
360
+ return (parameter as StatefulParameterSetting).definition?.getSettings()?.inputTransformation
361
+ }
362
+
363
+ return sp.type ? ParameterTransformationDefaults[sp.type] : undefined;
364
+ }
365
+
366
+ if (parameter.type === 'array'
367
+ && (parameter as ArrayParameterSetting).itemType
368
+ && ParameterTransformationDefaults[(parameter as ArrayParameterSetting).itemType!]
369
+ && !parameter.inputTransformation
370
+ ) {
371
+ const itemType = (parameter as ArrayParameterSetting).itemType!;
372
+ const itemTransformation = ParameterTransformationDefaults[itemType]!;
373
+
374
+ return {
375
+ to(input: unknown[]) {
376
+ return input.map((i) => itemTransformation.to(i))
377
+ },
378
+ from(input: unknown[]) {
379
+ return input.map((i) => itemTransformation.from(i))
380
+ }
381
+ }
382
+ }
383
+
320
384
  return parameter.inputTransformation ?? ParameterTransformationDefaults[parameter.type as ParameterSettingType] ?? undefined;
321
385
  }
@@ -5,8 +5,8 @@ import { ParsedArrayParameterSetting, ParsedParameterSetting, } from '../resourc
5
5
  import {
6
6
  ArrayParameterSetting,
7
7
  ParameterSetting,
8
+ resolveElementEqualsFn,
8
9
  resolveEqualsFn,
9
- resolveFnFromEqualsFnOrString
10
10
  } from '../resource/resource-settings.js';
11
11
  import { ArrayStatefulParameter, StatefulParameter } from './stateful-parameter.js';
12
12
 
@@ -31,8 +31,7 @@ export class StatefulParameterController<T extends StringIndexedObject, V extend
31
31
  this.parsedSettings = (this.isArrayStatefulParameter || this.settings.type === 'array') ? {
32
32
  ...this.settings,
33
33
  isEqual: resolveEqualsFn(this.settings),
34
- isElementEqual: resolveFnFromEqualsFnOrString((this.settings as ArrayParameterSetting).isElementEqual)
35
- ?? ((a: unknown, b: unknown) => a === b)
34
+ isElementEqual: resolveElementEqualsFn(this.settings as ArrayParameterSetting)
36
35
  } as ParsedParameterSetting : {
37
36
  ...this.settings,
38
37
  isEqual: resolveEqualsFn(this.settings),
@@ -5,6 +5,7 @@ import { Resource } from '../resource/resource.js';
5
5
  import { CreatePlan, DestroyPlan } from '../plan/plan-types.js';
6
6
  import { ArrayStatefulParameter, StatefulParameter } from '../stateful-parameter/stateful-parameter.js';
7
7
  import { ParsedResourceSettings } from '../resource/parsed-resource-settings.js';
8
+ import { describe, it } from 'vitest';
8
9
 
9
10
  export function testPlan<T extends StringIndexedObject>(params: {
10
11
  desired?: Partial<T> | null;
@@ -85,3 +86,8 @@ export class TestArrayStatefulParameter extends ArrayStatefulParameter<TestConfi
85
86
  return Promise.resolve(undefined);
86
87
  }
87
88
  }
89
+
90
+ describe('Empty tests', () => {
91
+ it('empty', () => {
92
+ })
93
+ })
@@ -33,6 +33,10 @@ export function untildify(pathWithTilde: string) {
33
33
  return homeDirectory ? pathWithTilde.replace(/^~(?=$|\/|\\)/, homeDirectory) : pathWithTilde;
34
34
  }
35
35
 
36
+ export function tildify(pathWithTilde: string) {
37
+ return homeDirectory ? pathWithTilde.replace(homeDirectory, '~') : pathWithTilde;
38
+ }
39
+
36
40
  export function areArraysEqual(
37
41
  isElementEqual: ((desired: unknown, current: unknown) => boolean) | undefined,
38
42
  desired: unknown,
package/vitest.config.ts CHANGED
@@ -2,10 +2,9 @@ import { defaultExclude, defineConfig } from 'vitest/config';
2
2
 
3
3
  export default defineConfig({
4
4
  test: {
5
+ pool: 'forks',
5
6
  exclude: [
6
- ...defaultExclude,
7
- './src/utils/test-utils.test.ts',
8
- './src/pty/*'
7
+ ...defaultExclude
9
8
  ]
10
9
  },
11
10
  });
@@ -1,11 +0,0 @@
1
- import { defaultExclude, defineConfig } from 'vitest/config';
2
-
3
- export default defineConfig({
4
- test: {
5
- pool: 'forks',
6
- fileParallelism: false,
7
- exclude: [
8
- ...defaultExclude,
9
- ]
10
- },
11
- });