@anvil-cloud/sdk 0.0.4 → 0.0.6

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/README.md CHANGED
@@ -2,58 +2,90 @@
2
2
 
3
3
  **Cloud infrastructure that's secure by default — not by accident.**
4
4
 
5
- Anvil wraps raw cloud resources into opinionated, production-ready components. No 200-line Terraform modules. No copy-pasting security configs from Stack Overflow. Just declare what you need and Anvil handles the rest.
5
+ Anvil wraps raw cloud resources into opinionated, production-ready components. No 200-line Terraform modules. No copy-pasting security configs. Just declare what you need and Anvil handles the rest.
6
6
 
7
- ```typescript
8
- import * as anvil from '@anvil-cloud/sdk';
7
+ ## Install
9
8
 
10
- const bucket = new anvil.aws.Bucket('uploads', {
11
- versioning: true,
12
- });
9
+ ```bash
10
+ npm install @anvil-cloud/sdk
13
11
  ```
14
12
 
15
- That S3 bucket ships with public access blocked, encryption enabled, and ACLs disabled — because that's how every bucket should start. You opt _in_ to risk, not out of it.
13
+ ## Quick start
16
14
 
17
- ## Multi-cloud
15
+ Create `anvil.config.ts` at your project root:
18
16
 
19
- Anvil supports any cloud provider. AWS and GCP are available today, with more coming soon.
17
+ ```typescript
18
+ import { App } from '@anvil-cloud/sdk';
19
+ import * as anvil from '@anvil-cloud/sdk';
20
20
 
21
- ## Quick start
21
+ export default new App({
22
+ run(ctx) {
23
+ const bucket = new anvil.aws.Bucket('uploads', {
24
+ dataClassification: 'sensitive',
25
+ });
26
+ ctx.export('bucketName', bucket.bucketName);
27
+ },
28
+ });
29
+ ```
22
30
 
23
- **1. Install the CLI**
31
+ Deploy:
24
32
 
25
33
  ```bash
26
- brew install anvil-cloud/tap/anvil
34
+ anvil deploy
27
35
  ```
28
36
 
29
- **2. Install the SDK**
37
+ That S3 bucket ships with public access blocked, encryption enabled, and versioning on — because that's how every bucket should start. You opt _in_ to risk, not out of it.
30
38
 
31
- ```bash
32
- npm install @anvil-cloud/sdk
33
- ```
39
+ ## The App class
34
40
 
35
- **3. Write infrastructure**
41
+ Every Anvil program starts with `new App()`. The `run` callback receives a `Context` with:
36
42
 
37
- ```typescript
38
- // index.ts
39
- import * as anvil from '@anvil-cloud/sdk';
43
+ - `ctx.stage` — the current deployment stage (defaults to your OS username for dev isolation)
44
+ - `ctx.project` — the project name from `anvil.yaml`
45
+ - `ctx.export(name, value)` export stack outputs
46
+ - `ctx.providers` — named cloud providers for multi-region / multi-account
40
47
 
41
- const bucket = new anvil.aws.Bucket('uploads');
48
+ ## Multi-cloud
49
+
50
+ ```typescript
51
+ export default new App({
52
+ run(ctx) {
53
+ // AWS
54
+ const bucket = new anvil.aws.Bucket('data', {
55
+ dataClassification: 'sensitive',
56
+ });
57
+
58
+ // GCP
59
+ const gcsBucket = new anvil.gcp.StorageBucket('backup', {
60
+ dataClassification: 'internal',
61
+ location: 'US',
62
+ });
63
+ },
64
+ });
42
65
  ```
43
66
 
44
- **4. Deploy**
67
+ ## Overrides
45
68
 
