@terraforge/terraform 0.0.7 → 0.0.9
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/cli/build-package.ts +46 -44
- package/dist/index.d.ts +19 -19
- package/dist/index.js +458 -406
- package/package.json +9 -2
package/cli/build-package.ts
CHANGED
|
@@ -1,61 +1,69 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
|
|
3
|
-
import { parseArgs } from 'util'
|
|
4
3
|
import { createLazyPlugin } from '../src/lazy-plugin'
|
|
5
4
|
import { Version } from '../src/plugin/registry'
|
|
6
5
|
import { generateTypes } from '../src/type-gen'
|
|
7
6
|
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
},
|
|
17
|
-
},
|
|
18
|
-
strict: true,
|
|
19
|
-
allowPositionals: true,
|
|
20
|
-
})
|
|
7
|
+
const packageData = (await Bun.file('./package.json').json()) as {
|
|
8
|
+
version?: string
|
|
9
|
+
provider?: {
|
|
10
|
+
version?: Version
|
|
11
|
+
org?: string
|
|
12
|
+
type?: string
|
|
13
|
+
}
|
|
14
|
+
}
|
|
21
15
|
|
|
22
|
-
if (!
|
|
23
|
-
console.error('
|
|
16
|
+
if (!packageData) {
|
|
17
|
+
console.error('Failed to read package.json')
|
|
24
18
|
process.exit(1)
|
|
25
19
|
}
|
|
26
20
|
|
|
27
|
-
|
|
28
|
-
|
|
21
|
+
const providerData = packageData.provider
|
|
22
|
+
|
|
23
|
+
if (!providerData) {
|
|
24
|
+
console.error('Missing required property: provider')
|
|
29
25
|
process.exit(1)
|
|
30
26
|
}
|
|
31
27
|
|
|
32
|
-
|
|
28
|
+
if (!providerData.version) {
|
|
29
|
+
console.error('Missing required property: provider.version')
|
|
30
|
+
process.exit(1)
|
|
31
|
+
}
|
|
33
32
|
|
|
34
|
-
if (!
|
|
35
|
-
console.error('
|
|
33
|
+
if (!providerData.org) {
|
|
34
|
+
console.error('Missing required property: provider.org')
|
|
36
35
|
process.exit(1)
|
|
37
36
|
}
|
|
38
37
|
|
|
39
|
-
if (!
|
|
40
|
-
console.error('Missing required
|
|
38
|
+
if (!providerData.type) {
|
|
39
|
+
console.error('Missing required property: provider.type')
|
|
41
40
|
process.exit(1)
|
|
42
41
|
}
|
|
43
42
|
|
|
44
|
-
const org =
|
|
45
|
-
const type =
|
|
46
|
-
const version =
|
|
43
|
+
const org = providerData.org
|
|
44
|
+
const type = providerData.type
|
|
45
|
+
const version = providerData.version
|
|
47
46
|
|
|
48
|
-
console.log('
|
|
49
|
-
console.log('
|
|
50
|
-
console.log('
|
|
47
|
+
console.log('')
|
|
48
|
+
console.log('Package version: ', packageData.version)
|
|
49
|
+
console.log('')
|
|
50
|
+
console.log('Provider org: ', org)
|
|
51
|
+
console.log('Provider type: ', type)
|
|
52
|
+
console.log('Provider version: ', version)
|
|
53
|
+
console.log('')
|
|
51
54
|
|
|
52
55
|
const ok = confirm('Continue?')
|
|
53
56
|
|
|
54
57
|
if (!ok) {
|
|
55
|
-
|
|
58
|
+
console.log('')
|
|
59
|
+
process.exit(1)
|
|
56
60
|
}
|
|
57
61
|
|
|
58
62
|
const load = createLazyPlugin({ org, type, version })
|
|
63
|
+
|
|
64
|
+
console.log('')
|
|
65
|
+
console.log('Loading provider plugin...')
|
|
66
|
+
|
|
59
67
|
const plugin = await load()
|
|
60
68
|
const schema = plugin.schema()
|
|
61
69
|
const types = generateTypes(
|
|
@@ -68,24 +76,18 @@ const types = generateTypes(
|
|
|
68
76
|
|
|
69
77
|
await plugin.stop()
|
|
70
78
|
|
|
71
|
-
await Bun.write(`./
|
|
72
|
-
|
|
79
|
+
await Bun.write(`./dist/index.d.ts`, types)
|
|
73
80
|
await Bun.write(
|
|
74
|
-
`./
|
|
81
|
+
`./dist/index.js`,
|
|
75
82
|
`
|
|
76
|
-
import {
|
|
77
|
-
import { root } from './types.ts'
|
|
83
|
+
import { createTerraformProxy } from '@terraforge/terraform'
|
|
78
84
|
|
|
79
|
-
|
|
80
|
-
export const ${type} = createTerraformAPI<typeof root.${type}>({
|
|
85
|
+
export const ${type} = createTerraformProxy({
|
|
81
86
|
namespace: '${type}',
|
|
82
87
|
provider: { org: '${org}', type: '${type}', version: '${version}' },
|
|
83
|
-
})
|
|
84
|
-
|
|
85
|
-
declare module '@terraforge/${type}' {
|
|
86
|
-
import ${type} = root.${type}
|
|
87
|
-
// @ts-ignore
|
|
88
|
-
export { ${type} }
|
|
89
|
-
}
|
|
88
|
+
})
|
|
90
89
|
`
|
|
91
90
|
)
|
|
91
|
+
|
|
92
|
+
console.log('Done.')
|
|
93
|
+
process.exit(0)
|
package/dist/index.d.ts
CHANGED
|
@@ -1,23 +1,5 @@
|
|
|
1
1
|
import { Provider, State as State$1, GetProps, CreateProps, UpdateProps, DeleteProps, GetDataProps } from '@terraforge/core';
|
|
2
2
|
|
|
3
|
-
type Version = `${number}.${number}.${number}` | 'latest';
|
|
4
|
-
|
|
5
|
-
type TerraformProviderConfig = {
|
|
6
|
-
id?: string;
|
|
7
|
-
location?: string;
|
|
8
|
-
};
|
|
9
|
-
type InstallProps = {
|
|
10
|
-
location?: string;
|
|
11
|
-
};
|
|
12
|
-
declare const createTerraformAPI: <T>(props: {
|
|
13
|
-
namespace: string;
|
|
14
|
-
provider: {
|
|
15
|
-
org: string;
|
|
16
|
-
type: string;
|
|
17
|
-
version: Version;
|
|
18
|
-
};
|
|
19
|
-
}) => T;
|
|
20
|
-
|
|
21
3
|
type Property = {
|
|
22
4
|
description?: string;
|
|
23
5
|
required?: boolean;
|
|
@@ -89,4 +71,22 @@ declare class TerraformProvider implements Provider {
|
|
|
89
71
|
}>;
|
|
90
72
|
}
|
|
91
73
|
|
|
92
|
-
|
|
74
|
+
type Version = `${number}.${number}.${number}` | 'latest';
|
|
75
|
+
|
|
76
|
+
type TerraformProviderConfig = {
|
|
77
|
+
id?: string;
|
|
78
|
+
location?: string;
|
|
79
|
+
};
|
|
80
|
+
type InstallProps = {
|
|
81
|
+
location?: string;
|
|
82
|
+
};
|
|
83
|
+
declare const createTerraformProxy: (props: {
|
|
84
|
+
namespace: string;
|
|
85
|
+
provider: {
|
|
86
|
+
org: string;
|
|
87
|
+
type: string;
|
|
88
|
+
version: Version;
|
|
89
|
+
};
|
|
90
|
+
}) => () => void;
|
|
91
|
+
|
|
92
|
+
export { type InstallProps, TerraformProvider, type TerraformProviderConfig, createTerraformProxy, generateTypes };
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,319 @@
|
|
|
1
|
+
// src/type-gen.ts
|
|
2
|
+
import { camelCase, pascalCase } from "change-case";
|
|
3
|
+
var tab = (indent) => {
|
|
4
|
+
return " ".repeat(indent);
|
|
5
|
+
};
|
|
6
|
+
var generateTypes = (providers, resources, dataSources) => {
|
|
7
|
+
return [
|
|
8
|
+
generateImport("c", "@terraforge/core"),
|
|
9
|
+
generateImport("t", "@terraforge/terraform"),
|
|
10
|
+
"type _Record<T> = Record<string, T>",
|
|
11
|
+
generateInstallHelperFunctions(providers),
|
|
12
|
+
generateNamespace(providers, (name, prop, indent) => {
|
|
13
|
+
const typeName = name.toLowerCase();
|
|
14
|
+
return `${tab(indent)}export declare function ${typeName}(props: ${generatePropertyInputConst(prop, indent)}, config?: t.TerraformProviderConfig): t.TerraformProvider`;
|
|
15
|
+
}),
|
|
16
|
+
generateNamespace(resources, (name, prop, indent) => {
|
|
17
|
+
const typeName = pascalCase(name);
|
|
18
|
+
return [
|
|
19
|
+
// `${tab(indent)}export type ${typeName}Input = ${generatePropertyInputType(prop, indent)}`,
|
|
20
|
+
// `${tab(indent)}export type ${typeName}Output = ${generatePropertyOutputType(prop, indent)}`,
|
|
21
|
+
// `${tab(indent)}export declare const ${typeName}: ResourceClass<${typeName}Input, ${typeName}Output>`,
|
|
22
|
+
`${tab(indent)}export type ${typeName}Input = ${generatePropertyInputType(prop, indent)}`,
|
|
23
|
+
`${tab(indent)}export type ${typeName}Output = ${generatePropertyOutputType(prop, indent)}`,
|
|
24
|
+
`${tab(indent)}export class ${typeName} {`,
|
|
25
|
+
`${tab(indent + 1)}constructor(parent: c.Group, id: string, props: ${typeName}Input, config?:c.ResourceConfig)`,
|
|
26
|
+
// `${tab(indent + 1)}readonly $: c.ResourceMeta<${typeName}Input, ${typeName}Output>`,
|
|
27
|
+
generateClassProperties(prop, indent + 1),
|
|
28
|
+
`${tab(indent)}}`
|
|
29
|
+
].join("\n\n");
|
|
30
|
+
}),
|
|
31
|
+
generateNamespace(dataSources, (name, prop, indent) => {
|
|
32
|
+
const typeName = pascalCase(name);
|
|
33
|
+
return [
|
|
34
|
+
`${tab(indent)}export type Get${typeName}Input = ${generatePropertyInputType(prop, indent)}`,
|
|
35
|
+
`${tab(indent)}export type Get${typeName}Output = ${generatePropertyOutputType(prop, indent)}`,
|
|
36
|
+
`${tab(indent)}export const get${typeName}:c.DataSourceFunction<Get${typeName}Input, Get${typeName}Output>`
|
|
37
|
+
].join("\n\n");
|
|
38
|
+
})
|
|
39
|
+
].join("\n\n");
|
|
40
|
+
};
|
|
41
|
+
var generateImport = (name, from) => {
|
|
42
|
+
return `import * as ${name} from '${from}'`;
|
|
43
|
+
};
|
|
44
|
+
var generateInstallHelperFunctions = (resources) => {
|
|
45
|
+
return generateNamespace(resources, (name, _prop, indent) => {
|
|
46
|
+
const typeName = name.toLowerCase();
|
|
47
|
+
return [
|
|
48
|
+
`${tab(indent)}export declare namespace ${typeName} {`,
|
|
49
|
+
`${tab(indent + 1)}export function install(props?: t.InstallProps): Promise<void>`,
|
|
50
|
+
`${tab(indent + 1)}export function uninstall(props?: t.InstallProps): Promise<void>`,
|
|
51
|
+
`${tab(indent + 1)}export function isInstalled(props?: t.InstallProps): Promise<boolean>`,
|
|
52
|
+
`${tab(indent)}}`
|
|
53
|
+
].join("\n");
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
var generatePropertyInputConst = (prop, indent) => {
|
|
57
|
+
return generateValue(prop, {
|
|
58
|
+
depth: 0,
|
|
59
|
+
indent: indent + 1,
|
|
60
|
+
wrap: (v, _, ctx) => {
|
|
61
|
+
return `${v}${ctx.depth === 1 ? "," : ""}`;
|
|
62
|
+
},
|
|
63
|
+
filter: () => true,
|
|
64
|
+
optional: (p) => p.optional ?? false
|
|
65
|
+
});
|
|
66
|
+
};
|
|
67
|
+
var generatePropertyInputType = (prop, indent) => {
|
|
68
|
+
return generateValue(prop, {
|
|
69
|
+
depth: 0,
|
|
70
|
+
indent: indent + 1,
|
|
71
|
+
wrap: (v, p, ctx) => {
|
|
72
|
+
return ctx.depth > 0 ? p.optional ? `c.OptionalInput<${v}>` : `c.Input<${v}>` : v;
|
|
73
|
+
},
|
|
74
|
+
filter: (prop2) => !(prop2.computed && typeof prop2.optional === "undefined" && typeof prop2.required === "undefined"),
|
|
75
|
+
optional: (p) => p.optional ?? false
|
|
76
|
+
});
|
|
77
|
+
};
|
|
78
|
+
var generatePropertyOutputType = (prop, indent) => {
|
|
79
|
+
return generateValue(prop, {
|
|
80
|
+
depth: 0,
|
|
81
|
+
indent: indent + 1,
|
|
82
|
+
wrap: (v, p, ctx) => ctx.depth === 1 ? p.optional && !p.computed ? `c.OptionalOutput<${v}>` : `c.Output<${v}>` : v,
|
|
83
|
+
filter: () => true,
|
|
84
|
+
readonly: true,
|
|
85
|
+
// required: true,
|
|
86
|
+
optional: (p, ctx) => ctx.depth > 1 && p.optional && !p.computed || false
|
|
87
|
+
});
|
|
88
|
+
};
|
|
89
|
+
var generateClassProperties = (prop, indent) => {
|
|
90
|
+
if (prop.type !== "object") {
|
|
91
|
+
return "";
|
|
92
|
+
}
|
|
93
|
+
return Object.entries(prop.properties).map(([name, prop2]) => {
|
|
94
|
+
return [
|
|
95
|
+
prop2.description ? [`
|
|
96
|
+
`, ` `.repeat(indent), `/** `, prop2.description.trim(), " */", "\n"].join("") : "",
|
|
97
|
+
` `.repeat(indent),
|
|
98
|
+
"readonly ",
|
|
99
|
+
camelCase(name),
|
|
100
|
+
// ctx.optional(prop, ctx) ? '?' : '',
|
|
101
|
+
": ",
|
|
102
|
+
generateValue(prop2, {
|
|
103
|
+
readonly: true,
|
|
104
|
+
filter: () => true,
|
|
105
|
+
optional: (p, ctx) => ctx.depth > 1 && p.optional && !p.computed || false,
|
|
106
|
+
wrap: (v, p, ctx) => {
|
|
107
|
+
return ctx.depth === 1 ? p.optional && !p.computed ? `c.OptionalOutput<${v}>` : `c.Output<${v}>` : v;
|
|
108
|
+
},
|
|
109
|
+
// ctx.depth === 1 ? `c.Output<${p.optional && !p.computed ? `${v} | undefined` : v}>` : v,
|
|
110
|
+
indent: indent + 1,
|
|
111
|
+
depth: 1
|
|
112
|
+
})
|
|
113
|
+
].join("");
|
|
114
|
+
}).join("\n");
|
|
115
|
+
};
|
|
116
|
+
var groupByNamespace = (resources, minLevel, maxLevel) => {
|
|
117
|
+
const grouped = {};
|
|
118
|
+
const types = Object.keys(resources).sort();
|
|
119
|
+
for (const type of types) {
|
|
120
|
+
const names = type.split("_");
|
|
121
|
+
if (names.length < minLevel) {
|
|
122
|
+
throw new Error(`Resource not properly namespaced: ${type}`);
|
|
123
|
+
}
|
|
124
|
+
let current = grouped;
|
|
125
|
+
let count = Math.min(maxLevel, names.length - 1);
|
|
126
|
+
while (count--) {
|
|
127
|
+
const ns = camelCase(names.shift());
|
|
128
|
+
if (!current[ns]) {
|
|
129
|
+
current[ns] = {};
|
|
130
|
+
}
|
|
131
|
+
current = current[ns];
|
|
132
|
+
}
|
|
133
|
+
const name = pascalCase(names.join("_"));
|
|
134
|
+
current[name] = type;
|
|
135
|
+
}
|
|
136
|
+
return grouped;
|
|
137
|
+
};
|
|
138
|
+
var generateNamespace = (resources, render) => {
|
|
139
|
+
const grouped = groupByNamespace(resources, 1, 2);
|
|
140
|
+
const renderNamespace = (name, group, indent) => {
|
|
141
|
+
if (name === "default") {
|
|
142
|
+
name = "$default";
|
|
143
|
+
}
|
|
144
|
+
if (typeof group === "string") {
|
|
145
|
+
return render(name, resources[group], indent);
|
|
146
|
+
}
|
|
147
|
+
return [
|
|
148
|
+
`${tab(indent)}export ${indent === 0 ? "declare " : ""}namespace ${name.toLowerCase()} {`,
|
|
149
|
+
Object.entries(group).map(([name2, entry]) => {
|
|
150
|
+
if (typeof entry !== "string") {
|
|
151
|
+
return renderNamespace(name2, entry, indent + 1);
|
|
152
|
+
} else {
|
|
153
|
+
return render(name2, resources[entry], indent + 1);
|
|
154
|
+
}
|
|
155
|
+
}).join("\n"),
|
|
156
|
+
`${tab(indent)}}`
|
|
157
|
+
].join("\n");
|
|
158
|
+
};
|
|
159
|
+
return Object.entries(grouped).map(([name, entry]) => {
|
|
160
|
+
return renderNamespace(name, entry, 0);
|
|
161
|
+
});
|
|
162
|
+
};
|
|
163
|
+
var generateValue = (prop, ctx) => {
|
|
164
|
+
if (["string", "number", "boolean", "unknown"].includes(prop.type)) {
|
|
165
|
+
return ctx.wrap(prop.type, prop, ctx);
|
|
166
|
+
}
|
|
167
|
+
if (prop.type === "array") {
|
|
168
|
+
const type = generateValue(prop.item, { ...ctx, depth: ctx.depth + 1 });
|
|
169
|
+
const array = ctx.readonly ? `ReadonlyArray<${type}>` : `Array<${type}>`;
|
|
170
|
+
return ctx.wrap(array, prop, ctx);
|
|
171
|
+
}
|
|
172
|
+
if (prop.type === "record") {
|
|
173
|
+
const type = generateValue(prop.item, { ...ctx, depth: ctx.depth + 1 });
|
|
174
|
+
const record = ctx.readonly ? `Readonly<_Record<${type}>>` : `_Record<${type}>`;
|
|
175
|
+
return ctx.wrap(record, prop, ctx);
|
|
176
|
+
}
|
|
177
|
+
if (prop.type === "object" || prop.type === "array-object") {
|
|
178
|
+
const type = [
|
|
179
|
+
"{",
|
|
180
|
+
Object.entries(prop.properties).filter(([_, p]) => ctx.filter(p)).map(
|
|
181
|
+
([name, prop2]) => [
|
|
182
|
+
prop2.description ? [`
|
|
183
|
+
`, ` `.repeat(ctx.indent), `/** `, prop2.description.trim(), " */", "\n"].join("") : "",
|
|
184
|
+
` `.repeat(ctx.indent),
|
|
185
|
+
// ctx.readonly ? "readonly " : "",
|
|
186
|
+
camelCase(name),
|
|
187
|
+
ctx.optional(prop2, ctx) ? "?" : "",
|
|
188
|
+
": ",
|
|
189
|
+
generateValue(prop2, { ...ctx, indent: ctx.indent + 1, depth: ctx.depth + 1 })
|
|
190
|
+
].join("")
|
|
191
|
+
).join("\n"),
|
|
192
|
+
`${` `.repeat(ctx.indent - 1)}}`
|
|
193
|
+
].join("\n");
|
|
194
|
+
const object = ctx.readonly ? `Readonly<${type}>` : type;
|
|
195
|
+
return ctx.wrap(object, prop, ctx);
|
|
196
|
+
}
|
|
197
|
+
throw new Error(`Unknown property type: ${prop.type}`);
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
// src/provider.ts
|
|
201
|
+
import {
|
|
202
|
+
ResourceNotFound
|
|
203
|
+
} from "@terraforge/core";
|
|
204
|
+
var TerraformProvider = class {
|
|
205
|
+
constructor(type, id, createPlugin, config) {
|
|
206
|
+
this.type = type;
|
|
207
|
+
this.id = id;
|
|
208
|
+
this.createPlugin = createPlugin;
|
|
209
|
+
this.config = config;
|
|
210
|
+
}
|
|
211
|
+
configured;
|
|
212
|
+
plugin;
|
|
213
|
+
async configure() {
|
|
214
|
+
const plugin = await this.prepare();
|
|
215
|
+
if (!this.configured) {
|
|
216
|
+
this.configured = plugin.configure(this.config);
|
|
217
|
+
}
|
|
218
|
+
await this.configured;
|
|
219
|
+
return plugin;
|
|
220
|
+
}
|
|
221
|
+
prepare() {
|
|
222
|
+
if (!this.plugin) {
|
|
223
|
+
this.plugin = this.createPlugin();
|
|
224
|
+
}
|
|
225
|
+
return this.plugin;
|
|
226
|
+
}
|
|
227
|
+
async destroy() {
|
|
228
|
+
if (this.plugin) {
|
|
229
|
+
const plugin = await this.plugin;
|
|
230
|
+
plugin.stop();
|
|
231
|
+
this.plugin = void 0;
|
|
232
|
+
this.configured = void 0;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
ownResource(id) {
|
|
236
|
+
return `terraform:${this.type}:${this.id}` === id;
|
|
237
|
+
}
|
|
238
|
+
async getResource({ type, state }) {
|
|
239
|
+
const plugin = await this.configure();
|
|
240
|
+
const newState = await plugin.readResource(type, state);
|
|
241
|
+
if (!newState) {
|
|
242
|
+
throw new ResourceNotFound();
|
|
243
|
+
}
|
|
244
|
+
return {
|
|
245
|
+
version: 0,
|
|
246
|
+
state: newState
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
async createResource({ type, state }) {
|
|
250
|
+
const plugin = await this.configure();
|
|
251
|
+
const newState = await plugin.applyResourceChange(type, null, state);
|
|
252
|
+
return {
|
|
253
|
+
version: 0,
|
|
254
|
+
state: newState
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
async updateResource({ type, priorState, proposedState }) {
|
|
258
|
+
const plugin = await this.configure();
|
|
259
|
+
const { requiresReplace } = await plugin.planResourceChange(type, priorState, proposedState);
|
|
260
|
+
if (requiresReplace.length > 0) {
|
|
261
|
+
const formattedAttrs = requiresReplace.map((p) => p.join(".")).join('", "');
|
|
262
|
+
throw new Error(
|
|
263
|
+
`Updating the "${formattedAttrs}" properties for the "${type}" resource will require the resource to be replaced.`
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
const newState = await plugin.applyResourceChange(type, priorState, proposedState);
|
|
267
|
+
return {
|
|
268
|
+
version: 0,
|
|
269
|
+
state: newState
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
async deleteResource({ type, state }) {
|
|
273
|
+
const plugin = await this.configure();
|
|
274
|
+
try {
|
|
275
|
+
await plugin.applyResourceChange(type, state, null);
|
|
276
|
+
} catch (error) {
|
|
277
|
+
try {
|
|
278
|
+
const newState = await plugin.readResource(type, state);
|
|
279
|
+
if (!newState) {
|
|
280
|
+
throw new ResourceNotFound();
|
|
281
|
+
}
|
|
282
|
+
} catch (_) {
|
|
283
|
+
}
|
|
284
|
+
throw error;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
async getData({ type, state }) {
|
|
288
|
+
const plugin = await this.configure();
|
|
289
|
+
const data = await plugin.readDataSource(type, state);
|
|
290
|
+
if (!data) {
|
|
291
|
+
throw new Error(`Data source not found ${type}`);
|
|
292
|
+
}
|
|
293
|
+
return {
|
|
294
|
+
state: data
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
// async generateTypes(dir: string) {
|
|
298
|
+
// const plugin = await this.prepare()
|
|
299
|
+
// const schema = plugin.schema()
|
|
300
|
+
// const types = generateTypes(
|
|
301
|
+
// {
|
|
302
|
+
// [`${this.type}_provider`]: schema.provider,
|
|
303
|
+
// },
|
|
304
|
+
// schema.resources,
|
|
305
|
+
// schema.dataSources
|
|
306
|
+
// )
|
|
307
|
+
// await mkdir(dir, { recursive: true })
|
|
308
|
+
// await writeFile(join(dir, `${this.type}.d.ts`), types)
|
|
309
|
+
// await this.destroy()
|
|
310
|
+
// }
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
// src/proxy.ts
|
|
314
|
+
import { createMeta, nodeMetaSymbol } from "@terraforge/core";
|
|
315
|
+
import { snakeCase as snakeCase2 } from "change-case";
|
|
316
|
+
|
|
1
317
|
// src/plugin/client.ts
|
|
2
318
|
import { credentials, loadPackageDefinition } from "@grpc/grpc-js";
|
|
3
319
|
import { fromJSON } from "@grpc/proto-loader";
|
|
@@ -737,9 +1053,9 @@ var createPluginClient = async (props) => {
|
|
|
737
1053
|
// src/plugin/download.ts
|
|
738
1054
|
import { createDebugger as createDebugger2 } from "@terraforge/core";
|
|
739
1055
|
import jszip from "jszip";
|
|
740
|
-
import { mkdir, stat, writeFile } from "fs/promises";
|
|
1056
|
+
import { mkdir, rm, stat, writeFile } from "fs/promises";
|
|
741
1057
|
import { homedir } from "os";
|
|
742
|
-
import { join } from "path";
|
|
1058
|
+
import { dirname, join } from "path";
|
|
743
1059
|
|
|
744
1060
|
// src/plugin/registry.ts
|
|
745
1061
|
import { arch, platform } from "os";
|
|
@@ -826,15 +1142,33 @@ var exists = async (file) => {
|
|
|
826
1142
|
};
|
|
827
1143
|
var debug2 = createDebugger2("Downloader");
|
|
828
1144
|
var installPath = join(homedir(), ".terraforge", "plugins");
|
|
1145
|
+
var getInstallPath = (props) => {
|
|
1146
|
+
const dir = props.location ?? installPath;
|
|
1147
|
+
const file = join(dir, `${props.org}-${props.type}-${props.version}`);
|
|
1148
|
+
return file;
|
|
1149
|
+
};
|
|
1150
|
+
var isPluginInstalled = (props) => {
|
|
1151
|
+
return exists(getInstallPath(props));
|
|
1152
|
+
};
|
|
1153
|
+
var deletePlugin = async (props) => {
|
|
1154
|
+
const file = getInstallPath(props);
|
|
1155
|
+
const isAlreadyInstalled = await isPluginInstalled(props);
|
|
1156
|
+
if (isAlreadyInstalled) {
|
|
1157
|
+
debug2(props.type, "deleting...");
|
|
1158
|
+
await rm(file);
|
|
1159
|
+
debug2(props.type, "deleted");
|
|
1160
|
+
} else {
|
|
1161
|
+
debug2(props.type, "not installed");
|
|
1162
|
+
}
|
|
1163
|
+
};
|
|
829
1164
|
var downloadPlugin = async (props) => {
|
|
830
1165
|
if (props.version === "latest") {
|
|
831
1166
|
const { latest } = await getProviderVersions(props.org, props.type);
|
|
832
1167
|
props.version = latest;
|
|
833
1168
|
}
|
|
834
|
-
const
|
|
835
|
-
const
|
|
836
|
-
|
|
837
|
-
if (!exist) {
|
|
1169
|
+
const file = getInstallPath(props);
|
|
1170
|
+
const isAlreadyInstalled = await isPluginInstalled(props);
|
|
1171
|
+
if (!isAlreadyInstalled) {
|
|
838
1172
|
debug2(props.type, "downloading...");
|
|
839
1173
|
const info = await getProviderDownloadUrl(props.org, props.type, props.version);
|
|
840
1174
|
const res = await fetch(info.url);
|
|
@@ -846,7 +1180,7 @@ var downloadPlugin = async (props) => {
|
|
|
846
1180
|
}
|
|
847
1181
|
const binary = await zipped.async("nodebuffer");
|
|
848
1182
|
debug2(props.type, "done");
|
|
849
|
-
await mkdir(
|
|
1183
|
+
await mkdir(dirname(file), { recursive: true });
|
|
850
1184
|
await writeFile(file, binary, {
|
|
851
1185
|
mode: 509
|
|
852
1186
|
});
|
|
@@ -1090,7 +1424,7 @@ var parseType = (type) => {
|
|
|
1090
1424
|
};
|
|
1091
1425
|
|
|
1092
1426
|
// src/plugin/version/util.ts
|
|
1093
|
-
import { camelCase, snakeCase } from "change-case";
|
|
1427
|
+
import { camelCase as camelCase2, snakeCase } from "change-case";
|
|
1094
1428
|
import { pack, unpack } from "msgpackr";
|
|
1095
1429
|
var encodeDynamicValue = (value) => {
|
|
1096
1430
|
return {
|
|
@@ -1184,7 +1518,7 @@ var formatInputState = (schema, state, includeSchemaFields = true, path = []) =>
|
|
|
1184
1518
|
const object = {};
|
|
1185
1519
|
if (includeSchemaFields) {
|
|
1186
1520
|
for (const [key, prop] of Object.entries(schema.properties)) {
|
|
1187
|
-
const value = state[
|
|
1521
|
+
const value = state[camelCase2(key)];
|
|
1188
1522
|
object[key] = formatInputState(prop, value, true, [...path, key]);
|
|
1189
1523
|
}
|
|
1190
1524
|
} else {
|
|
@@ -1229,7 +1563,7 @@ var formatOutputState = (schema, state, path = []) => {
|
|
|
1229
1563
|
const object = {};
|
|
1230
1564
|
for (const [key, prop] of Object.entries(schema.properties)) {
|
|
1231
1565
|
const value = state[key];
|
|
1232
|
-
object[
|
|
1566
|
+
object[camelCase2(key)] = formatOutputState(prop, value, [...path, key]);
|
|
1233
1567
|
}
|
|
1234
1568
|
return object;
|
|
1235
1569
|
}
|
|
@@ -1241,7 +1575,7 @@ var formatOutputState = (schema, state, path = []) => {
|
|
|
1241
1575
|
const object = {};
|
|
1242
1576
|
for (const [key, prop] of Object.entries(schema.properties)) {
|
|
1243
1577
|
const value = state[0][key];
|
|
1244
|
-
object[
|
|
1578
|
+
object[camelCase2(key)] = formatOutputState(prop, value, [...path, key]);
|
|
1245
1579
|
}
|
|
1246
1580
|
return object;
|
|
1247
1581
|
} else {
|
|
@@ -1474,133 +1808,37 @@ var retry = async (tries, cb) => {
|
|
|
1474
1808
|
throw latestError;
|
|
1475
1809
|
};
|
|
1476
1810
|
|
|
1477
|
-
// src/
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
}
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
configured;
|
|
1489
|
-
plugin;
|
|
1490
|
-
async configure() {
|
|
1491
|
-
const plugin = await this.prepare();
|
|
1492
|
-
if (!this.configured) {
|
|
1493
|
-
this.configured = plugin.configure(this.config);
|
|
1494
|
-
}
|
|
1495
|
-
await this.configured;
|
|
1496
|
-
return plugin;
|
|
1497
|
-
}
|
|
1498
|
-
prepare() {
|
|
1499
|
-
if (!this.plugin) {
|
|
1500
|
-
this.plugin = this.createPlugin();
|
|
1501
|
-
}
|
|
1502
|
-
return this.plugin;
|
|
1503
|
-
}
|
|
1504
|
-
async destroy() {
|
|
1505
|
-
if (this.plugin) {
|
|
1506
|
-
const plugin = await this.plugin;
|
|
1507
|
-
plugin.stop();
|
|
1508
|
-
this.plugin = void 0;
|
|
1509
|
-
this.configured = void 0;
|
|
1510
|
-
}
|
|
1511
|
-
}
|
|
1512
|
-
ownResource(id) {
|
|
1513
|
-
return `terraform:${this.type}:${this.id}` === id;
|
|
1514
|
-
}
|
|
1515
|
-
async getResource({ type, state }) {
|
|
1516
|
-
const plugin = await this.configure();
|
|
1517
|
-
const newState = await plugin.readResource(type, state);
|
|
1518
|
-
if (!newState) {
|
|
1519
|
-
throw new ResourceNotFound();
|
|
1520
|
-
}
|
|
1521
|
-
return {
|
|
1522
|
-
version: 0,
|
|
1523
|
-
state: newState
|
|
1524
|
-
};
|
|
1525
|
-
}
|
|
1526
|
-
async createResource({ type, state }) {
|
|
1527
|
-
const plugin = await this.configure();
|
|
1528
|
-
const newState = await plugin.applyResourceChange(type, null, state);
|
|
1529
|
-
return {
|
|
1530
|
-
version: 0,
|
|
1531
|
-
state: newState
|
|
1532
|
-
};
|
|
1533
|
-
}
|
|
1534
|
-
async updateResource({ type, priorState, proposedState }) {
|
|
1535
|
-
const plugin = await this.configure();
|
|
1536
|
-
const { requiresReplace } = await plugin.planResourceChange(type, priorState, proposedState);
|
|
1537
|
-
if (requiresReplace.length > 0) {
|
|
1538
|
-
const formattedAttrs = requiresReplace.map((p) => p.join(".")).join('", "');
|
|
1539
|
-
throw new Error(
|
|
1540
|
-
`Updating the "${formattedAttrs}" properties for the "${type}" resource will require the resource to be replaced.`
|
|
1541
|
-
);
|
|
1542
|
-
}
|
|
1543
|
-
const newState = await plugin.applyResourceChange(type, priorState, proposedState);
|
|
1544
|
-
return {
|
|
1545
|
-
version: 0,
|
|
1546
|
-
state: newState
|
|
1547
|
-
};
|
|
1548
|
-
}
|
|
1549
|
-
async deleteResource({ type, state }) {
|
|
1550
|
-
const plugin = await this.configure();
|
|
1551
|
-
try {
|
|
1552
|
-
await plugin.applyResourceChange(type, state, null);
|
|
1553
|
-
} catch (error) {
|
|
1554
|
-
try {
|
|
1555
|
-
const newState = await plugin.readResource(type, state);
|
|
1556
|
-
if (!newState) {
|
|
1557
|
-
throw new ResourceNotFound();
|
|
1811
|
+
// src/proxy.ts
|
|
1812
|
+
var createResourceProxy = (cb) => {
|
|
1813
|
+
return new Proxy(
|
|
1814
|
+
{},
|
|
1815
|
+
{
|
|
1816
|
+
get(_, key) {
|
|
1817
|
+
return cb(key);
|
|
1818
|
+
},
|
|
1819
|
+
set(_, key) {
|
|
1820
|
+
if (typeof key === "string") {
|
|
1821
|
+
throw new Error(`Cannot set property ${key} on read-only object.`);
|
|
1558
1822
|
}
|
|
1559
|
-
|
|
1823
|
+
throw new Error(`This object is read-only.`);
|
|
1560
1824
|
}
|
|
1561
|
-
throw error;
|
|
1562
1825
|
}
|
|
1563
|
-
|
|
1564
|
-
async getData({ type, state }) {
|
|
1565
|
-
const plugin = await this.configure();
|
|
1566
|
-
const data = await plugin.readDataSource(type, state);
|
|
1567
|
-
if (!data) {
|
|
1568
|
-
throw new Error(`Data source not found ${type}`);
|
|
1569
|
-
}
|
|
1570
|
-
return {
|
|
1571
|
-
state: data
|
|
1572
|
-
};
|
|
1573
|
-
}
|
|
1574
|
-
// async generateTypes(dir: string) {
|
|
1575
|
-
// const plugin = await this.prepare()
|
|
1576
|
-
// const schema = plugin.schema()
|
|
1577
|
-
// const types = generateTypes(
|
|
1578
|
-
// {
|
|
1579
|
-
// [`${this.type}_provider`]: schema.provider,
|
|
1580
|
-
// },
|
|
1581
|
-
// schema.resources,
|
|
1582
|
-
// schema.dataSources
|
|
1583
|
-
// )
|
|
1584
|
-
// await mkdir(dir, { recursive: true })
|
|
1585
|
-
// await writeFile(join(dir, `${this.type}.d.ts`), types)
|
|
1586
|
-
// await this.destroy()
|
|
1587
|
-
// }
|
|
1826
|
+
);
|
|
1588
1827
|
};
|
|
1589
|
-
|
|
1590
|
-
// src/resource.ts
|
|
1591
|
-
import { createMeta, nodeMetaSymbol } from "@terraforge/core";
|
|
1592
|
-
import { snakeCase as snakeCase2 } from "change-case";
|
|
1593
|
-
var createNamespaceProxy = (cb, scb) => {
|
|
1828
|
+
var createNamespaceProxy = (cb) => {
|
|
1594
1829
|
const cache = /* @__PURE__ */ new Map();
|
|
1595
1830
|
return new Proxy(
|
|
1596
1831
|
{},
|
|
1597
1832
|
{
|
|
1598
1833
|
get(_, key) {
|
|
1599
|
-
if (
|
|
1600
|
-
|
|
1601
|
-
|
|
1834
|
+
if (typeof key === "string") {
|
|
1835
|
+
if (!cache.has(key)) {
|
|
1836
|
+
const value = cb(key);
|
|
1837
|
+
cache.set(key, value);
|
|
1838
|
+
}
|
|
1839
|
+
return cache.get(key);
|
|
1602
1840
|
}
|
|
1603
|
-
return
|
|
1841
|
+
return;
|
|
1604
1842
|
},
|
|
1605
1843
|
set(_, key) {
|
|
1606
1844
|
if (typeof key === "string") {
|
|
@@ -1611,6 +1849,25 @@ var createNamespaceProxy = (cb, scb) => {
|
|
|
1611
1849
|
}
|
|
1612
1850
|
);
|
|
1613
1851
|
};
|
|
1852
|
+
var createRootProxy = (apply, get) => {
|
|
1853
|
+
const cache = /* @__PURE__ */ new Map();
|
|
1854
|
+
return new Proxy(() => {
|
|
1855
|
+
}, {
|
|
1856
|
+
apply(_, _this, args) {
|
|
1857
|
+
return apply(...args);
|
|
1858
|
+
},
|
|
1859
|
+
get(_, key) {
|
|
1860
|
+
if (typeof key === "string") {
|
|
1861
|
+
if (!cache.has(key)) {
|
|
1862
|
+
const value = get(key);
|
|
1863
|
+
cache.set(key, value);
|
|
1864
|
+
}
|
|
1865
|
+
return cache.get(key);
|
|
1866
|
+
}
|
|
1867
|
+
return;
|
|
1868
|
+
}
|
|
1869
|
+
});
|
|
1870
|
+
};
|
|
1614
1871
|
var createClassProxy = (construct, get) => {
|
|
1615
1872
|
return new Proxy(class {
|
|
1616
1873
|
}, {
|
|
@@ -1628,307 +1885,102 @@ var createClassProxy = (construct, get) => {
|
|
|
1628
1885
|
});
|
|
1629
1886
|
};
|
|
1630
1887
|
var createRecursiveProxy = ({
|
|
1888
|
+
provider,
|
|
1889
|
+
install,
|
|
1890
|
+
uninstall,
|
|
1891
|
+
isInstalled,
|
|
1631
1892
|
resource,
|
|
1632
1893
|
dataSource
|
|
1633
1894
|
}) => {
|
|
1634
|
-
const
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
});
|
|
1895
|
+
const findNextProxy = (ns, name) => {
|
|
1896
|
+
if (name === name.toLowerCase()) {
|
|
1897
|
+
return createNamespaceProxy((key) => {
|
|
1898
|
+
return findNextProxy([...ns, name], key);
|
|
1899
|
+
});
|
|
1900
|
+
} else if (name.startsWith("get")) {
|
|
1901
|
+
return (...args) => {
|
|
1902
|
+
return dataSource([...ns, name.substring(3)], ...args);
|
|
1903
|
+
};
|
|
1904
|
+
} else {
|
|
1905
|
+
return createClassProxy(
|
|
1906
|
+
(...args) => {
|
|
1907
|
+
return resource([...ns, name], ...args);
|
|
1908
|
+
},
|
|
1909
|
+
(...args) => {
|
|
1910
|
+
return dataSource([...ns, name], ...args);
|
|
1911
|
+
}
|
|
1912
|
+
);
|
|
1913
|
+
}
|
|
1654
1914
|
};
|
|
1655
|
-
return
|
|
1915
|
+
return createRootProxy(provider, (key) => {
|
|
1916
|
+
if (key === "install") {
|
|
1917
|
+
return install;
|
|
1918
|
+
}
|
|
1919
|
+
if (key === "uninstall") {
|
|
1920
|
+
return uninstall;
|
|
1921
|
+
}
|
|
1922
|
+
if (key === "isInstalled") {
|
|
1923
|
+
return isInstalled;
|
|
1924
|
+
}
|
|
1925
|
+
return findNextProxy([], key);
|
|
1926
|
+
});
|
|
1656
1927
|
};
|
|
1657
|
-
var
|
|
1928
|
+
var createTerraformProxy = (props) => {
|
|
1658
1929
|
return createRecursiveProxy({
|
|
1930
|
+
provider(input, config) {
|
|
1931
|
+
return new TerraformProvider(
|
|
1932
|
+
props.namespace,
|
|
1933
|
+
config?.id ?? "default",
|
|
1934
|
+
createLazyPlugin({
|
|
1935
|
+
...props.provider,
|
|
1936
|
+
location: config?.location
|
|
1937
|
+
}),
|
|
1938
|
+
input
|
|
1939
|
+
);
|
|
1940
|
+
},
|
|
1941
|
+
async install(installProps) {
|
|
1942
|
+
await downloadPlugin({ ...props.provider, ...installProps });
|
|
1943
|
+
},
|
|
1944
|
+
async uninstall(installProps) {
|
|
1945
|
+
await deletePlugin({ ...props.provider, ...installProps });
|
|
1946
|
+
},
|
|
1947
|
+
async isInstalled(installProps) {
|
|
1948
|
+
return await isPluginInstalled({ ...props.provider, ...installProps });
|
|
1949
|
+
},
|
|
1659
1950
|
resource: (ns, parent, id, input, config) => {
|
|
1660
|
-
const type = snakeCase2(
|
|
1661
|
-
const provider = `terraform:${
|
|
1951
|
+
const type = snakeCase2([props.namespace, ...ns].join("_"));
|
|
1952
|
+
const provider = `terraform:${props.namespace}:${config?.provider ?? "default"}`;
|
|
1662
1953
|
const meta = createMeta("resource", provider, parent, type, id, input, config);
|
|
1663
|
-
const resource =
|
|
1664
|
-
(key)
|
|
1954
|
+
const resource = createResourceProxy((key) => {
|
|
1955
|
+
if (typeof key === "string") {
|
|
1665
1956
|
return meta.output((data) => data[key]);
|
|
1666
|
-
}
|
|
1667
|
-
|
|
1668
|
-
if (key === nodeMetaSymbol) {
|
|
1669
|
-
return meta;
|
|
1670
|
-
}
|
|
1671
|
-
return;
|
|
1957
|
+
} else if (key === nodeMetaSymbol) {
|
|
1958
|
+
return meta;
|
|
1672
1959
|
}
|
|
1673
|
-
|
|
1960
|
+
return;
|
|
1961
|
+
});
|
|
1674
1962
|
parent.add(resource);
|
|
1675
1963
|
return resource;
|
|
1676
1964
|
},
|
|
1677
|
-
// external: (ns: string[], id: string, input: State, config?: ResourceConfig) => {
|
|
1678
|
-
// const type = snakeCase(ns.join('_'))
|
|
1679
|
-
// const provider = `terraform:${ns[0]}:${config?.provider ?? 'default'}`
|
|
1680
|
-
// const $ = createResourceMeta(provider, type, id, input, config)
|
|
1681
|
-
// const resource = createNamespaceProxy(
|
|
1682
|
-
// key => {
|
|
1683
|
-
// if (key === '$') {
|
|
1684
|
-
// return $
|
|
1685
|
-
// }
|
|
1686
|
-
// return $.output(data => data[key])
|
|
1687
|
-
// },
|
|
1688
|
-
// { $ }
|
|
1689
|
-
// ) as Resource
|
|
1690
|
-
// parent.add(resource)
|
|
1691
|
-
// return resource
|
|
1692
|
-
// },
|
|
1693
|
-
// (ns: string[], parent: Group, id: string, input: State, config?: ResourceConfig)
|
|
1694
1965
|
dataSource: (ns, parent, id, input, config) => {
|
|
1695
|
-
const type = snakeCase2(
|
|
1696
|
-
const provider = `terraform:${
|
|
1966
|
+
const type = snakeCase2([props.namespace, ...ns].join("_"));
|
|
1967
|
+
const provider = `terraform:${props.namespace}:${config?.provider ?? "default"}`;
|
|
1697
1968
|
const meta = createMeta("data", provider, parent, type, id, input, config);
|
|
1698
|
-
const dataSource =
|
|
1699
|
-
(key)
|
|
1969
|
+
const dataSource = createResourceProxy((key) => {
|
|
1970
|
+
if (typeof key === "string") {
|
|
1700
1971
|
return meta.output((data) => data[key]);
|
|
1701
|
-
}
|
|
1702
|
-
|
|
1703
|
-
if (key === nodeMetaSymbol) {
|
|
1704
|
-
return meta;
|
|
1705
|
-
}
|
|
1706
|
-
return;
|
|
1972
|
+
} else if (key === nodeMetaSymbol) {
|
|
1973
|
+
return meta;
|
|
1707
1974
|
}
|
|
1708
|
-
|
|
1975
|
+
return;
|
|
1976
|
+
});
|
|
1709
1977
|
parent.add(dataSource);
|
|
1710
1978
|
return dataSource;
|
|
1711
1979
|
}
|
|
1712
1980
|
});
|
|
1713
1981
|
};
|
|
1714
|
-
|
|
1715
|
-
// src/api.ts
|
|
1716
|
-
var createTerraformAPI = (props) => {
|
|
1717
|
-
const resource = createResourceProxy(props.namespace);
|
|
1718
|
-
const install = async (installProps) => {
|
|
1719
|
-
await downloadPlugin({ ...props.provider, ...installProps });
|
|
1720
|
-
};
|
|
1721
|
-
const createPlugin = (pluginProps) => {
|
|
1722
|
-
return createLazyPlugin({ ...props.provider, ...pluginProps });
|
|
1723
|
-
};
|
|
1724
|
-
return new Proxy(() => {
|
|
1725
|
-
}, {
|
|
1726
|
-
apply(_, _this, [input, config]) {
|
|
1727
|
-
return new TerraformProvider(
|
|
1728
|
-
props.namespace,
|
|
1729
|
-
config?.id ?? "default",
|
|
1730
|
-
createPlugin({ location: config?.location }),
|
|
1731
|
-
input
|
|
1732
|
-
);
|
|
1733
|
-
},
|
|
1734
|
-
get(_, prop) {
|
|
1735
|
-
if (prop === "install") {
|
|
1736
|
-
return install;
|
|
1737
|
-
}
|
|
1738
|
-
return resource;
|
|
1739
|
-
}
|
|
1740
|
-
});
|
|
1741
|
-
};
|
|
1742
|
-
|
|
1743
|
-
// src/type-gen.ts
|
|
1744
|
-
import { camelCase as camelCase2, pascalCase } from "change-case";
|
|
1745
|
-
var tab = (indent) => {
|
|
1746
|
-
return " ".repeat(indent);
|
|
1747
|
-
};
|
|
1748
|
-
var generateTypes = (providers, resources, dataSources) => {
|
|
1749
|
-
return [
|
|
1750
|
-
generateImport("c", "@terraforge/core"),
|
|
1751
|
-
generateImport("t", "@terraforge/terraform"),
|
|
1752
|
-
"type _Record<T> = Record<string, T>",
|
|
1753
|
-
generateInstallFunction(providers),
|
|
1754
|
-
generateNamespace(providers, (name, prop, indent) => {
|
|
1755
|
-
const typeName = name.toLowerCase();
|
|
1756
|
-
return `${tab(indent)}export function ${typeName}(props: ${generatePropertyInputConst(prop, indent)}, config?: t.TerraformProviderConfig): t.TerraformProvider`;
|
|
1757
|
-
}),
|
|
1758
|
-
generateNamespace(resources, (name, prop, indent) => {
|
|
1759
|
-
const typeName = pascalCase(name);
|
|
1760
|
-
return [
|
|
1761
|
-
// `${tab(indent)}export type ${typeName}Input = ${generatePropertyInputType(prop, indent)}`,
|
|
1762
|
-
// `${tab(indent)}export type ${typeName}Output = ${generatePropertyOutputType(prop, indent)}`,
|
|
1763
|
-
// `${tab(indent)}export declare const ${typeName}: ResourceClass<${typeName}Input, ${typeName}Output>`,
|
|
1764
|
-
`${tab(indent)}export type ${typeName}Input = ${generatePropertyInputType(prop, indent)}`,
|
|
1765
|
-
`${tab(indent)}export type ${typeName}Output = ${generatePropertyOutputType(prop, indent)}`,
|
|
1766
|
-
`${tab(indent)}export class ${typeName} {`,
|
|
1767
|
-
`${tab(indent + 1)}constructor(parent: c.Group, id: string, props: ${typeName}Input, config?:c.ResourceConfig)`,
|
|
1768
|
-
// `${tab(indent + 1)}readonly $: c.ResourceMeta<${typeName}Input, ${typeName}Output>`,
|
|
1769
|
-
generateClassProperties(prop, indent + 1),
|
|
1770
|
-
`${tab(indent)}}`
|
|
1771
|
-
].join("\n\n");
|
|
1772
|
-
}),
|
|
1773
|
-
generateNamespace(dataSources, (name, prop, indent) => {
|
|
1774
|
-
const typeName = pascalCase(name);
|
|
1775
|
-
return [
|
|
1776
|
-
`${tab(indent)}export type Get${typeName}Input = ${generatePropertyInputType(prop, indent)}`,
|
|
1777
|
-
`${tab(indent)}export type Get${typeName}Output = ${generatePropertyOutputType(prop, indent)}`,
|
|
1778
|
-
`${tab(indent)}export const get${typeName}:c.DataSourceFunction<Get${typeName}Input, Get${typeName}Output>`
|
|
1779
|
-
].join("\n\n");
|
|
1780
|
-
})
|
|
1781
|
-
].join("\n\n");
|
|
1782
|
-
};
|
|
1783
|
-
var generateImport = (name, from) => {
|
|
1784
|
-
return `import * as ${name} from '${from}'`;
|
|
1785
|
-
};
|
|
1786
|
-
var generateInstallFunction = (resources) => {
|
|
1787
|
-
return generateNamespace(resources, (name, _prop, indent) => {
|
|
1788
|
-
const typeName = name.toLowerCase();
|
|
1789
|
-
return `${tab(indent)}export namespace ${typeName} { export function install(props?: t.InstallProps): Promise<void> }`;
|
|
1790
|
-
});
|
|
1791
|
-
};
|
|
1792
|
-
var generatePropertyInputConst = (prop, indent) => {
|
|
1793
|
-
return generateValue(prop, {
|
|
1794
|
-
depth: 0,
|
|
1795
|
-
indent: indent + 1,
|
|
1796
|
-
wrap: (v, _, ctx) => {
|
|
1797
|
-
return `${v}${ctx.depth === 1 ? "," : ""}`;
|
|
1798
|
-
},
|
|
1799
|
-
filter: () => true,
|
|
1800
|
-
optional: (p) => p.optional ?? false
|
|
1801
|
-
});
|
|
1802
|
-
};
|
|
1803
|
-
var generatePropertyInputType = (prop, indent) => {
|
|
1804
|
-
return generateValue(prop, {
|
|
1805
|
-
depth: 0,
|
|
1806
|
-
indent: indent + 1,
|
|
1807
|
-
wrap: (v, p, ctx) => {
|
|
1808
|
-
return ctx.depth > 0 ? p.optional ? `c.OptionalInput<${v}>` : `c.Input<${v}>` : v;
|
|
1809
|
-
},
|
|
1810
|
-
filter: (prop2) => !(prop2.computed && typeof prop2.optional === "undefined" && typeof prop2.required === "undefined"),
|
|
1811
|
-
optional: (p) => p.optional ?? false
|
|
1812
|
-
});
|
|
1813
|
-
};
|
|
1814
|
-
var generatePropertyOutputType = (prop, indent) => {
|
|
1815
|
-
return generateValue(prop, {
|
|
1816
|
-
indent: indent + 1,
|
|
1817
|
-
depth: 0,
|
|
1818
|
-
wrap: (v, p, ctx) => ctx.depth === 1 ? p.optional && !p.computed ? `c.OptionalOutput<${v}>` : `c.Output<${v}>` : v,
|
|
1819
|
-
filter: () => true,
|
|
1820
|
-
readonly: true,
|
|
1821
|
-
// required: true,
|
|
1822
|
-
optional: (p, ctx) => ctx.depth > 1 && p.optional && !p.computed || false
|
|
1823
|
-
});
|
|
1824
|
-
};
|
|
1825
|
-
var generateClassProperties = (prop, indent) => {
|
|
1826
|
-
if (prop.type !== "object") {
|
|
1827
|
-
return "";
|
|
1828
|
-
}
|
|
1829
|
-
return Object.entries(prop.properties).map(([name, prop2]) => {
|
|
1830
|
-
return [
|
|
1831
|
-
prop2.description ? [`
|
|
1832
|
-
`, ` `.repeat(indent), `/** `, prop2.description.trim(), " */", "\n"].join("") : "",
|
|
1833
|
-
` `.repeat(indent),
|
|
1834
|
-
"readonly ",
|
|
1835
|
-
camelCase2(name),
|
|
1836
|
-
// ctx.optional(prop, ctx) ? '?' : '',
|
|
1837
|
-
": ",
|
|
1838
|
-
generateValue(prop2, {
|
|
1839
|
-
readonly: true,
|
|
1840
|
-
filter: () => true,
|
|
1841
|
-
optional: (p, ctx) => ctx.depth > 1 && p.optional && !p.computed || false,
|
|
1842
|
-
wrap: (v, p, ctx) => {
|
|
1843
|
-
return ctx.depth === 1 ? p.optional && !p.computed ? `c.OptionalOutput<${v}>` : `c.Output<${v}>` : v;
|
|
1844
|
-
},
|
|
1845
|
-
// ctx.depth === 1 ? `c.Output<${p.optional && !p.computed ? `${v} | undefined` : v}>` : v,
|
|
1846
|
-
indent: indent + 1,
|
|
1847
|
-
depth: 1
|
|
1848
|
-
})
|
|
1849
|
-
].join("");
|
|
1850
|
-
}).join("\n");
|
|
1851
|
-
};
|
|
1852
|
-
var groupByNamespace = (resources, minLevel, maxLevel) => {
|
|
1853
|
-
const grouped = {};
|
|
1854
|
-
const types = Object.keys(resources).sort();
|
|
1855
|
-
for (const type of types) {
|
|
1856
|
-
const names = type.split("_");
|
|
1857
|
-
if (names.length < minLevel) {
|
|
1858
|
-
throw new Error(`Resource not properly namespaced: ${type}`);
|
|
1859
|
-
}
|
|
1860
|
-
let current = grouped;
|
|
1861
|
-
let count = Math.min(maxLevel, names.length - 1);
|
|
1862
|
-
while (count--) {
|
|
1863
|
-
const ns = camelCase2(names.shift());
|
|
1864
|
-
if (!current[ns]) {
|
|
1865
|
-
current[ns] = {};
|
|
1866
|
-
}
|
|
1867
|
-
current = current[ns];
|
|
1868
|
-
}
|
|
1869
|
-
const name = pascalCase(names.join("_"));
|
|
1870
|
-
current[name] = type;
|
|
1871
|
-
}
|
|
1872
|
-
return grouped;
|
|
1873
|
-
};
|
|
1874
|
-
var generateNamespace = (resources, render) => {
|
|
1875
|
-
const grouped = groupByNamespace(resources, 1, 2);
|
|
1876
|
-
const renderNamespace = (name, group, indent) => {
|
|
1877
|
-
if (name === "default") {
|
|
1878
|
-
name = "$default";
|
|
1879
|
-
}
|
|
1880
|
-
return [
|
|
1881
|
-
`${tab(indent)}export ${indent === 0 ? "declare " : ""}namespace ${name.toLowerCase()} {`,
|
|
1882
|
-
Object.entries(group).map(([name2, entry]) => {
|
|
1883
|
-
if (typeof entry !== "string") {
|
|
1884
|
-
return renderNamespace(name2, entry, indent + 1);
|
|
1885
|
-
} else {
|
|
1886
|
-
return render(name2, resources[entry], indent + 1);
|
|
1887
|
-
}
|
|
1888
|
-
}).join("\n"),
|
|
1889
|
-
`${tab(indent)}}`
|
|
1890
|
-
].join("\n");
|
|
1891
|
-
};
|
|
1892
|
-
return renderNamespace("root", grouped, 0);
|
|
1893
|
-
};
|
|
1894
|
-
var generateValue = (prop, ctx) => {
|
|
1895
|
-
if (["string", "number", "boolean", "unknown"].includes(prop.type)) {
|
|
1896
|
-
return ctx.wrap(prop.type, prop, ctx);
|
|
1897
|
-
}
|
|
1898
|
-
if (prop.type === "array") {
|
|
1899
|
-
const type = generateValue(prop.item, { ...ctx, depth: ctx.depth + 1 });
|
|
1900
|
-
const array = ctx.readonly ? `ReadonlyArray<${type}>` : `Array<${type}>`;
|
|
1901
|
-
return ctx.wrap(array, prop, ctx);
|
|
1902
|
-
}
|
|
1903
|
-
if (prop.type === "record") {
|
|
1904
|
-
const type = generateValue(prop.item, { ...ctx, depth: ctx.depth + 1 });
|
|
1905
|
-
const record = ctx.readonly ? `Readonly<_Record<${type}>>` : `_Record<${type}>`;
|
|
1906
|
-
return ctx.wrap(record, prop, ctx);
|
|
1907
|
-
}
|
|
1908
|
-
if (prop.type === "object" || prop.type === "array-object") {
|
|
1909
|
-
const type = [
|
|
1910
|
-
"{",
|
|
1911
|
-
Object.entries(prop.properties).filter(([_, p]) => ctx.filter(p)).map(
|
|
1912
|
-
([name, prop2]) => [
|
|
1913
|
-
prop2.description ? [`
|
|
1914
|
-
`, ` `.repeat(ctx.indent), `/** `, prop2.description.trim(), " */", "\n"].join("") : "",
|
|
1915
|
-
` `.repeat(ctx.indent),
|
|
1916
|
-
// ctx.readonly ? "readonly " : "",
|
|
1917
|
-
camelCase2(name),
|
|
1918
|
-
ctx.optional(prop2, ctx) ? "?" : "",
|
|
1919
|
-
": ",
|
|
1920
|
-
generateValue(prop2, { ...ctx, indent: ctx.indent + 1, depth: ctx.depth + 1 })
|
|
1921
|
-
].join("")
|
|
1922
|
-
).join("\n"),
|
|
1923
|
-
`${` `.repeat(ctx.indent - 1)}}`
|
|
1924
|
-
].join("\n");
|
|
1925
|
-
const object = ctx.readonly ? `Readonly<${type}>` : type;
|
|
1926
|
-
return ctx.wrap(object, prop, ctx);
|
|
1927
|
-
}
|
|
1928
|
-
throw new Error(`Unknown property type: ${prop.type}`);
|
|
1929
|
-
};
|
|
1930
1982
|
export {
|
|
1931
1983
|
TerraformProvider,
|
|
1932
|
-
|
|
1984
|
+
createTerraformProxy,
|
|
1933
1985
|
generateTypes
|
|
1934
1986
|
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@terraforge/terraform",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.9",
|
|
4
4
|
"type": "module",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/terraforge-js/terraforge.git"
|
|
8
|
+
},
|
|
9
|
+
"bugs": {
|
|
10
|
+
"url": "https://github.com/terraforge-js/terraforge/issues"
|
|
11
|
+
},
|
|
5
12
|
"module": "./dist/index.js",
|
|
6
13
|
"types": "./dist/index.d.ts",
|
|
7
14
|
"bin": {
|
|
@@ -19,7 +26,7 @@
|
|
|
19
26
|
"test": "bun test"
|
|
20
27
|
},
|
|
21
28
|
"peerDependencies": {
|
|
22
|
-
"@terraforge/core": "0.0.
|
|
29
|
+
"@terraforge/core": "0.0.5"
|
|
23
30
|
},
|
|
24
31
|
"dependencies": {
|
|
25
32
|
"@grpc/grpc-js": "1.12.6",
|