@codifycli/plugin-core 1.0.0-beta1
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/.eslintignore +2 -0
- package/.eslintrc.json +30 -0
- package/.github/workflows/release.yaml +19 -0
- package/.github/workflows/unit-test-ci.yaml +18 -0
- package/.prettierrc.json +1 -0
- package/bin/build.js +189 -0
- package/dist/bin/build.d.ts +1 -0
- package/dist/bin/build.js +80 -0
- package/dist/bin/deploy-plugin.d.ts +2 -0
- package/dist/bin/deploy-plugin.js +8 -0
- package/dist/common/errors.d.ts +8 -0
- package/dist/common/errors.js +24 -0
- package/dist/entities/change-set.d.ts +24 -0
- package/dist/entities/change-set.js +152 -0
- package/dist/entities/errors.d.ts +4 -0
- package/dist/entities/errors.js +7 -0
- package/dist/entities/plan-types.d.ts +25 -0
- package/dist/entities/plan-types.js +1 -0
- package/dist/entities/plan.d.ts +15 -0
- package/dist/entities/plan.js +127 -0
- package/dist/entities/plugin.d.ts +16 -0
- package/dist/entities/plugin.js +80 -0
- package/dist/entities/resource-options.d.ts +31 -0
- package/dist/entities/resource-options.js +76 -0
- package/dist/entities/resource-types.d.ts +11 -0
- package/dist/entities/resource-types.js +1 -0
- package/dist/entities/resource.d.ts +42 -0
- package/dist/entities/resource.js +303 -0
- package/dist/entities/stateful-parameter.d.ts +29 -0
- package/dist/entities/stateful-parameter.js +46 -0
- package/dist/entities/transform-parameter.d.ts +4 -0
- package/dist/entities/transform-parameter.js +2 -0
- package/dist/errors.d.ts +4 -0
- package/dist/errors.js +7 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +26 -0
- package/dist/messages/handlers.d.ts +14 -0
- package/dist/messages/handlers.js +134 -0
- package/dist/messages/sender.d.ts +11 -0
- package/dist/messages/sender.js +57 -0
- package/dist/plan/change-set.d.ts +53 -0
- package/dist/plan/change-set.js +153 -0
- package/dist/plan/plan-types.d.ts +23 -0
- package/dist/plan/plan-types.js +1 -0
- package/dist/plan/plan.d.ts +66 -0
- package/dist/plan/plan.js +328 -0
- package/dist/plugin/plugin.d.ts +24 -0
- package/dist/plugin/plugin.js +200 -0
- package/dist/pty/background-pty.d.ts +21 -0
- package/dist/pty/background-pty.js +127 -0
- package/dist/pty/index.d.ts +50 -0
- package/dist/pty/index.js +20 -0
- package/dist/pty/promise-queue.d.ts +5 -0
- package/dist/pty/promise-queue.js +26 -0
- package/dist/pty/seqeuntial-pty.d.ts +17 -0
- package/dist/pty/seqeuntial-pty.js +119 -0
- package/dist/pty/vitest.config.d.ts +2 -0
- package/dist/pty/vitest.config.js +11 -0
- package/dist/resource/config-parser.d.ts +11 -0
- package/dist/resource/config-parser.js +21 -0
- package/dist/resource/parsed-resource-settings.d.ts +47 -0
- package/dist/resource/parsed-resource-settings.js +196 -0
- package/dist/resource/resource-controller.d.ts +36 -0
- package/dist/resource/resource-controller.js +402 -0
- package/dist/resource/resource-settings.d.ts +303 -0
- package/dist/resource/resource-settings.js +147 -0
- package/dist/resource/resource.d.ts +144 -0
- package/dist/resource/resource.js +44 -0
- package/dist/resource/stateful-parameter.d.ts +165 -0
- package/dist/resource/stateful-parameter.js +94 -0
- package/dist/scripts/deploy.d.ts +1 -0
- package/dist/scripts/deploy.js +2 -0
- package/dist/stateful-parameter/stateful-parameter-controller.d.ts +21 -0
- package/dist/stateful-parameter/stateful-parameter-controller.js +81 -0
- package/dist/stateful-parameter/stateful-parameter.d.ts +144 -0
- package/dist/stateful-parameter/stateful-parameter.js +43 -0
- package/dist/test.d.ts +1 -0
- package/dist/test.js +5 -0
- package/dist/utils/codify-spawn.d.ts +29 -0
- package/dist/utils/codify-spawn.js +136 -0
- package/dist/utils/debug.d.ts +2 -0
- package/dist/utils/debug.js +10 -0
- package/dist/utils/file-utils.d.ts +23 -0
- package/dist/utils/file-utils.js +186 -0
- package/dist/utils/functions.d.ts +12 -0
- package/dist/utils/functions.js +74 -0
- package/dist/utils/index.d.ts +46 -0
- package/dist/utils/index.js +271 -0
- package/dist/utils/internal-utils.d.ts +12 -0
- package/dist/utils/internal-utils.js +74 -0
- package/dist/utils/load-resources.d.ts +1 -0
- package/dist/utils/load-resources.js +46 -0
- package/dist/utils/package-json-utils.d.ts +12 -0
- package/dist/utils/package-json-utils.js +34 -0
- package/dist/utils/pty-local-storage.d.ts +2 -0
- package/dist/utils/pty-local-storage.js +2 -0
- package/dist/utils/spawn-2.d.ts +5 -0
- package/dist/utils/spawn-2.js +7 -0
- package/dist/utils/spawn.d.ts +29 -0
- package/dist/utils/spawn.js +124 -0
- package/dist/utils/utils.d.ts +18 -0
- package/dist/utils/utils.js +86 -0
- package/dist/utils/verbosity-level.d.ts +5 -0
- package/dist/utils/verbosity-level.js +9 -0
- package/package.json +59 -0
- package/rollup.config.js +24 -0
- package/src/common/errors.test.ts +43 -0
- package/src/common/errors.ts +31 -0
- package/src/errors.ts +8 -0
- package/src/index.test.ts +6 -0
- package/src/index.ts +30 -0
- package/src/messages/handlers.test.ts +329 -0
- package/src/messages/handlers.ts +181 -0
- package/src/messages/sender.ts +69 -0
- package/src/plan/change-set.test.ts +280 -0
- package/src/plan/change-set.ts +236 -0
- package/src/plan/plan-types.ts +27 -0
- package/src/plan/plan.test.ts +413 -0
- package/src/plan/plan.ts +499 -0
- package/src/plugin/plugin.test.ts +533 -0
- package/src/plugin/plugin.ts +291 -0
- package/src/pty/background-pty.test.ts +69 -0
- package/src/pty/background-pty.ts +154 -0
- package/src/pty/index.test.ts +129 -0
- package/src/pty/index.ts +66 -0
- package/src/pty/promise-queue.ts +33 -0
- package/src/pty/seqeuntial-pty.ts +151 -0
- package/src/pty/sequential-pty.test.ts +194 -0
- package/src/resource/config-parser.ts +42 -0
- package/src/resource/parsed-resource-settings.test.ts +186 -0
- package/src/resource/parsed-resource-settings.ts +307 -0
- package/src/resource/resource-controller-stateful-mode.test.ts +253 -0
- package/src/resource/resource-controller.test.ts +1081 -0
- package/src/resource/resource-controller.ts +563 -0
- package/src/resource/resource-settings.test.ts +1213 -0
- package/src/resource/resource-settings.ts +545 -0
- package/src/resource/resource.ts +157 -0
- package/src/stateful-parameter/stateful-parameter-controller.test.ts +244 -0
- package/src/stateful-parameter/stateful-parameter-controller.ts +111 -0
- package/src/stateful-parameter/stateful-parameter.ts +160 -0
- package/src/utils/debug.ts +11 -0
- package/src/utils/file-utils.test.ts +7 -0
- package/src/utils/file-utils.ts +231 -0
- package/src/utils/functions.ts +103 -0
- package/src/utils/index.ts +340 -0
- package/src/utils/internal-utils.test.ts +52 -0
- package/src/utils/pty-local-storage.ts +3 -0
- package/src/utils/test-utils.test.ts +96 -0
- package/src/utils/verbosity-level.ts +11 -0
- package/tsconfig.json +26 -0
- package/tsconfig.test.json +9 -0
- package/vitest.config.ts +10 -0
package/.eslintignore
ADDED
package/.eslintrc.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": [
|
|
3
|
+
"oclif",
|
|
4
|
+
"oclif-typescript",
|
|
5
|
+
"prettier"
|
|
6
|
+
],
|
|
7
|
+
"rules": {
|
|
8
|
+
"object-curly-spacing": [
|
|
9
|
+
"warn",
|
|
10
|
+
"always"
|
|
11
|
+
],
|
|
12
|
+
"perfectionist/sort-classes": "off",
|
|
13
|
+
"perfectionist/sort-interfaces": "off",
|
|
14
|
+
"perfectionist/sort-enums": "off",
|
|
15
|
+
"perfectionist/sort-objects": "off",
|
|
16
|
+
"perfectionist/sort-object-types": "off",
|
|
17
|
+
"unicorn/no-array-reduce": "off",
|
|
18
|
+
"unicorn/no-array-for-each": "off",
|
|
19
|
+
"unicorn/prefer-object-from-entries": "off",
|
|
20
|
+
"unicorn/prefer-type-error": "off",
|
|
21
|
+
"quotes": [
|
|
22
|
+
"error",
|
|
23
|
+
"single"
|
|
24
|
+
],
|
|
25
|
+
"no-await-in-loop": "off"
|
|
26
|
+
},
|
|
27
|
+
"ignorePatterns": [
|
|
28
|
+
"*.test.ts"
|
|
29
|
+
]
|
|
30
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
|
|
2
|
+
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs
|
|
3
|
+
|
|
4
|
+
name: Release
|
|
5
|
+
|
|
6
|
+
on: workflow_dispatch
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
release:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
- uses: actions/setup-node@v4
|
|
14
|
+
with:
|
|
15
|
+
node-version: '20.x'
|
|
16
|
+
cache: 'npm'
|
|
17
|
+
- run: npm ci
|
|
18
|
+
- run: tsc
|
|
19
|
+
- run: npm publish
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
|
|
2
|
+
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs
|
|
3
|
+
|
|
4
|
+
name: Unit tests
|
|
5
|
+
|
|
6
|
+
on: [ push ]
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
build-and-test:
|
|
10
|
+
runs-on: macos-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
- uses: actions/setup-node@v4
|
|
14
|
+
with:
|
|
15
|
+
node-version: '20.x'
|
|
16
|
+
cache: 'npm'
|
|
17
|
+
- run: npm ci
|
|
18
|
+
- run: npm run test
|
package/.prettierrc.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"@oclif/prettier-config"
|
package/bin/build.js
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Ajv } from 'ajv';
|
|
3
|
+
import { IpcMessageSchema, MessageStatus, ResourceSchema } from 'codify-schemas';
|
|
4
|
+
import mergeJsonSchemas from 'merge-json-schemas';
|
|
5
|
+
import { fork } from 'node:child_process';
|
|
6
|
+
import fs from 'node:fs';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
|
|
9
|
+
import { SequentialPty, VerbosityLevel } from '../dist/index.js';
|
|
10
|
+
|
|
11
|
+
const ajv = new Ajv({
|
|
12
|
+
strict: true
|
|
13
|
+
});
|
|
14
|
+
const ipcMessageValidator = ajv.compile(IpcMessageSchema);
|
|
15
|
+
|
|
16
|
+
function sendMessageAndAwaitResponse(process, message) {
|
|
17
|
+
return new Promise((resolve, reject) => {
|
|
18
|
+
process.on('message', (response) => {
|
|
19
|
+
if (!ipcMessageValidator(response)) {
|
|
20
|
+
throw new Error(`Invalid message from plugin. ${JSON.stringify(message, null, 2)}`);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Wait for the message response. Other messages such as sudoRequest may be sent before the response returns
|
|
24
|
+
if (response.cmd === message.cmd + '_Response') {
|
|
25
|
+
if (response.status === MessageStatus.SUCCESS) {
|
|
26
|
+
resolve(response.data)
|
|
27
|
+
} else {
|
|
28
|
+
reject(new Error(String(response.data)))
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// Send message last to ensure listeners are all registered
|
|
34
|
+
process.send(message);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function fetchDocumentationMaps() {
|
|
39
|
+
console.log('Building documentation...');
|
|
40
|
+
|
|
41
|
+
const results = new Map();
|
|
42
|
+
const resourcesPath = path.resolve(process.cwd(), 'src', 'resources');
|
|
43
|
+
const resourcesDir = fs.readdirSync(resourcesPath);
|
|
44
|
+
|
|
45
|
+
for (const resource of resourcesDir) {
|
|
46
|
+
const resourcePath = path.join(resourcesPath, resource);
|
|
47
|
+
if (!isDirectory(resourcePath)) continue;
|
|
48
|
+
|
|
49
|
+
const contents = fs.readdirSync(resourcePath);
|
|
50
|
+
const isGroup = contents.some((content) => isDirectory(path.join(resourcePath, content)));
|
|
51
|
+
const isAllDir = contents.every((content) => isDirectory(path.join(resourcePath, content)));
|
|
52
|
+
|
|
53
|
+
if (isGroup && !isAllDir) {
|
|
54
|
+
throw new Error(`Documentation groups must only contain directories. ${resourcePath} does not`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (!isGroup) {
|
|
58
|
+
if (contents.includes('README.md')) {
|
|
59
|
+
results.set(resource, resource);
|
|
60
|
+
}
|
|
61
|
+
} else {
|
|
62
|
+
for (const innerDir of contents) {
|
|
63
|
+
const innerDirReadme = path.join(resourcePath, innerDir, 'README.md');
|
|
64
|
+
if (isFile(innerDirReadme)) {
|
|
65
|
+
results.set(innerDir, path.relative('./src/resources', path.join(resourcePath, innerDir)));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return results;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function isDirectory(path) {
|
|
75
|
+
try {
|
|
76
|
+
return fs.statSync(path).isDirectory();
|
|
77
|
+
} catch {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function isFile(path) {
|
|
83
|
+
try {
|
|
84
|
+
return fs.statSync(path).isFile();
|
|
85
|
+
} catch {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
VerbosityLevel.set(3);
|
|
91
|
+
const $ = new SequentialPty();
|
|
92
|
+
|
|
93
|
+
await $.spawn('rm -rf ./dist')
|
|
94
|
+
await $.spawn('npm run rollup -- -f es', { interactive: true });
|
|
95
|
+
|
|
96
|
+
const plugin = fork(
|
|
97
|
+
'./dist/index.js',
|
|
98
|
+
[],
|
|
99
|
+
{
|
|
100
|
+
// Use default true to test plugins in secure mode (un-able to request sudo directly)
|
|
101
|
+
detached: true,
|
|
102
|
+
env: { ...process.env },
|
|
103
|
+
execArgv: ['--import', 'tsx/esm'],
|
|
104
|
+
},
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
const initializeResult = await sendMessageAndAwaitResponse(plugin, {
|
|
108
|
+
cmd: 'initialize',
|
|
109
|
+
data: {}
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
const { resourceDefinitions } = initializeResult;
|
|
113
|
+
const resourceTypes = resourceDefinitions.map((i) => i.type);
|
|
114
|
+
const resourceInfoMap = new Map();
|
|
115
|
+
|
|
116
|
+
const schemasMap = new Map()
|
|
117
|
+
for (const type of resourceTypes) {
|
|
118
|
+
const resourceInfo = await sendMessageAndAwaitResponse(plugin, {
|
|
119
|
+
cmd: 'getResourceInfo',
|
|
120
|
+
data: { type }
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
schemasMap.set(type, resourceInfo.schema);
|
|
124
|
+
resourceInfoMap.set(type, resourceInfo);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
console.log(resourceInfoMap);
|
|
128
|
+
|
|
129
|
+
const mergedSchemas = [...schemasMap.entries()].map(([type, schema]) => {
|
|
130
|
+
// const resolvedSchema = await $RefParser.dereference(schema)
|
|
131
|
+
const resourceSchema = JSON.parse(JSON.stringify(ResourceSchema));
|
|
132
|
+
|
|
133
|
+
delete resourceSchema.$id;
|
|
134
|
+
delete resourceSchema.$schema;
|
|
135
|
+
delete resourceSchema.title;
|
|
136
|
+
delete resourceSchema.oneOf;
|
|
137
|
+
delete resourceSchema.properties.type;
|
|
138
|
+
|
|
139
|
+
if (schema) {
|
|
140
|
+
delete schema.$id;
|
|
141
|
+
delete schema.$schema;
|
|
142
|
+
delete schema.title;
|
|
143
|
+
delete schema.oneOf;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return mergeJsonSchemas([schema ?? {}, resourceSchema, { properties: { type: { const: type, type: 'string' } } }]);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
await $.spawn('rm -rf ./dist')
|
|
151
|
+
await $.spawn('npm run rollup', { interactive: true }); // re-run rollup without building for es
|
|
152
|
+
|
|
153
|
+
console.log('Generated JSON Schemas for all resources')
|
|
154
|
+
|
|
155
|
+
const distFolder = path.resolve(process.cwd(), 'dist');
|
|
156
|
+
const schemaOutputPath = path.resolve(distFolder, 'schemas.json');
|
|
157
|
+
fs.writeFileSync(schemaOutputPath, JSON.stringify(mergedSchemas, null, 2));
|
|
158
|
+
|
|
159
|
+
console.log('Successfully wrote schema to ./dist/schemas.json');
|
|
160
|
+
|
|
161
|
+
const documentationMap = fetchDocumentationMaps();
|
|
162
|
+
|
|
163
|
+
const packageJson = JSON.parse(fs.readFileSync('./package.json', 'utf8'));
|
|
164
|
+
|
|
165
|
+
fs.writeFileSync('./dist/manifest.json', JSON.stringify({
|
|
166
|
+
name: packageJson.name,
|
|
167
|
+
version: packageJson.version,
|
|
168
|
+
description: packageJson.description,
|
|
169
|
+
resources: [...resourceInfoMap.values()].map((info) => ({
|
|
170
|
+
type: info.type,
|
|
171
|
+
description: info.description ?? info.schema?.description,
|
|
172
|
+
sensitiveParameters: info.sensitiveParameters,
|
|
173
|
+
schema: info.schema,
|
|
174
|
+
operatingSystems: info.operatingSystems,
|
|
175
|
+
documentationKey: documentationMap.get(info.type),
|
|
176
|
+
})),
|
|
177
|
+
}, null, 2), 'utf8');
|
|
178
|
+
|
|
179
|
+
for (const key of documentationMap.values()) {
|
|
180
|
+
fs.mkdirSync(path.join('dist', 'documentation', key), { recursive: true })
|
|
181
|
+
|
|
182
|
+
fs.copyFileSync(
|
|
183
|
+
path.resolve(path.join('src', 'resources', key, 'README.md')),
|
|
184
|
+
path.resolve(path.join('dist', 'documentation', key, 'README.md')),
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
plugin.kill(9);
|
|
189
|
+
process.exit(0);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function build(): Promise<void>;
|
|
@@ -0,0 +1,80 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
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();
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export class ApplyValidationError extends Error {
|
|
2
|
+
resourceType;
|
|
3
|
+
resourceName;
|
|
4
|
+
plan;
|
|
5
|
+
constructor(plan) {
|
|
6
|
+
super(`Failed to apply changes to resource: "${plan.resourceId}". Additional changes are needed to complete apply.\nChanges remaining:\n${ApplyValidationError.prettyPrintPlan(plan)}`);
|
|
7
|
+
this.resourceType = plan.coreParameters.type;
|
|
8
|
+
this.resourceName = plan.coreParameters.name;
|
|
9
|
+
this.plan = plan;
|
|
10
|
+
}
|
|
11
|
+
static prettyPrintPlan(plan) {
|
|
12
|
+
const { operation, parameters } = plan.toResponse();
|
|
13
|
+
const prettyParameters = parameters.map(({ name, operation, previousValue, newValue }) => ({
|
|
14
|
+
name,
|
|
15
|
+
operation,
|
|
16
|
+
currentValue: previousValue,
|
|
17
|
+
desiredValue: newValue,
|
|
18
|
+
}));
|
|
19
|
+
return JSON.stringify({
|
|
20
|
+
operation,
|
|
21
|
+
parameters: prettyParameters,
|
|
22
|
+
}, null, 2);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
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
|
+
}
|