46
- ```bash
47
- anvil deploy
69
+ Every component accepts a `transform` argument to override the underlying resource config:
70
+
71
+ ```typescript
72
+ const bucket = new anvil.aws.Bucket('custom', {
73
+ dataClassification: 'non-sensitive',
74
+ transform: {
75
+ bucket: { forceDestroy: true, tags: { env: 'dev' } },
76
+ },
77
+ });
48
78
  ```
49
79
 
50
80
  ## How it works
51
81
 
52
- Anvil is built on top of [Pulumi](https://www.pulumi.com/). Each component wraps one or more raw cloud resources with secure defaults baked in. You write TypeScript, Anvil handles the wiring.
82
+ Anvil is built on [Pulumi](https://www.pulumi.com/). Each component wraps one or more cloud resources with secure defaults. The `App` class handles config, providers, and exports so you write infrastructure — not boilerplate.
53
83
 
54
84
  ## Links
55
85
 
56
- - [GitHub](https://github.com/anvil-cloud/anvil)
86
+ - [GitHub](https://github.com/DamienPace15/anvil)
87
+ - [Python SDK](https://pypi.org/project/anvil-cloud/)
88
+ - [Go SDK](https://pkg.go.dev/github.com/DamienPace15/anvil/sdk/go/anvil)
57
89
 
58
90
  ## License
59
91
 
package/app.ts ADDED
@@ -0,0 +1,285 @@
1
+ import * as pulumi from '@pulumi/pulumi';
2
+ import * as aws from '@pulumi/aws';
3
+ import * as gcp from '@pulumi/gcp';
4
+
5
+ // ── Types ──────────────────────────────────────────────────
6
+
7
+ /**
8
+ * Context passed to the App's run callback.
9
+ * This is the user's only interface to Anvil runtime information.
10
+ */
11
+ export interface Context {
12
+ /** The current deployment stage (e.g. "dev", "staging", "prod", or OS username). */
13
+ readonly stage: string;
14
+
15
+ /** The project name from anvil.yaml. */
16
+ readonly project: string;
17
+
18
+ /** Named providers, keyed by their config name (e.g. "aws", "aws.us", "gcp.eu"). */
19
+ readonly providers: Record<string, pulumi.ProviderResource>;
20
+
21
+ /** Export a stack output value without importing Pulumi. */
22
+ export(name: string, value: pulumi.Input<any>): void;
23
+ }
24
+
25
+ /** AWS provider configuration. */
26
+ export interface AwsProviderConfig {
27
+ region?: string;
28
+ profile?: string;
29
+ assumeRole?: {
30
+ roleArn: string;
31
+ sessionName?: string;
32
+ externalId?: string;
33
+ };
34
+ }
35
+
36
+ /** GCP provider configuration. */
37
+ export interface GcpProviderConfig {
38
+ project?: string;
39
+ region?: string;
40
+ zone?: string;
41
+ credentials?: string;
42
+ }
43
+
44
+ /** Default options applied to all resources. */
45
+ export interface DefaultsConfig {
46
+ /**
47
+ * Tags merged into every taggable resource via the cloud provider's
48
+ * native defaultTags (AWS) or defaultLabels (GCP).
49
+ *
50
+ * `stage` and `project` are auto-injected. User tags override auto-injected ones.
51
+ * Per-resource tags override default tags.
52
+ */
53
+ tags?: Record<string, string>;
54
+ }
55
+
56
+ /**
57
+ * Configuration for the App class.
58
+ */
59
+ export interface AppConfig {
60
+ /** The infrastructure definition callback. Required. */
61
+ run: (ctx: Context) => void;
62
+
63
+ /** Default resource options applied to all resources. */
64
+ defaults?: DefaultsConfig;
65
+
66
+ /**
67
+ * Named provider configurations.
68
+ * Keys follow the pattern "cloud" or "cloud.name":
69
+ * "aws" → default AWS provider
70
+ * "aws.us" → named AWS provider for US region
71
+ * "gcp" → default GCP provider
72
+ * "gcp.eu" → named GCP provider for EU
73
+ */
74
+ providers?: Record<string, AwsProviderConfig | GcpProviderConfig>;
75
+
76
+ /**
77
+ * Called before the infrastructure program runs.
78
+ * @future Not yet implemented — reserved for forward compatibility.
79
+ */
80
+ beforeDeploy?: (ctx: Context) => void;
81
+
82
+ /**
83
+ * Called after the infrastructure program completes successfully.
84
+ * @future CLI-side hook — not yet implemented.
85
+ */
86
+ afterDeploy?: (ctx: Context) => void;
87
+
88
+ /**
89
+ * Called if the infrastructure program throws an error.
90
+ * @future CLI-side hook — not yet implemented.
91
+ */
92
+ onError?: (ctx: Context, error: Error) => void;
93
+ }
94
+
95
+ // ── Helpers ────────────────────────────────────────────────
96
+
97
+ /**
98
+ * Determine which cloud a provider key belongs to.
99
+ * "aws" → "aws", "aws.us" → "aws", "gcp.eu" → "gcp"
100
+ */
101
+ function getCloud(key: string): string {
102
+ const dot = key.indexOf('.');
103
+ return dot === -1 ? key : key.substring(0, dot);
104
+ }
105
+
106
+ // ── App ────────────────────────────────────────────────────
107
+
108
+ /**
109
+ * App is the entry point for an Anvil infrastructure program.
110
+ *
111
+ * It wraps Pulumi's runtime so users never import `@pulumi/pulumi` directly.
112
+ * The constructor reads stage/project from Pulumi config, creates providers
113
+ * with default tags, and invokes the user's `run` callback with a Context.
114
+ *
115
+ * @example
116
+ * ```typescript
117
+ * import { App } from "@anvil-cloud/sdk";
118
+ * import * as anvil from "@anvil-cloud/sdk";
119
+ *
120
+ * export default new App({
121
+ * defaults: {
122
+ * tags: { team: "platform" },
123
+ * },
124
+ * providers: {
125
+ * "aws": { region: "ap-southeast-2" },
126
+ * "aws.us": { region: "us-east-1" },
127
+ * },
128
+ * run(ctx) {
129
+ * const bucket = new anvil.aws.Bucket("my-data", {
130
+ * dataClassification: "sensitive",
131
+ * });
132
+ * ctx.export("bucketName", bucket.bucketName);
133
+ * },
134
+ * });
135
+ * ```
136
+ */
137
+ export class App {
138
+ /** Stack outputs collected via ctx.export(). */
139
+ [key: string]: any;
140
+
141
+ constructor(config: AppConfig) {
142
+ // ── Read config from Pulumi ────────────────────────
143
+ const anvilConfig = new pulumi.Config('anvil');
144
+ const stage = anvilConfig.require('stage');
145
+ const project = pulumi.getProject();
146
+
147
+ // ── Build default tags ─────────────────────────────
148
+ const autoTags: Record<string, string> = { stage, project };
149
+ const userTags = config.defaults?.tags ?? {};
150
+ const mergedTags: Record<string, string> = { ...autoTags, ...userTags };
151
+
152
+ // ── Create providers ───────────────────────────────
153
+ const providers: Record<string, pulumi.ProviderResource> = {};
154
+ const defaultProviders: Record<string, pulumi.ProviderResource> = {};
155
+
156
+ if (config.providers) {
157
+ for (const [key, providerConfig] of Object.entries(config.providers)) {
158
+ const cloud = getCloud(key);
159
+ const isDefault = !key.includes('.');
160
+ const providerName = `anvil-provider-${key}`;
161
+
162
+ let provider: pulumi.ProviderResource;
163
+
164
+ switch (cloud) {
165
+ case 'aws': {
166
+ const awsConfig = providerConfig as AwsProviderConfig;
167
+ const awsArgs: Record<string, any> = {
168
+ defaultTags: { tags: mergedTags },
169
+ };
170
+
171
+ if (awsConfig.region) awsArgs.region = awsConfig.region;
172
+ if (awsConfig.profile) awsArgs.profile = awsConfig.profile;
173
+ if (awsConfig.assumeRole) {
174
+ awsArgs.assumeRoles = [
175
+ {
176
+ roleArn: awsConfig.assumeRole.roleArn,
177
+ sessionName: awsConfig.assumeRole.sessionName,
178
+ externalId: awsConfig.assumeRole.externalId,
179
+ },
180
+ ];
181
+ }
182
+
183
+ provider = new aws.Provider(providerName, awsArgs);
184
+ break;
185
+ }
186
+
187
+ case 'gcp': {
188
+ const gcpConfig = providerConfig as GcpProviderConfig;
189
+ const gcpArgs: Record<string, any> = {
190
+ defaultLabels: mergedTags,
191
+ };
192
+
193
+ if (gcpConfig.project) gcpArgs.project = gcpConfig.project;
194
+ if (gcpConfig.region) gcpArgs.region = gcpConfig.region;
195
+ if (gcpConfig.zone) gcpArgs.zone = gcpConfig.zone;
196
+ if (gcpConfig.credentials)
197
+ gcpArgs.credentials = gcpConfig.credentials;
198
+
199
+ provider = new gcp.Provider(providerName, gcpArgs);
200
+ break;
201
+ }
202
+
203
+ default:
204
+ throw new Error(
205
+ `Unknown cloud provider "${cloud}" in providers config key "${key}".\n` +
206
+ ` Supported prefixes: "aws", "gcp".`
207
+ );
208
+ }
209
+
210
+ providers[key] = provider;
211
+ if (isDefault) {
212
+ defaultProviders[cloud] = provider;
213
+ }
214
+ }
215
+ }
216
+
217
+ // If no providers configured but defaults.tags is set,
218
+ // create implicit default providers to carry the tags.
219
+ if (!config.providers && config.defaults?.tags) {
220
+ const awsProvider = new aws.Provider('anvil-provider-aws', {
221
+ defaultTags: { tags: mergedTags },
222
+ });
223
+ providers['aws'] = awsProvider;
224
+ defaultProviders['aws'] = awsProvider;
225
+
226
+ const gcpProvider = new gcp.Provider('anvil-provider-gcp', {
227
+ defaultLabels: mergedTags,
228
+ });
229
+ providers['gcp'] = gcpProvider;
230
+ defaultProviders['gcp'] = gcpProvider;
231
+ }
232
+
233
+ // ── Register default provider injection ────────────
234
+ if (Object.keys(defaultProviders).length > 0) {
235
+ pulumi.runtime.registerStackTransformation(
236
+ (
237
+ args: pulumi.ResourceTransformationArgs
238
+ ): pulumi.ResourceTransformationResult | undefined => {
239
+ const typeParts = args.type.split(':');
240
+ let cloud: string | undefined;
241
+
242
+ if (typeParts[0] === 'anvil' && typeParts.length >= 3) {
243
+ cloud = typeParts[1];
244
+ } else if (typeParts.length >= 2) {
245
+ cloud = typeParts[0];
246
+ }
247
+
248
+ if (cloud && defaultProviders[cloud] && !args.opts.provider) {
249
+ return {
250
+ props: args.props,
251
+ opts: pulumi.mergeOptions(args.opts, {
252
+ provider: defaultProviders[cloud],
253
+ }),
254
+ };
255
+ }
256
+
257
+ return undefined;
258
+ }
259
+ );
260
+ }
261
+
262
+ // ── Collect exports reference ──────────────────────
263
+ // We store exports on the App instance itself.
264
+ // Since the entry point does `export default new App(...)`,
265
+ // Pulumi picks up all enumerable properties as stack outputs.
266
+ const self = this;
267
+
268
+ // ── Create Context ─────────────────────────────────
269
+ const ctx: Context = {
270
+ stage,
271
+ project,
272
+ providers,
273
+ export(name: string, value: pulumi.Input<any>) {
274
+ self[name] = value;
275
+ },
276
+ };
277
+
278
+ // ── Execute ────────────────────────────────────────
279
+ try {
280
+ config.run(ctx);
281
+ } catch (error) {
282
+ throw error;
283
+ }
284
+ }
285
+ }
package/bin/app.d.ts ADDED
@@ -0,0 +1,110 @@
1
+ import * as pulumi from '@pulumi/pulumi';
2
+ /**
3
+ * Context passed to the App's run callback.
4
+ * This is the user's only interface to Anvil runtime information.
5
+ */
6
+ export interface Context {
7
+ /** The current deployment stage (e.g. "dev", "staging", "prod", or OS username). */
8
+ readonly stage: string;
9
+ /** The project name from anvil.yaml. */
10
+ readonly project: string;
11
+ /** Named providers, keyed by their config name (e.g. "aws", "aws.us", "gcp.eu"). */
12
+ readonly providers: Record<string, pulumi.ProviderResource>;
13
+ /** Export a stack output value without importing Pulumi. */
14
+ export(name: string, value: pulumi.Input<any>): void;
15
+ }
16
+ /** AWS provider configuration. */
17
+ export interface AwsProviderConfig {
18
+ region?: string;
19
+ profile?: string;
20
+ assumeRole?: {
21
+ roleArn: string;
22
+ sessionName?: string;
23
+ externalId?: string;
24
+ };
25
+ }
26
+ /** GCP provider configuration. */
27
+ export interface GcpProviderConfig {
28
+ project?: string;
29
+ region?: string;
30
+ zone?: string;
31
+ credentials?: string;
32
+ }
33
+ /** Default options applied to all resources. */
34
+ export interface DefaultsConfig {
35
+ /**
36
+ * Tags merged into every taggable resource via the cloud provider's
37
+ * native defaultTags (AWS) or defaultLabels (GCP).
38
+ *
39
+ * `stage` and `project` are auto-injected. User tags override auto-injected ones.
40
+ * Per-resource tags override default tags.
41
+ */
42
+ tags?: Record<string, string>;
43
+ }
44
+ /**
45
+ * Configuration for the App class.
46
+ */
47
+ export interface AppConfig {
48
+ /** The infrastructure definition callback. Required. */
49
+ run: (ctx: Context) => void;
50
+ /** Default resource options applied to all resources. */
51
+ defaults?: DefaultsConfig;
52
+ /**
53
+ * Named provider configurations.
54
+ * Keys follow the pattern "cloud" or "cloud.name":
55
+ * "aws" → default AWS provider
56
+ * "aws.us" → named AWS provider for US region
57
+ * "gcp" → default GCP provider
58
+ * "gcp.eu" → named GCP provider for EU
59
+ */
60
+ providers?: Record<string, AwsProviderConfig | GcpProviderConfig>;
61
+ /**
62
+ * Called before the infrastructure program runs.
63
+ * @future Not yet implemented — reserved for forward compatibility.
64
+ */
65
+ beforeDeploy?: (ctx: Context) => void;
66
+ /**
67
+ * Called after the infrastructure program completes successfully.
68
+ * @future CLI-side hook — not yet implemented.
69
+ */
70
+ afterDeploy?: (ctx: Context) => void;
71
+ /**
72
+ * Called if the infrastructure program throws an error.
73
+ * @future CLI-side hook — not yet implemented.
74
+ */
75
+ onError?: (ctx: Context, error: Error) => void;
76
+ }
77
+ /**
78
+ * App is the entry point for an Anvil infrastructure program.
79
+ *
80
+ * It wraps Pulumi's runtime so users never import `@pulumi/pulumi` directly.
81
+ * The constructor reads stage/project from Pulumi config, creates providers
82
+ * with default tags, and invokes the user's `run` callback with a Context.
83
+ *
84
+ * @example
85
+ * ```typescript
86
+ * import { App } from "@anvil-cloud/sdk";
87
+ * import * as anvil from "@anvil-cloud/sdk";
88
+ *
89
+ * export default new App({
90
+ * defaults: {
91
+ * tags: { team: "platform" },
92
+ * },
93
+ * providers: {
94
+ * "aws": { region: "ap-southeast-2" },
95
+ * "aws.us": { region: "us-east-1" },
96
+ * },
97
+ * run(ctx) {
98
+ * const bucket = new anvil.aws.Bucket("my-data", {
99
+ * dataClassification: "sensitive",
100
+ * });
101
+ * ctx.export("bucketName", bucket.bucketName);
102
+ * },
103
+ * });
104
+ * ```
105
+ */
106
+ export declare class App {
107
+ /** Stack outputs collected via ctx.export(). */
108
+ [key: string]: any;
109
+ constructor(config: AppConfig);
110
+ }
package/bin/app.js ADDED
@@ -0,0 +1,173 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.App = void 0;
4
+ const pulumi = require("@pulumi/pulumi");
5
+ const aws = require("@pulumi/aws");
6
+ const gcp = require("@pulumi/gcp");
7
+ // ── Helpers ────────────────────────────────────────────────
8
+ /**
9
+ * Determine which cloud a provider key belongs to.
10
+ * "aws" → "aws", "aws.us" → "aws", "gcp.eu" → "gcp"
11
+ */
12
+ function getCloud(key) {
13
+ const dot = key.indexOf('.');
14
+ return dot === -1 ? key : key.substring(0, dot);
15
+ }
16
+ // ── App ────────────────────────────────────────────────────
17
+ /**
18
+ * App is the entry point for an Anvil infrastructure program.
19
+ *
20
+ * It wraps Pulumi's runtime so users never import `@pulumi/pulumi` directly.
21
+ * The constructor reads stage/project from Pulumi config, creates providers
22
+ * with default tags, and invokes the user's `run` callback with a Context.
23
+ *
24
+ * @example
25
+ * ```typescript
26
+ * import { App } from "@anvil-cloud/sdk";
27
+ * import * as anvil from "@anvil-cloud/sdk";
28
+ *
29
+ * export default new App({
30
+ * defaults: {
31
+ * tags: { team: "platform" },
32
+ * },
33
+ * providers: {
34
+ * "aws": { region: "ap-southeast-2" },
35
+ * "aws.us": { region: "us-east-1" },
36
+ * },
37
+ * run(ctx) {
38
+ * const bucket = new anvil.aws.Bucket("my-data", {
39
+ * dataClassification: "sensitive",
40
+ * });
41
+ * ctx.export("bucketName", bucket.bucketName);
42
+ * },
43
+ * });
44
+ * ```
45
+ */
46
+ class App {
47
+ constructor(config) {
48
+ // ── Read config from Pulumi ────────────────────────
49
+ const anvilConfig = new pulumi.Config('anvil');
50
+ const stage = anvilConfig.require('stage');
51
+ const project = pulumi.getProject();
52
+ // ── Build default tags ─────────────────────────────
53
+ const autoTags = { stage, project };
54
+ const userTags = config.defaults?.tags ?? {};
55
+ const mergedTags = { ...autoTags, ...userTags };
56
+ // ── Create providers ───────────────────────────────
57
+ const providers = {};
58
+ const defaultProviders = {};
59
+ if (config.providers) {
60
+ for (const [key, providerConfig] of Object.entries(config.providers)) {
61
+ const cloud = getCloud(key);
62
+ const isDefault = !key.includes('.');
63
+ const providerName = `anvil-provider-${key}`;
64
+ let provider;
65
+ switch (cloud) {
66
+ case 'aws': {
67
+ const awsConfig = providerConfig;
68
+ const awsArgs = {
69
+ defaultTags: { tags: mergedTags },
70
+ };
71
+ if (awsConfig.region)
72
+ awsArgs.region = awsConfig.region;
73
+ if (awsConfig.profile)
74
+ awsArgs.profile = awsConfig.profile;
75
+ if (awsConfig.assumeRole) {
76
+ awsArgs.assumeRoles = [
77
+ {
78
+ roleArn: awsConfig.assumeRole.roleArn,
79
+ sessionName: awsConfig.assumeRole.sessionName,
80
+ externalId: awsConfig.assumeRole.externalId,
81
+ },
82
+ ];
83
+ }
84
+ provider = new aws.Provider(providerName, awsArgs);
85
+ break;
86
+ }
87
+ case 'gcp': {
88
+ const gcpConfig = providerConfig;
89
+ const gcpArgs = {
90
+ defaultLabels: mergedTags,
91
+ };
92
+ if (gcpConfig.project)
93
+ gcpArgs.project = gcpConfig.project;
94
+ if (gcpConfig.region)
95
+ gcpArgs.region = gcpConfig.region;
96
+ if (gcpConfig.zone)
97
+ gcpArgs.zone = gcpConfig.zone;
98
+ if (gcpConfig.credentials)
99
+ gcpArgs.credentials = gcpConfig.credentials;
100
+ provider = new gcp.Provider(providerName, gcpArgs);
101
+ break;
102
+ }
103
+ default:
104
+ throw new Error(`Unknown cloud provider "${cloud}" in providers config key "${key}".\n` +
105
+ ` Supported prefixes: "aws", "gcp".`);
106
+ }
107
+ providers[key] = provider;
108
+ if (isDefault) {
109
+ defaultProviders[cloud] = provider;
110
+ }
111
+ }
112
+ }
113
+ // If no providers configured but defaults.tags is set,
114
+ // create implicit default providers to carry the tags.
115
+ if (!config.providers && config.defaults?.tags) {
116
+ const awsProvider = new aws.Provider('anvil-provider-aws', {
117
+ defaultTags: { tags: mergedTags },
118
+ });
119
+ providers['aws'] = awsProvider;
120
+ defaultProviders['aws'] = awsProvider;
121
+ const gcpProvider = new gcp.Provider('anvil-provider-gcp', {
122
+ defaultLabels: mergedTags,
123
+ });
124
+ providers['gcp'] = gcpProvider;
125
+ defaultProviders['gcp'] = gcpProvider;
126
+ }
127
+ // ── Register default provider injection ────────────
128
+ if (Object.keys(defaultProviders).length > 0) {
129
+ pulumi.runtime.registerStackTransformation((args) => {
130
+ const typeParts = args.type.split(':');
131
+ let cloud;
132
+ if (typeParts[0] === 'anvil' && typeParts.length >= 3) {
133
+ cloud = typeParts[1];
134
+ }
135
+ else if (typeParts.length >= 2) {
136
+ cloud = typeParts[0];
137
+ }
138
+ if (cloud && defaultProviders[cloud] && !args.opts.provider) {
139
+ return {
140
+ props: args.props,
141
+ opts: pulumi.mergeOptions(args.opts, {
142
+ provider: defaultProviders[cloud],
143
+ }),
144
+ };
145
+ }
146
+ return undefined;
147
+ });
148
+ }
149
+ // ── Collect exports reference ──────────────────────
150
+ // We store exports on the App instance itself.
151
+ // Since the entry point does `export default new App(...)`,
152
+ // Pulumi picks up all enumerable properties as stack outputs.
153
+ const self = this;
154
+ // ── Create Context ─────────────────────────────────
155
+ const ctx = {
156
+ stage,
157
+ project,
158
+ providers,
159
+ export(name, value) {
160
+ self[name] = value;
161
+ },
162
+ };
163
+ // ── Execute ────────────────────────────────────────
164
+ try {
165
+ config.run(ctx);
166
+ }
167
+ catch (error) {
168
+ throw error;
169
+ }
170
+ }
171
+ }
172
+ exports.App = App;
173
+ //# sourceMappingURL=app.js.map
package/bin/app.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app.js","sourceRoot":"","sources":["../app.ts"],"names":[],"mappings":"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"}
package/bin/block.d.ts ADDED
@@ -0,0 +1,53 @@
1
+ import * as pulumi from '@pulumi/pulumi';
2
+ /**
3
+ * Block is an optional organisational grouping for Anvil resources.
4
+ *
5
+ * Resources created inside a Block constructor are automatically parented
6
+ * to the Block via Pulumi's built-in auto-parenting. Blocks can expose
7
+ * public properties (outputs) for cross-Block references.
8
+ *
9
+ * `stage` and `project` are automatically available via `this.stage` and
10
+ * `this.project` — read from the same Pulumi config that App sets.
11
+ *
12
+ * Tagging is automatic — the App's provider injection applies `defaultTags`
13
+ * to all resources, including those inside Blocks.
14
+ *
15
+ * Blocks are purely optional — flat top-level resources work identically.
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * import * as anvil from "@anvil-cloud/sdk";
20
+ *
21
+ * class RulesEngine extends anvil.Block {
22
+ * public readonly bucketName: pulumi.Output<string>;
23
+ *
24
+ * constructor(name: string, opts?: pulumi.ComponentResourceOptions) {
25
+ * super(name, {}, opts);
26
+ *
27
+ * const bucket = new anvil.aws.Bucket("events", {
28
+ * dataClassification: "internal",
29
+ * transform: {
30
+ * bucket: { bucket: `rules-${this.stage}-events` },
31
+ * },
32
+ * });
33
+ * }
34
+ * }
35
+ * ```
36
+ */
37
+ export interface BlockArgs {
38
+ [key: string]: any;
39
+ }
40
+ export declare class Block extends pulumi.ComponentResource {
41
+ /** The current deployment stage (e.g. "dev", "staging", "prod", or OS username). */
42
+ readonly stage: string;
43
+ /** The project name from anvil.yaml. */
44
+ readonly project: string;
45
+ /**
46
+ * Creates a new Block.
47
+ *
48
+ * @param name The unique name of this Block within the stack.
49
+ * @param args Optional arguments (passed through for subclass use).
50
+ * @param opts Standard Pulumi ComponentResourceOptions (aliases, providers, etc.).
51
+ */
52
+ constructor(name: string, args?: BlockArgs, opts?: pulumi.ComponentResourceOptions);
53
+ }
package/bin/block.js ADDED
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Block = void 0;
4
+ const pulumi = require("@pulumi/pulumi");
5
+ class Block extends pulumi.ComponentResource {
6
+ /**
7
+ * Creates a new Block.
8
+ *
9
+ * @param name The unique name of this Block within the stack.
10
+ * @param args Optional arguments (passed through for subclass use).
11
+ * @param opts Standard Pulumi ComponentResourceOptions (aliases, providers, etc.).
12
+ */
13
+ constructor(name, args, opts) {
14
+ const typeName = new.target?.name ?? name;
15
+ super(`anvil:block:${typeName}`, name, args ?? {}, opts);
16
+ const anvilConfig = new pulumi.Config('anvil');
17
+ this.stage = anvilConfig.require('stage');
18
+ this.project = pulumi.getProject();
19
+ }
20
+ }
21
+ exports.Block = Block;
22
+ //# sourceMappingURL=block.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"block.js","sourceRoot":"","sources":["../block.ts"],"names":[],"mappings":";;;AAAA,yCAAyC;AA0CzC,MAAa,KAAM,SAAQ,MAAM,CAAC,iBAAiB;IAOjD;;;;;;OAMG;IACH,YACE,IAAY,EACZ,IAAgB,EAChB,IAAsC;QAEtC,MAAM,QAAQ,GAAG,GAAG,CAAC,MAAM,EAAE,IAAI,IAAI,IAAI,CAAC;QAC1C,KAAK,CAAC,eAAe,QAAQ,EAAE,EAAE,IAAI,EAAE,IAAI,IAAI,EAAE,EAAE,IAAI,CAAC,CAAC;QAEzD,MAAM,WAAW,GAAG,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAC/C,IAAI,CAAC,KAAK,GAAG,WAAW,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC1C,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;IACrC,CAAC;CACF;AA1BD,sBA0BC"}
package/bin/index.d.ts CHANGED
@@ -5,3 +5,5 @@ import * as aws from "./aws";
5
5
  import * as gcp from "./gcp";
