@codifycli/plugin-core 1.2.0 → 1.2.1
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/plan/change-set.d.ts +1 -0
- package/dist/plan/change-set.js +12 -3
- package/dist/utils/functions.js +1 -1
- package/package.json +1 -1
- package/src/plan/change-set.test.ts +27 -0
- package/src/plan/change-set.ts +10 -3
- package/src/utils/functions.ts +1 -1
- package/.claude/settings.local.json +0 -13
- package/dist/bin/build.d.ts +0 -1
- package/dist/bin/build.js +0 -80
- package/dist/bin/deploy-plugin.d.ts +0 -2
- package/dist/bin/deploy-plugin.js +0 -8
- package/dist/entities/change-set.d.ts +0 -24
- package/dist/entities/change-set.js +0 -152
- package/dist/entities/errors.d.ts +0 -4
- package/dist/entities/errors.js +0 -7
- package/dist/entities/plan-types.d.ts +0 -25
- package/dist/entities/plan-types.js +0 -1
- package/dist/entities/plan.d.ts +0 -15
- package/dist/entities/plan.js +0 -127
- package/dist/entities/plugin.d.ts +0 -16
- package/dist/entities/plugin.js +0 -80
- package/dist/entities/resource-options.d.ts +0 -31
- package/dist/entities/resource-options.js +0 -76
- package/dist/entities/resource-types.d.ts +0 -11
- package/dist/entities/resource-types.js +0 -1
- package/dist/entities/resource.d.ts +0 -42
- package/dist/entities/resource.js +0 -303
- package/dist/entities/stateful-parameter.d.ts +0 -29
- package/dist/entities/stateful-parameter.js +0 -46
- package/dist/entities/transform-parameter.d.ts +0 -4
- package/dist/entities/transform-parameter.js +0 -2
- package/dist/pty/vitest.config.d.ts +0 -2
- package/dist/pty/vitest.config.js +0 -11
- package/dist/resource/stateful-parameter.d.ts +0 -165
- package/dist/resource/stateful-parameter.js +0 -94
- package/dist/scripts/deploy.d.ts +0 -1
- package/dist/scripts/deploy.js +0 -2
- package/dist/test.d.ts +0 -1
- package/dist/test.js +0 -5
- package/dist/utils/codify-spawn.d.ts +0 -29
- package/dist/utils/codify-spawn.js +0 -136
- package/dist/utils/internal-utils.d.ts +0 -12
- package/dist/utils/internal-utils.js +0 -74
- package/dist/utils/load-resources.d.ts +0 -1
- package/dist/utils/load-resources.js +0 -46
- package/dist/utils/package-json-utils.d.ts +0 -12
- package/dist/utils/package-json-utils.js +0 -34
- package/dist/utils/spawn-2.d.ts +0 -5
- package/dist/utils/spawn-2.js +0 -7
- package/dist/utils/spawn.d.ts +0 -29
- package/dist/utils/spawn.js +0 -124
- package/dist/utils/utils.d.ts +0 -18
- package/dist/utils/utils.js +0 -86
package/dist/plan/change-set.js
CHANGED
|
@@ -92,9 +92,9 @@ export class ChangeSet {
|
|
|
92
92
|
*/
|
|
93
93
|
static calculateParameterChanges(desiredParameters, currentParameters, parameterOptions) {
|
|
94
94
|
const parameterChangeSet = new Array();
|
|
95
|
-
// Filter out null
|
|
96
|
-
const desired = Object.fromEntries(Object.entries(desiredParameters).filter(([, v]) => v
|
|
97
|
-
const current = Object.fromEntries(Object.entries(currentParameters).filter(([, v]) => v
|
|
95
|
+
// Filter out null, undefined, [], and {} — all treated as "no value"
|
|
96
|
+
const desired = Object.fromEntries(Object.entries(desiredParameters).filter(([, v]) => !ChangeSet.isAbsent(v)));
|
|
97
|
+
const current = Object.fromEntries(Object.entries(currentParameters).filter(([, v]) => !ChangeSet.isAbsent(v)));
|
|
98
98
|
for (const k of new Set([...Object.keys(current), ...Object.keys(desired)])) {
|
|
99
99
|
if (ChangeSet.isSame(desired[k], current[k], parameterOptions?.[k])) {
|
|
100
100
|
parameterChangeSet.push({
|
|
@@ -148,6 +148,15 @@ export class ChangeSet {
|
|
|
148
148
|
const indexNext = orderOfOperations.indexOf(next);
|
|
149
149
|
return orderOfOperations[Math.max(indexPrev, indexNext)];
|
|
150
150
|
}
|
|
151
|
+
static isAbsent(v) {
|
|
152
|
+
if (v === null || v === undefined)
|
|
153
|
+
return true;
|
|
154
|
+
if (Array.isArray(v))
|
|
155
|
+
return v.length === 0;
|
|
156
|
+
if (typeof v === 'object')
|
|
157
|
+
return Object.keys(v).length === 0;
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
151
160
|
static isSame(desired, current, setting) {
|
|
152
161
|
return (setting?.isEqual ?? ((a, b) => a === b))(desired, current);
|
|
153
162
|
}
|
package/dist/utils/functions.js
CHANGED
|
@@ -31,7 +31,7 @@ export function resolvePathWithVariables(pathWithVariables) {
|
|
|
31
31
|
export function addVariablesToPath(pathWithoutVariables) {
|
|
32
32
|
let result = pathWithoutVariables;
|
|
33
33
|
for (const [key, value] of Object.entries(process.env)) {
|
|
34
|
-
if (!value || !path.isAbsolute(value) || value === '/' || key === 'HOME' || key === 'PATH' || key === 'SHELL' || key === 'PWD') {
|
|
34
|
+
if (!value || !path.isAbsolute(value) || value === '/' || value === homeDirectory || key === 'HOME' || key === 'PATH' || key === 'SHELL' || key === 'PWD') {
|
|
35
35
|
continue;
|
|
36
36
|
}
|
|
37
37
|
result = result.replaceAll(value, `$${key}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codifycli/plugin-core",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "TypeScript library for building Codify plugins to manage system resources (applications, CLI tools, settings) through infrastructure-as-code",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"typings": "dist/index.d.ts",
|
|
@@ -304,6 +304,33 @@ describe('Change set tests', () => {
|
|
|
304
304
|
expect(cs.operation).to.eq(ResourceOperation.DESTROY);
|
|
305
305
|
})
|
|
306
306
|
|
|
307
|
+
it('treats empty objects {} as no-op', () => {
|
|
308
|
+
const desired = {
|
|
309
|
+
keyboard: {},
|
|
310
|
+
dock: {},
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const current = {
|
|
314
|
+
keyboard: {},
|
|
315
|
+
dock: {},
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const cs = ChangeSet.calculateModification(desired, current);
|
|
319
|
+
expect(cs.parameterChanges.length).to.eq(0);
|
|
320
|
+
expect(cs.operation).to.eq(ResourceOperation.NOOP);
|
|
321
|
+
})
|
|
322
|
+
|
|
323
|
+
it('treats empty object {} as absent when current has no value', () => {
|
|
324
|
+
const desired = {
|
|
325
|
+
keyboard: {},
|
|
326
|
+
dock: {},
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const cs = ChangeSet.calculateModification(desired, {});
|
|
330
|
+
expect(cs.parameterChanges.length).to.eq(0);
|
|
331
|
+
expect(cs.operation).to.eq(ResourceOperation.NOOP);
|
|
332
|
+
})
|
|
333
|
+
|
|
307
334
|
it('correctly determines array equality 5', () => {
|
|
308
335
|
const arrA = [{ key1: 'b' }, { key1: 'a' }, { key1: 'a' }];
|
|
309
336
|
const arrB = [{ key1: 'a' }, { key1: 'a' }, { key1: 'b' }];
|
package/src/plan/change-set.ts
CHANGED
|
@@ -154,13 +154,13 @@ export class ChangeSet<T extends StringIndexedObject> {
|
|
|
154
154
|
): ParameterChange<T>[] {
|
|
155
155
|
const parameterChangeSet = new Array<ParameterChange<T>>();
|
|
156
156
|
|
|
157
|
-
// Filter out null
|
|
157
|
+
// Filter out null, undefined, [], and {} — all treated as "no value"
|
|
158
158
|
const desired = Object.fromEntries(
|
|
159
|
-
Object.entries(desiredParameters).filter(([, v]) => v
|
|
159
|
+
Object.entries(desiredParameters).filter(([, v]) => !ChangeSet.isAbsent(v))
|
|
160
160
|
) as Partial<T>
|
|
161
161
|
|
|
162
162
|
const current = Object.fromEntries(
|
|
163
|
-
Object.entries(currentParameters).filter(([, v]) => v
|
|
163
|
+
Object.entries(currentParameters).filter(([, v]) => !ChangeSet.isAbsent(v))
|
|
164
164
|
) as Partial<T>
|
|
165
165
|
|
|
166
166
|
for (const k of new Set([...Object.keys(current), ...Object.keys(desired)])) {
|
|
@@ -227,6 +227,13 @@ export class ChangeSet<T extends StringIndexedObject> {
|
|
|
227
227
|
return orderOfOperations[Math.max(indexPrev, indexNext)];
|
|
228
228
|
}
|
|
229
229
|
|
|
230
|
+
private static isAbsent(v: unknown): boolean {
|
|
231
|
+
if (v === null || v === undefined) return true;
|
|
232
|
+
if (Array.isArray(v)) return v.length === 0;
|
|
233
|
+
if (typeof v === 'object') return Object.keys(v as object).length === 0;
|
|
234
|
+
return false;
|
|
235
|
+
}
|
|
236
|
+
|
|
230
237
|
private static isSame(
|
|
231
238
|
desired: unknown,
|
|
232
239
|
current: unknown,
|
package/src/utils/functions.ts
CHANGED
|
@@ -43,7 +43,7 @@ export function resolvePathWithVariables(pathWithVariables: string) {
|
|
|
43
43
|
export function addVariablesToPath(pathWithoutVariables: string) {
|
|
44
44
|
let result = pathWithoutVariables;
|
|
45
45
|
for (const [key, value] of Object.entries(process.env)) {
|
|
46
|
-
if (!value || !path.isAbsolute(value) || value === '/' || key === 'HOME' || key === 'PATH' || key === 'SHELL' || key === 'PWD') {
|
|
46
|
+
if (!value || !path.isAbsolute(value) || value === '/' || value === homeDirectory || key === 'HOME' || key === 'PATH' || key === 'SHELL' || key === 'PWD') {
|
|
47
47
|
continue;
|
|
48
48
|
}
|
|
49
49
|
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"permissions": {
|
|
3
|
-
"allow": [
|
|
4
|
-
"Bash(grep -r \"COMMAND_REQUEST\\\\|PRESS_KEY_TO_CONTINUE_REQUEST\\\\|CODIFY_CREDENTIALS_REQUEST\" /Users/kevinwang/Projects/codify-parent/codify/src --include=\"*.ts\" ! -name \"*.test.ts\")",
|
|
5
|
-
"Read(//Users/kevinwang/Projects/codify-parent/codify-schemas/**)",
|
|
6
|
-
"Bash(npm run *)"
|
|
7
|
-
]
|
|
8
|
-
},
|
|
9
|
-
"disabledMcpjsonServers": [
|
|
10
|
-
"supabase",
|
|
11
|
-
"codify"
|
|
12
|
-
]
|
|
13
|
-
}
|
package/dist/bin/build.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function build(): Promise<void>;
|
package/dist/bin/build.js
DELETED
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import { Ajv } from 'ajv';
|
|
2
|
-
import { IpcMessageSchema, MessageStatus, ResourceSchema } from 'codify-schemas';
|
|
3
|
-
// @ts-ignore
|
|
4
|
-
import mergeJsonSchemas from 'merge-json-schemas';
|
|
5
|
-
import { fork } from 'node:child_process';
|
|
6
|
-
import fs from 'node:fs/promises';
|
|
7
|
-
import path from 'node:path';
|
|
8
|
-
import * as url from 'node:url';
|
|
9
|
-
import { codifySpawn } from '../utils/codify-spawn.js';
|
|
10
|
-
const ajv = new Ajv({
|
|
11
|
-
strict: true
|
|
12
|
-
});
|
|
13
|
-
const ipcMessageValidator = ajv.compile(IpcMessageSchema);
|
|
14
|
-
function sendMessageAndAwaitResponse(process, message) {
|
|
15
|
-
return new Promise((resolve, reject) => {
|
|
16
|
-
process.on('message', (response) => {
|
|
17
|
-
if (!ipcMessageValidator(response)) {
|
|
18
|
-
throw new Error(`Invalid message from plugin. ${JSON.stringify(message, null, 2)}`);
|
|
19
|
-
}
|
|
20
|
-
// Wait for the message response. Other messages such as sudoRequest may be sent before the response returns
|
|
21
|
-
if (response.cmd === message.cmd + '_Response') {
|
|
22
|
-
if (response.status === MessageStatus.SUCCESS) {
|
|
23
|
-
resolve(response.data);
|
|
24
|
-
}
|
|
25
|
-
else {
|
|
26
|
-
reject(new Error(String(response.data)));
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
-
// Send message last to ensure listeners are all registered
|
|
31
|
-
process.send(message);
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
export async function build() {
|
|
35
|
-
await fs.rm('./dist', { force: true, recursive: true });
|
|
36
|
-
await codifySpawn('npm run rollup -- -f es');
|
|
37
|
-
const plugin = fork('./dist/index.js', [], {
|
|
38
|
-
// Use default true to test plugins in secure mode (un-able to request sudo directly)
|
|
39
|
-
detached: true,
|
|
40
|
-
env: { ...process.env },
|
|
41
|
-
execArgv: ['--import', 'tsx/esm'],
|
|
42
|
-
});
|
|
43
|
-
const initializeResult = await sendMessageAndAwaitResponse(plugin, {
|
|
44
|
-
cmd: 'initialize',
|
|
45
|
-
data: {}
|
|
46
|
-
});
|
|
47
|
-
const { resourceDefinitions } = initializeResult;
|
|
48
|
-
const resourceTypes = resourceDefinitions.map((i) => i.type);
|
|
49
|
-
const schemasMap = new Map();
|
|
50
|
-
for (const type of resourceTypes) {
|
|
51
|
-
const resourceInfo = await sendMessageAndAwaitResponse(plugin, {
|
|
52
|
-
cmd: 'getResourceInfo',
|
|
53
|
-
data: { type }
|
|
54
|
-
});
|
|
55
|
-
schemasMap.set(type, resourceInfo.schema);
|
|
56
|
-
}
|
|
57
|
-
const mergedSchemas = [...schemasMap.entries()].map(([type, schema]) => {
|
|
58
|
-
// const resolvedSchema = await $RefParser.dereference(schema)
|
|
59
|
-
const resourceSchema = JSON.parse(JSON.stringify(ResourceSchema));
|
|
60
|
-
delete resourceSchema.$id;
|
|
61
|
-
delete resourceSchema.$schema;
|
|
62
|
-
delete resourceSchema.title;
|
|
63
|
-
delete resourceSchema.oneOf;
|
|
64
|
-
delete resourceSchema.properties.type;
|
|
65
|
-
if (schema) {
|
|
66
|
-
delete schema.$id;
|
|
67
|
-
delete schema.$schema;
|
|
68
|
-
delete schema.title;
|
|
69
|
-
delete schema.oneOf;
|
|
70
|
-
}
|
|
71
|
-
return mergeJsonSchemas([schema ?? {}, resourceSchema, { properties: { type: { const: type, type: 'string' } } }]);
|
|
72
|
-
});
|
|
73
|
-
await fs.rm('./dist', { force: true, recursive: true });
|
|
74
|
-
await codifySpawn('npm run rollup'); // re-run rollup without building for es
|
|
75
|
-
console.log('Generated JSON Schemas for all resources');
|
|
76
|
-
const distFolder = path.resolve(path.dirname(url.fileURLToPath(import.meta.url)), '..', 'dist');
|
|
77
|
-
const schemaOutputPath = path.resolve(distFolder, 'schemas.json');
|
|
78
|
-
await fs.writeFile(schemaOutputPath, JSON.stringify(mergedSchemas, null, 2));
|
|
79
|
-
console.log('Successfully wrote schema to ./dist/schemas.json');
|
|
80
|
-
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import * as fs from 'node:fs';
|
|
4
|
-
import { build } from './build.js';
|
|
5
|
-
const packageJson = fs.readFileSync(path.join(process.env['npm_config_local_prefix'], 'package.json'), 'utf8');
|
|
6
|
-
const { name: libraryName, version: libraryVersion } = JSON.parse(packageJson);
|
|
7
|
-
console.log(libraryName, libraryVersion);
|
|
8
|
-
await build();
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { ParameterOperation, ResourceOperation, StringIndexedObject } from 'codify-schemas';
|
|
2
|
-
import { ParameterOptions } from './plan-types.js';
|
|
3
|
-
export interface ParameterChange<T extends StringIndexedObject> {
|
|
4
|
-
name: keyof T & string;
|
|
5
|
-
operation: ParameterOperation;
|
|
6
|
-
previousValue: any | null;
|
|
7
|
-
newValue: any | null;
|
|
8
|
-
}
|
|
9
|
-
export declare class ChangeSet<T extends StringIndexedObject> {
|
|
10
|
-
operation: ResourceOperation;
|
|
11
|
-
parameterChanges: Array<ParameterChange<T>>;
|
|
12
|
-
constructor(operation: ResourceOperation, parameterChanges: Array<ParameterChange<T>>);
|
|
13
|
-
get desiredParameters(): T;
|
|
14
|
-
get currentParameters(): T;
|
|
15
|
-
static calculateParameterChangeSet<T extends StringIndexedObject>(desired: T | null, current: T | null, options: {
|
|
16
|
-
statefulMode: boolean;
|
|
17
|
-
parameterOptions?: Record<keyof T, ParameterOptions>;
|
|
18
|
-
}): ParameterChange<T>[];
|
|
19
|
-
static combineResourceOperations(prev: ResourceOperation, next: ResourceOperation): ResourceOperation;
|
|
20
|
-
static isSame(desired: unknown, current: unknown, options?: ParameterOptions): boolean;
|
|
21
|
-
private static calculateStatefulModeChangeSet;
|
|
22
|
-
private static calculateStatelessModeChangeSet;
|
|
23
|
-
private static addDefaultValues;
|
|
24
|
-
}
|
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
import { ParameterOperation, ResourceOperation } from 'codify-schemas';
|
|
2
|
-
export class ChangeSet {
|
|
3
|
-
operation;
|
|
4
|
-
parameterChanges;
|
|
5
|
-
constructor(operation, parameterChanges) {
|
|
6
|
-
this.operation = operation;
|
|
7
|
-
this.parameterChanges = parameterChanges;
|
|
8
|
-
}
|
|
9
|
-
get desiredParameters() {
|
|
10
|
-
return this.parameterChanges
|
|
11
|
-
.reduce((obj, pc) => ({
|
|
12
|
-
...obj,
|
|
13
|
-
[pc.name]: pc.newValue,
|
|
14
|
-
}), {});
|
|
15
|
-
}
|
|
16
|
-
get currentParameters() {
|
|
17
|
-
return this.parameterChanges
|
|
18
|
-
.reduce((obj, pc) => ({
|
|
19
|
-
...obj,
|
|
20
|
-
[pc.name]: pc.previousValue,
|
|
21
|
-
}), {});
|
|
22
|
-
}
|
|
23
|
-
static calculateParameterChangeSet(desired, current, options) {
|
|
24
|
-
if (options.statefulMode) {
|
|
25
|
-
return ChangeSet.calculateStatefulModeChangeSet(desired, current, options.parameterOptions);
|
|
26
|
-
}
|
|
27
|
-
else {
|
|
28
|
-
return ChangeSet.calculateStatelessModeChangeSet(desired, current, options.parameterOptions);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
static combineResourceOperations(prev, next) {
|
|
32
|
-
const orderOfOperations = [
|
|
33
|
-
ResourceOperation.NOOP,
|
|
34
|
-
ResourceOperation.MODIFY,
|
|
35
|
-
ResourceOperation.RECREATE,
|
|
36
|
-
ResourceOperation.CREATE,
|
|
37
|
-
ResourceOperation.DESTROY,
|
|
38
|
-
];
|
|
39
|
-
const indexPrev = orderOfOperations.indexOf(prev);
|
|
40
|
-
const indexNext = orderOfOperations.indexOf(next);
|
|
41
|
-
return orderOfOperations[Math.max(indexPrev, indexNext)];
|
|
42
|
-
}
|
|
43
|
-
static isSame(desired, current, options) {
|
|
44
|
-
if (options?.isEqual) {
|
|
45
|
-
return options.isEqual(desired, current);
|
|
46
|
-
}
|
|
47
|
-
if (Array.isArray(desired) && Array.isArray(current)) {
|
|
48
|
-
const sortedDesired = desired.map((x) => x).sort();
|
|
49
|
-
const sortedCurrent = current.map((x) => x).sort();
|
|
50
|
-
if (sortedDesired.length !== sortedCurrent.length) {
|
|
51
|
-
return false;
|
|
52
|
-
}
|
|
53
|
-
if (options?.isElementEqual) {
|
|
54
|
-
return sortedDesired.every((value, index) => options.isElementEqual(value, sortedCurrent[index]));
|
|
55
|
-
}
|
|
56
|
-
return JSON.stringify(sortedDesired) === JSON.stringify(sortedCurrent);
|
|
57
|
-
}
|
|
58
|
-
return desired === current;
|
|
59
|
-
}
|
|
60
|
-
static calculateStatefulModeChangeSet(desired, current, parameterOptions) {
|
|
61
|
-
const parameterChangeSet = new Array();
|
|
62
|
-
const _desired = Object.fromEntries(Object.entries(desired ?? {}).filter(([, v]) => v != null));
|
|
63
|
-
const _current = Object.fromEntries(Object.entries(current ?? {}).filter(([, v]) => v != null));
|
|
64
|
-
this.addDefaultValues(_desired, parameterOptions);
|
|
65
|
-
for (const [k, v] of Object.entries(_current)) {
|
|
66
|
-
if (_desired[k] == null) {
|
|
67
|
-
parameterChangeSet.push({
|
|
68
|
-
name: k,
|
|
69
|
-
previousValue: v,
|
|
70
|
-
newValue: null,
|
|
71
|
-
operation: ParameterOperation.REMOVE,
|
|
72
|
-
});
|
|
73
|
-
delete _current[k];
|
|
74
|
-
continue;
|
|
75
|
-
}
|
|
76
|
-
if (!ChangeSet.isSame(_desired[k], _current[k], parameterOptions?.[k])) {
|
|
77
|
-
parameterChangeSet.push({
|
|
78
|
-
name: k,
|
|
79
|
-
previousValue: v,
|
|
80
|
-
newValue: _desired[k],
|
|
81
|
-
operation: ParameterOperation.MODIFY,
|
|
82
|
-
});
|
|
83
|
-
delete _current[k];
|
|
84
|
-
delete _desired[k];
|
|
85
|
-
continue;
|
|
86
|
-
}
|
|
87
|
-
parameterChangeSet.push({
|
|
88
|
-
name: k,
|
|
89
|
-
previousValue: v,
|
|
90
|
-
newValue: _desired[k],
|
|
91
|
-
operation: ParameterOperation.NOOP,
|
|
92
|
-
});
|
|
93
|
-
delete _current[k];
|
|
94
|
-
delete _desired[k];
|
|
95
|
-
}
|
|
96
|
-
if (Object.keys(_current).length !== 0) {
|
|
97
|
-
throw Error('Diff algorithm error');
|
|
98
|
-
}
|
|
99
|
-
for (const [k, v] of Object.entries(_desired)) {
|
|
100
|
-
parameterChangeSet.push({
|
|
101
|
-
name: k,
|
|
102
|
-
previousValue: null,
|
|
103
|
-
newValue: v,
|
|
104
|
-
operation: ParameterOperation.ADD,
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
return parameterChangeSet;
|
|
108
|
-
}
|
|
109
|
-
static calculateStatelessModeChangeSet(desired, current, parameterOptions) {
|
|
110
|
-
const parameterChangeSet = new Array();
|
|
111
|
-
const _desired = Object.fromEntries(Object.entries(desired ?? {}).filter(([, v]) => v != null));
|
|
112
|
-
const _current = Object.fromEntries(Object.entries(current ?? {}).filter(([, v]) => v != null));
|
|
113
|
-
this.addDefaultValues(_desired, parameterOptions);
|
|
114
|
-
for (const [k, v] of Object.entries(_desired)) {
|
|
115
|
-
if (_current[k] == null) {
|
|
116
|
-
parameterChangeSet.push({
|
|
117
|
-
name: k,
|
|
118
|
-
previousValue: null,
|
|
119
|
-
newValue: v,
|
|
120
|
-
operation: ParameterOperation.ADD,
|
|
121
|
-
});
|
|
122
|
-
continue;
|
|
123
|
-
}
|
|
124
|
-
if (!ChangeSet.isSame(_desired[k], _current[k], parameterOptions?.[k])) {
|
|
125
|
-
parameterChangeSet.push({
|
|
126
|
-
name: k,
|
|
127
|
-
previousValue: _current[k],
|
|
128
|
-
newValue: _desired[k],
|
|
129
|
-
operation: ParameterOperation.MODIFY,
|
|
130
|
-
});
|
|
131
|
-
continue;
|
|
132
|
-
}
|
|
133
|
-
parameterChangeSet.push({
|
|
134
|
-
name: k,
|
|
135
|
-
previousValue: v,
|
|
136
|
-
newValue: v,
|
|
137
|
-
operation: ParameterOperation.NOOP,
|
|
138
|
-
});
|
|
139
|
-
}
|
|
140
|
-
return parameterChangeSet;
|
|
141
|
-
}
|
|
142
|
-
static addDefaultValues(obj, options) {
|
|
143
|
-
Object.entries(options ?? {})
|
|
144
|
-
.filter(([, option]) => option.default !== undefined)
|
|
145
|
-
.map(([name, option]) => [name, option.default])
|
|
146
|
-
.forEach(([key, defaultValue]) => {
|
|
147
|
-
if (obj[key] === undefined) {
|
|
148
|
-
obj[key] = defaultValue;
|
|
149
|
-
}
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
}
|
package/dist/entities/errors.js
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { Plan } from './plan.js';
|
|
2
|
-
import { StringIndexedObject } from 'codify-schemas';
|
|
3
|
-
export interface ParameterOptions {
|
|
4
|
-
modifyOnChange?: boolean;
|
|
5
|
-
isEqual?: (desired: any, current: any) => boolean;
|
|
6
|
-
isElementEqual?: (desired: any, current: any) => boolean;
|
|
7
|
-
default?: unknown;
|
|
8
|
-
isStatefulParameter?: boolean;
|
|
9
|
-
}
|
|
10
|
-
export interface PlanOptions<T> {
|
|
11
|
-
statefulMode: boolean;
|
|
12
|
-
parameterOptions?: Record<keyof T, ParameterOptions>;
|
|
13
|
-
}
|
|
14
|
-
export interface CreatePlan<T extends StringIndexedObject> extends Plan<T> {
|
|
15
|
-
desiredConfig: T;
|
|
16
|
-
currentConfig: null;
|
|
17
|
-
}
|
|
18
|
-
export interface DestroyPlan<T extends StringIndexedObject> extends Plan<T> {
|
|
19
|
-
desiredConfig: null;
|
|
20
|
-
currentConfig: T;
|
|
21
|
-
}
|
|
22
|
-
export interface ModifyPlan<T extends StringIndexedObject> extends Plan<T> {
|
|
23
|
-
desiredConfig: T;
|
|
24
|
-
currentConfig: T;
|
|
25
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/entities/plan.d.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { ChangeSet } from './change-set.js';
|
|
2
|
-
import { ApplyRequestData, PlanResponseData, ResourceConfig, StringIndexedObject } from 'codify-schemas';
|
|
3
|
-
import { PlanOptions } from './plan-types.js';
|
|
4
|
-
export declare class Plan<T extends StringIndexedObject> {
|
|
5
|
-
id: string;
|
|
6
|
-
changeSet: ChangeSet<T>;
|
|
7
|
-
resourceMetadata: ResourceConfig;
|
|
8
|
-
constructor(id: string, changeSet: ChangeSet<T>, resourceMetadata: ResourceConfig);
|
|
9
|
-
static create<T extends StringIndexedObject>(desiredParameters: Partial<T> | null, currentParameters: Partial<T> | null, resourceMetadata: ResourceConfig, options: PlanOptions<T>): Plan<T>;
|
|
10
|
-
getResourceType(): string;
|
|
11
|
-
static fromResponse<T extends ResourceConfig>(data: ApplyRequestData['plan'], defaultValues?: Partial<Record<keyof T, unknown>>): Plan<T>;
|
|
12
|
-
get desiredConfig(): T | null;
|
|
13
|
-
get currentConfig(): T | null;
|
|
14
|
-
toResponse(): PlanResponseData;
|
|
15
|
-
}
|
package/dist/entities/plan.js
DELETED
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
import { ChangeSet } from './change-set.js';
|
|
2
|
-
import { ParameterOperation, ResourceOperation, } from 'codify-schemas';
|
|
3
|
-
import { randomUUID } from 'crypto';
|
|
4
|
-
export class Plan {
|
|
5
|
-
id;
|
|
6
|
-
changeSet;
|
|
7
|
-
resourceMetadata;
|
|
8
|
-
constructor(id, changeSet, resourceMetadata) {
|
|
9
|
-
this.id = id;
|
|
10
|
-
this.changeSet = changeSet;
|
|
11
|
-
this.resourceMetadata = resourceMetadata;
|
|
12
|
-
}
|
|
13
|
-
static create(desiredParameters, currentParameters, resourceMetadata, options) {
|
|
14
|
-
const parameterOptions = options.parameterOptions ?? {};
|
|
15
|
-
const statefulParameterNames = new Set([...Object.entries(parameterOptions)]
|
|
16
|
-
.filter(([k, v]) => v.isStatefulParameter)
|
|
17
|
-
.map(([k, v]) => k));
|
|
18
|
-
const parameterChangeSet = ChangeSet.calculateParameterChangeSet(desiredParameters, currentParameters, { statefulMode: options.statefulMode, parameterOptions });
|
|
19
|
-
let resourceOperation;
|
|
20
|
-
if (!currentParameters && desiredParameters) {
|
|
21
|
-
resourceOperation = ResourceOperation.CREATE;
|
|
22
|
-
}
|
|
23
|
-
else if (currentParameters && !desiredParameters) {
|
|
24
|
-
resourceOperation = ResourceOperation.DESTROY;
|
|
25
|
-
}
|
|
26
|
-
else {
|
|
27
|
-
resourceOperation = parameterChangeSet
|
|
28
|
-
.filter((change) => change.operation !== ParameterOperation.NOOP)
|
|
29
|
-
.reduce((operation, curr) => {
|
|
30
|
-
let newOperation;
|
|
31
|
-
if (statefulParameterNames.has(curr.name)) {
|
|
32
|
-
newOperation = ResourceOperation.MODIFY;
|
|
33
|
-
}
|
|
34
|
-
else if (parameterOptions[curr.name]?.modifyOnChange) {
|
|
35
|
-
newOperation = parameterOptions[curr.name].modifyOnChange ? ResourceOperation.MODIFY : ResourceOperation.RECREATE;
|
|
36
|
-
}
|
|
37
|
-
else {
|
|
38
|
-
newOperation = ResourceOperation.RECREATE;
|
|
39
|
-
}
|
|
40
|
-
return ChangeSet.combineResourceOperations(operation, newOperation);
|
|
41
|
-
}, ResourceOperation.NOOP);
|
|
42
|
-
}
|
|
43
|
-
return new Plan(randomUUID(), new ChangeSet(resourceOperation, parameterChangeSet), resourceMetadata);
|
|
44
|
-
}
|
|
45
|
-
getResourceType() {
|
|
46
|
-
return this.resourceMetadata.type;
|
|
47
|
-
}
|
|
48
|
-
static fromResponse(data, defaultValues) {
|
|
49
|
-
if (!data) {
|
|
50
|
-
throw new Error('Data is empty');
|
|
51
|
-
}
|
|
52
|
-
addDefaultValues();
|
|
53
|
-
return new Plan(randomUUID(), new ChangeSet(data.operation, data.parameters), {
|
|
54
|
-
type: data.resourceType,
|
|
55
|
-
name: data.resourceName,
|
|
56
|
-
});
|
|
57
|
-
function addDefaultValues() {
|
|
58
|
-
Object.entries(defaultValues ?? {})
|
|
59
|
-
.forEach(([key, defaultValue]) => {
|
|
60
|
-
const configValueExists = data
|
|
61
|
-
.parameters
|
|
62
|
-
.some((p) => p.name === key);
|
|
63
|
-
if (configValueExists) {
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
switch (data.operation) {
|
|
67
|
-
case ResourceOperation.CREATE: {
|
|
68
|
-
data.parameters.push({
|
|
69
|
-
name: key,
|
|
70
|
-
operation: ParameterOperation.ADD,
|
|
71
|
-
previousValue: null,
|
|
72
|
-
newValue: defaultValue,
|
|
73
|
-
});
|
|
74
|
-
break;
|
|
75
|
-
}
|
|
76
|
-
case ResourceOperation.DESTROY: {
|
|
77
|
-
data.parameters.push({
|
|
78
|
-
name: key,
|
|
79
|
-
operation: ParameterOperation.REMOVE,
|
|
80
|
-
previousValue: defaultValue,
|
|
81
|
-
newValue: null,
|
|
82
|
-
});
|
|
83
|
-
break;
|
|
84
|
-
}
|
|
85
|
-
case ResourceOperation.MODIFY:
|
|
86
|
-
case ResourceOperation.RECREATE:
|
|
87
|
-
case ResourceOperation.NOOP: {
|
|
88
|
-
data.parameters.push({
|
|
89
|
-
name: key,
|
|
90
|
-
operation: ParameterOperation.NOOP,
|
|
91
|
-
previousValue: defaultValue,
|
|
92
|
-
newValue: defaultValue,
|
|
93
|
-
});
|
|
94
|
-
break;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
get desiredConfig() {
|
|
101
|
-
if (this.changeSet.operation === ResourceOperation.DESTROY) {
|
|
102
|
-
return null;
|
|
103
|
-
}
|
|
104
|
-
return {
|
|
105
|
-
...this.resourceMetadata,
|
|
106
|
-
...this.changeSet.desiredParameters,
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
get currentConfig() {
|
|
110
|
-
if (this.changeSet.operation === ResourceOperation.CREATE) {
|
|
111
|
-
return null;
|
|
112
|
-
}
|
|
113
|
-
return {
|
|
114
|
-
...this.resourceMetadata,
|
|
115
|
-
...this.changeSet.currentParameters,
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
toResponse() {
|
|
119
|
-
return {
|
|
120
|
-
planId: this.id,
|
|
121
|
-
operation: this.changeSet.operation,
|
|
122
|
-
resourceName: this.resourceMetadata.name,
|
|
123
|
-
resourceType: this.resourceMetadata.type,
|
|
124
|
-
parameters: this.changeSet.parameterChanges,
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { ApplyRequestData, InitializeResponseData, PlanRequestData, PlanResponseData, ResourceConfig, ValidateRequestData, ValidateResponseData } from 'codify-schemas';
|
|
2
|
-
import { Plan } from './plan.js';
|
|
3
|
-
import { Resource } from './resource.js';
|
|
4
|
-
export declare class Plugin {
|
|
5
|
-
name: string;
|
|
6
|
-
resources: Map<string, Resource<ResourceConfig>>;
|
|
7
|
-
planStorage: Map<string, Plan<any>>;
|
|
8
|
-
static create(name: string, resources: Resource<any>[]): Plugin;
|
|
9
|
-
constructor(name: string, resources: Map<string, Resource<ResourceConfig>>);
|
|
10
|
-
initialize(): Promise<InitializeResponseData>;
|
|
11
|
-
validate(data: ValidateRequestData): Promise<ValidateResponseData>;
|
|
12
|
-
plan(data: PlanRequestData): Promise<PlanResponseData>;
|
|
13
|
-
apply(data: ApplyRequestData): Promise<void>;
|
|
14
|
-
private resolvePlan;
|
|
15
|
-
protected crossValidateResources(configs: ResourceConfig[]): Promise<void>;
|
|
16
|
-
}
|