@terraforge/terraform 0.0.6 → 0.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,61 +1,64 @@
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 { values } = parseArgs({
9
- args: Bun.argv,
10
- options: {
11
- org: {
12
- type: 'string',
13
- },
14
- type: {
15
- type: 'string',
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 (!values.org) {
23
- console.error('Missing required arguments: org')
16
+ if (!packageData || !packageData.provider) {
17
+ console.error('Failed to read package.json')
24
18
  process.exit(1)
25
19
  }
26
20
 
27
- if (!values.type) {
28
- console.error('Missing required arguments: type')
21
+ const providerData = packageData.provider
22
+
23
+ if (!providerData.version) {
24
+ console.error('Missing required arguments: version')
29
25
  process.exit(1)
30
26
  }
31
27
 
32
- const packageData = (await Bun.file('./package.json').json()) as { version?: Version }
33
-
34
- if (!packageData) {
35
- console.error('Failed to read package.json')
28
+ if (!providerData.org) {
29
+ console.error('Missing required arguments: org')
36
30
  process.exit(1)
37
31
  }
38
32
 
39
- if (!packageData.version) {
40
- console.error('Missing required arguments: version')
33
+ if (!providerData.type) {
34
+ console.error('Missing required arguments: type')
41
35
  process.exit(1)
42
36
  }
43
37
 
44
- const org = values.org
45
- const type = values.type
46
- const version = packageData.version
38
+ const org = providerData.org
39
+ const type = providerData.type
40
+ const version = providerData.version
47
41
 
48
- console.log('org: ', org)
49
- console.log('type: ', type)
50
- console.log('version: ', version)
42
+ console.log('')
43
+ console.log('Package version: ', packageData.version)
44
+ console.log('')
45
+ console.log('Provider org: ', org)
46
+ console.log('Provider type: ', type)
47
+ console.log('Provider version: ', version)
48
+ console.log('')
51
49
 
52
50
  const ok = confirm('Continue?')
53
51
 
54
52
  if (!ok) {
55
- process.exit(0)
53
+ console.log('')
54
+ process.exit(1)
56
55
  }
57
56
 
58
57
  const load = createLazyPlugin({ org, type, version })
58
+
59
+ console.log('')
60
+ console.log('Loading provider plugin...')
61
+
59
62
  const plugin = await load()
60
63
  const schema = plugin.schema()
61
64
  const types = generateTypes(
@@ -68,17 +71,18 @@ const types = generateTypes(
68
71
 
69
72
  await plugin.stop()
70
73
 
71
- await Bun.write(`./src/types.ts`, types)
72
-
74
+ await Bun.write(`./dist/index.d.ts`, types)
73
75
  await Bun.write(
74
- `./src/index.ts`,
76
+ `./dist/index.js`,
75
77
  `
76
78
  import { createTerraformAPI } from '@terraforge/terraform'
77
- import { root } from './types.ts'
78
79
 
79
- export const ${type} = createTerraformAPI<typeof root.${type}>({
80
+ export const ${type} = createTerraformAPI({
80
81
  namespace: '${type}',
81
82
  provider: { org: '${org}', type: '${type}', version: '${version}' },
82
- }) as typeof root.${type}
83
+ })
83
84
  `
84
85
  )
86
+
87
+ console.log('Done.')
88
+ 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
- export { type InstallProps, TerraformProvider, type TerraformProviderConfig, createTerraformAPI, generateTypes };
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,313 @@
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
+ generateInstallFunction(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 generateInstallFunction = (resources) => {
45
+ return generateNamespace(resources, (name, _prop, indent) => {
46
+ const typeName = name.toLowerCase();
47
+ return `${tab(indent)}export declare namespace ${typeName} { export function install(props?: t.InstallProps): Promise<void> }`;
48
+ });
49
+ };
50
+ var generatePropertyInputConst = (prop, indent) => {
51
+ return generateValue(prop, {
52
+ depth: 0,
53
+ indent: indent + 1,
54
+ wrap: (v, _, ctx) => {
55
+ return `${v}${ctx.depth === 1 ? "," : ""}`;
56
+ },
57
+ filter: () => true,
58
+ optional: (p) => p.optional ?? false
59
+ });
60
+ };
61
+ var generatePropertyInputType = (prop, indent) => {
62
+ return generateValue(prop, {
63
+ depth: 0,
64
+ indent: indent + 1,
65
+ wrap: (v, p, ctx) => {
66
+ return ctx.depth > 0 ? p.optional ? `c.OptionalInput<${v}>` : `c.Input<${v}>` : v;
67
+ },
68
+ filter: (prop2) => !(prop2.computed && typeof prop2.optional === "undefined" && typeof prop2.required === "undefined"),
69
+ optional: (p) => p.optional ?? false
70
+ });
71
+ };
72
+ var generatePropertyOutputType = (prop, indent) => {
73
+ return generateValue(prop, {
74
+ indent: indent + 1,
75
+ depth: 0,
76
+ wrap: (v, p, ctx) => ctx.depth === 1 ? p.optional && !p.computed ? `c.OptionalOutput<${v}>` : `c.Output<${v}>` : v,
77
+ filter: () => true,
78
+ readonly: true,
79
+ // required: true,
80
+ optional: (p, ctx) => ctx.depth > 1 && p.optional && !p.computed || false
81
+ });
82
+ };
83
+ var generateClassProperties = (prop, indent) => {
84
+ if (prop.type !== "object") {
85
+ return "";
86
+ }
87
+ return Object.entries(prop.properties).map(([name, prop2]) => {
88
+ return [
89
+ prop2.description ? [`
90
+ `, ` `.repeat(indent), `/** `, prop2.description.trim(), " */", "\n"].join("") : "",
91
+ ` `.repeat(indent),
92
+ "readonly ",
93
+ camelCase(name),
94
+ // ctx.optional(prop, ctx) ? '?' : '',
95
+ ": ",
96
+ generateValue(prop2, {
97
+ readonly: true,
98
+ filter: () => true,
99
+ optional: (p, ctx) => ctx.depth > 1 && p.optional && !p.computed || false,
100
+ wrap: (v, p, ctx) => {
101
+ return ctx.depth === 1 ? p.optional && !p.computed ? `c.OptionalOutput<${v}>` : `c.Output<${v}>` : v;
102
+ },
103
+ // ctx.depth === 1 ? `c.Output<${p.optional && !p.computed ? `${v} | undefined` : v}>` : v,
104
+ indent: indent + 1,
105
+ depth: 1
106
+ })
107
+ ].join("");
108
+ }).join("\n");
109
+ };
110
+ var groupByNamespace = (resources, minLevel, maxLevel) => {
111
+ const grouped = {};
112
+ const types = Object.keys(resources).sort();
113
+ for (const type of types) {
114
+ const names = type.split("_");
115
+ if (names.length < minLevel) {
116
+ throw new Error(`Resource not properly namespaced: ${type}`);
117
+ }
118
+ let current = grouped;
119
+ let count = Math.min(maxLevel, names.length - 1);
120
+ while (count--) {
121
+ const ns = camelCase(names.shift());
122
+ if (!current[ns]) {
123
+ current[ns] = {};
124
+ }
125
+ current = current[ns];
126
+ }
127
+ const name = pascalCase(names.join("_"));
128
+ current[name] = type;
129
+ }
130
+ return grouped;
131
+ };
132
+ var generateNamespace = (resources, render) => {
133
+ const grouped = groupByNamespace(resources, 1, 2);
134
+ const renderNamespace = (name, group, indent) => {
135
+ if (name === "default") {
136
+ name = "$default";
137
+ }
138
+ if (typeof group === "string") {
139
+ return render(name, resources[group], indent);
140
+ }
141
+ return [
142
+ `${tab(indent)}export ${indent === 0 ? "declare " : ""}namespace ${name.toLowerCase()} {`,
143
+ Object.entries(group).map(([name2, entry]) => {
144
+ if (typeof entry !== "string") {
145
+ return renderNamespace(name2, entry, indent + 1);
146
+ } else {
147
+ return render(name2, resources[entry], indent + 1);
148
+ }
149
+ }).join("\n"),
150
+ `${tab(indent)}}`
151
+ ].join("\n");
152
+ };
153
+ return Object.entries(grouped).map(([name, entry]) => {
154
+ return renderNamespace(name, entry, 0);
155
+ });
156
+ };
157
+ var generateValue = (prop, ctx) => {
158
+ if (["string", "number", "boolean", "unknown"].includes(prop.type)) {
159
+ return ctx.wrap(prop.type, prop, ctx);
160
+ }
161
+ if (prop.type === "array") {
162
+ const type = generateValue(prop.item, { ...ctx, depth: ctx.depth + 1 });
163
+ const array = ctx.readonly ? `ReadonlyArray<${type}>` : `Array<${type}>`;
164
+ return ctx.wrap(array, prop, ctx);
165
+ }
166
+ if (prop.type === "record") {
167
+ const type = generateValue(prop.item, { ...ctx, depth: ctx.depth + 1 });
168
+ const record = ctx.readonly ? `Readonly<_Record<${type}>>` : `_Record<${type}>`;
169
+ return ctx.wrap(record, prop, ctx);
170
+ }
171
+ if (prop.type === "object" || prop.type === "array-object") {
172
+ const type = [
173
+ "{",
174
+ Object.entries(prop.properties).filter(([_, p]) => ctx.filter(p)).map(
175
+ ([name, prop2]) => [
176
+ prop2.description ? [`
177
+ `, ` `.repeat(ctx.indent), `/** `, prop2.description.trim(), " */", "\n"].join("") : "",
178
+ ` `.repeat(ctx.indent),
179
+ // ctx.readonly ? "readonly " : "",
180
+ camelCase(name),
181
+ ctx.optional(prop2, ctx) ? "?" : "",
182
+ ": ",
183
+ generateValue(prop2, { ...ctx, indent: ctx.indent + 1, depth: ctx.depth + 1 })
184
+ ].join("")
185
+ ).join("\n"),
186
+ `${` `.repeat(ctx.indent - 1)}}`
187
+ ].join("\n");
188
+ const object = ctx.readonly ? `Readonly<${type}>` : type;
189
+ return ctx.wrap(object, prop, ctx);
190
+ }
191
+ throw new Error(`Unknown property type: ${prop.type}`);
192
+ };
193
+
194
+ // src/provider.ts
195
+ import {
196
+ ResourceNotFound
197
+ } from "@terraforge/core";
198
+ var TerraformProvider = class {
199
+ constructor(type, id, createPlugin, config) {
200
+ this.type = type;
201
+ this.id = id;
202
+ this.createPlugin = createPlugin;
203
+ this.config = config;
204
+ }
205
+ configured;
206
+ plugin;
207
+ async configure() {
208
+ const plugin = await this.prepare();
209
+ if (!this.configured) {
210
+ this.configured = plugin.configure(this.config);
211
+ }
212
+ await this.configured;
213
+ return plugin;
214
+ }
215
+ prepare() {
216
+ if (!this.plugin) {
217
+ this.plugin = this.createPlugin();
218
+ }
219
+ return this.plugin;
220
+ }
221
+ async destroy() {
222
+ if (this.plugin) {
223
+ const plugin = await this.plugin;
224
+ plugin.stop();
225
+ this.plugin = void 0;
226
+ this.configured = void 0;
227
+ }
228
+ }
229
+ ownResource(id) {
230
+ return `terraform:${this.type}:${this.id}` === id;
231
+ }
232
+ async getResource({ type, state }) {
233
+ const plugin = await this.configure();
234
+ const newState = await plugin.readResource(type, state);
235
+ if (!newState) {
236
+ throw new ResourceNotFound();
237
+ }
238
+ return {
239
+ version: 0,
240
+ state: newState
241
+ };
242
+ }
243
+ async createResource({ type, state }) {
244
+ const plugin = await this.configure();
245
+ const newState = await plugin.applyResourceChange(type, null, state);
246
+ return {
247
+ version: 0,
248
+ state: newState
249
+ };
250
+ }
251
+ async updateResource({ type, priorState, proposedState }) {
252
+ const plugin = await this.configure();
253
+ const { requiresReplace } = await plugin.planResourceChange(type, priorState, proposedState);
254
+ if (requiresReplace.length > 0) {
255
+ const formattedAttrs = requiresReplace.map((p) => p.join(".")).join('", "');
256
+ throw new Error(
257
+ `Updating the "${formattedAttrs}" properties for the "${type}" resource will require the resource to be replaced.`
258
+ );
259
+ }
260
+ const newState = await plugin.applyResourceChange(type, priorState, proposedState);
261
+ return {
262
+ version: 0,
263
+ state: newState
264
+ };
265
+ }
266
+ async deleteResource({ type, state }) {
267
+ const plugin = await this.configure();
268
+ try {
269
+ await plugin.applyResourceChange(type, state, null);
270
+ } catch (error) {
271
+ try {
272
+ const newState = await plugin.readResource(type, state);
273
+ if (!newState) {
274
+ throw new ResourceNotFound();
275
+ }
276
+ } catch (_) {
277
+ }
278
+ throw error;
279
+ }
280
+ }
281
+ async getData({ type, state }) {
282
+ const plugin = await this.configure();
283
+ const data = await plugin.readDataSource(type, state);
284
+ if (!data) {
285
+ throw new Error(`Data source not found ${type}`);
286
+ }
287
+ return {
288
+ state: data
289
+ };
290
+ }
291
+ // async generateTypes(dir: string) {
292
+ // const plugin = await this.prepare()
293
+ // const schema = plugin.schema()
294
+ // const types = generateTypes(
295
+ // {
296
+ // [`${this.type}_provider`]: schema.provider,
297
+ // },
298
+ // schema.resources,
299
+ // schema.dataSources
300
+ // )
301
+ // await mkdir(dir, { recursive: true })
302
+ // await writeFile(join(dir, `${this.type}.d.ts`), types)
303
+ // await this.destroy()
304
+ // }
305
+ };
306
+
307
+ // src/proxy.ts
308
+ import { createMeta, nodeMetaSymbol } from "@terraforge/core";
309
+ import { snakeCase as snakeCase2 } from "change-case";
310
+
1
311
  // src/plugin/client.ts
2
312
  import { credentials, loadPackageDefinition } from "@grpc/grpc-js";
3
313
  import { fromJSON } from "@grpc/proto-loader";
@@ -1090,7 +1400,7 @@ var parseType = (type) => {
1090
1400
  };
1091
1401
 
1092
1402
  // src/plugin/version/util.ts
1093
- import { camelCase, snakeCase } from "change-case";
1403
+ import { camelCase as camelCase2, snakeCase } from "change-case";
1094
1404
  import { pack, unpack } from "msgpackr";
1095
1405
  var encodeDynamicValue = (value) => {
1096
1406
  return {
@@ -1184,7 +1494,7 @@ var formatInputState = (schema, state, includeSchemaFields = true, path = []) =>
1184
1494
  const object = {};
1185
1495
  if (includeSchemaFields) {
1186
1496
  for (const [key, prop] of Object.entries(schema.properties)) {
1187
- const value = state[camelCase(key)];
1497
+ const value = state[camelCase2(key)];
1188
1498
  object[key] = formatInputState(prop, value, true, [...path, key]);
1189
1499
  }
1190
1500
  } else {
@@ -1229,7 +1539,7 @@ var formatOutputState = (schema, state, path = []) => {
1229
1539
  const object = {};
1230
1540
  for (const [key, prop] of Object.entries(schema.properties)) {
1231
1541
  const value = state[key];
1232
- object[camelCase(key)] = formatOutputState(prop, value, [...path, key]);
1542
+ object[camelCase2(key)] = formatOutputState(prop, value, [...path, key]);
1233
1543
  }
1234
1544
  return object;
1235
1545
  }
@@ -1241,7 +1551,7 @@ var formatOutputState = (schema, state, path = []) => {
1241
1551
  const object = {};
1242
1552
  for (const [key, prop] of Object.entries(schema.properties)) {
1243
1553
  const value = state[0][key];
1244
- object[camelCase(key)] = formatOutputState(prop, value, [...path, key]);
1554
+ object[camelCase2(key)] = formatOutputState(prop, value, [...path, key]);
1245
1555
  }
1246
1556
  return object;
1247
1557
  } else {
@@ -1474,133 +1784,37 @@ var retry = async (tries, cb) => {
1474
1784
  throw latestError;
1475
1785
  };
1476
1786
 
1477
- // src/provider.ts
1478
- import {
1479
- ResourceNotFound
1480
- } from "@terraforge/core";
1481
- var TerraformProvider = class {
1482
- constructor(type, id, createPlugin, config) {
1483
- this.type = type;
1484
- this.id = id;
1485
- this.createPlugin = createPlugin;
1486
- this.config = config;
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();
1787
+ // src/proxy.ts
1788
+ var createResourceProxy = (cb) => {
1789
+ return new Proxy(
1790
+ {},
1791
+ {
1792
+ get(_, key) {
1793
+ return cb(key);
1794
+ },
1795
+ set(_, key) {
1796
+ if (typeof key === "string") {
1797
+ throw new Error(`Cannot set property ${key} on read-only object.`);
1558
1798
  }
1559
- } catch (_) {
1560
- }
1561
- throw error;
1562
- }
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
- // }
1799
+ throw new Error(`This object is read-only.`);
1800
+ }
1801
+ }
1802
+ );
1588
1803
  };
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) => {
1804
+ var createNamespaceProxy = (cb) => {
1594
1805
  const cache = /* @__PURE__ */ new Map();
1595
1806
  return new Proxy(
1596
1807
  {},
1597
1808
  {
1598
1809
  get(_, key) {
1599
- if (!cache.has(key)) {
1600
- const value = typeof key === "symbol" ? scb?.(key) : cb(key);
1601
- cache.set(key, value);
1810
+ if (typeof key === "string") {
1811
+ if (!cache.has(key)) {
1812
+ const value = cb(key);
1813
+ cache.set(key, value);
1814
+ }
1815
+ return cache.get(key);
1602
1816
  }
1603
- return cache.get(key);
1817
+ return;
1604
1818
  },
1605
1819
  set(_, key) {
1606
1820
  if (typeof key === "string") {
@@ -1611,6 +1825,25 @@ var createNamespaceProxy = (cb, scb) => {
1611
1825
  }
1612
1826
  );
1613
1827
  };
1828
+ var createRootProxy = (apply, get) => {
1829
+ const cache = /* @__PURE__ */ new Map();
1830
+ return new Proxy(() => {
1831
+ }, {
1832
+ apply(_, _this, args) {
1833
+ return apply(...args);
1834
+ },
1835
+ get(_, key) {
1836
+ if (typeof key === "string") {
1837
+ if (!cache.has(key)) {
1838
+ const value = get(key);
1839
+ cache.set(key, value);
1840
+ }
1841
+ return cache.get(key);
1842
+ }
1843
+ return;
1844
+ }
1845
+ });
1846
+ };
1614
1847
  var createClassProxy = (construct, get) => {
1615
1848
  return new Proxy(class {
1616
1849
  }, {
@@ -1628,307 +1861,88 @@ var createClassProxy = (construct, get) => {
1628
1861
  });
1629
1862
  };
1630
1863
  var createRecursiveProxy = ({
1864
+ provider,
1865
+ install,
1631
1866
  resource,
1632
1867
  dataSource
1633
1868
  }) => {
1634
- const createProxy = (names) => {
1635
- return createNamespaceProxy((name) => {
1636
- const ns = [...names, name];
1637
- if (name === name.toLowerCase()) {
1638
- return createProxy(ns);
1639
- } else if (name.startsWith("get")) {
1640
- return (...args) => {
1641
- return dataSource([...names, name.substring(3)], ...args);
1642
- };
1643
- } else {
1644
- return createClassProxy(
1645
- (...args) => {
1646
- return resource(ns, ...args);
1647
- },
1648
- (...args) => {
1649
- return dataSource(ns, ...args);
1650
- }
1651
- );
1652
- }
1653
- });
1869
+ const findNextProxy = (ns, name) => {
1870
+ if (name === name.toLowerCase()) {
1871
+ return createNamespaceProxy((key) => {
1872
+ return findNextProxy([...ns, name], key);
1873
+ });
1874
+ } else if (name.startsWith("get")) {
1875
+ return (...args) => {
1876
+ return dataSource([...ns, name.substring(3)], ...args);
1877
+ };
1878
+ } else {
1879
+ return createClassProxy(
1880
+ (...args) => {
1881
+ return resource([...ns, name], ...args);
1882
+ },
1883
+ (...args) => {
1884
+ return dataSource([...ns, name], ...args);
1885
+ }
1886
+ );
1887
+ }
1654
1888
  };
1655
- return createProxy([]);
1889
+ return createRootProxy(provider, (key) => {
1890
+ if (key === "install") {
1891
+ return install;
1892
+ }
1893
+ return findNextProxy([], key);
1894
+ });
1656
1895
  };
1657
- var createResourceProxy = (name) => {
1896
+ var createTerraformProxy = (props) => {
1658
1897
  return createRecursiveProxy({
1898
+ provider(input, config) {
1899
+ return new TerraformProvider(
1900
+ props.namespace,
1901
+ config?.id ?? "default",
1902
+ createLazyPlugin({
1903
+ ...props.provider,
1904
+ location: config?.location
1905
+ }),
1906
+ input
1907
+ );
1908
+ },
1909
+ async install(installProps) {
1910
+ await downloadPlugin({ ...props.provider, ...installProps });
1911
+ },
1659
1912
  resource: (ns, parent, id, input, config) => {
1660
- const type = snakeCase2(name + "_" + ns.join("_"));
1661
- const provider = `terraform:${name}:${config?.provider ?? "default"}`;
1913
+ const type = snakeCase2([props.namespace, ...ns].join("_"));
1914
+ const provider = `terraform:${props.namespace}:${config?.provider ?? "default"}`;
1662
1915
  const meta = createMeta("resource", provider, parent, type, id, input, config);
1663
- const resource = createNamespaceProxy(
1664
- (key) => {
1916
+ const resource = createResourceProxy((key) => {
1917
+ if (typeof key === "string") {
1665
1918
  return meta.output((data) => data[key]);
1666
- },
1667
- (key) => {
1668
- if (key === nodeMetaSymbol) {
1669
- return meta;
1670
- }
1671
- return;
1919
+ } else if (key === nodeMetaSymbol) {
1920
+ return meta;
1672
1921
  }
1673
- );
1922
+ return;
1923
+ });
1674
1924
  parent.add(resource);
1675
1925
  return resource;
1676
1926
  },
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
1927
  dataSource: (ns, parent, id, input, config) => {
1695
- const type = snakeCase2(name + "_" + ns.join("_"));
1696
- const provider = `terraform:${name}:${config?.provider ?? "default"}`;
1928
+ const type = snakeCase2([props.namespace, ...ns].join("_"));
1929
+ const provider = `terraform:${props.namespace}:${config?.provider ?? "default"}`;
1697
1930
  const meta = createMeta("data", provider, parent, type, id, input, config);
1698
- const dataSource = createNamespaceProxy(
1699
- (key) => {
1931
+ const dataSource = createResourceProxy((key) => {
1932
+ if (typeof key === "string") {
1700
1933
  return meta.output((data) => data[key]);
1701
- },
1702
- (key) => {
1703
- if (key === nodeMetaSymbol) {
1704
- return meta;
1705
- }
1706
- return;
1934
+ } else if (key === nodeMetaSymbol) {
1935
+ return meta;
1707
1936
  }
1708
- );
1937
+ return;
1938
+ });
1709
1939
  parent.add(dataSource);
1710
1940
  return dataSource;
1711
1941
  }
1712
1942
  });
1713
1943
  };
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
1944
  export {
1931
1945
  TerraformProvider,
1932
- createTerraformAPI,
1946
+ createTerraformProxy,
1933
1947
  generateTypes
1934
1948
  };
package/package.json CHANGED
@@ -1,7 +1,14 @@
1
1
  {
2
2
  "name": "@terraforge/terraform",
3
- "version": "0.0.6",
3
+ "version": "0.0.8",
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.4"
29
+ "@terraforge/core": "0.0.5"
23
30
  },
24
31
  "dependencies": {
25
32
  "@grpc/grpc-js": "1.12.6",