6
6
  import * as types from "./types";
7
7
  export { aws, gcp, types, };
8
+ export { App, AppConfig, Context, AwsProviderConfig, GcpProviderConfig, DefaultsConfig } from "./app";
9
+ export { Block, BlockArgs } from "./block";
package/bin/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  // *** WARNING: this file was generated by pulumi-language-nodejs. ***
3
3
  // *** Do not edit by hand unless you're certain you know what you are doing! ***
4
4
  Object.defineProperty(exports, "__esModule", { value: true });
5
- exports.types = exports.gcp = exports.aws = exports.Provider = void 0;
5
+ exports.Block = exports.App = exports.types = exports.gcp = exports.aws = exports.Provider = void 0;
6
6
  const pulumi = require("@pulumi/pulumi");
7
7
  const utilities = require("./utilities");
8
8
  exports.Provider = null;
@@ -23,4 +23,10 @@ pulumi.runtime.registerResourcePackage("anvil", {
23
23
  return new exports.Provider(name, undefined, { urn });
24
24
  },
25
25
  });
26
+ // Hand-written App class
27
+ var app_1 = require("./app");
28
+ Object.defineProperty(exports, "App", { enumerable: true, get: function () { return app_1.App; } });
29
+ // Hand-written Block class
30
+ var block_1 = require("./block");
31
+ Object.defineProperty(exports, "Block", { enumerable: true, get: function () { return block_1.Block; } });
26
32
  //# sourceMappingURL=index.js.map
