@infracraft/pulumi 1.29.2 → 1.30.0
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 +32 -11
- package/dist/dynamic/resolve-credential.cjs +40 -0
- package/dist/dynamic/resolve-credential.cjs.map +1 -0
- package/dist/dynamic/resolve-credential.d.cts +27 -0
- package/dist/dynamic/resolve-credential.d.cts.map +1 -0
- package/dist/dynamic/resolve-credential.d.mts +27 -0
- package/dist/dynamic/resolve-credential.d.mts.map +1 -0
- package/dist/dynamic/resolve-credential.mjs +37 -0
- package/dist/dynamic/resolve-credential.mjs.map +1 -0
- package/dist/fly/app.cjs +4 -2
- package/dist/fly/app.cjs.map +1 -1
- package/dist/fly/app.d.cts +4 -2
- package/dist/fly/app.d.cts.map +1 -1
- package/dist/fly/app.d.mts +4 -2
- package/dist/fly/app.d.mts.map +1 -1
- package/dist/fly/app.mjs +4 -2
- package/dist/fly/app.mjs.map +1 -1
- package/dist/fly/certificate.cjs +5 -3
- package/dist/fly/certificate.cjs.map +1 -1
- package/dist/fly/certificate.d.cts +4 -2
- package/dist/fly/certificate.d.cts.map +1 -1
- package/dist/fly/certificate.d.mts +4 -2
- package/dist/fly/certificate.d.mts.map +1 -1
- package/dist/fly/certificate.mjs +5 -3
- package/dist/fly/certificate.mjs.map +1 -1
- package/dist/fly/deploy.cjs +2 -1
- package/dist/fly/deploy.cjs.map +1 -1
- package/dist/fly/deploy.d.cts.map +1 -1
- package/dist/fly/deploy.d.mts.map +1 -1
- package/dist/fly/deploy.mjs +2 -1
- package/dist/fly/deploy.mjs.map +1 -1
- package/dist/fly/ip.cjs +4 -2
- package/dist/fly/ip.cjs.map +1 -1
- package/dist/fly/ip.d.cts +4 -2
- package/dist/fly/ip.d.cts.map +1 -1
- package/dist/fly/ip.d.mts +4 -2
- package/dist/fly/ip.d.mts.map +1 -1
- package/dist/fly/ip.mjs +4 -2
- package/dist/fly/ip.mjs.map +1 -1
- package/dist/fly/provider.cjs +6 -2
- package/dist/fly/provider.cjs.map +1 -1
- package/dist/fly/provider.d.cts +24 -6
- package/dist/fly/provider.d.cts.map +1 -1
- package/dist/fly/provider.d.mts +24 -6
- package/dist/fly/provider.d.mts.map +1 -1
- package/dist/fly/provider.mjs +6 -2
- package/dist/fly/provider.mjs.map +1 -1
- package/dist/fly/secret.cjs +5 -3
- package/dist/fly/secret.cjs.map +1 -1
- package/dist/fly/secret.d.cts +4 -2
- package/dist/fly/secret.d.cts.map +1 -1
- package/dist/fly/secret.d.mts +4 -2
- package/dist/fly/secret.d.mts.map +1 -1
- package/dist/fly/secret.mjs +5 -3
- package/dist/fly/secret.mjs.map +1 -1
- package/dist/fly/volume.cjs +6 -4
- package/dist/fly/volume.cjs.map +1 -1
- package/dist/fly/volume.d.cts +4 -2
- package/dist/fly/volume.d.cts.map +1 -1
- package/dist/fly/volume.d.mts +4 -2
- package/dist/fly/volume.d.mts.map +1 -1
- package/dist/fly/volume.mjs +6 -4
- package/dist/fly/volume.mjs.map +1 -1
- package/dist/neon/branch.cjs +5 -3
- package/dist/neon/branch.cjs.map +1 -1
- package/dist/neon/branch.d.cts +4 -2
- package/dist/neon/branch.d.cts.map +1 -1
- package/dist/neon/branch.d.mts +4 -2
- package/dist/neon/branch.d.mts.map +1 -1
- package/dist/neon/branch.mjs +5 -3
- package/dist/neon/branch.mjs.map +1 -1
- package/dist/neon/database.cjs +5 -3
- package/dist/neon/database.cjs.map +1 -1
- package/dist/neon/database.d.cts.map +1 -1
- package/dist/neon/database.d.mts.map +1 -1
- package/dist/neon/database.mjs +5 -3
- package/dist/neon/database.mjs.map +1 -1
- package/dist/neon/endpoint.cjs +6 -4
- package/dist/neon/endpoint.cjs.map +1 -1
- package/dist/neon/endpoint.d.cts.map +1 -1
- package/dist/neon/endpoint.d.mts.map +1 -1
- package/dist/neon/endpoint.mjs +6 -4
- package/dist/neon/endpoint.mjs.map +1 -1
- package/dist/neon/project.cjs +5 -3
- package/dist/neon/project.cjs.map +1 -1
- package/dist/neon/project.d.cts.map +1 -1
- package/dist/neon/project.d.mts.map +1 -1
- package/dist/neon/project.mjs +5 -3
- package/dist/neon/project.mjs.map +1 -1
- package/dist/neon/provider.cjs +9 -3
- package/dist/neon/provider.cjs.map +1 -1
- package/dist/neon/provider.d.cts +21 -6
- package/dist/neon/provider.d.cts.map +1 -1
- package/dist/neon/provider.d.mts +21 -6
- package/dist/neon/provider.d.mts.map +1 -1
- package/dist/neon/provider.mjs +9 -3
- package/dist/neon/provider.mjs.map +1 -1
- package/dist/neon/role.cjs +6 -4
- package/dist/neon/role.cjs.map +1 -1
- package/dist/neon/role.d.cts +4 -2
- package/dist/neon/role.d.cts.map +1 -1
- package/dist/neon/role.d.mts +4 -2
- package/dist/neon/role.d.mts.map +1 -1
- package/dist/neon/role.mjs +6 -4
- package/dist/neon/role.mjs.map +1 -1
- package/dist/railway/bin/monitor-deployment.cjs +3 -1
- package/dist/railway/bin/monitor-deployment.cjs.map +1 -1
- package/dist/railway/bin/monitor-deployment.mjs +3 -1
- package/dist/railway/bin/monitor-deployment.mjs.map +1 -1
- package/dist/railway/deploy.cjs +7 -1
- package/dist/railway/deploy.cjs.map +1 -1
- package/dist/railway/deploy.d.cts +11 -0
- package/dist/railway/deploy.d.cts.map +1 -1
- package/dist/railway/deploy.d.mts +11 -0
- package/dist/railway/deploy.d.mts.map +1 -1
- package/dist/railway/deploy.mjs +7 -1
- package/dist/railway/deploy.mjs.map +1 -1
- package/dist/railway/deployment-monitor.cjs +43 -8
- package/dist/railway/deployment-monitor.cjs.map +1 -1
- package/dist/railway/deployment-monitor.d.cts +9 -0
- package/dist/railway/deployment-monitor.d.cts.map +1 -1
- package/dist/railway/deployment-monitor.d.mts +9 -0
- package/dist/railway/deployment-monitor.d.mts.map +1 -1
- package/dist/railway/deployment-monitor.mjs +43 -8
- package/dist/railway/deployment-monitor.mjs.map +1 -1
- package/dist/railway/domain.cjs +5 -3
- package/dist/railway/domain.cjs.map +1 -1
- package/dist/railway/domain.d.cts +4 -2
- package/dist/railway/domain.d.cts.map +1 -1
- package/dist/railway/domain.d.mts +4 -2
- package/dist/railway/domain.d.mts.map +1 -1
- package/dist/railway/domain.mjs +5 -3
- package/dist/railway/domain.mjs.map +1 -1
- package/dist/railway/environment.cjs +6 -4
- package/dist/railway/environment.cjs.map +1 -1
- package/dist/railway/environment.d.cts +4 -2
- package/dist/railway/environment.d.cts.map +1 -1
- package/dist/railway/environment.d.mts +4 -2
- package/dist/railway/environment.d.mts.map +1 -1
- package/dist/railway/environment.mjs +6 -4
- package/dist/railway/environment.mjs.map +1 -1
- package/dist/railway/project-token.cjs +4 -2
- package/dist/railway/project-token.cjs.map +1 -1
- package/dist/railway/project-token.d.cts +4 -2
- package/dist/railway/project-token.d.cts.map +1 -1
- package/dist/railway/project-token.d.mts +4 -2
- package/dist/railway/project-token.d.mts.map +1 -1
- package/dist/railway/project-token.mjs +4 -2
- package/dist/railway/project-token.mjs.map +1 -1
- package/dist/railway/project.cjs +5 -3
- package/dist/railway/project.cjs.map +1 -1
- package/dist/railway/project.d.cts.map +1 -1
- package/dist/railway/project.d.mts.map +1 -1
- package/dist/railway/project.mjs +5 -3
- package/dist/railway/project.mjs.map +1 -1
- package/dist/railway/provider.cjs +9 -3
- package/dist/railway/provider.cjs.map +1 -1
- package/dist/railway/provider.d.cts +21 -6
- package/dist/railway/provider.d.cts.map +1 -1
- package/dist/railway/provider.d.mts +21 -6
- package/dist/railway/provider.d.mts.map +1 -1
- package/dist/railway/provider.mjs +9 -3
- package/dist/railway/provider.mjs.map +1 -1
- package/dist/railway/service.cjs +50 -9
- package/dist/railway/service.cjs.map +1 -1
- package/dist/railway/service.d.cts +4 -2
- package/dist/railway/service.d.cts.map +1 -1
- package/dist/railway/service.d.mts +4 -2
- package/dist/railway/service.d.mts.map +1 -1
- package/dist/railway/service.mjs +50 -9
- package/dist/railway/service.mjs.map +1 -1
- package/dist/railway/variable.cjs +5 -3
- package/dist/railway/variable.cjs.map +1 -1
- package/dist/railway/variable.d.cts +4 -2
- package/dist/railway/variable.d.cts.map +1 -1
- package/dist/railway/variable.d.mts +4 -2
- package/dist/railway/variable.d.mts.map +1 -1
- package/dist/railway/variable.mjs +5 -3
- package/dist/railway/variable.mjs.map +1 -1
- package/dist/railway/volume.cjs +5 -3
- package/dist/railway/volume.cjs.map +1 -1
- package/dist/railway/volume.d.cts +4 -2
- package/dist/railway/volume.d.cts.map +1 -1
- package/dist/railway/volume.d.mts +4 -2
- package/dist/railway/volume.d.mts.map +1 -1
- package/dist/railway/volume.mjs +5 -3
- package/dist/railway/volume.mjs.map +1 -1
- package/dist/vercel/deploy.cjs +2 -1
- package/dist/vercel/deploy.cjs.map +1 -1
- package/dist/vercel/deploy.d.cts.map +1 -1
- package/dist/vercel/deploy.d.mts.map +1 -1
- package/dist/vercel/deploy.mjs +2 -1
- package/dist/vercel/deploy.mjs.map +1 -1
- package/dist/vercel/domain.cjs +5 -3
- package/dist/vercel/domain.cjs.map +1 -1
- package/dist/vercel/domain.d.cts +4 -2
- package/dist/vercel/domain.d.cts.map +1 -1
- package/dist/vercel/domain.d.mts +4 -2
- package/dist/vercel/domain.d.mts.map +1 -1
- package/dist/vercel/domain.mjs +5 -3
- package/dist/vercel/domain.mjs.map +1 -1
- package/dist/vercel/integration.cjs +3 -1
- package/dist/vercel/integration.cjs.map +1 -1
- package/dist/vercel/integration.d.cts +4 -2
- package/dist/vercel/integration.d.cts.map +1 -1
- package/dist/vercel/integration.d.mts +4 -2
- package/dist/vercel/integration.d.mts.map +1 -1
- package/dist/vercel/integration.mjs +3 -1
- package/dist/vercel/integration.mjs.map +1 -1
- package/dist/vercel/marketplace-resource.cjs +3 -1
- package/dist/vercel/marketplace-resource.cjs.map +1 -1
- package/dist/vercel/marketplace-resource.d.cts +4 -2
- package/dist/vercel/marketplace-resource.d.cts.map +1 -1
- package/dist/vercel/marketplace-resource.d.mts +4 -2
- package/dist/vercel/marketplace-resource.d.mts.map +1 -1
- package/dist/vercel/marketplace-resource.mjs +3 -1
- package/dist/vercel/marketplace-resource.mjs.map +1 -1
- package/dist/vercel/project.cjs +7 -5
- package/dist/vercel/project.cjs.map +1 -1
- package/dist/vercel/project.d.cts +4 -2
- package/dist/vercel/project.d.cts.map +1 -1
- package/dist/vercel/project.d.mts +4 -2
- package/dist/vercel/project.d.mts.map +1 -1
- package/dist/vercel/project.mjs +7 -5
- package/dist/vercel/project.mjs.map +1 -1
- package/dist/vercel/provider.cjs +6 -2
- package/dist/vercel/provider.cjs.map +1 -1
- package/dist/vercel/provider.d.cts +21 -6
- package/dist/vercel/provider.d.cts.map +1 -1
- package/dist/vercel/provider.d.mts +21 -6
- package/dist/vercel/provider.d.mts.map +1 -1
- package/dist/vercel/provider.mjs +6 -2
- package/dist/vercel/provider.mjs.map +1 -1
- package/dist/vercel/resource-connection.cjs +3 -1
- package/dist/vercel/resource-connection.cjs.map +1 -1
- package/dist/vercel/resource-connection.d.cts +4 -2
- package/dist/vercel/resource-connection.d.cts.map +1 -1
- package/dist/vercel/resource-connection.d.mts +4 -2
- package/dist/vercel/resource-connection.d.mts.map +1 -1
- package/dist/vercel/resource-connection.mjs +3 -1
- package/dist/vercel/resource-connection.mjs.map +1 -1
- package/dist/vercel/variable.cjs +6 -4
- package/dist/vercel/variable.cjs.map +1 -1
- package/dist/vercel/variable.d.cts +4 -2
- package/dist/vercel/variable.d.cts.map +1 -1
- package/dist/vercel/variable.d.mts +4 -2
- package/dist/vercel/variable.d.mts.map +1 -1
- package/dist/vercel/variable.mjs +6 -4
- package/dist/vercel/variable.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -24,7 +24,7 @@ Native Pulumi providers with adopt-or-create semantics and deploy orchestration.
|
|
|
24
24
|
- **Inputs fail at plan time.** `check()` rejects locally decidable mistakes during preview with the offending property named: `RailwayVolume.mountPath` (must be absolute), `RailwayService.source.image` / `RailwayProjectToken.name` / `NeonBranch.name` / `NeonRole.name` (non-empty), `VercelProject.name` (Vercel's published naming rule), `FlyVolume.sizeGb` (positive integer). Preview-unknown inputs are skipped, never failed.
|
|
25
25
|
- **Previews stay faithful.** Identity outputs that provably survive an in-place update are declared stable (`RailwayProject.id`, `RailwayService.id`, `NeonProject.id`, `NeonEndpoint.host`, `NeonRole`'s identity, `VercelProject.id`, `FlyVolume.id`), so dependents keep known values during preview instead of showing phantom replaces. `NeonRole.password` is deliberately not stable — a rotation must cascade.
|
|
26
26
|
- **One resilient transport.** All HTTP goes through a single fetch wrapper with a per-attempt timeout, bounded retries on transient failures (network errors, 5xx, 429), and `Retry-After` support. See [Transport & errors](#transport--errors).
|
|
27
|
-
- **Secrets stay secret.** Provider credentials and minted values are marked secret in Pulumi state, and deploy tokens travel via stdin — never in command text.
|
|
27
|
+
- **Secrets stay secret.** Provider credentials and minted values are marked secret in Pulumi state, and deploy tokens travel via stdin — never in command text. Better yet, credentials can stay out of state entirely: every provider accepts the credential as an env var *name* instead of a value — see [Provider credentials](#provider-credentials).
|
|
28
28
|
- **Consumer-controlled triggers and protection.** Deploy resources accept a `triggers` array — hash source directories, env values, or anything else; you decide what causes a redeploy. Use `protect: true` on shared/production resources to prevent accidental deletion.
|
|
29
29
|
|
|
30
30
|
## Providers
|
|
@@ -50,6 +50,24 @@ bun add @infracraft/pulumi
|
|
|
50
50
|
|
|
51
51
|
Peer dependencies: `@pulumi/pulumi` ^3, `@pulumi/command` ^1 (optional)
|
|
52
52
|
|
|
53
|
+
## Provider credentials
|
|
54
|
+
|
|
55
|
+
Every provider takes its API credential in one of two mutually exclusive forms — the constructor throws unless exactly one is set:
|
|
56
|
+
|
|
57
|
+
- **Env-var-first (recommended)** — `tokenEnvVar` (Neon: `apiKeyEnvVar`): the *name* of an environment variable holding the credential. Resources carry only the plain name; each dynamic-provider operation reads the value from the environment at execution time and fails loudly — naming the variable — when it is unset.
|
|
58
|
+
- **Direct** — `token` (Neon: `apiKey`): a secret `Input<string>`, marked secret in per-resource state via `additionalSecretOutputs`.
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
const railway = new RailwayProvider("railway", { tokenEnvVar: "RAILWAY_TOKEN" })
|
|
62
|
+
const neon = new NeonProvider("neon", { apiKeyEnvVar: "NEON_API_KEY" })
|
|
63
|
+
const vercel = new VercelProvider("vercel", { tokenEnvVar: "VERCEL_TOKEN", teamId: "team_xxx" })
|
|
64
|
+
const fly = new FlyProvider("fly", { tokenEnvVar: "FLY_API_TOKEN" })
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Prefer the env-var form. It keeps the credential out of dynamic-resource inputs and per-resource state entirely, which removes the substrate for [pulumi/pulumi#16041](https://github.com/pulumi/pulumi/issues/16041) ("Unexpected struct type": secret Outputs in dynamic-provider inputs intermittently fail engine serialization — closed not-planned upstream) and matches how first-class provider configuration handles credentials. Dynamic-provider operations execute in the Pulumi CLI's plugin process, which inherits the program's environment — so variables provided by the shell or by an ESC environment's `environmentVariables` block reach them.
|
|
68
|
+
|
|
69
|
+
The deploy components that feed the credential to a CLI (`VercelDeploy`, `FlyDeploy`) and program-runtime lookups (`VercelProject.url`) resolve the env var at program runtime into a secret Output, so the command env still receives the actual value without it ever becoming a dynamic-resource input.
|
|
70
|
+
|
|
53
71
|
## Railway
|
|
54
72
|
|
|
55
73
|
```typescript
|
|
@@ -66,7 +84,7 @@ import {
|
|
|
66
84
|
import { hash } from "@infracraft/pulumi/hash"
|
|
67
85
|
|
|
68
86
|
const provider = new RailwayProvider("railway", {
|
|
69
|
-
token: config.requireSecret("railwayToken")
|
|
87
|
+
tokenEnvVar: "RAILWAY_TOKEN", // or token: config.requireSecret("railwayToken")
|
|
70
88
|
})
|
|
71
89
|
|
|
72
90
|
const project = new RailwayProject("my-project", {
|
|
@@ -105,6 +123,7 @@ const deployToken = new RailwayProjectToken("api-token", {
|
|
|
105
123
|
|
|
106
124
|
new RailwayDeploy("api-deploy", {
|
|
107
125
|
triggers: [hash("apps/api"), hash(env)], // hash(env): a non-secret digest, not raw secret values
|
|
126
|
+
healthcheckPath: "/health", // applied post-deploy by the monitor (see Healthcheck config)
|
|
108
127
|
}, { provider, project, environment, service, projectToken: deployToken.token })
|
|
109
128
|
```
|
|
110
129
|
|
|
@@ -112,20 +131,22 @@ new RailwayDeploy("api-deploy", {
|
|
|
112
131
|
|
|
113
132
|
| Class | Key outputs | Notes |
|
|
114
133
|
|---|---|---|
|
|
115
|
-
| `RailwayProvider` | — | Pass as `provider` option to every Railway resource |
|
|
134
|
+
| `RailwayProvider` | — | Pass as `provider` option to every Railway resource. `token` or `tokenEnvVar` — see [Provider credentials](#provider-credentials) |
|
|
116
135
|
| `RailwayProject` | `.id` (project UUID) | Adopt-or-create by name |
|
|
117
136
|
| `RailwayEnvironment` | `.id` | Optional `source` env to fork from |
|
|
118
|
-
| `RailwayService` | `.id` | Instance config (builder, commands, healthcheck, restart policy) applied per target environment; image services (`source.image`) are deployed by the provider itself |
|
|
137
|
+
| `RailwayService` | `.id` | Instance config (builder, commands, healthcheck, restart policy) applied per target environment; image services (`source.image`) are deployed by the provider itself. Healthcheck fields a fresh instance rejects are re-applied post-deploy — see [Healthcheck config](#railway-api-surface) below |
|
|
119
138
|
| `RailwayDomain` | `.fqdn`, `.cnameTarget`, `.verificationTxtName` / `.verificationTxtValue` | Omit `customDomain` for an auto-generated domain; custom domains expose the CNAME target and ownership-verification TXT record to write into DNS |
|
|
120
139
|
| `RailwayVariable` | — | Batch upsert; uses `skipDeploys` to avoid snapshot errors |
|
|
121
140
|
| `RailwayVolume` | — | Persistent volume; `mountPath` must be absolute. Adoption matches BOTH service and environment (never a sibling environment's volume); a newly attached volume redeploys its service so the mount lands |
|
|
122
141
|
| `RailwayProjectToken` | `.token` (secret) | Environment-scoped deploy token; feed into `RailwayDeploy`. Bump `tokenVersion` to rotate — see [Rotating credentials](#rotating-credentials) |
|
|
123
|
-
| `RailwayDeploy` | `.deploymentUrl` | Runs `railway up --detach`, then monitors the deployment via the Railway API (the API, not the CLI exit code, decides pass/fail). Also accepts `excludePaths` and `
|
|
142
|
+
| `RailwayDeploy` | `.deploymentUrl` | Runs `railway up --detach`, then monitors the deployment via the Railway API (the API, not the CLI exit code, decides pass/fail). Also accepts `excludePaths`, `railpackConfig`, and `healthcheckPath` / `healthcheckTimeout` (applied by the monitor post-deploy) |
|
|
124
143
|
|
|
125
144
|
**Enums:** `RailwayBuilder` (`RAILPACK`, `NIXPACKS`, `DOCKERFILE`, `HEROKU`, `PAKETO`), `RailwayRestartPolicy` (`ON_FAILURE`, `ALWAYS`, `NEVER`)
|
|
126
145
|
|
|
127
146
|
**Deploy token security:** `RailwayDeploy` pipes the project token to `railway up` via the command's stdin — never in the script text. pulumi-command embeds the executed command verbatim in its failure error and Pulumi does not scrub secrets from provider diagnostics, so an inlined token would print in plaintext exactly when a deploy fails.
|
|
128
147
|
|
|
148
|
+
**Healthcheck config:** Railway rejects healthcheck fields on a fresh service instance with no deployment (`serviceInstanceUpdate` fails with "Invalid input"), so a from-zero `up` cannot set them at configure time. `RailwayService` still sends them on the first attempt — an instance with existing deployments accepts them in one call — and when the API rejects, drops them for the retry and guarantees they land later instead of silently losing them: for image services the provider re-applies them right after its own `serviceInstanceDeployV2`; for code services pass `healthcheckPath` / `healthcheckTimeout` to `RailwayDeploy` too, and its monitor applies them once the deployment reaches a live status. Either re-apply failing fails the resource loudly — healthcheck config is never dropped silently.
|
|
149
|
+
|
|
129
150
|
## Neon
|
|
130
151
|
|
|
131
152
|
```typescript
|
|
@@ -139,7 +160,7 @@ import {
|
|
|
139
160
|
} from "@infracraft/pulumi/neon"
|
|
140
161
|
|
|
141
162
|
const provider = new NeonProvider("neon", {
|
|
142
|
-
apiKey: config.requireSecret("neonApiKey")
|
|
163
|
+
apiKeyEnvVar: "NEON_API_KEY", // or apiKey: config.requireSecret("neonApiKey")
|
|
143
164
|
})
|
|
144
165
|
|
|
145
166
|
const project = new NeonProject("db", { name: "my-app" }, { provider })
|
|
@@ -175,7 +196,7 @@ const connectionString = pulumi.interpolate`postgresql://${roleName}:${role.pass
|
|
|
175
196
|
|
|
176
197
|
| Class | Key outputs | Notes |
|
|
177
198
|
|---|---|---|
|
|
178
|
-
| `NeonProvider` | — | `apiKey` + optional `orgId` |
|
|
199
|
+
| `NeonProvider` | — | `apiKey` or `apiKeyEnvVar` (see [Provider credentials](#provider-credentials)) + optional `orgId` |
|
|
179
200
|
| `NeonProject` | `.id` | Adopt-or-create by name |
|
|
180
201
|
| `NeonBranch` | `.id` | Optional `parent` for copy-on-write branching |
|
|
181
202
|
| `NeonEndpoint` | `.host` | Read-write compute endpoint; use `.host` in connection strings |
|
|
@@ -221,7 +242,7 @@ import {
|
|
|
221
242
|
import { hash } from "@infracraft/pulumi/hash"
|
|
222
243
|
|
|
223
244
|
const provider = new VercelProvider("vercel", {
|
|
224
|
-
token: config.requireSecret("vercelToken")
|
|
245
|
+
tokenEnvVar: "VERCEL_TOKEN", // or token: config.requireSecret("vercelToken")
|
|
225
246
|
teamId: "team_xxx",
|
|
226
247
|
})
|
|
227
248
|
|
|
@@ -272,7 +293,7 @@ new VercelResourceConnection("kv-conn", {
|
|
|
272
293
|
|
|
273
294
|
| Class | Key outputs | Notes |
|
|
274
295
|
|---|---|---|
|
|
275
|
-
| `VercelProvider` | — | `token` + `teamId` |
|
|
296
|
+
| `VercelProvider` | — | `token` or `tokenEnvVar` (see [Provider credentials](#provider-credentials)) + `teamId` |
|
|
276
297
|
| `VercelProject` | `.id`, `.url` | `.url` is a full `https://` URL; prefers the custom production domain over `<name>.vercel.app`. Deletes the project on destroy — `protect: true` production projects |
|
|
277
298
|
| `VercelVariable` | `.contentHash` | Use as a deploy trigger to redeploy on env var changes. Takes `opts.project` or `args.projectId` |
|
|
278
299
|
| `VercelDeploy` | `.deploymentUrl` | Runs `vercel deploy --prod --yes` |
|
|
@@ -302,7 +323,7 @@ import { hash } from "@infracraft/pulumi/hash"
|
|
|
302
323
|
|
|
303
324
|
// Provider: auth context (token + optional default org)
|
|
304
325
|
const provider = new FlyProvider("fly", {
|
|
305
|
-
token: config.requireSecret("flyToken")
|
|
326
|
+
tokenEnvVar: "FLY_API_TOKEN", // or token: config.requireSecret("flyToken")
|
|
306
327
|
organization: "personal",
|
|
307
328
|
})
|
|
308
329
|
|
|
@@ -356,7 +377,7 @@ new FlyDeploy("api-deploy", {
|
|
|
356
377
|
|
|
357
378
|
| Class | Key outputs | Notes |
|
|
358
379
|
|---|---|---|
|
|
359
|
-
| `FlyProvider` | — | Pass as `provider` option to every Fly resource |
|
|
380
|
+
| `FlyProvider` | — | Pass as `provider` option to every Fly resource. `token` or `tokenEnvVar` — see [Provider credentials](#provider-credentials) |
|
|
360
381
|
| `FlyApp` | `.id` (app name) | Adopt-or-create |
|
|
361
382
|
| `FlySecret` | `.version` | Feed into `FlyDeploy` triggers to redeploy on secret changes |
|
|
362
383
|
| `FlyVolume` | `.id` (`vol_…`) | `sizeGb` can only grow |
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
const require_chunk = require('../chunk-BVYJZCqc.cjs');
|
|
3
|
+
let _pulumi_pulumi = require("@pulumi/pulumi");
|
|
4
|
+
_pulumi_pulumi = require_chunk.__toESM(_pulumi_pulumi, 1);
|
|
5
|
+
|
|
6
|
+
//#region src/dynamic/resolve-credential.ts
|
|
7
|
+
/**
|
|
8
|
+
* Resolves a provider credential inside a dynamic-provider operation: the
|
|
9
|
+
* direct value when one was configured, otherwise the value of the named
|
|
10
|
+
* environment variable.
|
|
11
|
+
*
|
|
12
|
+
* Dynamic-provider operations execute in the Pulumi CLI's plugin process,
|
|
13
|
+
* which inherits the program's environment — so variables provided by the
|
|
14
|
+
* shell or by an ESC environment's `environmentVariables` block reach this
|
|
15
|
+
* lookup. Keeping only the variable NAME in resource inputs (never the secret
|
|
16
|
+
* value) removes the substrate for pulumi/pulumi#16041 ("Unexpected struct
|
|
17
|
+
* type": secret Outputs in dynamic-provider inputs intermittently fail engine
|
|
18
|
+
* serialization) and keeps the credential out of per-resource state.
|
|
19
|
+
*/
|
|
20
|
+
function resolveCredential(value, envVarName) {
|
|
21
|
+
if (value !== void 0) return value;
|
|
22
|
+
if (envVarName === void 0) throw new Error("provider credential is missing — neither a direct value nor a credential env var name was configured");
|
|
23
|
+
const fromEnv = process.env[envVarName];
|
|
24
|
+
if (!fromEnv) throw new Error(`provider credential env var ${envVarName} is not set in the Pulumi execution environment`);
|
|
25
|
+
return fromEnv;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Program-runtime variant for the deploy components: resolves the credential
|
|
29
|
+
* into a secret Output for a command's env map or stdin. The command still
|
|
30
|
+
* receives the actual value, but it never becomes a dynamic-resource input.
|
|
31
|
+
*/
|
|
32
|
+
function resolveCredentialOutput(value, envVarName) {
|
|
33
|
+
if (value !== void 0) return value;
|
|
34
|
+
return _pulumi_pulumi.secret(_pulumi_pulumi.output(envVarName).apply((name) => resolveCredential(void 0, name)));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
//#endregion
|
|
38
|
+
exports.resolveCredential = resolveCredential;
|
|
39
|
+
exports.resolveCredentialOutput = resolveCredentialOutput;
|
|
40
|
+
//# sourceMappingURL=resolve-credential.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve-credential.cjs","names":["pulumi"],"sources":["../../src/dynamic/resolve-credential.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\n\n/**\n * Resolves a provider credential inside a dynamic-provider operation: the\n * direct value when one was configured, otherwise the value of the named\n * environment variable.\n *\n * Dynamic-provider operations execute in the Pulumi CLI's plugin process,\n * which inherits the program's environment — so variables provided by the\n * shell or by an ESC environment's `environmentVariables` block reach this\n * lookup. Keeping only the variable NAME in resource inputs (never the secret\n * value) removes the substrate for pulumi/pulumi#16041 (\"Unexpected struct\n * type\": secret Outputs in dynamic-provider inputs intermittently fail engine\n * serialization) and keeps the credential out of per-resource state.\n */\nexport function resolveCredential(\n\tvalue: string | undefined,\n\tenvVarName: string | undefined,\n): string {\n\tif (value !== undefined) {\n\t\treturn value;\n\t}\n\n\tif (envVarName === undefined) {\n\t\tthrow new Error(\n\t\t\t\"provider credential is missing — neither a direct value nor a credential env var name was configured\",\n\t\t);\n\t}\n\n\tconst fromEnv = process.env[envVarName];\n\n\tif (!fromEnv) {\n\t\tthrow new Error(\n\t\t\t`provider credential env var ${envVarName} is not set in the Pulumi execution environment`,\n\t\t);\n\t}\n\n\treturn fromEnv;\n}\n\n/**\n * Program-runtime variant for the deploy components: resolves the credential\n * into a secret Output for a command's env map or stdin. The command still\n * receives the actual value, but it never becomes a dynamic-resource input.\n */\nexport function resolveCredentialOutput(\n\tvalue: pulumi.Output<string> | undefined,\n\tenvVarName: pulumi.Output<string> | undefined,\n): pulumi.Output<string> {\n\tif (value !== undefined) {\n\t\treturn value;\n\t}\n\n\treturn pulumi.secret(\n\t\tpulumi\n\t\t\t.output(envVarName)\n\t\t\t.apply((name) => resolveCredential(undefined, name)),\n\t);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAeA,SAAgB,kBACf,OACA,YACS;CACT,IAAI,UAAU,QACb,OAAO;CAGR,IAAI,eAAe,QAClB,MAAM,IAAI,MACT,sGACD;CAGD,MAAM,UAAU,QAAQ,IAAI;CAE5B,IAAI,CAAC,SACJ,MAAM,IAAI,MACT,+BAA+B,WAAW,gDAC3C;CAGD,OAAO;AACR;;;;;;AAOA,SAAgB,wBACf,OACA,YACwB;CACxB,IAAI,UAAU,QACb,OAAO;CAGR,OAAOA,eAAO,OACbA,eACE,OAAO,UAAU,EACjB,OAAO,SAAS,kBAAkB,QAAW,IAAI,CAAC,CACrD;AACD"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { t as __name } from "../chunk-OPjESj5l.cjs";
|
|
2
|
+
import * as pulumi from "@pulumi/pulumi";
|
|
3
|
+
|
|
4
|
+
//#region src/dynamic/resolve-credential.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Resolves a provider credential inside a dynamic-provider operation: the
|
|
7
|
+
* direct value when one was configured, otherwise the value of the named
|
|
8
|
+
* environment variable.
|
|
9
|
+
*
|
|
10
|
+
* Dynamic-provider operations execute in the Pulumi CLI's plugin process,
|
|
11
|
+
* which inherits the program's environment — so variables provided by the
|
|
12
|
+
* shell or by an ESC environment's `environmentVariables` block reach this
|
|
13
|
+
* lookup. Keeping only the variable NAME in resource inputs (never the secret
|
|
14
|
+
* value) removes the substrate for pulumi/pulumi#16041 ("Unexpected struct
|
|
15
|
+
* type": secret Outputs in dynamic-provider inputs intermittently fail engine
|
|
16
|
+
* serialization) and keeps the credential out of per-resource state.
|
|
17
|
+
*/
|
|
18
|
+
declare function resolveCredential(value: string | undefined, envVarName: string | undefined): string;
|
|
19
|
+
/**
|
|
20
|
+
* Program-runtime variant for the deploy components: resolves the credential
|
|
21
|
+
* into a secret Output for a command's env map or stdin. The command still
|
|
22
|
+
* receives the actual value, but it never becomes a dynamic-resource input.
|
|
23
|
+
*/
|
|
24
|
+
declare function resolveCredentialOutput(value: pulumi.Output<string> | undefined, envVarName: pulumi.Output<string> | undefined): pulumi.Output<string>;
|
|
25
|
+
//#endregion
|
|
26
|
+
export { resolveCredential, resolveCredentialOutput };
|
|
27
|
+
//# sourceMappingURL=resolve-credential.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve-credential.d.cts","names":[],"sources":["../../src/dynamic/resolve-credential.ts"],"mappings":";;;;;;;AAeA;;;;AAE+B;AA4B/B;;;;;iBA9BgB,iBAAA,CACf,KAAA,sBACA,UAA8B;;;;;;iBA4Bf,uBAAA,CACf,KAAA,EAAO,MAAA,CAAO,MAAA,sBACd,UAAA,EAAY,MAAA,CAAO,MAAA,uBACjB,MAAA,CAAO,MAAA"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { t as __name } from "../chunk-OPjESj5l.mjs";
|
|
2
|
+
import * as pulumi from "@pulumi/pulumi";
|
|
3
|
+
|
|
4
|
+
//#region src/dynamic/resolve-credential.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Resolves a provider credential inside a dynamic-provider operation: the
|
|
7
|
+
* direct value when one was configured, otherwise the value of the named
|
|
8
|
+
* environment variable.
|
|
9
|
+
*
|
|
10
|
+
* Dynamic-provider operations execute in the Pulumi CLI's plugin process,
|
|
11
|
+
* which inherits the program's environment — so variables provided by the
|
|
12
|
+
* shell or by an ESC environment's `environmentVariables` block reach this
|
|
13
|
+
* lookup. Keeping only the variable NAME in resource inputs (never the secret
|
|
14
|
+
* value) removes the substrate for pulumi/pulumi#16041 ("Unexpected struct
|
|
15
|
+
* type": secret Outputs in dynamic-provider inputs intermittently fail engine
|
|
16
|
+
* serialization) and keeps the credential out of per-resource state.
|
|
17
|
+
*/
|
|
18
|
+
declare function resolveCredential(value: string | undefined, envVarName: string | undefined): string;
|
|
19
|
+
/**
|
|
20
|
+
* Program-runtime variant for the deploy components: resolves the credential
|
|
21
|
+
* into a secret Output for a command's env map or stdin. The command still
|
|
22
|
+
* receives the actual value, but it never becomes a dynamic-resource input.
|
|
23
|
+
*/
|
|
24
|
+
declare function resolveCredentialOutput(value: pulumi.Output<string> | undefined, envVarName: pulumi.Output<string> | undefined): pulumi.Output<string>;
|
|
25
|
+
//#endregion
|
|
26
|
+
export { resolveCredential, resolveCredentialOutput };
|
|
27
|
+
//# sourceMappingURL=resolve-credential.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve-credential.d.mts","names":[],"sources":["../../src/dynamic/resolve-credential.ts"],"mappings":";;;;;;;AAeA;;;;AAE+B;AA4B/B;;;;;iBA9BgB,iBAAA,CACf,KAAA,sBACA,UAA8B;;;;;;iBA4Bf,uBAAA,CACf,KAAA,EAAO,MAAA,CAAO,MAAA,sBACd,UAAA,EAAY,MAAA,CAAO,MAAA,uBACjB,MAAA,CAAO,MAAA"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { t as __name } from "../chunk-OPjESj5l.mjs";
|
|
2
|
+
import * as pulumi from "@pulumi/pulumi";
|
|
3
|
+
|
|
4
|
+
//#region src/dynamic/resolve-credential.ts
|
|
5
|
+
/**
|
|
6
|
+
* Resolves a provider credential inside a dynamic-provider operation: the
|
|
7
|
+
* direct value when one was configured, otherwise the value of the named
|
|
8
|
+
* environment variable.
|
|
9
|
+
*
|
|
10
|
+
* Dynamic-provider operations execute in the Pulumi CLI's plugin process,
|
|
11
|
+
* which inherits the program's environment — so variables provided by the
|
|
12
|
+
* shell or by an ESC environment's `environmentVariables` block reach this
|
|
13
|
+
* lookup. Keeping only the variable NAME in resource inputs (never the secret
|
|
14
|
+
* value) removes the substrate for pulumi/pulumi#16041 ("Unexpected struct
|
|
15
|
+
* type": secret Outputs in dynamic-provider inputs intermittently fail engine
|
|
16
|
+
* serialization) and keeps the credential out of per-resource state.
|
|
17
|
+
*/
|
|
18
|
+
function resolveCredential(value, envVarName) {
|
|
19
|
+
if (value !== void 0) return value;
|
|
20
|
+
if (envVarName === void 0) throw new Error("provider credential is missing — neither a direct value nor a credential env var name was configured");
|
|
21
|
+
const fromEnv = process.env[envVarName];
|
|
22
|
+
if (!fromEnv) throw new Error(`provider credential env var ${envVarName} is not set in the Pulumi execution environment`);
|
|
23
|
+
return fromEnv;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Program-runtime variant for the deploy components: resolves the credential
|
|
27
|
+
* into a secret Output for a command's env map or stdin. The command still
|
|
28
|
+
* receives the actual value, but it never becomes a dynamic-resource input.
|
|
29
|
+
*/
|
|
30
|
+
function resolveCredentialOutput(value, envVarName) {
|
|
31
|
+
if (value !== void 0) return value;
|
|
32
|
+
return pulumi.secret(pulumi.output(envVarName).apply((name) => resolveCredential(void 0, name)));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
//#endregion
|
|
36
|
+
export { resolveCredential, resolveCredentialOutput };
|
|
37
|
+
//# sourceMappingURL=resolve-credential.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve-credential.mjs","names":[],"sources":["../../src/dynamic/resolve-credential.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\n\n/**\n * Resolves a provider credential inside a dynamic-provider operation: the\n * direct value when one was configured, otherwise the value of the named\n * environment variable.\n *\n * Dynamic-provider operations execute in the Pulumi CLI's plugin process,\n * which inherits the program's environment — so variables provided by the\n * shell or by an ESC environment's `environmentVariables` block reach this\n * lookup. Keeping only the variable NAME in resource inputs (never the secret\n * value) removes the substrate for pulumi/pulumi#16041 (\"Unexpected struct\n * type\": secret Outputs in dynamic-provider inputs intermittently fail engine\n * serialization) and keeps the credential out of per-resource state.\n */\nexport function resolveCredential(\n\tvalue: string | undefined,\n\tenvVarName: string | undefined,\n): string {\n\tif (value !== undefined) {\n\t\treturn value;\n\t}\n\n\tif (envVarName === undefined) {\n\t\tthrow new Error(\n\t\t\t\"provider credential is missing — neither a direct value nor a credential env var name was configured\",\n\t\t);\n\t}\n\n\tconst fromEnv = process.env[envVarName];\n\n\tif (!fromEnv) {\n\t\tthrow new Error(\n\t\t\t`provider credential env var ${envVarName} is not set in the Pulumi execution environment`,\n\t\t);\n\t}\n\n\treturn fromEnv;\n}\n\n/**\n * Program-runtime variant for the deploy components: resolves the credential\n * into a secret Output for a command's env map or stdin. The command still\n * receives the actual value, but it never becomes a dynamic-resource input.\n */\nexport function resolveCredentialOutput(\n\tvalue: pulumi.Output<string> | undefined,\n\tenvVarName: pulumi.Output<string> | undefined,\n): pulumi.Output<string> {\n\tif (value !== undefined) {\n\t\treturn value;\n\t}\n\n\treturn pulumi.secret(\n\t\tpulumi\n\t\t\t.output(envVarName)\n\t\t\t.apply((name) => resolveCredential(undefined, name)),\n\t);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAeA,SAAgB,kBACf,OACA,YACS;CACT,IAAI,UAAU,QACb,OAAO;CAGR,IAAI,eAAe,QAClB,MAAM,IAAI,MACT,sGACD;CAGD,MAAM,UAAU,QAAQ,IAAI;CAE5B,IAAI,CAAC,SACJ,MAAM,IAAI,MACT,+BAA+B,WAAW,gDAC3C;CAGD,OAAO;AACR;;;;;;AAOA,SAAgB,wBACf,OACA,YACwB;CACxB,IAAI,UAAU,QACb,OAAO;CAGR,OAAO,OAAO,OACb,OACE,OAAO,UAAU,EACjB,OAAO,SAAS,kBAAkB,QAAW,IAAI,CAAC,CACrD;AACD"}
|
package/dist/fly/app.cjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
2
|
const require_chunk = require('../chunk-BVYJZCqc.cjs');
|
|
3
|
+
const require_dynamic_resolve_credential = require('../dynamic/resolve-credential.cjs');
|
|
3
4
|
const require_fly_client = require('./client.cjs');
|
|
4
5
|
let _pulumi_pulumi = require("@pulumi/pulumi");
|
|
5
6
|
_pulumi_pulumi = require_chunk.__toESM(_pulumi_pulumi, 1);
|
|
@@ -17,7 +18,7 @@ _pulumi_pulumi = require_chunk.__toESM(_pulumi_pulumi, 1);
|
|
|
17
18
|
*/
|
|
18
19
|
var FlyAppResourceProvider = class {
|
|
19
20
|
async create(inputs) {
|
|
20
|
-
const client = new require_fly_client.FlyClient(inputs.token);
|
|
21
|
+
const client = new require_fly_client.FlyClient(require_dynamic_resolve_credential.resolveCredential(inputs.token, inputs.tokenEnvVar));
|
|
21
22
|
if (await client.tryGet(`/v1/apps/${inputs.name}`)) _pulumi_pulumi.log.info(`Adopting existing Fly app "${inputs.name}"`);
|
|
22
23
|
else {
|
|
23
24
|
if (!inputs.organization) throw new Error(`FlyApp "${inputs.name}": an organization is required to create a new app — set it on FlyProvider or FlyApp args`);
|
|
@@ -37,7 +38,7 @@ var FlyAppResourceProvider = class {
|
|
|
37
38
|
};
|
|
38
39
|
}
|
|
39
40
|
async read(id, props) {
|
|
40
|
-
const app = await new require_fly_client.FlyClient(props.token).tryGet(`/v1/apps/${id}`);
|
|
41
|
+
const app = await new require_fly_client.FlyClient(require_dynamic_resolve_credential.resolveCredential(props.token, props.tokenEnvVar)).tryGet(`/v1/apps/${id}`);
|
|
41
42
|
if (!app) return {};
|
|
42
43
|
return {
|
|
43
44
|
id,
|
|
@@ -94,6 +95,7 @@ var FlyApp = class extends _pulumi_pulumi.ComponentResource {
|
|
|
94
95
|
super("infracraft:fly:App", name, {}, pulumiOpts);
|
|
95
96
|
const resource = new FlyAppResource(`${name}-resource`, {
|
|
96
97
|
token: provider.token,
|
|
98
|
+
tokenEnvVar: provider.tokenEnvVar,
|
|
97
99
|
name: args.name,
|
|
98
100
|
organization: args.organization ?? provider.organization
|
|
99
101
|
}, { parent: this });
|
package/dist/fly/app.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app.cjs","names":["FlyClient","pulumi"],"sources":["../../src/fly/app.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\n\nimport { FlyClient } from \"./client\";\nimport type { FlyProvider } from \"./provider\";\n\n/** Resolved inputs for the Fly app dynamic provider. */\ninterface FlyAppInputs {\n\t/** Fly API token. */\n\ttoken
|
|
1
|
+
{"version":3,"file":"app.cjs","names":["FlyClient","resolveCredential","pulumi"],"sources":["../../src/fly/app.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\n\nimport { resolveCredential } from \"../dynamic/resolve-credential\";\nimport { FlyClient } from \"./client\";\nimport type { FlyProvider } from \"./provider\";\n\n/** Resolved inputs for the Fly app dynamic provider. */\ninterface FlyAppInputs {\n\t/** Fly API token. Absent when `tokenEnvVar` is used instead. */\n\ttoken?: string;\n\n\t/** Env var name resolved to the token when `token` is absent (see `FlyProviderArgs.tokenEnvVar`). */\n\ttokenEnvVar?: string;\n\n\t/** App name (globally unique). Used as the resource identifier. */\n\tname: string;\n\n\t/** Org slug used only when creating a new app. */\n\torganization?: string;\n}\n\n/** Persisted state for the Fly app. */\ninterface FlyAppOutputs extends FlyAppInputs {\n\t/** App identifier — equals the app name (all child paths key off the name). */\n\tappId: string;\n}\n\n/** Get-app response (only the fields we read). */\ninterface FlyAppResponse {\n\tid: string;\n\tname: string;\n}\n\n/**\n * Dynamic provider implementing adopt-or-create for Fly apps.\n *\n * `create()` does `GET /v1/apps/{name}`; if found it adopts, otherwise it\n * `POST /v1/apps`. `delete()` is a no-op — deleting a Fly app destroys\n * everything in it, so (like Railway/Neon/Vercel top-level resources) Pulumi\n * does not delete apps.\n *\n * @internal Exported only for unit testing; not part of the public API surface.\n */\nexport class FlyAppResourceProvider implements pulumi.dynamic.ResourceProvider {\n\tasync create(inputs: FlyAppInputs): Promise<pulumi.dynamic.CreateResult> {\n\t\tconst client = new FlyClient(\n\t\t\tresolveCredential(inputs.token, inputs.tokenEnvVar),\n\t\t);\n\n\t\tconst existing = await client.tryGet<FlyAppResponse>(\n\t\t\t`/v1/apps/${inputs.name}`,\n\t\t);\n\n\t\tif (existing) {\n\t\t\tpulumi.log.info(`Adopting existing Fly app \"${inputs.name}\"`);\n\t\t} else {\n\t\t\tif (!inputs.organization) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`FlyApp \"${inputs.name}\": an organization is required to create a new app — set it on FlyProvider or FlyApp args`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tpulumi.log.info(`Fly app \"${inputs.name}\" not found — creating...`);\n\n\t\t\tawait client.post(\"/v1/apps\", {\n\t\t\t\tapp_name: inputs.name,\n\t\t\t\torg_slug: inputs.organization,\n\t\t\t});\n\t\t}\n\n\t\tconst outs: FlyAppOutputs = { ...inputs, appId: inputs.name };\n\n\t\treturn { id: inputs.name, outs };\n\t}\n\n\tasync read(\n\t\tid: string,\n\t\tprops: FlyAppOutputs,\n\t): Promise<pulumi.dynamic.ReadResult> {\n\t\tconst client = new FlyClient(\n\t\t\tresolveCredential(props.token, props.tokenEnvVar),\n\t\t);\n\n\t\tconst app = await client.tryGet<FlyAppResponse>(`/v1/apps/${id}`);\n\n\t\tif (!app) {\n\t\t\t// Resource gone → blank id lets refresh reconcile the deletion.\n\t\t\treturn {};\n\t\t}\n\n\t\treturn { id, props: { ...props, name: app.name, appId: app.name } };\n\t}\n\n\tasync update(\n\t\tid: string,\n\t\t_olds: FlyAppOutputs,\n\t\tnews: FlyAppInputs,\n\t): Promise<pulumi.dynamic.UpdateResult> {\n\t\treturn { outs: { ...news, appId: id } };\n\t}\n\n\tasync delete(): Promise<void> {\n\t\tpulumi.log.warn(\n\t\t\t\"Fly app deletion skipped — apps are not deleted by Pulumi (would destroy all contained resources)\",\n\t\t);\n\t}\n\n\tasync diff(\n\t\t_id: string,\n\t\tolds: FlyAppOutputs,\n\t\tnews: FlyAppInputs,\n\t): Promise<pulumi.dynamic.DiffResult> {\n\t\tconst replaces: string[] = [];\n\n\t\tif (olds.name !== news.name) {\n\t\t\treplaces.push(\"name\");\n\t\t}\n\n\t\tif (olds.organization !== news.organization) {\n\t\t\treplaces.push(\"organization\");\n\t\t}\n\n\t\treturn {\n\t\t\tchanges: replaces.length > 0,\n\t\t\treplaces,\n\t\t\tdeleteBeforeReplace: true,\n\t\t};\n\t}\n}\n\n/** Internal dynamic resource — not part of the public API. */\nclass FlyAppResource extends pulumi.dynamic.Resource {\n\tpublic declare readonly appId: pulumi.Output<string>;\n\n\tconstructor(\n\t\tname: string,\n\t\targs: {\n\t\t\ttoken?: pulumi.Input<string>;\n\t\t\ttokenEnvVar?: pulumi.Input<string>;\n\t\t\tname: pulumi.Input<string>;\n\t\t\torganization?: pulumi.Input<string | undefined>;\n\t\t},\n\t\topts?: pulumi.CustomResourceOptions,\n\t) {\n\t\tsuper(\n\t\t\tnew FlyAppResourceProvider(),\n\t\t\tname,\n\t\t\t{ ...args, appId: undefined },\n\t\t\t// The API token flows into dynamic-provider state with the outputs — mark it secret there.\n\t\t\t{ ...opts, additionalSecretOutputs: [\"token\"] },\n\t\t);\n\t}\n}\n\n/** Options type for FlyApp — replaces Pulumi's native `provider` field. */\ntype FlyAppOptions = Omit<pulumi.ComponentResourceOptions, \"provider\"> & {\n\t/** Fly authentication context. */\n\tprovider: FlyProvider;\n};\n\n/** Args for FlyApp. */\nexport interface FlyAppArgs {\n\t/**\n\t * App name (globally unique). Used for adoption lookup and as `.id`.\n\t * Maps to the Fly Machines API field `app_name`.\n\t */\n\tname: pulumi.Input<string>;\n\n\t/**\n\t * Org slug for app creation. Overrides `FlyProvider.organization`.\n\t * Ignored when the app already exists (adoption).\n\t * Maps to the Fly Machines API field `org_slug`.\n\t */\n\torganization?: pulumi.Input<string>;\n}\n\n/**\n * Manages a Fly app with adopt-or-create semantics.\n *\n * @example\n * ```typescript\n * const app = new FlyApp(\"api\", { name: \"rby-api\" }, { provider });\n * ```\n */\nexport class FlyApp extends pulumi.ComponentResource {\n\t/** App identifier (equals the app name). */\n\tpublic readonly id: pulumi.Output<string>;\n\n\tconstructor(name: string, args: FlyAppArgs, opts: FlyAppOptions) {\n\t\tconst { provider, ...pulumiOpts } = opts;\n\n\t\tsuper(\"infracraft:fly:App\", name, {}, pulumiOpts);\n\n\t\tconst resource = new FlyAppResource(\n\t\t\t`${name}-resource`,\n\t\t\t{\n\t\t\t\ttoken: provider.token,\n\t\t\t\ttokenEnvVar: provider.tokenEnvVar,\n\t\t\t\tname: args.name,\n\t\t\t\torganization: args.organization ?? provider.organization,\n\t\t\t},\n\t\t\t{ parent: this },\n\t\t);\n\n\t\tthis.id = resource.appId;\n\n\t\tthis.registerOutputs({ id: this.id });\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AA2CA,IAAa,yBAAb,MAA+E;CAC9E,MAAM,OAAO,QAA4D;EACxE,MAAM,SAAS,IAAIA,6BAClBC,qDAAkB,OAAO,OAAO,OAAO,WAAW,CACnD;EAMA,IAAI,MAJmB,OAAO,OAC7B,YAAY,OAAO,MACpB,GAGC,eAAO,IAAI,KAAK,8BAA8B,OAAO,KAAK,EAAE;OACtD;GACN,IAAI,CAAC,OAAO,cACX,MAAM,IAAI,MACT,WAAW,OAAO,KAAK,0FACxB;GAGD,eAAO,IAAI,KAAK,YAAY,OAAO,KAAK,0BAA0B;GAElE,MAAM,OAAO,KAAK,YAAY;IAC7B,UAAU,OAAO;IACjB,UAAU,OAAO;GAClB,CAAC;EACF;EAEA,MAAM,OAAsB;GAAE,GAAG;GAAQ,OAAO,OAAO;EAAK;EAE5D,OAAO;GAAE,IAAI,OAAO;GAAM;EAAK;CAChC;CAEA,MAAM,KACL,IACA,OACqC;EAKrC,MAAM,MAAM,MAAM,IAJCD,6BAClBC,qDAAkB,MAAM,OAAO,MAAM,WAAW,CAG1B,EAAE,OAAuB,YAAY,IAAI;EAEhE,IAAI,CAAC,KAEJ,OAAO,CAAC;EAGT,OAAO;GAAE;GAAI,OAAO;IAAE,GAAG;IAAO,MAAM,IAAI;IAAM,OAAO,IAAI;GAAK;EAAE;CACnE;CAEA,MAAM,OACL,IACA,OACA,MACuC;EACvC,OAAO,EAAE,MAAM;GAAE,GAAG;GAAM,OAAO;EAAG,EAAE;CACvC;CAEA,MAAM,SAAwB;EAC7B,eAAO,IAAI,KACV,mGACD;CACD;CAEA,MAAM,KACL,KACA,MACA,MACqC;EACrC,MAAM,WAAqB,CAAC;EAE5B,IAAI,KAAK,SAAS,KAAK,MACtB,SAAS,KAAK,MAAM;EAGrB,IAAI,KAAK,iBAAiB,KAAK,cAC9B,SAAS,KAAK,cAAc;EAG7B,OAAO;GACN,SAAS,SAAS,SAAS;GAC3B;GACA,qBAAqB;EACtB;CACD;AACD;;AAGA,IAAM,iBAAN,cAA6BC,eAAO,QAAQ,SAAS;CAGpD,YACC,MACA,MAMA,MACC;EACD,MACC,IAAI,uBAAuB,GAC3B,MACA;GAAE,GAAG;GAAM,OAAO;EAAU,GAE5B;GAAE,GAAG;GAAM,yBAAyB,CAAC,OAAO;EAAE,CAC/C;CACD;AACD;;;;;;;;;AAgCA,IAAa,SAAb,cAA4BA,eAAO,kBAAkB;CAIpD,YAAY,MAAc,MAAkB,MAAqB;EAChE,MAAM,EAAE,UAAU,GAAG,eAAe;EAEpC,MAAM,sBAAsB,MAAM,CAAC,GAAG,UAAU;EAEhD,MAAM,WAAW,IAAI,eACpB,GAAG,KAAK,YACR;GACC,OAAO,SAAS;GAChB,aAAa,SAAS;GACtB,MAAM,KAAK;GACX,cAAc,KAAK,gBAAgB,SAAS;EAC7C,GACA,EAAE,QAAQ,KAAK,CAChB;EAEA,KAAK,KAAK,SAAS;EAEnB,KAAK,gBAAgB,EAAE,IAAI,KAAK,GAAG,CAAC;CACrC;AACD"}
|
package/dist/fly/app.d.cts
CHANGED
|
@@ -5,8 +5,10 @@ import * as pulumi from "@pulumi/pulumi";
|
|
|
5
5
|
//#region src/fly/app.d.ts
|
|
6
6
|
/** Resolved inputs for the Fly app dynamic provider. */
|
|
7
7
|
interface FlyAppInputs {
|
|
8
|
-
/** Fly API token. */
|
|
9
|
-
token
|
|
8
|
+
/** Fly API token. Absent when `tokenEnvVar` is used instead. */
|
|
9
|
+
token?: string;
|
|
10
|
+
/** Env var name resolved to the token when `token` is absent (see `FlyProviderArgs.tokenEnvVar`). */
|
|
11
|
+
tokenEnvVar?: string;
|
|
10
12
|
/** App name (globally unique). Used as the resource identifier. */
|
|
11
13
|
name: string;
|
|
12
14
|
/** Org slug used only when creating a new app. */
|
package/dist/fly/app.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app.d.cts","names":[],"sources":["../../src/fly/app.ts"],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"app.d.cts","names":[],"sources":["../../src/fly/app.ts"],"mappings":";;;;;;UAOU,YAAA;;EAET,KAAA;EAFqB;EAKrB,WAAA;EALqB;EAQrB,IAAA;EAHA;EAMA,YAAA;AAAA;;UAIS,aAAA,SAAsB,YAAY;EAAlC;EAET,KAAK;AAAA;;AAAA;AAmBN;;;;;;;;cAAa,sBAAA,YAAkC,MAAA,CAAO,OAAA,CAAQ,gBAAA;EACvD,MAAA,CAAO,MAAA,EAAQ,YAAA,GAAe,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,YAAA;EA+BrD,IAAA,CACL,EAAA,UACA,KAAA,EAAO,aAAA,GACL,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,UAAA;EAepB,MAAA,CACL,EAAA,UACA,KAAA,EAAO,aAAA,EACP,IAAA,EAAM,YAAA,GACJ,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,YAAA;EAIpB,MAAA,CAAA,GAAU,OAAA;EAMV,IAAA,CACL,GAAA,UACA,IAAA,EAAM,aAAA,EACN,IAAA,EAAM,YAAA,GACJ,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,UAAA;AAAA;;KA4CtB,aAAA,GAAgB,IAAA,CAAK,MAAA,CAAO,wBAAA;EA5C7B,kCA8CH,QAAA,EAAU,WAAA;AAAA;;UAIM,UAAA;EAtHqC;;;;EA2HrD,IAAA,EAAM,MAAA,CAAO,KAAA;EA1HuB;;;;;EAiIpC,YAAA,GAAe,MAAA,CAAO,KAAK;AAAA;;;;;;;;;cAWf,MAAA,SAAe,MAAA,CAAO,iBAAA;EAxF3B;EAAA,SA0FS,EAAA,EAAI,MAAA,CAAO,MAAA;cAEf,IAAA,UAAc,IAAA,EAAM,UAAA,EAAY,IAAA,EAAM,aAAA;AAAA"}
|
package/dist/fly/app.d.mts
CHANGED
|
@@ -5,8 +5,10 @@ import * as pulumi from "@pulumi/pulumi";
|
|
|
5
5
|
//#region src/fly/app.d.ts
|
|
6
6
|
/** Resolved inputs for the Fly app dynamic provider. */
|
|
7
7
|
interface FlyAppInputs {
|
|
8
|
-
/** Fly API token. */
|
|
9
|
-
token
|
|
8
|
+
/** Fly API token. Absent when `tokenEnvVar` is used instead. */
|
|
9
|
+
token?: string;
|
|
10
|
+
/** Env var name resolved to the token when `token` is absent (see `FlyProviderArgs.tokenEnvVar`). */
|
|
11
|
+
tokenEnvVar?: string;
|
|
10
12
|
/** App name (globally unique). Used as the resource identifier. */
|
|
11
13
|
name: string;
|
|
12
14
|
/** Org slug used only when creating a new app. */
|
package/dist/fly/app.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app.d.mts","names":[],"sources":["../../src/fly/app.ts"],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"app.d.mts","names":[],"sources":["../../src/fly/app.ts"],"mappings":";;;;;;UAOU,YAAA;;EAET,KAAA;EAFqB;EAKrB,WAAA;EALqB;EAQrB,IAAA;EAHA;EAMA,YAAA;AAAA;;UAIS,aAAA,SAAsB,YAAY;EAAlC;EAET,KAAK;AAAA;;AAAA;AAmBN;;;;;;;;cAAa,sBAAA,YAAkC,MAAA,CAAO,OAAA,CAAQ,gBAAA;EACvD,MAAA,CAAO,MAAA,EAAQ,YAAA,GAAe,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,YAAA;EA+BrD,IAAA,CACL,EAAA,UACA,KAAA,EAAO,aAAA,GACL,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,UAAA;EAepB,MAAA,CACL,EAAA,UACA,KAAA,EAAO,aAAA,EACP,IAAA,EAAM,YAAA,GACJ,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,YAAA;EAIpB,MAAA,CAAA,GAAU,OAAA;EAMV,IAAA,CACL,GAAA,UACA,IAAA,EAAM,aAAA,EACN,IAAA,EAAM,YAAA,GACJ,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,UAAA;AAAA;;KA4CtB,aAAA,GAAgB,IAAA,CAAK,MAAA,CAAO,wBAAA;EA5C7B,kCA8CH,QAAA,EAAU,WAAA;AAAA;;UAIM,UAAA;EAtHqC;;;;EA2HrD,IAAA,EAAM,MAAA,CAAO,KAAA;EA1HuB;;;;;EAiIpC,YAAA,GAAe,MAAA,CAAO,KAAK;AAAA;;;;;;;;;cAWf,MAAA,SAAe,MAAA,CAAO,iBAAA;EAxF3B;EAAA,SA0FS,EAAA,EAAI,MAAA,CAAO,MAAA;cAEf,IAAA,UAAc,IAAA,EAAM,UAAA,EAAY,IAAA,EAAM,aAAA;AAAA"}
|
package/dist/fly/app.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { t as __name } from "../chunk-OPjESj5l.mjs";
|
|
2
|
+
import { resolveCredential } from "../dynamic/resolve-credential.mjs";
|
|
2
3
|
import { FlyClient } from "./client.mjs";
|
|
3
4
|
import * as pulumi from "@pulumi/pulumi";
|
|
4
5
|
|
|
@@ -15,7 +16,7 @@ import * as pulumi from "@pulumi/pulumi";
|
|
|
15
16
|
*/
|
|
16
17
|
var FlyAppResourceProvider = class {
|
|
17
18
|
async create(inputs) {
|
|
18
|
-
const client = new FlyClient(inputs.token);
|
|
19
|
+
const client = new FlyClient(resolveCredential(inputs.token, inputs.tokenEnvVar));
|
|
19
20
|
if (await client.tryGet(`/v1/apps/${inputs.name}`)) pulumi.log.info(`Adopting existing Fly app "${inputs.name}"`);
|
|
20
21
|
else {
|
|
21
22
|
if (!inputs.organization) throw new Error(`FlyApp "${inputs.name}": an organization is required to create a new app — set it on FlyProvider or FlyApp args`);
|
|
@@ -35,7 +36,7 @@ var FlyAppResourceProvider = class {
|
|
|
35
36
|
};
|
|
36
37
|
}
|
|
37
38
|
async read(id, props) {
|
|
38
|
-
const app = await new FlyClient(props.token).tryGet(`/v1/apps/${id}`);
|
|
39
|
+
const app = await new FlyClient(resolveCredential(props.token, props.tokenEnvVar)).tryGet(`/v1/apps/${id}`);
|
|
39
40
|
if (!app) return {};
|
|
40
41
|
return {
|
|
41
42
|
id,
|
|
@@ -92,6 +93,7 @@ var FlyApp = class extends pulumi.ComponentResource {
|
|
|
92
93
|
super("infracraft:fly:App", name, {}, pulumiOpts);
|
|
93
94
|
const resource = new FlyAppResource(`${name}-resource`, {
|
|
94
95
|
token: provider.token,
|
|
96
|
+
tokenEnvVar: provider.tokenEnvVar,
|
|
95
97
|
name: args.name,
|
|
96
98
|
organization: args.organization ?? provider.organization
|
|
97
99
|
}, { parent: this });
|
package/dist/fly/app.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app.mjs","names":[],"sources":["../../src/fly/app.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\n\nimport { FlyClient } from \"./client\";\nimport type { FlyProvider } from \"./provider\";\n\n/** Resolved inputs for the Fly app dynamic provider. */\ninterface FlyAppInputs {\n\t/** Fly API token. */\n\ttoken
|
|
1
|
+
{"version":3,"file":"app.mjs","names":[],"sources":["../../src/fly/app.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\n\nimport { resolveCredential } from \"../dynamic/resolve-credential\";\nimport { FlyClient } from \"./client\";\nimport type { FlyProvider } from \"./provider\";\n\n/** Resolved inputs for the Fly app dynamic provider. */\ninterface FlyAppInputs {\n\t/** Fly API token. Absent when `tokenEnvVar` is used instead. */\n\ttoken?: string;\n\n\t/** Env var name resolved to the token when `token` is absent (see `FlyProviderArgs.tokenEnvVar`). */\n\ttokenEnvVar?: string;\n\n\t/** App name (globally unique). Used as the resource identifier. */\n\tname: string;\n\n\t/** Org slug used only when creating a new app. */\n\torganization?: string;\n}\n\n/** Persisted state for the Fly app. */\ninterface FlyAppOutputs extends FlyAppInputs {\n\t/** App identifier — equals the app name (all child paths key off the name). */\n\tappId: string;\n}\n\n/** Get-app response (only the fields we read). */\ninterface FlyAppResponse {\n\tid: string;\n\tname: string;\n}\n\n/**\n * Dynamic provider implementing adopt-or-create for Fly apps.\n *\n * `create()` does `GET /v1/apps/{name}`; if found it adopts, otherwise it\n * `POST /v1/apps`. `delete()` is a no-op — deleting a Fly app destroys\n * everything in it, so (like Railway/Neon/Vercel top-level resources) Pulumi\n * does not delete apps.\n *\n * @internal Exported only for unit testing; not part of the public API surface.\n */\nexport class FlyAppResourceProvider implements pulumi.dynamic.ResourceProvider {\n\tasync create(inputs: FlyAppInputs): Promise<pulumi.dynamic.CreateResult> {\n\t\tconst client = new FlyClient(\n\t\t\tresolveCredential(inputs.token, inputs.tokenEnvVar),\n\t\t);\n\n\t\tconst existing = await client.tryGet<FlyAppResponse>(\n\t\t\t`/v1/apps/${inputs.name}`,\n\t\t);\n\n\t\tif (existing) {\n\t\t\tpulumi.log.info(`Adopting existing Fly app \"${inputs.name}\"`);\n\t\t} else {\n\t\t\tif (!inputs.organization) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`FlyApp \"${inputs.name}\": an organization is required to create a new app — set it on FlyProvider or FlyApp args`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tpulumi.log.info(`Fly app \"${inputs.name}\" not found — creating...`);\n\n\t\t\tawait client.post(\"/v1/apps\", {\n\t\t\t\tapp_name: inputs.name,\n\t\t\t\torg_slug: inputs.organization,\n\t\t\t});\n\t\t}\n\n\t\tconst outs: FlyAppOutputs = { ...inputs, appId: inputs.name };\n\n\t\treturn { id: inputs.name, outs };\n\t}\n\n\tasync read(\n\t\tid: string,\n\t\tprops: FlyAppOutputs,\n\t): Promise<pulumi.dynamic.ReadResult> {\n\t\tconst client = new FlyClient(\n\t\t\tresolveCredential(props.token, props.tokenEnvVar),\n\t\t);\n\n\t\tconst app = await client.tryGet<FlyAppResponse>(`/v1/apps/${id}`);\n\n\t\tif (!app) {\n\t\t\t// Resource gone → blank id lets refresh reconcile the deletion.\n\t\t\treturn {};\n\t\t}\n\n\t\treturn { id, props: { ...props, name: app.name, appId: app.name } };\n\t}\n\n\tasync update(\n\t\tid: string,\n\t\t_olds: FlyAppOutputs,\n\t\tnews: FlyAppInputs,\n\t): Promise<pulumi.dynamic.UpdateResult> {\n\t\treturn { outs: { ...news, appId: id } };\n\t}\n\n\tasync delete(): Promise<void> {\n\t\tpulumi.log.warn(\n\t\t\t\"Fly app deletion skipped — apps are not deleted by Pulumi (would destroy all contained resources)\",\n\t\t);\n\t}\n\n\tasync diff(\n\t\t_id: string,\n\t\tolds: FlyAppOutputs,\n\t\tnews: FlyAppInputs,\n\t): Promise<pulumi.dynamic.DiffResult> {\n\t\tconst replaces: string[] = [];\n\n\t\tif (olds.name !== news.name) {\n\t\t\treplaces.push(\"name\");\n\t\t}\n\n\t\tif (olds.organization !== news.organization) {\n\t\t\treplaces.push(\"organization\");\n\t\t}\n\n\t\treturn {\n\t\t\tchanges: replaces.length > 0,\n\t\t\treplaces,\n\t\t\tdeleteBeforeReplace: true,\n\t\t};\n\t}\n}\n\n/** Internal dynamic resource — not part of the public API. */\nclass FlyAppResource extends pulumi.dynamic.Resource {\n\tpublic declare readonly appId: pulumi.Output<string>;\n\n\tconstructor(\n\t\tname: string,\n\t\targs: {\n\t\t\ttoken?: pulumi.Input<string>;\n\t\t\ttokenEnvVar?: pulumi.Input<string>;\n\t\t\tname: pulumi.Input<string>;\n\t\t\torganization?: pulumi.Input<string | undefined>;\n\t\t},\n\t\topts?: pulumi.CustomResourceOptions,\n\t) {\n\t\tsuper(\n\t\t\tnew FlyAppResourceProvider(),\n\t\t\tname,\n\t\t\t{ ...args, appId: undefined },\n\t\t\t// The API token flows into dynamic-provider state with the outputs — mark it secret there.\n\t\t\t{ ...opts, additionalSecretOutputs: [\"token\"] },\n\t\t);\n\t}\n}\n\n/** Options type for FlyApp — replaces Pulumi's native `provider` field. */\ntype FlyAppOptions = Omit<pulumi.ComponentResourceOptions, \"provider\"> & {\n\t/** Fly authentication context. */\n\tprovider: FlyProvider;\n};\n\n/** Args for FlyApp. */\nexport interface FlyAppArgs {\n\t/**\n\t * App name (globally unique). Used for adoption lookup and as `.id`.\n\t * Maps to the Fly Machines API field `app_name`.\n\t */\n\tname: pulumi.Input<string>;\n\n\t/**\n\t * Org slug for app creation. Overrides `FlyProvider.organization`.\n\t * Ignored when the app already exists (adoption).\n\t * Maps to the Fly Machines API field `org_slug`.\n\t */\n\torganization?: pulumi.Input<string>;\n}\n\n/**\n * Manages a Fly app with adopt-or-create semantics.\n *\n * @example\n * ```typescript\n * const app = new FlyApp(\"api\", { name: \"rby-api\" }, { provider });\n * ```\n */\nexport class FlyApp extends pulumi.ComponentResource {\n\t/** App identifier (equals the app name). */\n\tpublic readonly id: pulumi.Output<string>;\n\n\tconstructor(name: string, args: FlyAppArgs, opts: FlyAppOptions) {\n\t\tconst { provider, ...pulumiOpts } = opts;\n\n\t\tsuper(\"infracraft:fly:App\", name, {}, pulumiOpts);\n\n\t\tconst resource = new FlyAppResource(\n\t\t\t`${name}-resource`,\n\t\t\t{\n\t\t\t\ttoken: provider.token,\n\t\t\t\ttokenEnvVar: provider.tokenEnvVar,\n\t\t\t\tname: args.name,\n\t\t\t\torganization: args.organization ?? provider.organization,\n\t\t\t},\n\t\t\t{ parent: this },\n\t\t);\n\n\t\tthis.id = resource.appId;\n\n\t\tthis.registerOutputs({ id: this.id });\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;;;AA2CA,IAAa,yBAAb,MAA+E;CAC9E,MAAM,OAAO,QAA4D;EACxE,MAAM,SAAS,IAAI,UAClB,kBAAkB,OAAO,OAAO,OAAO,WAAW,CACnD;EAMA,IAAI,MAJmB,OAAO,OAC7B,YAAY,OAAO,MACpB,GAGC,OAAO,IAAI,KAAK,8BAA8B,OAAO,KAAK,EAAE;OACtD;GACN,IAAI,CAAC,OAAO,cACX,MAAM,IAAI,MACT,WAAW,OAAO,KAAK,0FACxB;GAGD,OAAO,IAAI,KAAK,YAAY,OAAO,KAAK,0BAA0B;GAElE,MAAM,OAAO,KAAK,YAAY;IAC7B,UAAU,OAAO;IACjB,UAAU,OAAO;GAClB,CAAC;EACF;EAEA,MAAM,OAAsB;GAAE,GAAG;GAAQ,OAAO,OAAO;EAAK;EAE5D,OAAO;GAAE,IAAI,OAAO;GAAM;EAAK;CAChC;CAEA,MAAM,KACL,IACA,OACqC;EAKrC,MAAM,MAAM,MAAM,IAJC,UAClB,kBAAkB,MAAM,OAAO,MAAM,WAAW,CAG1B,EAAE,OAAuB,YAAY,IAAI;EAEhE,IAAI,CAAC,KAEJ,OAAO,CAAC;EAGT,OAAO;GAAE;GAAI,OAAO;IAAE,GAAG;IAAO,MAAM,IAAI;IAAM,OAAO,IAAI;GAAK;EAAE;CACnE;CAEA,MAAM,OACL,IACA,OACA,MACuC;EACvC,OAAO,EAAE,MAAM;GAAE,GAAG;GAAM,OAAO;EAAG,EAAE;CACvC;CAEA,MAAM,SAAwB;EAC7B,OAAO,IAAI,KACV,mGACD;CACD;CAEA,MAAM,KACL,KACA,MACA,MACqC;EACrC,MAAM,WAAqB,CAAC;EAE5B,IAAI,KAAK,SAAS,KAAK,MACtB,SAAS,KAAK,MAAM;EAGrB,IAAI,KAAK,iBAAiB,KAAK,cAC9B,SAAS,KAAK,cAAc;EAG7B,OAAO;GACN,SAAS,SAAS,SAAS;GAC3B;GACA,qBAAqB;EACtB;CACD;AACD;;AAGA,IAAM,iBAAN,cAA6B,OAAO,QAAQ,SAAS;CAGpD,YACC,MACA,MAMA,MACC;EACD,MACC,IAAI,uBAAuB,GAC3B,MACA;GAAE,GAAG;GAAM,OAAO;EAAU,GAE5B;GAAE,GAAG;GAAM,yBAAyB,CAAC,OAAO;EAAE,CAC/C;CACD;AACD;;;;;;;;;AAgCA,IAAa,SAAb,cAA4B,OAAO,kBAAkB;CAIpD,YAAY,MAAc,MAAkB,MAAqB;EAChE,MAAM,EAAE,UAAU,GAAG,eAAe;EAEpC,MAAM,sBAAsB,MAAM,CAAC,GAAG,UAAU;EAEhD,MAAM,WAAW,IAAI,eACpB,GAAG,KAAK,YACR;GACC,OAAO,SAAS;GAChB,aAAa,SAAS;GACtB,MAAM,KAAK;GACX,cAAc,KAAK,gBAAgB,SAAS;EAC7C,GACA,EAAE,QAAQ,KAAK,CAChB;EAEA,KAAK,KAAK,SAAS;EAEnB,KAAK,gBAAgB,EAAE,IAAI,KAAK,GAAG,CAAC;CACrC;AACD"}
|
package/dist/fly/certificate.cjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
2
|
const require_chunk = require('../chunk-BVYJZCqc.cjs');
|
|
3
|
+
const require_dynamic_resolve_credential = require('../dynamic/resolve-credential.cjs');
|
|
3
4
|
const require_errors_api_not_found_error = require('../errors/api-not-found-error.cjs');
|
|
4
5
|
const require_fly_client = require('./client.cjs');
|
|
5
6
|
let _pulumi_pulumi = require("@pulumi/pulumi");
|
|
@@ -16,7 +17,7 @@ _pulumi_pulumi = require_chunk.__toESM(_pulumi_pulumi, 1);
|
|
|
16
17
|
*/
|
|
17
18
|
var FlyCertificateResourceProvider = class {
|
|
18
19
|
async create(inputs) {
|
|
19
|
-
const client = new require_fly_client.FlyClient(inputs.token);
|
|
20
|
+
const client = new require_fly_client.FlyClient(require_dynamic_resolve_credential.resolveCredential(inputs.token, inputs.tokenEnvVar));
|
|
20
21
|
const path = `/v1/apps/${inputs.appName}/certificates/${encodeURIComponent(inputs.hostname)}`;
|
|
21
22
|
let cert = await client.tryGet(path);
|
|
22
23
|
if (cert) _pulumi_pulumi.log.info(`Adopting existing Fly certificate "${inputs.hostname}"`);
|
|
@@ -32,7 +33,7 @@ var FlyCertificateResourceProvider = class {
|
|
|
32
33
|
};
|
|
33
34
|
}
|
|
34
35
|
async read(id, props) {
|
|
35
|
-
const cert = await new require_fly_client.FlyClient(props.token).tryGet(`/v1/apps/${props.appName}/certificates/${encodeURIComponent(id)}`);
|
|
36
|
+
const cert = await new require_fly_client.FlyClient(require_dynamic_resolve_credential.resolveCredential(props.token, props.tokenEnvVar)).tryGet(`/v1/apps/${props.appName}/certificates/${encodeURIComponent(id)}`);
|
|
36
37
|
if (!cert) return {};
|
|
37
38
|
return {
|
|
38
39
|
id,
|
|
@@ -51,7 +52,7 @@ var FlyCertificateResourceProvider = class {
|
|
|
51
52
|
} };
|
|
52
53
|
}
|
|
53
54
|
async delete(id, props) {
|
|
54
|
-
const client = new require_fly_client.FlyClient(props.token);
|
|
55
|
+
const client = new require_fly_client.FlyClient(require_dynamic_resolve_credential.resolveCredential(props.token, props.tokenEnvVar));
|
|
55
56
|
try {
|
|
56
57
|
await client.delete(`/v1/apps/${props.appName}/certificates/${encodeURIComponent(id)}`);
|
|
57
58
|
} catch (error) {
|
|
@@ -105,6 +106,7 @@ var FlyCertificate = class extends _pulumi_pulumi.ComponentResource {
|
|
|
105
106
|
super("infracraft:fly:Certificate", name, {}, pulumiOpts);
|
|
106
107
|
const resource = new FlyCertificateResource(`${name}-resource`, {
|
|
107
108
|
token: provider.token,
|
|
109
|
+
tokenEnvVar: provider.tokenEnvVar,
|
|
108
110
|
appName: app.id,
|
|
109
111
|
hostname: args.hostname
|
|
110
112
|
}, { parent: this });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"certificate.cjs","names":["FlyClient","ApiNotFoundError","pulumi"],"sources":["../../src/fly/certificate.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\n\nimport { ApiNotFoundError } from \"../errors/api-not-found-error\";\nimport type { FlyApp } from \"./app\";\nimport { FlyClient } from \"./client\";\nimport type { FlyProvider } from \"./provider\";\n\n/** DNS records the consumer must create for certificate validation. */\nexport interface FlyDnsRequirements {\n\t/** ACME challenge CNAME record. */\n\tacme_challenge?: { name: string; target: string };\n\n\t/** `_fly-ownership` TXT record. */\n\townership?: { name: string; app_value: string };\n\n\t/** CNAME target for the hostname itself. */\n\tcname?: string;\n}\n\n/** Resolved inputs for the Fly certificate dynamic provider. */\ninterface FlyCertificateInputs {\n\t/** Fly API token. */\n\ttoken: string;\n\n\t/** App name the certificate belongs to. */\n\tappName: string;\n\n\t/** Hostname to issue an ACME certificate for. Used as the resource key. */\n\thostname: string;\n}\n\n/** Persisted state for the Fly certificate. */\ninterface FlyCertificateOutputs extends FlyCertificateInputs {\n\t/** Whether the certificate is fully provisioned (DNS correct). */\n\tconfigured: boolean;\n\n\t/** DNS records required for validation. */\n\tdnsRequirements: FlyDnsRequirements;\n}\n\n/** Certificate response (only the fields we read). */\ninterface FlyCertificateResponse {\n\thostname: string;\n\tconfigured: boolean;\n\tdns_requirements?: FlyDnsRequirements;\n}\n\n/**\n * Dynamic provider for Fly ACME (Let's Encrypt) certificates. `create()` checks\n * for an existing cert by hostname and adopts it, otherwise it requests one via\n * `POST /v1/apps/{app}/certificates/acme`. The Machines API returns no `id` —\n * the hostname is the resource key.\n *\n * @internal Exported only for unit testing; not part of the public API surface.\n */\nexport class FlyCertificateResourceProvider\n\timplements pulumi.dynamic.ResourceProvider\n{\n\tasync create(\n\t\tinputs: FlyCertificateInputs,\n\t): Promise<pulumi.dynamic.CreateResult> {\n\t\tconst client = new FlyClient(inputs.token);\n\t\tconst path = `/v1/apps/${inputs.appName}/certificates/${encodeURIComponent(inputs.hostname)}`;\n\n\t\tlet cert = await client.tryGet<FlyCertificateResponse>(path);\n\n\t\tif (cert) {\n\t\t\tpulumi.log.info(`Adopting existing Fly certificate \"${inputs.hostname}\"`);\n\t\t} else {\n\t\t\tcert = await client.post<FlyCertificateResponse>(\n\t\t\t\t`/v1/apps/${inputs.appName}/certificates/acme`,\n\t\t\t\t{ hostname: inputs.hostname },\n\t\t\t);\n\t\t}\n\n\t\tconst outs: FlyCertificateOutputs = {\n\t\t\t...inputs,\n\t\t\tconfigured: cert.configured ?? false,\n\t\t\tdnsRequirements: cert.dns_requirements ?? {},\n\t\t};\n\n\t\treturn { id: inputs.hostname, outs };\n\t}\n\n\tasync read(\n\t\tid: string,\n\t\tprops: FlyCertificateOutputs,\n\t): Promise<pulumi.dynamic.ReadResult> {\n\t\tconst client = new FlyClient(props.token);\n\n\t\tconst cert = await client.tryGet<FlyCertificateResponse>(\n\t\t\t`/v1/apps/${props.appName}/certificates/${encodeURIComponent(id)}`,\n\t\t);\n\n\t\tif (!cert) {\n\t\t\t// Resource gone → blank id lets refresh reconcile the deletion.\n\t\t\treturn {};\n\t\t}\n\n\t\treturn {\n\t\t\tid,\n\t\t\tprops: {\n\t\t\t\t...props,\n\t\t\t\tconfigured: cert.configured ?? false,\n\t\t\t\tdnsRequirements: cert.dns_requirements ?? {},\n\t\t\t},\n\t\t};\n\t}\n\n\tasync update(\n\t\t_id: string,\n\t\t_olds: FlyCertificateOutputs,\n\t\tnews: FlyCertificateInputs,\n\t): Promise<pulumi.dynamic.UpdateResult> {\n\t\t// Hostname/app changes force replacement (see diff); nothing else is updatable.\n\t\treturn {\n\t\t\touts: { ...news, configured: false, dnsRequirements: {} },\n\t\t};\n\t}\n\n\tasync delete(id: string, props: FlyCertificateOutputs): Promise<void> {\n\t\tconst client = new FlyClient(props.token);\n\n\t\ttry {\n\t\t\tawait client.delete(\n\t\t\t\t`/v1/apps/${props.appName}/certificates/${encodeURIComponent(id)}`,\n\t\t\t);\n\t\t} catch (error) {\n\t\t\t// Already gone — deletion is idempotent.\n\t\t\tif (error instanceof ApiNotFoundError) {\n\t\t\t\tpulumi.log.warn(`Fly certificate \"${id}\" already deleted`);\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tasync diff(\n\t\t_id: string,\n\t\tolds: FlyCertificateOutputs,\n\t\tnews: FlyCertificateInputs,\n\t): Promise<pulumi.dynamic.DiffResult> {\n\t\tconst replaces: string[] = [];\n\n\t\tif (olds.appName !== news.appName) {\n\t\t\treplaces.push(\"appName\");\n\t\t}\n\n\t\tif (olds.hostname !== news.hostname) {\n\t\t\treplaces.push(\"hostname\");\n\t\t}\n\n\t\treturn {\n\t\t\tchanges: replaces.length > 0,\n\t\t\treplaces,\n\t\t\tdeleteBeforeReplace: true,\n\t\t};\n\t}\n}\n\n/** Internal dynamic resource — not part of the public API. */\nclass FlyCertificateResource extends pulumi.dynamic.Resource {\n\tpublic declare readonly configured: pulumi.Output<boolean>;\n\tpublic declare readonly dnsRequirements: pulumi.Output<FlyDnsRequirements>;\n\n\tconstructor(\n\t\tname: string,\n\t\targs: {\n\t\t\ttoken: pulumi.Input<string>;\n\t\t\tappName: pulumi.Input<string>;\n\t\t\thostname: pulumi.Input<string>;\n\t\t},\n\t\topts?: pulumi.CustomResourceOptions,\n\t) {\n\t\tsuper(\n\t\t\tnew FlyCertificateResourceProvider(),\n\t\t\tname,\n\t\t\t{ ...args, configured: undefined, dnsRequirements: undefined },\n\t\t\t// The API token flows into dynamic-provider state with the outputs — mark it secret there.\n\t\t\t{ ...opts, additionalSecretOutputs: [\"token\"] },\n\t\t);\n\t}\n}\n\n/** Options type for FlyCertificate. */\ntype FlyCertificateOptions = Omit<\n\tpulumi.ComponentResourceOptions,\n\t\"provider\"\n> & {\n\t/** Fly authentication context. */\n\tprovider: FlyProvider;\n\n\t/** App the certificate belongs to. */\n\tapp: FlyApp;\n};\n\n/** Args for FlyCertificate. */\nexport interface FlyCertificateArgs {\n\t/** Hostname to issue an ACME certificate for (e.g. `\"api.example.com\"`). */\n\thostname: pulumi.Input<string>;\n}\n\n/**\n * Manages a Fly ACME certificate for a custom hostname.\n *\n * Exposes `.configured` and `.dnsRequirements` so the consumer can wire up the\n * required DNS records.\n *\n * @example\n * ```typescript\n * const cert = new FlyCertificate(\"api-cert\", {\n * hostname: \"api.example.com\",\n * }, { provider, app });\n * ```\n */\nexport class FlyCertificate extends pulumi.ComponentResource {\n\t/** Certificate identifier (equals the hostname). */\n\tpublic readonly id: pulumi.Output<string>;\n\n\t/** Whether the certificate is fully provisioned. */\n\tpublic readonly configured: pulumi.Output<boolean>;\n\n\t/** DNS records required for validation. */\n\tpublic readonly dnsRequirements: pulumi.Output<FlyDnsRequirements>;\n\n\tconstructor(\n\t\tname: string,\n\t\targs: FlyCertificateArgs,\n\t\topts: FlyCertificateOptions,\n\t) {\n\t\tconst { provider, app, ...pulumiOpts } = opts;\n\n\t\tsuper(\"infracraft:fly:Certificate\", name, {}, pulumiOpts);\n\n\t\tconst resource = new FlyCertificateResource(\n\t\t\t`${name}-resource`,\n\t\t\t{\n\t\t\t\ttoken: provider.token,\n\t\t\t\tappName: app.id,\n\t\t\t\thostname: args.hostname,\n\t\t\t},\n\t\t\t{ parent: this },\n\t\t);\n\n\t\tthis.id = pulumi.output(args.hostname);\n\t\tthis.configured = resource.configured;\n\t\tthis.dnsRequirements = resource.dnsRequirements;\n\n\t\tthis.registerOutputs({\n\t\t\tid: this.id,\n\t\t\tconfigured: this.configured,\n\t\t\tdnsRequirements: this.dnsRequirements,\n\t\t});\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAuDA,IAAa,iCAAb,MAEA;CACC,MAAM,OACL,QACuC;EACvC,MAAM,SAAS,IAAIA,6BAAU,OAAO,KAAK;EACzC,MAAM,OAAO,YAAY,OAAO,QAAQ,gBAAgB,mBAAmB,OAAO,QAAQ;EAE1F,IAAI,OAAO,MAAM,OAAO,OAA+B,IAAI;EAE3D,IAAI,MACH,eAAO,IAAI,KAAK,sCAAsC,OAAO,SAAS,EAAE;OAExE,OAAO,MAAM,OAAO,KACnB,YAAY,OAAO,QAAQ,qBAC3B,EAAE,UAAU,OAAO,SAAS,CAC7B;EAGD,MAAM,OAA8B;GACnC,GAAG;GACH,YAAY,KAAK,cAAc;GAC/B,iBAAiB,KAAK,oBAAoB,CAAC;EAC5C;EAEA,OAAO;GAAE,IAAI,OAAO;GAAU;EAAK;CACpC;CAEA,MAAM,KACL,IACA,OACqC;EAGrC,MAAM,OAAO,MAAM,IAFAA,6BAAU,MAAM,KAEX,EAAE,OACzB,YAAY,MAAM,QAAQ,gBAAgB,mBAAmB,EAAE,GAChE;EAEA,IAAI,CAAC,MAEJ,OAAO,CAAC;EAGT,OAAO;GACN;GACA,OAAO;IACN,GAAG;IACH,YAAY,KAAK,cAAc;IAC/B,iBAAiB,KAAK,oBAAoB,CAAC;GAC5C;EACD;CACD;CAEA,MAAM,OACL,KACA,OACA,MACuC;EAEvC,OAAO,EACN,MAAM;GAAE,GAAG;GAAM,YAAY;GAAO,iBAAiB,CAAC;EAAE,EACzD;CACD;CAEA,MAAM,OAAO,IAAY,OAA6C;EACrE,MAAM,SAAS,IAAIA,6BAAU,MAAM,KAAK;EAExC,IAAI;GACH,MAAM,OAAO,OACZ,YAAY,MAAM,QAAQ,gBAAgB,mBAAmB,EAAE,GAChE;EACD,SAAS,OAAO;GAEf,IAAI,iBAAiBC,qDAAkB;IACtC,eAAO,IAAI,KAAK,oBAAoB,GAAG,kBAAkB;IAEzD;GACD;GAEA,MAAM;EACP;CACD;CAEA,MAAM,KACL,KACA,MACA,MACqC;EACrC,MAAM,WAAqB,CAAC;EAE5B,IAAI,KAAK,YAAY,KAAK,SACzB,SAAS,KAAK,SAAS;EAGxB,IAAI,KAAK,aAAa,KAAK,UAC1B,SAAS,KAAK,UAAU;EAGzB,OAAO;GACN,SAAS,SAAS,SAAS;GAC3B;GACA,qBAAqB;EACtB;CACD;AACD;;AAGA,IAAM,yBAAN,cAAqCC,eAAO,QAAQ,SAAS;CAI5D,YACC,MACA,MAKA,MACC;EACD,MACC,IAAI,+BAA+B,GACnC,MACA;GAAE,GAAG;GAAM,YAAY;GAAW,iBAAiB;EAAU,GAE7D;GAAE,GAAG;GAAM,yBAAyB,CAAC,OAAO;EAAE,CAC/C;CACD;AACD;;;;;;;;;;;;;;AAiCA,IAAa,iBAAb,cAAoCA,eAAO,kBAAkB;CAU5D,YACC,MACA,MACA,MACC;EACD,MAAM,EAAE,UAAU,KAAK,GAAG,eAAe;EAEzC,MAAM,8BAA8B,MAAM,CAAC,GAAG,UAAU;EAExD,MAAM,WAAW,IAAI,uBACpB,GAAG,KAAK,YACR;GACC,OAAO,SAAS;GAChB,SAAS,IAAI;GACb,UAAU,KAAK;EAChB,GACA,EAAE,QAAQ,KAAK,CAChB;EAEA,KAAK,KAAKA,eAAO,OAAO,KAAK,QAAQ;EACrC,KAAK,aAAa,SAAS;EAC3B,KAAK,kBAAkB,SAAS;EAEhC,KAAK,gBAAgB;GACpB,IAAI,KAAK;GACT,YAAY,KAAK;GACjB,iBAAiB,KAAK;EACvB,CAAC;CACF;AACD"}
|
|
1
|
+
{"version":3,"file":"certificate.cjs","names":["FlyClient","resolveCredential","ApiNotFoundError","pulumi"],"sources":["../../src/fly/certificate.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\n\nimport { resolveCredential } from \"../dynamic/resolve-credential\";\nimport { ApiNotFoundError } from \"../errors/api-not-found-error\";\nimport type { FlyApp } from \"./app\";\nimport { FlyClient } from \"./client\";\nimport type { FlyProvider } from \"./provider\";\n\n/** DNS records the consumer must create for certificate validation. */\nexport interface FlyDnsRequirements {\n\t/** ACME challenge CNAME record. */\n\tacme_challenge?: { name: string; target: string };\n\n\t/** `_fly-ownership` TXT record. */\n\townership?: { name: string; app_value: string };\n\n\t/** CNAME target for the hostname itself. */\n\tcname?: string;\n}\n\n/** Resolved inputs for the Fly certificate dynamic provider. */\ninterface FlyCertificateInputs {\n\t/** Fly API token. Absent when `tokenEnvVar` is used instead. */\n\ttoken?: string;\n\n\t/** Env var name resolved to the token when `token` is absent (see `FlyProviderArgs.tokenEnvVar`). */\n\ttokenEnvVar?: string;\n\n\t/** App name the certificate belongs to. */\n\tappName: string;\n\n\t/** Hostname to issue an ACME certificate for. Used as the resource key. */\n\thostname: string;\n}\n\n/** Persisted state for the Fly certificate. */\ninterface FlyCertificateOutputs extends FlyCertificateInputs {\n\t/** Whether the certificate is fully provisioned (DNS correct). */\n\tconfigured: boolean;\n\n\t/** DNS records required for validation. */\n\tdnsRequirements: FlyDnsRequirements;\n}\n\n/** Certificate response (only the fields we read). */\ninterface FlyCertificateResponse {\n\thostname: string;\n\tconfigured: boolean;\n\tdns_requirements?: FlyDnsRequirements;\n}\n\n/**\n * Dynamic provider for Fly ACME (Let's Encrypt) certificates. `create()` checks\n * for an existing cert by hostname and adopts it, otherwise it requests one via\n * `POST /v1/apps/{app}/certificates/acme`. The Machines API returns no `id` —\n * the hostname is the resource key.\n *\n * @internal Exported only for unit testing; not part of the public API surface.\n */\nexport class FlyCertificateResourceProvider\n\timplements pulumi.dynamic.ResourceProvider\n{\n\tasync create(\n\t\tinputs: FlyCertificateInputs,\n\t): Promise<pulumi.dynamic.CreateResult> {\n\t\tconst client = new FlyClient(\n\t\t\tresolveCredential(inputs.token, inputs.tokenEnvVar),\n\t\t);\n\n\t\tconst path = `/v1/apps/${inputs.appName}/certificates/${encodeURIComponent(inputs.hostname)}`;\n\n\t\tlet cert = await client.tryGet<FlyCertificateResponse>(path);\n\n\t\tif (cert) {\n\t\t\tpulumi.log.info(`Adopting existing Fly certificate \"${inputs.hostname}\"`);\n\t\t} else {\n\t\t\tcert = await client.post<FlyCertificateResponse>(\n\t\t\t\t`/v1/apps/${inputs.appName}/certificates/acme`,\n\t\t\t\t{ hostname: inputs.hostname },\n\t\t\t);\n\t\t}\n\n\t\tconst outs: FlyCertificateOutputs = {\n\t\t\t...inputs,\n\t\t\tconfigured: cert.configured ?? false,\n\t\t\tdnsRequirements: cert.dns_requirements ?? {},\n\t\t};\n\n\t\treturn { id: inputs.hostname, outs };\n\t}\n\n\tasync read(\n\t\tid: string,\n\t\tprops: FlyCertificateOutputs,\n\t): Promise<pulumi.dynamic.ReadResult> {\n\t\tconst client = new FlyClient(\n\t\t\tresolveCredential(props.token, props.tokenEnvVar),\n\t\t);\n\n\t\tconst cert = await client.tryGet<FlyCertificateResponse>(\n\t\t\t`/v1/apps/${props.appName}/certificates/${encodeURIComponent(id)}`,\n\t\t);\n\n\t\tif (!cert) {\n\t\t\t// Resource gone → blank id lets refresh reconcile the deletion.\n\t\t\treturn {};\n\t\t}\n\n\t\treturn {\n\t\t\tid,\n\t\t\tprops: {\n\t\t\t\t...props,\n\t\t\t\tconfigured: cert.configured ?? false,\n\t\t\t\tdnsRequirements: cert.dns_requirements ?? {},\n\t\t\t},\n\t\t};\n\t}\n\n\tasync update(\n\t\t_id: string,\n\t\t_olds: FlyCertificateOutputs,\n\t\tnews: FlyCertificateInputs,\n\t): Promise<pulumi.dynamic.UpdateResult> {\n\t\t// Hostname/app changes force replacement (see diff); nothing else is updatable.\n\t\treturn {\n\t\t\touts: { ...news, configured: false, dnsRequirements: {} },\n\t\t};\n\t}\n\n\tasync delete(id: string, props: FlyCertificateOutputs): Promise<void> {\n\t\tconst client = new FlyClient(\n\t\t\tresolveCredential(props.token, props.tokenEnvVar),\n\t\t);\n\n\t\ttry {\n\t\t\tawait client.delete(\n\t\t\t\t`/v1/apps/${props.appName}/certificates/${encodeURIComponent(id)}`,\n\t\t\t);\n\t\t} catch (error) {\n\t\t\t// Already gone — deletion is idempotent.\n\t\t\tif (error instanceof ApiNotFoundError) {\n\t\t\t\tpulumi.log.warn(`Fly certificate \"${id}\" already deleted`);\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tasync diff(\n\t\t_id: string,\n\t\tolds: FlyCertificateOutputs,\n\t\tnews: FlyCertificateInputs,\n\t): Promise<pulumi.dynamic.DiffResult> {\n\t\tconst replaces: string[] = [];\n\n\t\tif (olds.appName !== news.appName) {\n\t\t\treplaces.push(\"appName\");\n\t\t}\n\n\t\tif (olds.hostname !== news.hostname) {\n\t\t\treplaces.push(\"hostname\");\n\t\t}\n\n\t\treturn {\n\t\t\tchanges: replaces.length > 0,\n\t\t\treplaces,\n\t\t\tdeleteBeforeReplace: true,\n\t\t};\n\t}\n}\n\n/** Internal dynamic resource — not part of the public API. */\nclass FlyCertificateResource extends pulumi.dynamic.Resource {\n\tpublic declare readonly configured: pulumi.Output<boolean>;\n\tpublic declare readonly dnsRequirements: pulumi.Output<FlyDnsRequirements>;\n\n\tconstructor(\n\t\tname: string,\n\t\targs: {\n\t\t\ttoken?: pulumi.Input<string>;\n\t\t\ttokenEnvVar?: pulumi.Input<string>;\n\t\t\tappName: pulumi.Input<string>;\n\t\t\thostname: pulumi.Input<string>;\n\t\t},\n\t\topts?: pulumi.CustomResourceOptions,\n\t) {\n\t\tsuper(\n\t\t\tnew FlyCertificateResourceProvider(),\n\t\t\tname,\n\t\t\t{ ...args, configured: undefined, dnsRequirements: undefined },\n\t\t\t// The API token flows into dynamic-provider state with the outputs — mark it secret there.\n\t\t\t{ ...opts, additionalSecretOutputs: [\"token\"] },\n\t\t);\n\t}\n}\n\n/** Options type for FlyCertificate. */\ntype FlyCertificateOptions = Omit<\n\tpulumi.ComponentResourceOptions,\n\t\"provider\"\n> & {\n\t/** Fly authentication context. */\n\tprovider: FlyProvider;\n\n\t/** App the certificate belongs to. */\n\tapp: FlyApp;\n};\n\n/** Args for FlyCertificate. */\nexport interface FlyCertificateArgs {\n\t/** Hostname to issue an ACME certificate for (e.g. `\"api.example.com\"`). */\n\thostname: pulumi.Input<string>;\n}\n\n/**\n * Manages a Fly ACME certificate for a custom hostname.\n *\n * Exposes `.configured` and `.dnsRequirements` so the consumer can wire up the\n * required DNS records.\n *\n * @example\n * ```typescript\n * const cert = new FlyCertificate(\"api-cert\", {\n * hostname: \"api.example.com\",\n * }, { provider, app });\n * ```\n */\nexport class FlyCertificate extends pulumi.ComponentResource {\n\t/** Certificate identifier (equals the hostname). */\n\tpublic readonly id: pulumi.Output<string>;\n\n\t/** Whether the certificate is fully provisioned. */\n\tpublic readonly configured: pulumi.Output<boolean>;\n\n\t/** DNS records required for validation. */\n\tpublic readonly dnsRequirements: pulumi.Output<FlyDnsRequirements>;\n\n\tconstructor(\n\t\tname: string,\n\t\targs: FlyCertificateArgs,\n\t\topts: FlyCertificateOptions,\n\t) {\n\t\tconst { provider, app, ...pulumiOpts } = opts;\n\n\t\tsuper(\"infracraft:fly:Certificate\", name, {}, pulumiOpts);\n\n\t\tconst resource = new FlyCertificateResource(\n\t\t\t`${name}-resource`,\n\t\t\t{\n\t\t\t\ttoken: provider.token,\n\t\t\t\ttokenEnvVar: provider.tokenEnvVar,\n\t\t\t\tappName: app.id,\n\t\t\t\thostname: args.hostname,\n\t\t\t},\n\t\t\t{ parent: this },\n\t\t);\n\n\t\tthis.id = pulumi.output(args.hostname);\n\t\tthis.configured = resource.configured;\n\t\tthis.dnsRequirements = resource.dnsRequirements;\n\n\t\tthis.registerOutputs({\n\t\t\tid: this.id,\n\t\t\tconfigured: this.configured,\n\t\t\tdnsRequirements: this.dnsRequirements,\n\t\t});\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AA2DA,IAAa,iCAAb,MAEA;CACC,MAAM,OACL,QACuC;EACvC,MAAM,SAAS,IAAIA,6BAClBC,qDAAkB,OAAO,OAAO,OAAO,WAAW,CACnD;EAEA,MAAM,OAAO,YAAY,OAAO,QAAQ,gBAAgB,mBAAmB,OAAO,QAAQ;EAE1F,IAAI,OAAO,MAAM,OAAO,OAA+B,IAAI;EAE3D,IAAI,MACH,eAAO,IAAI,KAAK,sCAAsC,OAAO,SAAS,EAAE;OAExE,OAAO,MAAM,OAAO,KACnB,YAAY,OAAO,QAAQ,qBAC3B,EAAE,UAAU,OAAO,SAAS,CAC7B;EAGD,MAAM,OAA8B;GACnC,GAAG;GACH,YAAY,KAAK,cAAc;GAC/B,iBAAiB,KAAK,oBAAoB,CAAC;EAC5C;EAEA,OAAO;GAAE,IAAI,OAAO;GAAU;EAAK;CACpC;CAEA,MAAM,KACL,IACA,OACqC;EAKrC,MAAM,OAAO,MAAM,IAJAD,6BAClBC,qDAAkB,MAAM,OAAO,MAAM,WAAW,CAGzB,EAAE,OACzB,YAAY,MAAM,QAAQ,gBAAgB,mBAAmB,EAAE,GAChE;EAEA,IAAI,CAAC,MAEJ,OAAO,CAAC;EAGT,OAAO;GACN;GACA,OAAO;IACN,GAAG;IACH,YAAY,KAAK,cAAc;IAC/B,iBAAiB,KAAK,oBAAoB,CAAC;GAC5C;EACD;CACD;CAEA,MAAM,OACL,KACA,OACA,MACuC;EAEvC,OAAO,EACN,MAAM;GAAE,GAAG;GAAM,YAAY;GAAO,iBAAiB,CAAC;EAAE,EACzD;CACD;CAEA,MAAM,OAAO,IAAY,OAA6C;EACrE,MAAM,SAAS,IAAID,6BAClBC,qDAAkB,MAAM,OAAO,MAAM,WAAW,CACjD;EAEA,IAAI;GACH,MAAM,OAAO,OACZ,YAAY,MAAM,QAAQ,gBAAgB,mBAAmB,EAAE,GAChE;EACD,SAAS,OAAO;GAEf,IAAI,iBAAiBC,qDAAkB;IACtC,eAAO,IAAI,KAAK,oBAAoB,GAAG,kBAAkB;IAEzD;GACD;GAEA,MAAM;EACP;CACD;CAEA,MAAM,KACL,KACA,MACA,MACqC;EACrC,MAAM,WAAqB,CAAC;EAE5B,IAAI,KAAK,YAAY,KAAK,SACzB,SAAS,KAAK,SAAS;EAGxB,IAAI,KAAK,aAAa,KAAK,UAC1B,SAAS,KAAK,UAAU;EAGzB,OAAO;GACN,SAAS,SAAS,SAAS;GAC3B;GACA,qBAAqB;EACtB;CACD;AACD;;AAGA,IAAM,yBAAN,cAAqCC,eAAO,QAAQ,SAAS;CAI5D,YACC,MACA,MAMA,MACC;EACD,MACC,IAAI,+BAA+B,GACnC,MACA;GAAE,GAAG;GAAM,YAAY;GAAW,iBAAiB;EAAU,GAE7D;GAAE,GAAG;GAAM,yBAAyB,CAAC,OAAO;EAAE,CAC/C;CACD;AACD;;;;;;;;;;;;;;AAiCA,IAAa,iBAAb,cAAoCA,eAAO,kBAAkB;CAU5D,YACC,MACA,MACA,MACC;EACD,MAAM,EAAE,UAAU,KAAK,GAAG,eAAe;EAEzC,MAAM,8BAA8B,MAAM,CAAC,GAAG,UAAU;EAExD,MAAM,WAAW,IAAI,uBACpB,GAAG,KAAK,YACR;GACC,OAAO,SAAS;GAChB,aAAa,SAAS;GACtB,SAAS,IAAI;GACb,UAAU,KAAK;EAChB,GACA,EAAE,QAAQ,KAAK,CAChB;EAEA,KAAK,KAAKA,eAAO,OAAO,KAAK,QAAQ;EACrC,KAAK,aAAa,SAAS;EAC3B,KAAK,kBAAkB,SAAS;EAEhC,KAAK,gBAAgB;GACpB,IAAI,KAAK;GACT,YAAY,KAAK;GACjB,iBAAiB,KAAK;EACvB,CAAC;CACF;AACD"}
|
|
@@ -21,8 +21,10 @@ interface FlyDnsRequirements {
|
|
|
21
21
|
}
|
|
22
22
|
/** Resolved inputs for the Fly certificate dynamic provider. */
|
|
23
23
|
interface FlyCertificateInputs {
|
|
24
|
-
/** Fly API token. */
|
|
25
|
-
token
|
|
24
|
+
/** Fly API token. Absent when `tokenEnvVar` is used instead. */
|
|
25
|
+
token?: string;
|
|
26
|
+
/** Env var name resolved to the token when `token` is absent (see `FlyProviderArgs.tokenEnvVar`). */
|
|
27
|
+
tokenEnvVar?: string;
|
|
26
28
|
/** App name the certificate belongs to. */
|
|
27
29
|
appName: string;
|
|
28
30
|
/** Hostname to issue an ACME certificate for. Used as the resource key. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"certificate.d.cts","names":[],"sources":["../../src/fly/certificate.ts"],"mappings":";;;;;;;
|
|
1
|
+
{"version":3,"file":"certificate.d.cts","names":[],"sources":["../../src/fly/certificate.ts"],"mappings":";;;;;;;UASiB,kBAAA;;EAEhB,cAAA;IAAmB,IAAA;IAAc,MAAA;EAAA;EAAjC;EAGA,SAAA;IAAc,IAAA;IAAc,SAAA;EAAA;EAAA;EAG5B,KAAA;AAAA;AAAK;AAAA,UAII,oBAAA;EAAoB;EAE7B,KAAA;EAF6B;EAK7B,WAAA;EAAA;EAGA,OAAA;EAGA;EAAA,QAAA;AAAA;AAAQ;AAAA,UAIC,qBAAA,SAA8B,oBAAoB;;EAE3D,UAAA;EAFuC;EAKvC,eAAA,EAAiB,kBAAA;AAAA;;;AAAkB;AAkBpC;;;;;cAAa,8BAAA,YACD,MAAA,CAAO,OAAA,CAAQ,gBAAA;EAEpB,MAAA,CACL,MAAA,EAAQ,oBAAA,GACN,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,YAAA;EA2BpB,IAAA,CACL,EAAA,UACA,KAAA,EAAO,qBAAA,GACL,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,UAAA;EAwBpB,MAAA,CACL,GAAA,UACA,KAAA,EAAO,qBAAA,EACP,IAAA,EAAM,oBAAA,GACJ,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,YAAA;EAOpB,MAAA,CAAO,EAAA,UAAY,KAAA,EAAO,qBAAA,GAAwB,OAAA;EAqBlD,IAAA,CACL,GAAA,UACA,IAAA,EAAM,qBAAA,EACN,IAAA,EAAM,oBAAA,GACJ,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,UAAA;AAAA;;KA6CtB,qBAAA,GAAwB,IAAA,CAC5B,MAAA,CAAO,wBAAA;EAvEiD,kCA2ExD,QAAA,EAAU,WAAA,EAnDH;EAsDP,GAAA,EAAK,MAAA;AAAA;;UAIW,kBAAA;EAvJ0B;EAyJ1C,QAAA,EAAU,MAAA,CAAO,KAAK;AAAA;;;;;;;;;;;;;;cAgBV,cAAA,SAAuB,MAAA,CAAO,iBAAA;EAvIxB;EAAA,SAyIF,EAAA,EAAI,MAAA,CAAO,MAAA;EAjHrB;EAAA,SAoHU,UAAA,EAAY,MAAA,CAAO,MAAA;EAlH3B;EAAA,SAqHQ,eAAA,EAAiB,MAAA,CAAO,MAAA,CAAO,kBAAA;cAG9C,IAAA,UACA,IAAA,EAAM,kBAAA,EACN,IAAA,EAAM,qBAAA;AAAA"}
|