package/bin/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":";AAAA,sEAAsE;AACtE,iFAAiF;;;AAEjF,yCAAyC;AACzC,yCAAyC;AAK5B,QAAA,QAAQ,GAAyC,IAAW,CAAC;AAC1E,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,UAAU,CAAC,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC;AAGvE,sBAAsB;AACtB,6BAA6B;AAKzB,kBAAG;AAJP,6BAA6B;AAKzB,kBAAG;AAJP,iCAAiC;AAK7B,sBAAK;AAET,MAAM,CAAC,OAAO,CAAC,uBAAuB,CAAC,OAAO,EAAE;IAC5C,OAAO,EAAE,SAAS,CAAC,UAAU,EAAE;IAC/B,iBAAiB,EAAE,CAAC,IAAY,EAAE,IAAY,EAAE,GAAW,EAA2B,EAAE;QACpF,IAAI,IAAI,KAAK,wBAAwB,EAAE;YACnC,MAAM,IAAI,KAAK,CAAC,yBAAyB,IAAI,EAAE,CAAC,CAAC;SACpD;QACD,OAAO,IAAI,gBAAQ,CAAC,IAAI,EAAO,SAAS,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;IACvD,CAAC;CACJ,CAAC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":";AAAA,sEAAsE;AACtE,iFAAiF;;;AAEjF,yCAAyC;AACzC,yCAAyC;AAK5B,QAAA,QAAQ,GAAyC,IAAW,CAAC;AAC1E,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,UAAU,CAAC,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC;AAGvE,sBAAsB;AACtB,6BAA6B;AAKzB,kBAAG;AAJP,6BAA6B;AAKzB,kBAAG;AAJP,iCAAiC;AAK7B,sBAAK;AAET,MAAM,CAAC,OAAO,CAAC,uBAAuB,CAAC,OAAO,EAAE;IAC5C,OAAO,EAAE,SAAS,CAAC,UAAU,EAAE;IAC/B,iBAAiB,EAAE,CAAC,IAAY,EAAE,IAAY,EAAE,GAAW,EAA2B,EAAE;QACpF,IAAI,IAAI,KAAK,wBAAwB,EAAE;YACnC,MAAM,IAAI,KAAK,CAAC,yBAAyB,IAAI,EAAE,CAAC,CAAC;SACpD;QACD,OAAO,IAAI,gBAAQ,CAAC,IAAI,EAAO,SAAS,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;IACvD,CAAC;CACJ,CAAC,CAAC;AAEH,yBAAyB;AACzB,6BAAsG;AAA7F,0FAAA,GAAG,OAAA;AAEZ,2BAA2B;AAC3B,iCAA2C;AAAlC,8FAAA,KAAK,OAAA"}
package/bin/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anvil-cloud/sdk",
3
- "version": "0.0.4",
3
+ "version": "0.0.6",
4
4
  "scripts": {
5
5
  "build": "tsc && cp package.json bin/"
6
6
  },
package/block.ts ADDED
@@ -0,0 +1,69 @@
1
+ import * as pulumi from '@pulumi/pulumi';
2
+
3
+ /**
4
+ * Block is an optional organisational grouping for Anvil resources.
5
+ *
6
+ * Resources created inside a Block constructor are automatically parented
7
+ * to the Block via Pulumi's built-in auto-parenting. Blocks can expose
8
+ * public properties (outputs) for cross-Block references.
9
+ *
10
+ * `stage` and `project` are automatically available via `this.stage` and
11
+ * `this.project` — read from the same Pulumi config that App sets.
12
+ *
13
+ * Tagging is automatic — the App's provider injection applies `defaultTags`
14
+ * to all resources, including those inside Blocks.
15
+ *
16
+ * Blocks are purely optional — flat top-level resources work identically.
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * import * as anvil from "@anvil-cloud/sdk";
21
+ *
22
+ * class RulesEngine extends anvil.Block {
23
+ * public readonly bucketName: pulumi.Output<string>;
24
+ *
25
+ * constructor(name: string, opts?: pulumi.ComponentResourceOptions) {
26
+ * super(name, {}, opts);
27
+ *
28
+ * const bucket = new anvil.aws.Bucket("events", {
29
+ * dataClassification: "internal",
30
+ * transform: {
31
+ * bucket: { bucket: `rules-${this.stage}-events` },
32
+ * },
33
+ * });
34
+ * }
35
+ * }
36
+ * ```
37
+ */
38
+
39
+ export interface BlockArgs {
40
+ [key: string]: any;
41
+ }
42
+
43
+ export class Block extends pulumi.ComponentResource {
44
+ /** The current deployment stage (e.g. "dev", "staging", "prod", or OS username). */
45
+ public readonly stage: string;
46
+
47
+ /** The project name from anvil.yaml. */
48
+ public readonly project: string;
49
+
50
+ /**
51
+ * Creates a new Block.
52
+ *
53
+ * @param name The unique name of this Block within the stack.
54
+ * @param args Optional arguments (passed through for subclass use).
55
+ * @param opts Standard Pulumi ComponentResourceOptions (aliases, providers, etc.).
56
+ */
57
+ constructor(
58
+ name: string,
59
+ args?: BlockArgs,
60
+ opts?: pulumi.ComponentResourceOptions
61
+ ) {
62
+ const typeName = new.target?.name ?? name;
63
+ super(`anvil:block:${typeName}`, name, args ?? {}, opts);
64
+
65
+ const anvilConfig = new pulumi.Config('anvil');
66
+ this.stage = anvilConfig.require('stage');
67
+ this.project = pulumi.getProject();
68
+ }
69
+ }
package/index.ts CHANGED
@@ -30,3 +30,9 @@ pulumi.runtime.registerResourcePackage("anvil", {
30
30
  return new Provider(name, <any>undefined, { urn });
31
31
  },
32
32
  });
33
+
34
+ // Hand-written App class
35
+ export { App, AppConfig, Context, AwsProviderConfig, GcpProviderConfig, DefaultsConfig } from "./app";
36
+
37
+ // Hand-written Block class
38
+ export { Block, BlockArgs } from "./block";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anvil-cloud/sdk",
3
- "version": "0.0.4",
3
+ "version": "0.0.6",
4
4
  "scripts": {
5
5
  "build": "tsc && cp package.json bin/"
6
6
  },