@infracraft/pulumi 1.28.1 → 1.29.1

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.
Files changed (46) hide show
  1. package/README.md +79 -29
  2. package/dist/fly/deploy.cjs.map +1 -1
  3. package/dist/fly/deploy.d.cts.map +1 -1
  4. package/dist/fly/deploy.d.mts.map +1 -1
  5. package/dist/fly/deploy.mjs.map +1 -1
  6. package/dist/fly/index.cjs +2 -0
  7. package/dist/fly/index.d.cts +2 -1
  8. package/dist/fly/index.d.mts +2 -1
  9. package/dist/fly/index.mjs +2 -1
  10. package/dist/neon/index.cjs +2 -0
  11. package/dist/neon/index.d.cts +2 -1
  12. package/dist/neon/index.d.mts +2 -1
  13. package/dist/neon/index.mjs +2 -1
  14. package/dist/railway/deploy.cjs.map +1 -1
  15. package/dist/railway/deploy.d.cts.map +1 -1
  16. package/dist/railway/deploy.d.mts.map +1 -1
  17. package/dist/railway/deploy.mjs.map +1 -1
  18. package/dist/railway/project-token.cjs +0 -1
  19. package/dist/railway/project-token.cjs.map +1 -1
  20. package/dist/railway/project-token.d.cts +0 -1
  21. package/dist/railway/project-token.d.cts.map +1 -1
  22. package/dist/railway/project-token.d.mts +0 -1
  23. package/dist/railway/project-token.d.mts.map +1 -1
  24. package/dist/railway/project-token.mjs +0 -1
  25. package/dist/railway/project-token.mjs.map +1 -1
  26. package/dist/railway/service.cjs +51 -0
  27. package/dist/railway/service.cjs.map +1 -1
  28. package/dist/railway/service.d.cts.map +1 -1
  29. package/dist/railway/service.d.mts.map +1 -1
  30. package/dist/railway/service.mjs +51 -0
  31. package/dist/railway/service.mjs.map +1 -1
  32. package/dist/vercel/deploy.cjs.map +1 -1
  33. package/dist/vercel/deploy.d.cts.map +1 -1
  34. package/dist/vercel/deploy.d.mts.map +1 -1
  35. package/dist/vercel/deploy.mjs.map +1 -1
  36. package/dist/vercel/index.cjs +2 -0
  37. package/dist/vercel/index.d.cts +2 -1
  38. package/dist/vercel/index.d.mts +2 -1
  39. package/dist/vercel/index.mjs +2 -1
  40. package/dist/vercel/project.cjs +2 -2
  41. package/dist/vercel/project.cjs.map +1 -1
  42. package/dist/vercel/project.d.cts +2 -2
  43. package/dist/vercel/project.d.mts +2 -2
  44. package/dist/vercel/project.mjs +2 -2
  45. package/dist/vercel/project.mjs.map +1 -1
  46. package/package.json +1 -1
package/README.md CHANGED
@@ -16,20 +16,24 @@ Native Pulumi providers with adopt-or-create semantics and deploy orchestration.
16
16
 
17
17
  ## Design principles
18
18
 
19
- - **Resources model single API objects.** Each resource wraps exactly one platform API object, and argument names mirror the platform API's field names.
20
- - **Adopt-or-create IS the import principle.** `pulumi import` is unimplemented for dynamic providers, so `create()` looks the object up by name and adopts it before creating a new one.
19
+ - **Resources model single API objects.** Each resource wraps exactly one platform API object, and argument names mirror the platform API's field names (where a name deviates, its JSDoc documents the mapped field).
20
+ - **Context-based.** Resources inherit auth, project, and environment from their options (`{ provider, project, environment }`); no manual ID passing.
21
+ - **Adopt-or-create IS the import principle.** `pulumi import` is unimplemented for dynamic providers, so `create()` looks the object up by name and adopts it before creating a new one. Run `pulumi up` against a pre-existing project and it just works.
21
22
  - **Reads reconcile drift.** A resource deleted out of band returns blank on `pulumi refresh` and gets recreated on the next `up`. Write-once secrets and env-var batches are deliberate pass-throughs — their stored state is the source of truth.
22
- - **Deletes are conservative — and idempotent.** Shared containers (projects, project-level services) are never deleted by Pulumi; volumes retain; deleting an already-gone resource succeeds instead of stranding state.
23
- - **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.
23
+ - **Deletes are conservative — and idempotent.** Shared containers (Railway/Neon projects, Railway services, Fly apps) and data stores (Vercel marketplace resources) are never deleted by Pulumi; deleting an already-gone resource succeeds instead of stranding state. Guard everything else that is shared or production-grade with `protect: true`; volumes honor `retainOnDelete`.
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
+ - **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
+ - **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).
24
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.
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.
25
29
 
26
30
  ## Providers
27
31
 
28
32
  | | Provider | Import | What it does |
29
33
  |---|---|---|---|
30
- | 🚂 | **Railway** | `@infracraft/pulumi/railway` | The only Pulumi provider for Railway. Projects, environments, services, variables, volumes, domains, deploys. |
34
+ | 🚂 | **Railway** | `@infracraft/pulumi/railway` | The only Pulumi provider for Railway. Projects, environments, services, variables, volumes, domains, deploy tokens, deploys. |
31
35
  | 🐘 | **Neon** | `@infracraft/pulumi/neon` | Adopt-or-create layer for Neon Postgres. Projects, branches, endpoints, roles, databases. |
32
- | ▲ | **Vercel** | `@infracraft/pulumi/vercel` | Projects with adopt-or-create, deploy orchestration, marketplace resources, and sensitive env var drift detection. |
36
+ | ▲ | **Vercel** | `@infracraft/pulumi/vercel` | Projects with adopt-or-create, deploy orchestration, custom domains, marketplace resources, and sensitive env var drift detection. |
33
37
  | 🎯 | **Fly.io** | `@infracraft/pulumi/fly` | App, Secret, Volume, Certificate, IP, and Deploy resources via the Machines REST API and Fly GraphQL API. |
34
38
  | 🤖 | **Agents** | `@infracraft/pulumi/agents` | Emit operating hints for AI coding agents working on the stack. |
35
39
  | #️⃣ | **Hash** | `@infracraft/pulumi/hash` | Deterministic directory/env-var/app hashing for deploy triggers. |
@@ -79,6 +83,16 @@ const service = new RailwayService("api", {
79
83
  startCommand: "node dist/index.js",
80
84
  }, { provider, project, environment })
81
85
 
86
+ // Image-sourced service: the provider applies `source` to the target
87
+ // environment's instance and owns its deploys (`serviceInstanceDeployV2`) —
88
+ // on create and on config changes. No RailwayDeploy needed; code services
89
+ // (like `api` above) deploy via RailwayDeploy instead. Secret-bearing start
90
+ // commands (e.g. `redis-server --requirepass …`) belong in `startCommand`.
91
+ new RailwayService("redis", {
92
+ name: "redis",
93
+ source: { image: "redis:8-alpine" },
94
+ }, { provider, project, environment })
95
+
82
96
  const env = { DATABASE_URL: dbUrl }
83
97
 
84
98
  new RailwayVariable("api-vars", {
@@ -101,15 +115,17 @@ new RailwayDeploy("api-deploy", {
101
115
  | `RailwayProvider` | — | Pass as `provider` option to every Railway resource |
102
116
  | `RailwayProject` | `.id` (project UUID) | Adopt-or-create by name |
103
117
  | `RailwayEnvironment` | `.id` | Optional `source` env to fork from |
104
- | `RailwayService` | `.id` | Full instance config: builder, healthcheck, restart policy |
105
- | `RailwayDomain` | `.fqdn` | Omit `customDomain` for an auto-generated domain |
118
+ | `RailwayService` | `.id` | Instance config (builder, commands, healthcheck, restart policy) applied per target environment; image services (`source.image`) are deployed by the provider itself |
119
+ | `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 |
106
120
  | `RailwayVariable` | — | Batch upsert; uses `skipDeploys` to avoid snapshot errors |
107
- | `RailwayVolume` | `.id` | Persistent volume; `mountPath` must be absolute |
108
- | `RailwayProjectToken` | `.token` (secret) | Environment-scoped deploy token; feed into `RailwayDeploy` |
109
- | `RailwayDeploy` | `.deploymentUrl` | Runs `railway up --detach`, then monitors the deployment via the Railway API |
121
+ | `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
+ | `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 `railpackConfig` |
110
124
 
111
125
  **Enums:** `RailwayBuilder` (`RAILPACK`, `NIXPACKS`, `DOCKERFILE`, `HEROKU`, `PAKETO`), `RailwayRestartPolicy` (`ON_FAILURE`, `ALWAYS`, `NEVER`)
112
126
 
127
+ **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
+
113
129
  ## Neon
114
130
 
115
131
  ```typescript
@@ -163,9 +179,32 @@ const connectionString = pulumi.interpolate`postgresql://${roleName}:${role.pass
163
179
  | `NeonProject` | `.id` | Adopt-or-create by name |
164
180
  | `NeonBranch` | `.id` | Optional `parent` for copy-on-write branching |
165
181
  | `NeonEndpoint` | `.host` | Read-write compute endpoint; use `.host` in connection strings |
166
- | `NeonRole` | `.password` (secret) | `resetPassword: true` isolates COW branch passwords from parent |
182
+ | `NeonRole` | `.password` (secret) | `resetPassword: true` isolates COW branch passwords from parent. Bump `passwordVersion` to rotate — see [Rotating credentials](#rotating-credentials) |
167
183
  | `NeonDatabase` | — | `name` + `ownerName` |
168
184
 
185
+ ## Rotating credentials
186
+
187
+ Minted credentials rotate through a version-bump input — no target-replace URN archaeology, no manual revoke-then-recreate. Bump the number, run `up`, and everything consuming the credential (connection strings, env vars, dependent redeploys) cascades automatically:
188
+
189
+ ```typescript
190
+ // Mints a fresh token BEFORE revoking the old one (create-before-delete),
191
+ // so there is never a tokenless window.
192
+ const deployToken = new RailwayProjectToken("api-token", {
193
+ name: "api-deploy",
194
+ tokenVersion: 2, // bump to rotate
195
+ }, { provider, project, environment })
196
+
197
+ // Resets the password IN PLACE via Neon's reset_password endpoint — an
198
+ // update, never a replace (a replace would try to delete the role, which
199
+ // Neon refuses for default roles and which would drop grants for others).
200
+ const role = new NeonRole("owner", {
201
+ name: "neondb_owner",
202
+ passwordVersion: 2, // bump to rotate
203
+ }, { provider, project, branch })
204
+ ```
205
+
206
+ Leave the version unset until the first rotation is needed. Identity changes (name, project, environment) still replace normally.
207
+
169
208
  ## Vercel
170
209
 
171
210
  ```typescript
@@ -174,6 +213,7 @@ import {
174
213
  VercelProject,
175
214
  VercelVariable,
176
215
  VercelDeploy,
216
+ VercelDomain,
177
217
  VercelIntegration,
178
218
  VercelMarketplaceResource,
179
219
  VercelResourceConnection,
@@ -202,6 +242,13 @@ new VercelDeploy("web-deploy", {
202
242
  triggers: [hash("apps/web"), vars.contentHash],
203
243
  }, { provider, project })
204
244
 
245
+ // Custom domain: point the domain's DNS CNAME at `cnameTarget`
246
+ const domain = new VercelDomain("web-domain", {
247
+ name: "app.example.com",
248
+ }, { provider, project })
249
+
250
+ export const cnameTarget = domain.cnameTarget
251
+
205
252
  // Marketplace example: provision an Upstash KV store
206
253
  const integration = new VercelIntegration("upstash", {
207
254
  slug: "upstash",
@@ -210,7 +257,7 @@ const integration = new VercelIntegration("upstash", {
210
257
  const store = new VercelMarketplaceResource("kv", {
211
258
  integrationConfigurationId: integration.configurationId,
212
259
  name: "my-kv",
213
- type: "kv",
260
+ type: "upstash-kv", // the integration's product ID or slug
214
261
  externalId: "my-kv",
215
262
  }, { provider })
216
263
 
@@ -226,12 +273,14 @@ new VercelResourceConnection("kv-conn", {
226
273
  | Class | Key outputs | Notes |
227
274
  |---|---|---|
228
275
  | `VercelProvider` | — | `token` + `teamId` |
229
- | `VercelProject` | `.id`, `.url` | `.url` is a full `https://` URL; prefers the custom production domain over `<name>.vercel.app` |
230
- | `VercelVariable` | `.contentHash` | Use as a deploy trigger to redeploy on env var changes |
276
+ | `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
+ | `VercelVariable` | `.contentHash` | Use as a deploy trigger to redeploy on env var changes. Takes `opts.project` or `args.projectId` |
231
278
  | `VercelDeploy` | `.deploymentUrl` | Runs `vercel deploy --prod --yes` |
232
- | `VercelIntegration` | `.configurationId` (`icfg_…`) | Resolves an installed marketplace integration by slug |
233
- | `VercelMarketplaceResource` | `.id`, `.externalResourceId`, `.status` | Provisions a marketplace store |
234
- | `VercelResourceConnection` | | Wires a store to a project; injects env vars into target environments |
279
+ | `VercelDomain` | `.name`, `.verified`, `.cnameTarget` | Attaches a custom domain to a project (adopt-or-create); `.cnameTarget` is Vercel's own DNS recommendation for that specific domain |
280
+ | `VercelIntegration` | `.configurationId` (`icfg_…`) | Resolves an installed marketplace integration by slug (install it once via the dashboard first) |
281
+ | `VercelMarketplaceResource` | `.id`, `.externalResourceId`, `.status` | Provisions a marketplace store; `type` is the integration product ID or slug |
282
+ | `VercelResourceConnection` | — | Wires a store to a project; injects env vars into target environments (`makeEnvVarsSensitive` defaults to `true` — then `targets` must not include `development`) |
283
+ | `VercelClient` | — | Typed REST client behind every Vercel resource (`get` / `tryGet` / `post` / `patch` / `delete`); appends `teamId` to every request and rides the resilient transport |
235
284
 
236
285
  **Helpers:** `VERCEL_FRAMEWORKS` (const array), `VercelFramework` (derived union type)
237
286
 
@@ -301,7 +350,7 @@ new FlyDeploy("api-deploy", {
301
350
  }, { provider, app, dependsOn: [secrets] })
302
351
  ```
303
352
 
304
- **Requirements:** `flyctl` must be installed on the machine running `pulumi up` (used by `FlyDeploy`). Generate a token with `fly tokens create deploy`. Dedicated IP allocation uses the Fly GraphQL API; everything else uses the Machines REST API.
353
+ **Requirements:** `flyctl` must be installed on the machine running `pulumi up` (used by `FlyDeploy`) — call `assertHostBinaries(["fly"])` from `@infracraft/pulumi/sandbox` at program start to fail fast with an install hint instead of mid-deploy. Generate a token with `fly tokens create deploy`. Dedicated IP allocation uses the Fly GraphQL API; everything else uses the Machines REST API.
305
354
 
306
355
  ### Fly.io API surface
307
356
 
@@ -313,13 +362,13 @@ new FlyDeploy("api-deploy", {
313
362
  | `FlyVolume` | `.id` (`vol_…`) | `sizeGb` can only grow |
314
363
  | `FlyCertificate` | `.id` (hostname), `.configured`, `.dnsRequirements` | `.dnsRequirements` contains ACME validation records |
315
364
  | `FlyIp` | `.id` (IP address) | `type`: `FlyIpType.V4`, `V6`, `SHARED_V4`, `PRIVATE_V6` |
316
- | `FlyDeploy` | `.deploymentUrl` | Writes fly.toml at deploy time; triggers on config + source hash |
365
+ | `FlyDeploy` | `.deploymentUrl` | Writes fly.toml at deploy time; triggers on config + source hash. Optional `waitTimeout`, `releaseCommandTimeout`, `highAvailability` |
317
366
 
318
367
  **Enums:** `FlyIpType`, `FlyDeployStrategy` (`ROLLING`, `IMMEDIATE`, `CANARY`, `BLUEGREEN`), `FlyRestartPolicy` (`ALWAYS`, `ON_FAILURE`, `NEVER`), `FlyAutoStopMachines` (`OFF`, `STOP`, `SUSPEND`), `FlyConcurrencyType` (`CONNECTIONS`, `REQUESTS`), `FlyServiceProtocol` (`TCP`, `UDP`), `FlyPortHandler` (`HTTP`, `TLS`, `PG_TLS`, `PROXY_PROTO`, `EDGE_HTTP`), `FlyCpuKind` (`SHARED`, `PERFORMANCE`), `FlyCheckType` (`HTTP`, `TCP`)
319
368
 
320
369
  **Constants:** `FLY_REGIONS` (IATA codes array), `FlyRegion` (derived type), `FLY_VM_SIZES` (size preset array), `FlyVmSize` (derived type)
321
370
 
322
- **fly.toml types:** `FlyTomlConfig`, `FlyBuildConfig`, `FlyHttpService`, `FlyService`, `FlyServicePort`, `FlyMount`, `FlyVm`, `FlyDeployConfig`, `FlyRestartConfig`, `FlyCheck`, `FlyConcurrency`
371
+ **fly.toml types:** `FlyTomlConfig`, `FlyBuildConfig`, `FlyHttpService`, `FlyService`, `FlyServicePort`, `FlyMount`, `FlyVm`, `FlyCpuCount`, `FlyDeployConfig`, `FlyRestartConfig`, `FlyCheck`, `FlyConcurrency`, `FlyDnsRequirements`
323
372
 
324
373
  **Helper:** `generateFlyToml(config)` serializes a `FlyTomlConfig` to fly.toml text (camelCase to snake_case, deterministic output).
325
374
 
@@ -412,18 +461,15 @@ new VercelDeploy("web-deploy", {
412
461
  | `GitGuard` | ComponentResource | Stub-`.git` marker; requires a `DeploySandbox` alongside it |
413
462
  | `SandboxMode` | enum | `NONE`, `ORIGINAL`, `STUB`; derived from the markers by the deploy seam |
414
463
  | `buildSandboxScript(options)` | function | Builds the sandboxed shell a deploy command runs (used by the deploy resources) |
415
- | `buildSandboxFileFilter(excludePaths)` | function | Portable awk filter applied to `git ls-files` before the copy |
464
+ | `buildSandboxFileFilter(excludePaths)` | function | Portable awk filter applied to `git ls-files` before the copy; keeps each excluded directory's `package.json` so the workspace graph survives |
465
+ | `assertHostBinaries(binaries)` | function | Preflight doctor: throws one error naming ALL missing host binaries, with install hints. Re-exported from [`@infracraft/sandbox`](https://www.npmjs.com/package/@infracraft/sandbox) |
416
466
  | `isDeploySandbox(value)` / `isGitGuard(value)` | functions | Bundle-safe marker checks |
417
467
 
418
- ## Design
419
-
420
- **Context-based**: Resources inherit auth, project, and environment from their options; no manual ID passing.
421
-
422
- **Adopt-or-create**: Existing infrastructure is discovered by name and adopted into Pulumi state. Run `pulumi up` against a pre-existing project and it just works.
468
+ `DeploySandbox` runs the preflight for the core POSIX set (`git`, `rsync`, `awk`, `mktemp`) automatically, so a broken host fails fast instead of midway through a deploy script. Call `assertHostBinaries(["railway"])` / `["vercel"]` / `["fly"]` at program start to preflight the platform CLIs your deploys use — see [`@infracraft/sandbox`](../sandbox) for the full doctor.
423
469
 
424
- **Consumer-controlled protection**: Use `protect: true` on shared/production resources to prevent accidental deletion. Deploy resources accept a `triggers` array; you decide what causes a redeploy.
470
+ ## Transport & errors
425
471
 
426
- **Consumer-controlled triggers**: Hash source directories, env values, or anything else. Pass results into `triggers` arrays.
472
+ Every provider client (`RailwayClient`, `NeonClient`, `VercelClient`, `FlyClient`) routes its HTTP through one resilient fetch wrapper: a 15s per-attempt timeout, up to 3 attempts on transient failures (network errors, 5xx, 429), a numeric `Retry-After` honored on 429 (capped at 30s), and exponential backoff otherwise (1s/2s/4s, capped at 20s) — everything else, including non-429 4xx, returns to the caller untouched. The REST clients turn a 404 into a typed `ApiNotFoundError` (carrying the provider and path), and catch sites test `instanceof` rather than matching message strings: adopt-or-create lookups turn it into "create", `read()` turns it into a blank result so `pulumi refresh` reconciles out-of-band deletions, and `delete()` turns it into an idempotent no-op.
427
473
 
428
474
  ## Why
429
475
 
@@ -434,6 +480,10 @@ new VercelDeploy("web-deploy", {
434
480
  | Vercel | `@pulumiverse/vercel`; no adopt-or-create, no CLI deploys | Adopt-or-create projects + consumer-controlled deploy triggers |
435
481
  | Fly.io | `@ediri/pulumi-fly`; bridges a Terraform provider Fly archived March 2024, no secrets support | Hand-rolled `dynamic` resources matching every other provider: secrets, adopt-or-create, consumer-controlled deploys; no unmaintained upstream |
436
482
 
483
+ ## Release history
484
+
485
+ See [CHANGELOG.md](CHANGELOG.md) for release history.
486
+
437
487
  ## License
438
488
 
439
489
  MIT
@@ -1 +1 @@
1
- {"version":3,"file":"deploy.cjs","names":["pulumi","generateFlyToml","dependsOnList","createDeployCommand"],"sources":["../../src/fly/deploy.ts"],"sourcesContent":["// src/fly/deploy.ts (replace entire file)\nimport * as pulumi from \"@pulumi/pulumi\";\n\nimport { createDeployCommand, dependsOnList } from \"../commands/deploy\";\nimport type { FlyApp } from \"./app\";\nimport type { FlyProvider } from \"./provider\";\nimport { type FlyTomlConfig, generateFlyToml } from \"./toml\";\n\ntype FlyDeployOptions = Omit<pulumi.ComponentResourceOptions, \"provider\"> & {\n\tprovider: FlyProvider;\n\tapp: FlyApp;\n};\n\nexport interface FlyDeployArgs {\n\t/** fly.toml configuration. `config.app` must equal the FlyApp name. */\n\tconfig: FlyTomlConfig;\n\t/** Redeploy triggers; the generated toml content is appended automatically. */\n\ttriggers: pulumi.Input<pulumi.Input<string>[]>;\n\t/** `fly deploy --wait-timeout` seconds (default 300). */\n\twaitTimeout?: number;\n\t/** `fly deploy --release-command-timeout` seconds (default 600). */\n\treleaseCommandTimeout?: number;\n\t/** `fly deploy --ha` (default false). */\n\thighAvailability?: boolean;\n}\n\n/**\n * Deploys a Fly app via `fly deploy --remote-only` from a generated fly.toml.\n * Isolation/git are the seam's job (list a `DeploySandbox`, optionally a `GitGuard`).\n *\n * Recommended preflight: `assertHostBinaries([\"fly\"])` (from\n * `@infracraft/pulumi/sandbox`) at program start, so a missing CLI fails fast\n * with an install hint instead of mid-deploy.\n */\nexport class FlyDeploy extends pulumi.ComponentResource {\n\t/** The deploy CLI's final stdout line (the Fly app URL when emitted). */\n\tpublic readonly deploymentUrl: pulumi.Output<string>;\n\n\tconstructor(name: string, args: FlyDeployArgs, opts: FlyDeployOptions) {\n\t\tconst { provider, app, ...pulumiOpts } = opts;\n\n\t\tsuper(\"infracraft:fly:Deploy\", name, {}, pulumiOpts);\n\n\t\tconst tomlContent = generateFlyToml(args.config);\n\t\tconst configPath = `.fly/${args.config.app}.toml`;\n\n\t\tconst waitTimeout = args.waitTimeout ?? 300;\n\t\tconst releaseCommandTimeout = args.releaseCommandTimeout ?? 600;\n\t\tconst highAvailability = args.highAvailability ?? false;\n\n\t\t// The toml content arrives via FLY_TOML_CONTENT (avoids shell escaping).\n\t\tconst setup = `mkdir -p .fly && printf '%s' \"$FLY_TOML_CONTENT\" > ${configPath}`;\n\t\tconst cli = `fly deploy --config ${configPath} --remote-only --ha=${highAvailability} --wait-timeout ${waitTimeout} --release-command-timeout ${releaseCommandTimeout}`;\n\n\t\tconst triggers = pulumi\n\t\t\t.output(args.triggers)\n\t\t\t.apply((values) => [...values, tomlContent]);\n\n\t\t// Keep the `app` ordering anchor first; append the consumer's deps\n\t\t// (DeploySandbox / GitGuard) using the seam's shared normaliser.\n\t\tconst consumerDeps = dependsOnList(pulumiOpts) as pulumi.Resource[];\n\n\t\tconst { deploymentUrl } = createDeployCommand(\n\t\t\t{\n\t\t\t\tname,\n\t\t\t\tcli,\n\t\t\t\ttriggers,\n\t\t\t\tsetup,\n\t\t\t\tenvironment: {\n\t\t\t\t\tFLY_API_TOKEN: provider.token,\n\t\t\t\t\tFLY_TOML_CONTENT: tomlContent,\n\t\t\t\t},\n\t\t\t},\n\t\t\t{ ...pulumiOpts, parent: this, dependsOn: [app, ...consumerDeps] },\n\t\t);\n\n\t\tthis.deploymentUrl = deploymentUrl;\n\n\t\tthis.registerOutputs({ deploymentUrl: this.deploymentUrl });\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAkCA,IAAa,YAAb,cAA+BA,eAAO,kBAAkB;CAIvD,YAAY,MAAc,MAAqB,MAAwB;EACtE,MAAM,EAAE,UAAU,KAAK,GAAG,eAAe;EAEzC,MAAM,yBAAyB,MAAM,CAAC,GAAG,UAAU;EAEnD,MAAM,cAAcC,iCAAgB,KAAK,MAAM;EAC/C,MAAM,aAAa,QAAQ,KAAK,OAAO,IAAI;EAE3C,MAAM,cAAc,KAAK,eAAe;EACxC,MAAM,wBAAwB,KAAK,yBAAyB;EAC5D,MAAM,mBAAmB,KAAK,oBAAoB;EAGlD,MAAM,QAAQ,sDAAsD;EACpE,MAAM,MAAM,uBAAuB,WAAW,sBAAsB,iBAAiB,kBAAkB,YAAY,6BAA6B;EAEhJ,MAAM,WAAWD,eACf,OAAO,KAAK,QAAQ,EACpB,OAAO,WAAW,CAAC,GAAG,QAAQ,WAAW,CAAC;EAI5C,MAAM,eAAeE,sCAAc,UAAU;EAE7C,MAAM,EAAE,kBAAkBC,4CACzB;GACC;GACA;GACA;GACA;GACA,aAAa;IACZ,eAAe,SAAS;IACxB,kBAAkB;GACnB;EACD,GACA;GAAE,GAAG;GAAY,QAAQ;GAAM,WAAW,CAAC,KAAK,GAAG,YAAY;EAAE,CAClE;EAEA,KAAK,gBAAgB;EAErB,KAAK,gBAAgB,EAAE,eAAe,KAAK,cAAc,CAAC;CAC3D;AACD"}
1
+ {"version":3,"file":"deploy.cjs","names":["pulumi","generateFlyToml","dependsOnList","createDeployCommand"],"sources":["../../src/fly/deploy.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\n\nimport { createDeployCommand, dependsOnList } from \"../commands/deploy\";\nimport type { FlyApp } from \"./app\";\nimport type { FlyProvider } from \"./provider\";\nimport { type FlyTomlConfig, generateFlyToml } from \"./toml\";\n\ntype FlyDeployOptions = Omit<pulumi.ComponentResourceOptions, \"provider\"> & {\n\tprovider: FlyProvider;\n\tapp: FlyApp;\n};\n\nexport interface FlyDeployArgs {\n\t/** fly.toml configuration. `config.app` must equal the FlyApp name. */\n\tconfig: FlyTomlConfig;\n\t/** Redeploy triggers; the generated toml content is appended automatically. */\n\ttriggers: pulumi.Input<pulumi.Input<string>[]>;\n\t/** `fly deploy --wait-timeout` seconds (default 300). */\n\twaitTimeout?: number;\n\t/** `fly deploy --release-command-timeout` seconds (default 600). */\n\treleaseCommandTimeout?: number;\n\t/** `fly deploy --ha` (default false). */\n\thighAvailability?: boolean;\n}\n\n/**\n * Deploys a Fly app via `fly deploy --remote-only` from a generated fly.toml.\n * Isolation/git are the seam's job (list a `DeploySandbox`, optionally a `GitGuard`).\n *\n * Recommended preflight: `assertHostBinaries([\"fly\"])` (from\n * `@infracraft/pulumi/sandbox`) at program start, so a missing CLI fails fast\n * with an install hint instead of mid-deploy.\n */\nexport class FlyDeploy extends pulumi.ComponentResource {\n\t/** The deploy CLI's final stdout line (the Fly app URL when emitted). */\n\tpublic readonly deploymentUrl: pulumi.Output<string>;\n\n\tconstructor(name: string, args: FlyDeployArgs, opts: FlyDeployOptions) {\n\t\tconst { provider, app, ...pulumiOpts } = opts;\n\n\t\tsuper(\"infracraft:fly:Deploy\", name, {}, pulumiOpts);\n\n\t\tconst tomlContent = generateFlyToml(args.config);\n\t\tconst configPath = `.fly/${args.config.app}.toml`;\n\n\t\tconst waitTimeout = args.waitTimeout ?? 300;\n\t\tconst releaseCommandTimeout = args.releaseCommandTimeout ?? 600;\n\t\tconst highAvailability = args.highAvailability ?? false;\n\n\t\t// The toml content arrives via FLY_TOML_CONTENT (avoids shell escaping).\n\t\tconst setup = `mkdir -p .fly && printf '%s' \"$FLY_TOML_CONTENT\" > ${configPath}`;\n\t\tconst cli = `fly deploy --config ${configPath} --remote-only --ha=${highAvailability} --wait-timeout ${waitTimeout} --release-command-timeout ${releaseCommandTimeout}`;\n\n\t\tconst triggers = pulumi\n\t\t\t.output(args.triggers)\n\t\t\t.apply((values) => [...values, tomlContent]);\n\n\t\t// Keep the `app` ordering anchor first; append the consumer's deps\n\t\t// (DeploySandbox / GitGuard) using the seam's shared normaliser.\n\t\tconst consumerDeps = dependsOnList(pulumiOpts) as pulumi.Resource[];\n\n\t\tconst { deploymentUrl } = createDeployCommand(\n\t\t\t{\n\t\t\t\tname,\n\t\t\t\tcli,\n\t\t\t\ttriggers,\n\t\t\t\tsetup,\n\t\t\t\tenvironment: {\n\t\t\t\t\tFLY_API_TOKEN: provider.token,\n\t\t\t\t\tFLY_TOML_CONTENT: tomlContent,\n\t\t\t\t},\n\t\t\t},\n\t\t\t{ ...pulumiOpts, parent: this, dependsOn: [app, ...consumerDeps] },\n\t\t);\n\n\t\tthis.deploymentUrl = deploymentUrl;\n\n\t\tthis.registerOutputs({ deploymentUrl: this.deploymentUrl });\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAiCA,IAAa,YAAb,cAA+BA,eAAO,kBAAkB;CAIvD,YAAY,MAAc,MAAqB,MAAwB;EACtE,MAAM,EAAE,UAAU,KAAK,GAAG,eAAe;EAEzC,MAAM,yBAAyB,MAAM,CAAC,GAAG,UAAU;EAEnD,MAAM,cAAcC,iCAAgB,KAAK,MAAM;EAC/C,MAAM,aAAa,QAAQ,KAAK,OAAO,IAAI;EAE3C,MAAM,cAAc,KAAK,eAAe;EACxC,MAAM,wBAAwB,KAAK,yBAAyB;EAC5D,MAAM,mBAAmB,KAAK,oBAAoB;EAGlD,MAAM,QAAQ,sDAAsD;EACpE,MAAM,MAAM,uBAAuB,WAAW,sBAAsB,iBAAiB,kBAAkB,YAAY,6BAA6B;EAEhJ,MAAM,WAAWD,eACf,OAAO,KAAK,QAAQ,EACpB,OAAO,WAAW,CAAC,GAAG,QAAQ,WAAW,CAAC;EAI5C,MAAM,eAAeE,sCAAc,UAAU;EAE7C,MAAM,EAAE,kBAAkBC,4CACzB;GACC;GACA;GACA;GACA;GACA,aAAa;IACZ,eAAe,SAAS;IACxB,kBAAkB;GACnB;EACD,GACA;GAAE,GAAG;GAAY,QAAQ;GAAM,WAAW,CAAC,KAAK,GAAG,YAAY;EAAE,CAClE;EAEA,KAAK,gBAAgB;EAErB,KAAK,gBAAgB,EAAE,eAAe,KAAK,cAAc,CAAC;CAC3D;AACD"}
@@ -1 +1 @@
1
- {"version":3,"file":"deploy.d.cts","names":[],"sources":["../../src/fly/deploy.ts"],"mappings":";;;;;;;KAQK,gBAAA,GAAmB,IAAA,CAAK,MAAA,CAAO,wBAAA;EACnC,QAAA,EAAU,WAAA;EACV,GAAA,EAAK,MAAA;AAAA;AAAA,UAGW,aAAA;;EAEhB,MAAA,EAAQ,aAAA;EAPe;EASvB,QAAA,EAAU,MAAA,CAAO,KAAA,CAAM,MAAA,CAAO,KAAA;EAPzB;EASL,WAAA;EATW;EAWX,qBAAA;EAb4B;EAe5B,gBAAA;AAAA;;;;;AAbW;AAGZ;;;cAqBa,SAAA,SAAkB,MAAA,CAAO,iBAAA;EAjBd;EAAA,SAmBP,aAAA,EAAe,MAAA,CAAO,MAAA;cAE1B,IAAA,UAAc,IAAA,EAAM,aAAA,EAAe,IAAA,EAAM,gBAAA;AAAA"}
1
+ {"version":3,"file":"deploy.d.cts","names":[],"sources":["../../src/fly/deploy.ts"],"mappings":";;;;;;;KAOK,gBAAA,GAAmB,IAAA,CAAK,MAAA,CAAO,wBAAA;EACnC,QAAA,EAAU,WAAA;EACV,GAAA,EAAK,MAAA;AAAA;AAAA,UAGW,aAAA;;EAEhB,MAAA,EAAQ,aAAA;EAPe;EASvB,QAAA,EAAU,MAAA,CAAO,KAAA,CAAM,MAAA,CAAO,KAAA;EAPzB;EASL,WAAA;EATW;EAWX,qBAAA;EAb4B;EAe5B,gBAAA;AAAA;;;;;AAbW;AAGZ;;;cAqBa,SAAA,SAAkB,MAAA,CAAO,iBAAA;EAjBd;EAAA,SAmBP,aAAA,EAAe,MAAA,CAAO,MAAA;cAE1B,IAAA,UAAc,IAAA,EAAM,aAAA,EAAe,IAAA,EAAM,gBAAA;AAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"deploy.d.mts","names":[],"sources":["../../src/fly/deploy.ts"],"mappings":";;;;;;;KAQK,gBAAA,GAAmB,IAAA,CAAK,MAAA,CAAO,wBAAA;EACnC,QAAA,EAAU,WAAA;EACV,GAAA,EAAK,MAAA;AAAA;AAAA,UAGW,aAAA;;EAEhB,MAAA,EAAQ,aAAA;EAPe;EASvB,QAAA,EAAU,MAAA,CAAO,KAAA,CAAM,MAAA,CAAO,KAAA;EAPzB;EASL,WAAA;EATW;EAWX,qBAAA;EAb4B;EAe5B,gBAAA;AAAA;;;;;AAbW;AAGZ;;;cAqBa,SAAA,SAAkB,MAAA,CAAO,iBAAA;EAjBd;EAAA,SAmBP,aAAA,EAAe,MAAA,CAAO,MAAA;cAE1B,IAAA,UAAc,IAAA,EAAM,aAAA,EAAe,IAAA,EAAM,gBAAA;AAAA"}
1
+ {"version":3,"file":"deploy.d.mts","names":[],"sources":["../../src/fly/deploy.ts"],"mappings":";;;;;;;KAOK,gBAAA,GAAmB,IAAA,CAAK,MAAA,CAAO,wBAAA;EACnC,QAAA,EAAU,WAAA;EACV,GAAA,EAAK,MAAA;AAAA;AAAA,UAGW,aAAA;;EAEhB,MAAA,EAAQ,aAAA;EAPe;EASvB,QAAA,EAAU,MAAA,CAAO,KAAA,CAAM,MAAA,CAAO,KAAA;EAPzB;EASL,WAAA;EATW;EAWX,qBAAA;EAb4B;EAe5B,gBAAA;AAAA;;;;;AAbW;AAGZ;;;cAqBa,SAAA,SAAkB,MAAA,CAAO,iBAAA;EAjBd;EAAA,SAmBP,aAAA,EAAe,MAAA,CAAO,MAAA;cAE1B,IAAA,UAAc,IAAA,EAAM,aAAA,EAAe,IAAA,EAAM,gBAAA;AAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"deploy.mjs","names":[],"sources":["../../src/fly/deploy.ts"],"sourcesContent":["// src/fly/deploy.ts (replace entire file)\nimport * as pulumi from \"@pulumi/pulumi\";\n\nimport { createDeployCommand, dependsOnList } from \"../commands/deploy\";\nimport type { FlyApp } from \"./app\";\nimport type { FlyProvider } from \"./provider\";\nimport { type FlyTomlConfig, generateFlyToml } from \"./toml\";\n\ntype FlyDeployOptions = Omit<pulumi.ComponentResourceOptions, \"provider\"> & {\n\tprovider: FlyProvider;\n\tapp: FlyApp;\n};\n\nexport interface FlyDeployArgs {\n\t/** fly.toml configuration. `config.app` must equal the FlyApp name. */\n\tconfig: FlyTomlConfig;\n\t/** Redeploy triggers; the generated toml content is appended automatically. */\n\ttriggers: pulumi.Input<pulumi.Input<string>[]>;\n\t/** `fly deploy --wait-timeout` seconds (default 300). */\n\twaitTimeout?: number;\n\t/** `fly deploy --release-command-timeout` seconds (default 600). */\n\treleaseCommandTimeout?: number;\n\t/** `fly deploy --ha` (default false). */\n\thighAvailability?: boolean;\n}\n\n/**\n * Deploys a Fly app via `fly deploy --remote-only` from a generated fly.toml.\n * Isolation/git are the seam's job (list a `DeploySandbox`, optionally a `GitGuard`).\n *\n * Recommended preflight: `assertHostBinaries([\"fly\"])` (from\n * `@infracraft/pulumi/sandbox`) at program start, so a missing CLI fails fast\n * with an install hint instead of mid-deploy.\n */\nexport class FlyDeploy extends pulumi.ComponentResource {\n\t/** The deploy CLI's final stdout line (the Fly app URL when emitted). */\n\tpublic readonly deploymentUrl: pulumi.Output<string>;\n\n\tconstructor(name: string, args: FlyDeployArgs, opts: FlyDeployOptions) {\n\t\tconst { provider, app, ...pulumiOpts } = opts;\n\n\t\tsuper(\"infracraft:fly:Deploy\", name, {}, pulumiOpts);\n\n\t\tconst tomlContent = generateFlyToml(args.config);\n\t\tconst configPath = `.fly/${args.config.app}.toml`;\n\n\t\tconst waitTimeout = args.waitTimeout ?? 300;\n\t\tconst releaseCommandTimeout = args.releaseCommandTimeout ?? 600;\n\t\tconst highAvailability = args.highAvailability ?? false;\n\n\t\t// The toml content arrives via FLY_TOML_CONTENT (avoids shell escaping).\n\t\tconst setup = `mkdir -p .fly && printf '%s' \"$FLY_TOML_CONTENT\" > ${configPath}`;\n\t\tconst cli = `fly deploy --config ${configPath} --remote-only --ha=${highAvailability} --wait-timeout ${waitTimeout} --release-command-timeout ${releaseCommandTimeout}`;\n\n\t\tconst triggers = pulumi\n\t\t\t.output(args.triggers)\n\t\t\t.apply((values) => [...values, tomlContent]);\n\n\t\t// Keep the `app` ordering anchor first; append the consumer's deps\n\t\t// (DeploySandbox / GitGuard) using the seam's shared normaliser.\n\t\tconst consumerDeps = dependsOnList(pulumiOpts) as pulumi.Resource[];\n\n\t\tconst { deploymentUrl } = createDeployCommand(\n\t\t\t{\n\t\t\t\tname,\n\t\t\t\tcli,\n\t\t\t\ttriggers,\n\t\t\t\tsetup,\n\t\t\t\tenvironment: {\n\t\t\t\t\tFLY_API_TOKEN: provider.token,\n\t\t\t\t\tFLY_TOML_CONTENT: tomlContent,\n\t\t\t\t},\n\t\t\t},\n\t\t\t{ ...pulumiOpts, parent: this, dependsOn: [app, ...consumerDeps] },\n\t\t);\n\n\t\tthis.deploymentUrl = deploymentUrl;\n\n\t\tthis.registerOutputs({ deploymentUrl: this.deploymentUrl });\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;AAkCA,IAAa,YAAb,cAA+B,OAAO,kBAAkB;CAIvD,YAAY,MAAc,MAAqB,MAAwB;EACtE,MAAM,EAAE,UAAU,KAAK,GAAG,eAAe;EAEzC,MAAM,yBAAyB,MAAM,CAAC,GAAG,UAAU;EAEnD,MAAM,cAAc,gBAAgB,KAAK,MAAM;EAC/C,MAAM,aAAa,QAAQ,KAAK,OAAO,IAAI;EAE3C,MAAM,cAAc,KAAK,eAAe;EACxC,MAAM,wBAAwB,KAAK,yBAAyB;EAC5D,MAAM,mBAAmB,KAAK,oBAAoB;EAGlD,MAAM,QAAQ,sDAAsD;EACpE,MAAM,MAAM,uBAAuB,WAAW,sBAAsB,iBAAiB,kBAAkB,YAAY,6BAA6B;EAEhJ,MAAM,WAAW,OACf,OAAO,KAAK,QAAQ,EACpB,OAAO,WAAW,CAAC,GAAG,QAAQ,WAAW,CAAC;EAI5C,MAAM,eAAe,cAAc,UAAU;EAE7C,MAAM,EAAE,kBAAkB,oBACzB;GACC;GACA;GACA;GACA;GACA,aAAa;IACZ,eAAe,SAAS;IACxB,kBAAkB;GACnB;EACD,GACA;GAAE,GAAG;GAAY,QAAQ;GAAM,WAAW,CAAC,KAAK,GAAG,YAAY;EAAE,CAClE;EAEA,KAAK,gBAAgB;EAErB,KAAK,gBAAgB,EAAE,eAAe,KAAK,cAAc,CAAC;CAC3D;AACD"}
1
+ {"version":3,"file":"deploy.mjs","names":[],"sources":["../../src/fly/deploy.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\n\nimport { createDeployCommand, dependsOnList } from \"../commands/deploy\";\nimport type { FlyApp } from \"./app\";\nimport type { FlyProvider } from \"./provider\";\nimport { type FlyTomlConfig, generateFlyToml } from \"./toml\";\n\ntype FlyDeployOptions = Omit<pulumi.ComponentResourceOptions, \"provider\"> & {\n\tprovider: FlyProvider;\n\tapp: FlyApp;\n};\n\nexport interface FlyDeployArgs {\n\t/** fly.toml configuration. `config.app` must equal the FlyApp name. */\n\tconfig: FlyTomlConfig;\n\t/** Redeploy triggers; the generated toml content is appended automatically. */\n\ttriggers: pulumi.Input<pulumi.Input<string>[]>;\n\t/** `fly deploy --wait-timeout` seconds (default 300). */\n\twaitTimeout?: number;\n\t/** `fly deploy --release-command-timeout` seconds (default 600). */\n\treleaseCommandTimeout?: number;\n\t/** `fly deploy --ha` (default false). */\n\thighAvailability?: boolean;\n}\n\n/**\n * Deploys a Fly app via `fly deploy --remote-only` from a generated fly.toml.\n * Isolation/git are the seam's job (list a `DeploySandbox`, optionally a `GitGuard`).\n *\n * Recommended preflight: `assertHostBinaries([\"fly\"])` (from\n * `@infracraft/pulumi/sandbox`) at program start, so a missing CLI fails fast\n * with an install hint instead of mid-deploy.\n */\nexport class FlyDeploy extends pulumi.ComponentResource {\n\t/** The deploy CLI's final stdout line (the Fly app URL when emitted). */\n\tpublic readonly deploymentUrl: pulumi.Output<string>;\n\n\tconstructor(name: string, args: FlyDeployArgs, opts: FlyDeployOptions) {\n\t\tconst { provider, app, ...pulumiOpts } = opts;\n\n\t\tsuper(\"infracraft:fly:Deploy\", name, {}, pulumiOpts);\n\n\t\tconst tomlContent = generateFlyToml(args.config);\n\t\tconst configPath = `.fly/${args.config.app}.toml`;\n\n\t\tconst waitTimeout = args.waitTimeout ?? 300;\n\t\tconst releaseCommandTimeout = args.releaseCommandTimeout ?? 600;\n\t\tconst highAvailability = args.highAvailability ?? false;\n\n\t\t// The toml content arrives via FLY_TOML_CONTENT (avoids shell escaping).\n\t\tconst setup = `mkdir -p .fly && printf '%s' \"$FLY_TOML_CONTENT\" > ${configPath}`;\n\t\tconst cli = `fly deploy --config ${configPath} --remote-only --ha=${highAvailability} --wait-timeout ${waitTimeout} --release-command-timeout ${releaseCommandTimeout}`;\n\n\t\tconst triggers = pulumi\n\t\t\t.output(args.triggers)\n\t\t\t.apply((values) => [...values, tomlContent]);\n\n\t\t// Keep the `app` ordering anchor first; append the consumer's deps\n\t\t// (DeploySandbox / GitGuard) using the seam's shared normaliser.\n\t\tconst consumerDeps = dependsOnList(pulumiOpts) as pulumi.Resource[];\n\n\t\tconst { deploymentUrl } = createDeployCommand(\n\t\t\t{\n\t\t\t\tname,\n\t\t\t\tcli,\n\t\t\t\ttriggers,\n\t\t\t\tsetup,\n\t\t\t\tenvironment: {\n\t\t\t\t\tFLY_API_TOKEN: provider.token,\n\t\t\t\t\tFLY_TOML_CONTENT: tomlContent,\n\t\t\t\t},\n\t\t\t},\n\t\t\t{ ...pulumiOpts, parent: this, dependsOn: [app, ...consumerDeps] },\n\t\t);\n\n\t\tthis.deploymentUrl = deploymentUrl;\n\n\t\tthis.registerOutputs({ deploymentUrl: this.deploymentUrl });\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;AAiCA,IAAa,YAAb,cAA+B,OAAO,kBAAkB;CAIvD,YAAY,MAAc,MAAqB,MAAwB;EACtE,MAAM,EAAE,UAAU,KAAK,GAAG,eAAe;EAEzC,MAAM,yBAAyB,MAAM,CAAC,GAAG,UAAU;EAEnD,MAAM,cAAc,gBAAgB,KAAK,MAAM;EAC/C,MAAM,aAAa,QAAQ,KAAK,OAAO,IAAI;EAE3C,MAAM,cAAc,KAAK,eAAe;EACxC,MAAM,wBAAwB,KAAK,yBAAyB;EAC5D,MAAM,mBAAmB,KAAK,oBAAoB;EAGlD,MAAM,QAAQ,sDAAsD;EACpE,MAAM,MAAM,uBAAuB,WAAW,sBAAsB,iBAAiB,kBAAkB,YAAY,6BAA6B;EAEhJ,MAAM,WAAW,OACf,OAAO,KAAK,QAAQ,EACpB,OAAO,WAAW,CAAC,GAAG,QAAQ,WAAW,CAAC;EAI5C,MAAM,eAAe,cAAc,UAAU;EAE7C,MAAM,EAAE,kBAAkB,oBACzB;GACC;GACA;GACA;GACA;GACA,aAAa;IACZ,eAAe,SAAS;IACxB,kBAAkB;GACnB;EACD,GACA;GAAE,GAAG;GAAY,QAAQ;GAAM,WAAW,CAAC,KAAK,GAAG,YAAY;EAAE,CAClE;EAEA,KAAK,gBAAgB;EAErB,KAAK,gBAAgB,EAAE,eAAe,KAAK,cAAc,CAAC;CAC3D;AACD"}
@@ -1,4 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ const require_errors_api_not_found_error = require('../errors/api-not-found-error.cjs');
2
3
  const require_fly_client = require('./client.cjs');
3
4
  const require_fly_app = require('./app.cjs');
4
5
  const require_fly_certificate = require('./certificate.cjs');
@@ -9,6 +10,7 @@ const require_fly_provider = require('./provider.cjs');
9
10
  const require_fly_secret = require('./secret.cjs');
10
11
  const require_fly_volume = require('./volume.cjs');
11
12
 
13
+ exports.ApiNotFoundError = require_errors_api_not_found_error.ApiNotFoundError;
12
14
  exports.FLY_REGIONS = require_fly_toml.FLY_REGIONS;
13
15
  exports.FLY_VM_SIZES = require_fly_toml.FLY_VM_SIZES;
14
16
  exports.FlyApp = require_fly_app.FlyApp;
@@ -1,3 +1,4 @@
1
+ import { ApiNotFoundError } from "../errors/api-not-found-error.cjs";
1
2
  import { FlyProvider, FlyProviderArgs } from "./provider.cjs";
2
3
  import { FlyApp, FlyAppArgs } from "./app.cjs";
3
4
  import { FlyCertificate, FlyCertificateArgs, FlyDnsRequirements } from "./certificate.cjs";
@@ -7,4 +8,4 @@ import { FlyDeploy, FlyDeployArgs } from "./deploy.cjs";
7
8
  import { FlyIp, FlyIpArgs, FlyIpType } from "./ip.cjs";
8
9
  import { FlySecret, FlySecretArgs } from "./secret.cjs";
9
10
  import { FlyVolume, FlyVolumeArgs } from "./volume.cjs";
10
- export { FLY_REGIONS, FLY_VM_SIZES, FlyApp, type FlyAppArgs, FlyAutoStopMachines, type FlyBuildConfig, FlyCertificate, type FlyCertificateArgs, type FlyCheck, FlyCheckType, FlyClient, type FlyConcurrency, FlyConcurrencyType, type FlyCpuCount, FlyCpuKind, FlyDeploy, type FlyDeployArgs, type FlyDeployConfig, FlyDeployStrategy, type FlyDnsRequirements, type FlyHttpService, FlyIp, type FlyIpArgs, FlyIpType, type FlyMount, FlyPortHandler, FlyProvider, type FlyProviderArgs, type FlyRegion, type FlyRestartConfig, FlyRestartPolicy, FlySecret, type FlySecretArgs, type FlyService, type FlyServicePort, FlyServiceProtocol, type FlyTomlConfig, type FlyVm, type FlyVmSize, FlyVolume, type FlyVolumeArgs, generateFlyToml };
11
+ export { ApiNotFoundError, FLY_REGIONS, FLY_VM_SIZES, FlyApp, type FlyAppArgs, FlyAutoStopMachines, type FlyBuildConfig, FlyCertificate, type FlyCertificateArgs, type FlyCheck, FlyCheckType, FlyClient, type FlyConcurrency, FlyConcurrencyType, type FlyCpuCount, FlyCpuKind, FlyDeploy, type FlyDeployArgs, type FlyDeployConfig, FlyDeployStrategy, type FlyDnsRequirements, type FlyHttpService, FlyIp, type FlyIpArgs, FlyIpType, type FlyMount, FlyPortHandler, FlyProvider, type FlyProviderArgs, type FlyRegion, type FlyRestartConfig, FlyRestartPolicy, FlySecret, type FlySecretArgs, type FlyService, type FlyServicePort, FlyServiceProtocol, type FlyTomlConfig, type FlyVm, type FlyVmSize, FlyVolume, type FlyVolumeArgs, generateFlyToml };
@@ -1,3 +1,4 @@
1
+ import { ApiNotFoundError } from "../errors/api-not-found-error.mjs";
1
2
  import { FlyProvider, FlyProviderArgs } from "./provider.mjs";
2
3
  import { FlyApp, FlyAppArgs } from "./app.mjs";
3
4
  import { FlyCertificate, FlyCertificateArgs, FlyDnsRequirements } from "./certificate.mjs";
@@ -7,4 +8,4 @@ import { FlyDeploy, FlyDeployArgs } from "./deploy.mjs";
7
8
  import { FlyIp, FlyIpArgs, FlyIpType } from "./ip.mjs";
8
9
  import { FlySecret, FlySecretArgs } from "./secret.mjs";
9
10
  import { FlyVolume, FlyVolumeArgs } from "./volume.mjs";
10
- export { FLY_REGIONS, FLY_VM_SIZES, FlyApp, type FlyAppArgs, FlyAutoStopMachines, type FlyBuildConfig, FlyCertificate, type FlyCertificateArgs, type FlyCheck, FlyCheckType, FlyClient, type FlyConcurrency, FlyConcurrencyType, type FlyCpuCount, FlyCpuKind, FlyDeploy, type FlyDeployArgs, type FlyDeployConfig, FlyDeployStrategy, type FlyDnsRequirements, type FlyHttpService, FlyIp, type FlyIpArgs, FlyIpType, type FlyMount, FlyPortHandler, FlyProvider, type FlyProviderArgs, type FlyRegion, type FlyRestartConfig, FlyRestartPolicy, FlySecret, type FlySecretArgs, type FlyService, type FlyServicePort, FlyServiceProtocol, type FlyTomlConfig, type FlyVm, type FlyVmSize, FlyVolume, type FlyVolumeArgs, generateFlyToml };
11
+ export { ApiNotFoundError, FLY_REGIONS, FLY_VM_SIZES, FlyApp, type FlyAppArgs, FlyAutoStopMachines, type FlyBuildConfig, FlyCertificate, type FlyCertificateArgs, type FlyCheck, FlyCheckType, FlyClient, type FlyConcurrency, FlyConcurrencyType, type FlyCpuCount, FlyCpuKind, FlyDeploy, type FlyDeployArgs, type FlyDeployConfig, FlyDeployStrategy, type FlyDnsRequirements, type FlyHttpService, FlyIp, type FlyIpArgs, FlyIpType, type FlyMount, FlyPortHandler, FlyProvider, type FlyProviderArgs, type FlyRegion, type FlyRestartConfig, FlyRestartPolicy, FlySecret, type FlySecretArgs, type FlyService, type FlyServicePort, FlyServiceProtocol, type FlyTomlConfig, type FlyVm, type FlyVmSize, FlyVolume, type FlyVolumeArgs, generateFlyToml };
@@ -1,3 +1,4 @@
1
+ import { ApiNotFoundError } from "../errors/api-not-found-error.mjs";
1
2
  import { FlyClient } from "./client.mjs";
2
3
  import { FlyApp } from "./app.mjs";
3
4
  import { FlyCertificate } from "./certificate.mjs";
@@ -8,4 +9,4 @@ import { FlyProvider } from "./provider.mjs";
8
9
  import { FlySecret } from "./secret.mjs";
9
10
  import { FlyVolume } from "./volume.mjs";
10
11
 
11
- export { FLY_REGIONS, FLY_VM_SIZES, FlyApp, FlyAutoStopMachines, FlyCertificate, FlyCheckType, FlyClient, FlyConcurrencyType, FlyCpuKind, FlyDeploy, FlyDeployStrategy, FlyIp, FlyIpType, FlyPortHandler, FlyProvider, FlyRestartPolicy, FlySecret, FlyServiceProtocol, FlyVolume, generateFlyToml };
12
+ export { ApiNotFoundError, FLY_REGIONS, FLY_VM_SIZES, FlyApp, FlyAutoStopMachines, FlyCertificate, FlyCheckType, FlyClient, FlyConcurrencyType, FlyCpuKind, FlyDeploy, FlyDeployStrategy, FlyIp, FlyIpType, FlyPortHandler, FlyProvider, FlyRestartPolicy, FlySecret, FlyServiceProtocol, FlyVolume, generateFlyToml };
@@ -1,4 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ const require_errors_api_not_found_error = require('../errors/api-not-found-error.cjs');
2
3
  const require_neon_client = require('./client.cjs');
3
4
  const require_neon_branch = require('./branch.cjs');
4
5
  const require_neon_database = require('./database.cjs');
@@ -7,6 +8,7 @@ const require_neon_project = require('./project.cjs');
7
8
  const require_neon_provider = require('./provider.cjs');
8
9
  const require_neon_role = require('./role.cjs');
9
10
 
11
+ exports.ApiNotFoundError = require_errors_api_not_found_error.ApiNotFoundError;
10
12
  exports.NeonBranch = require_neon_branch.NeonBranch;
11
13
  exports.NeonClient = require_neon_client.NeonClient;
12
14
  exports.NeonDatabase = require_neon_database.NeonDatabase;
@@ -1,3 +1,4 @@
1
+ import { ApiNotFoundError } from "../errors/api-not-found-error.cjs";
1
2
  import { NeonProvider, NeonProviderArgs } from "./provider.cjs";
2
3
  import { NeonProject, NeonProjectArgs } from "./project.cjs";
3
4
  import { NeonBranch, NeonBranchArgs } from "./branch.cjs";
@@ -5,4 +6,4 @@ import { NeonClient } from "./client.cjs";
5
6
  import { NeonDatabase, NeonDatabaseArgs } from "./database.cjs";
6
7
  import { NeonEndpoint, NeonEndpointArgs } from "./endpoint.cjs";
7
8
  import { NeonRole, NeonRoleArgs } from "./role.cjs";
8
- export { NeonBranch, type NeonBranchArgs, NeonClient, NeonDatabase, type NeonDatabaseArgs, NeonEndpoint, type NeonEndpointArgs, NeonProject, type NeonProjectArgs, NeonProvider, type NeonProviderArgs, NeonRole, type NeonRoleArgs };
9
+ export { ApiNotFoundError, NeonBranch, type NeonBranchArgs, NeonClient, NeonDatabase, type NeonDatabaseArgs, NeonEndpoint, type NeonEndpointArgs, NeonProject, type NeonProjectArgs, NeonProvider, type NeonProviderArgs, NeonRole, type NeonRoleArgs };
@@ -1,3 +1,4 @@
1
+ import { ApiNotFoundError } from "../errors/api-not-found-error.mjs";
1
2
  import { NeonProvider, NeonProviderArgs } from "./provider.mjs";
2
3
  import { NeonProject, NeonProjectArgs } from "./project.mjs";
3
4
  import { NeonBranch, NeonBranchArgs } from "./branch.mjs";
@@ -5,4 +6,4 @@ import { NeonClient } from "./client.mjs";
5
6
  import { NeonDatabase, NeonDatabaseArgs } from "./database.mjs";
6
7
  import { NeonEndpoint, NeonEndpointArgs } from "./endpoint.mjs";
7
8
  import { NeonRole, NeonRoleArgs } from "./role.mjs";
8
- export { NeonBranch, type NeonBranchArgs, NeonClient, NeonDatabase, type NeonDatabaseArgs, NeonEndpoint, type NeonEndpointArgs, NeonProject, type NeonProjectArgs, NeonProvider, type NeonProviderArgs, NeonRole, type NeonRoleArgs };
9
+ export { ApiNotFoundError, NeonBranch, type NeonBranchArgs, NeonClient, NeonDatabase, type NeonDatabaseArgs, NeonEndpoint, type NeonEndpointArgs, NeonProject, type NeonProjectArgs, NeonProvider, type NeonProviderArgs, NeonRole, type NeonRoleArgs };
@@ -1,3 +1,4 @@
1
+ import { ApiNotFoundError } from "../errors/api-not-found-error.mjs";
1
2
  import { NeonClient } from "./client.mjs";
2
3
  import { NeonBranch } from "./branch.mjs";
3
4
  import { NeonDatabase } from "./database.mjs";
@@ -6,4 +7,4 @@ import { NeonProject } from "./project.mjs";
6
7
  import { NeonProvider } from "./provider.mjs";
7
8
  import { NeonRole } from "./role.mjs";
8
9
 
9
- export { NeonBranch, NeonClient, NeonDatabase, NeonEndpoint, NeonProject, NeonProvider, NeonRole };
10
+ export { ApiNotFoundError, NeonBranch, NeonClient, NeonDatabase, NeonEndpoint, NeonProject, NeonProvider, NeonRole };
@@ -1 +1 @@
1
- {"version":3,"file":"deploy.cjs","names":["pulumi","createDeployCommand"],"sources":["../../src/railway/deploy.ts"],"sourcesContent":["// src/railway/deploy.ts (replace entire file)\nimport { fileURLToPath } from \"node:url\";\n\nimport * as pulumi from \"@pulumi/pulumi\";\n\nimport { createDeployCommand } from \"../commands/deploy\";\nimport type { RailwayEnvironment } from \"./environment\";\nimport type { RailwayProject } from \"./project\";\nimport type { RailwayProvider } from \"./provider\";\nimport { RailwayBuilder, type RailwayService } from \"./service\";\n\nexport interface RailwayDeployConfig {\n\tbuilder?: RailwayBuilder;\n\tstartCommand?: string;\n\tpreDeployCommand?: string;\n}\n\nexport interface RailwayDeployArgs {\n\t/** Redeploy triggers (e.g. source hashes, env hashes). */\n\ttriggers: pulumi.Input<pulumi.Input<string>[]>;\n\t/** Paths excluded from the upload when running with `DeploySandbox` + `GitGuard`. */\n\texcludePaths?: string[];\n\t/** Railpack configuration written to `railpack.json` before deploy. */\n\trailpackConfig?: Record<string, unknown>;\n}\n\ntype RailwayDeployOptions = Omit<\n\tpulumi.ComponentResourceOptions,\n\t\"provider\"\n> & {\n\tprovider: RailwayProvider;\n\tproject: RailwayProject;\n\tenvironment: RailwayEnvironment;\n\tservice: RailwayService;\n\t/** Environment-scoped Railway deploy token (provision via RailwayProjectToken). */\n\tprojectToken: pulumi.Input<string>;\n};\n\n/**\n * Absolute path to the runnable deploy monitor, resolved next to this module in `dist`.\n * `railway up` only uploads + triggers; this bin makes the Railway GraphQL API — not the\n * CLI's exit code — the source of truth for pass/fail, and dumps build + deploy logs on\n * failure. Its logic lives in the unit-tested `deployment-monitor` module.\n */\nconst MONITOR_BIN = fileURLToPath(\n\tnew URL(\"./bin/monitor-deployment.mjs\", import.meta.url),\n);\n\n/**\n * Deploys a Railway service and waits for a terminal status. Isolation/git are the\n * seam's job (list a `DeploySandbox` and optionally a `GitGuard` in `dependsOn`).\n *\n * @example\n * ```typescript\n * new RailwayDeploy(\"mesh\", { triggers: [sourceHash], railpackConfig: { apt: [\"libatomic1\"] } },\n * { provider, project, environment, service, projectToken: token.token, dependsOn: [sandbox, gitGuard] });\n * ```\n */\nexport class RailwayDeploy extends pulumi.ComponentResource {\n\t/** The deploy CLI's final stdout line (Railway service URL when emitted). */\n\tpublic readonly deploymentUrl: pulumi.Output<string>;\n\n\tconstructor(\n\t\tname: string,\n\t\targs: RailwayDeployArgs,\n\t\topts: RailwayDeployOptions,\n\t) {\n\t\tconst {\n\t\t\tprovider,\n\t\t\tproject,\n\t\t\tenvironment,\n\t\t\tservice,\n\t\t\tprojectToken,\n\t\t\t...pulumiOpts\n\t\t} = opts;\n\n\t\tsuper(\"infracraft:railway:Deploy\", name, {}, pulumiOpts);\n\n\t\t// `railway up --detach` uploads + triggers WITHOUT attaching to the build-log\n\t\t// stream — that long-lived stream is what intermittently times out and makes the\n\t\t// CLI exit non-zero even when the deploy actually succeeds. We capture its `--json`\n\t\t// output (for the exact deployment id) and exit code, re-emit it for visibility,\n\t\t// then hand off to the monitor bin which polls the Railway API to a terminal status.\n\t\t// The API — not the CLI exit code — decides pass/fail.\n\t\t//\n\t\t// The token travels via the command's STDIN, never in the script text: on failure\n\t\t// pulumi-command embeds the executed command verbatim in its error message, and\n\t\t// Pulumi does not scrub secrets from provider diagnostics — an inlined token prints\n\t\t// in plaintext exactly when a deploy fails. The `environment` map is no alternative:\n\t\t// an unknown secret there makes `pulumi preview` fail (the token is a resource\n\t\t// output, unknown on first preview). `|| true` keeps `set -e` alive when the stdin\n\t\t// payload has no trailing newline (read then exits 1 but still fills IC_TOK).\n\t\t// IC_SINCE is captured just before `railway up` as a createdAt fallback for id\n\t\t// resolution.\n\t\t// The capture is guarded with if/else because the whole script runs under\n\t\t// `set -e`: a bare `VAR=$(cmd); EXIT=$?` DIES AT THE ASSIGNMENT when cmd\n\t\t// fails, before the exit code is saved and before the output is re-emitted\n\t\t// — swallowing the CLI's error entirely (live incident: a failed production\n\t\t// `railway up` left zero diagnostics).\n\t\tconst cli = pulumi.interpolate`IFS= read -r IC_TOK || true; IC_SINCE=$(node -e \"process.stdout.write(String(Date.now()))\"); if IC_UP_OUT=$(RAILWAY_TOKEN=\"$IC_TOK\" railway up --detach --json --project ${project.id} --service ${service.id} --environment ${environment.id} 2>&1); then IC_UP_EXIT=0; else IC_UP_EXIT=$?; fi; printf '%s\\\\n' \"$IC_UP_OUT\"; if [ -n \"$INFRACRAFT_SKIP_DEPLOY_WAIT\" ]; then exit \"$IC_UP_EXIT\"; fi; IC_UP_OUT=\"$IC_UP_OUT\" IC_UP_EXIT=$IC_UP_EXIT IC_TOK=\"$IC_TOK\" IC_PROJ=${project.id} IC_ENV=${environment.id} IC_SVC=${service.id} IC_SINCE=$IC_SINCE node \"${MONITOR_BIN}\"`;\n\n\t\t// `printf '%s'` (not a bare format string) so railpack values containing %\n\t\t// are literal; the JSON is single-quote-escaped the POSIX way (' -> '\\'').\n\t\tconst setup = args.railpackConfig\n\t\t\t? `printf '%s' '${JSON.stringify(args.railpackConfig).replace(/'/g, \"'\\\\''\")}' > railpack.json`\n\t\t\t: undefined;\n\n\t\tconst { deploymentUrl } = createDeployCommand(\n\t\t\t{\n\t\t\t\tname,\n\t\t\t\tcli,\n\t\t\t\ttriggers: args.triggers,\n\t\t\t\texcludePaths: args.excludePaths,\n\t\t\t\tsetup,\n\t\t\t\tstdin: projectToken,\n\t\t\t},\n\t\t\t{ parent: this, ...pulumiOpts },\n\t\t);\n\n\t\tthis.deploymentUrl = deploymentUrl;\n\n\t\tthis.registerOutputs({ deploymentUrl: this.deploymentUrl });\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;AA4CA,MAAM,0CACL,IAAI,IAAI,6EAA+C,CACxD;;;;;;;;;;;AAYA,IAAa,gBAAb,cAAmCA,eAAO,kBAAkB;CAI3D,YACC,MACA,MACA,MACC;EACD,MAAM,EACL,UACA,SACA,aACA,SACA,cACA,GAAG,eACA;EAEJ,MAAM,6BAA6B,MAAM,CAAC,GAAG,UAAU;EAuBvD,MAAM,MAAM,eAAO,WAAW,4KAA4K,QAAQ,GAAG,aAAa,QAAQ,GAAG,iBAAiB,YAAY,GAAG,gOAAgO,QAAQ,GAAG,UAAU,YAAY,GAAG,UAAU,QAAQ,GAAG,4BAA4B,YAAY;EAI9kB,MAAM,QAAQ,KAAK,iBAChB,gBAAgB,KAAK,UAAU,KAAK,cAAc,EAAE,QAAQ,MAAM,OAAO,EAAE,qBAC3E;EAEH,MAAM,EAAE,kBAAkBC,4CACzB;GACC;GACA;GACA,UAAU,KAAK;GACf,cAAc,KAAK;GACnB;GACA,OAAO;EACR,GACA;GAAE,QAAQ;GAAM,GAAG;EAAW,CAC/B;EAEA,KAAK,gBAAgB;EAErB,KAAK,gBAAgB,EAAE,eAAe,KAAK,cAAc,CAAC;CAC3D;AACD"}
1
+ {"version":3,"file":"deploy.cjs","names":["pulumi","createDeployCommand"],"sources":["../../src/railway/deploy.ts"],"sourcesContent":["import { fileURLToPath } from \"node:url\";\n\nimport * as pulumi from \"@pulumi/pulumi\";\n\nimport { createDeployCommand } from \"../commands/deploy\";\nimport type { RailwayEnvironment } from \"./environment\";\nimport type { RailwayProject } from \"./project\";\nimport type { RailwayProvider } from \"./provider\";\nimport { RailwayBuilder, type RailwayService } from \"./service\";\n\nexport interface RailwayDeployConfig {\n\tbuilder?: RailwayBuilder;\n\tstartCommand?: string;\n\tpreDeployCommand?: string;\n}\n\nexport interface RailwayDeployArgs {\n\t/** Redeploy triggers (e.g. source hashes, env hashes). */\n\ttriggers: pulumi.Input<pulumi.Input<string>[]>;\n\t/** Paths excluded from the upload when running with `DeploySandbox` + `GitGuard`. */\n\texcludePaths?: string[];\n\t/** Railpack configuration written to `railpack.json` before deploy. */\n\trailpackConfig?: Record<string, unknown>;\n}\n\ntype RailwayDeployOptions = Omit<\n\tpulumi.ComponentResourceOptions,\n\t\"provider\"\n> & {\n\tprovider: RailwayProvider;\n\tproject: RailwayProject;\n\tenvironment: RailwayEnvironment;\n\tservice: RailwayService;\n\t/** Environment-scoped Railway deploy token (provision via RailwayProjectToken). */\n\tprojectToken: pulumi.Input<string>;\n};\n\n/**\n * Absolute path to the runnable deploy monitor, resolved next to this module in `dist`.\n * `railway up` only uploads + triggers; this bin makes the Railway GraphQL API — not the\n * CLI's exit code — the source of truth for pass/fail, and dumps build + deploy logs on\n * failure. Its logic lives in the unit-tested `deployment-monitor` module.\n */\nconst MONITOR_BIN = fileURLToPath(\n\tnew URL(\"./bin/monitor-deployment.mjs\", import.meta.url),\n);\n\n/**\n * Deploys a Railway service and waits for a terminal status. Isolation/git are the\n * seam's job (list a `DeploySandbox` and optionally a `GitGuard` in `dependsOn`).\n *\n * @example\n * ```typescript\n * new RailwayDeploy(\"mesh\", { triggers: [sourceHash], railpackConfig: { apt: [\"libatomic1\"] } },\n * { provider, project, environment, service, projectToken: token.token, dependsOn: [sandbox, gitGuard] });\n * ```\n */\nexport class RailwayDeploy extends pulumi.ComponentResource {\n\t/** The deploy CLI's final stdout line (Railway service URL when emitted). */\n\tpublic readonly deploymentUrl: pulumi.Output<string>;\n\n\tconstructor(\n\t\tname: string,\n\t\targs: RailwayDeployArgs,\n\t\topts: RailwayDeployOptions,\n\t) {\n\t\tconst {\n\t\t\tprovider,\n\t\t\tproject,\n\t\t\tenvironment,\n\t\t\tservice,\n\t\t\tprojectToken,\n\t\t\t...pulumiOpts\n\t\t} = opts;\n\n\t\tsuper(\"infracraft:railway:Deploy\", name, {}, pulumiOpts);\n\n\t\t// `railway up --detach` uploads + triggers WITHOUT attaching to the build-log\n\t\t// stream — that long-lived stream is what intermittently times out and makes the\n\t\t// CLI exit non-zero even when the deploy actually succeeds. We capture its `--json`\n\t\t// output (for the exact deployment id) and exit code, re-emit it for visibility,\n\t\t// then hand off to the monitor bin which polls the Railway API to a terminal status.\n\t\t// The API — not the CLI exit code — decides pass/fail.\n\t\t//\n\t\t// The token travels via the command's STDIN, never in the script text: on failure\n\t\t// pulumi-command embeds the executed command verbatim in its error message, and\n\t\t// Pulumi does not scrub secrets from provider diagnostics — an inlined token prints\n\t\t// in plaintext exactly when a deploy fails. The `environment` map is no alternative:\n\t\t// an unknown secret there makes `pulumi preview` fail (the token is a resource\n\t\t// output, unknown on first preview). `|| true` keeps `set -e` alive when the stdin\n\t\t// payload has no trailing newline (read then exits 1 but still fills IC_TOK).\n\t\t// IC_SINCE is captured just before `railway up` as a createdAt fallback for id\n\t\t// resolution.\n\t\t// The capture is guarded with if/else because the whole script runs under\n\t\t// `set -e`: a bare `VAR=$(cmd); EXIT=$?` DIES AT THE ASSIGNMENT when cmd\n\t\t// fails, before the exit code is saved and before the output is re-emitted\n\t\t// — swallowing the CLI's error entirely (live incident: a failed production\n\t\t// `railway up` left zero diagnostics).\n\t\tconst cli = pulumi.interpolate`IFS= read -r IC_TOK || true; IC_SINCE=$(node -e \"process.stdout.write(String(Date.now()))\"); if IC_UP_OUT=$(RAILWAY_TOKEN=\"$IC_TOK\" railway up --detach --json --project ${project.id} --service ${service.id} --environment ${environment.id} 2>&1); then IC_UP_EXIT=0; else IC_UP_EXIT=$?; fi; printf '%s\\\\n' \"$IC_UP_OUT\"; if [ -n \"$INFRACRAFT_SKIP_DEPLOY_WAIT\" ]; then exit \"$IC_UP_EXIT\"; fi; IC_UP_OUT=\"$IC_UP_OUT\" IC_UP_EXIT=$IC_UP_EXIT IC_TOK=\"$IC_TOK\" IC_PROJ=${project.id} IC_ENV=${environment.id} IC_SVC=${service.id} IC_SINCE=$IC_SINCE node \"${MONITOR_BIN}\"`;\n\n\t\t// `printf '%s'` (not a bare format string) so railpack values containing %\n\t\t// are literal; the JSON is single-quote-escaped the POSIX way (' -> '\\'').\n\t\tconst setup = args.railpackConfig\n\t\t\t? `printf '%s' '${JSON.stringify(args.railpackConfig).replace(/'/g, \"'\\\\''\")}' > railpack.json`\n\t\t\t: undefined;\n\n\t\tconst { deploymentUrl } = createDeployCommand(\n\t\t\t{\n\t\t\t\tname,\n\t\t\t\tcli,\n\t\t\t\ttriggers: args.triggers,\n\t\t\t\texcludePaths: args.excludePaths,\n\t\t\t\tsetup,\n\t\t\t\tstdin: projectToken,\n\t\t\t},\n\t\t\t{ parent: this, ...pulumiOpts },\n\t\t);\n\n\t\tthis.deploymentUrl = deploymentUrl;\n\n\t\tthis.registerOutputs({ deploymentUrl: this.deploymentUrl });\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;AA2CA,MAAM,0CACL,IAAI,IAAI,6EAA+C,CACxD;;;;;;;;;;;AAYA,IAAa,gBAAb,cAAmCA,eAAO,kBAAkB;CAI3D,YACC,MACA,MACA,MACC;EACD,MAAM,EACL,UACA,SACA,aACA,SACA,cACA,GAAG,eACA;EAEJ,MAAM,6BAA6B,MAAM,CAAC,GAAG,UAAU;EAuBvD,MAAM,MAAM,eAAO,WAAW,4KAA4K,QAAQ,GAAG,aAAa,QAAQ,GAAG,iBAAiB,YAAY,GAAG,gOAAgO,QAAQ,GAAG,UAAU,YAAY,GAAG,UAAU,QAAQ,GAAG,4BAA4B,YAAY;EAI9kB,MAAM,QAAQ,KAAK,iBAChB,gBAAgB,KAAK,UAAU,KAAK,cAAc,EAAE,QAAQ,MAAM,OAAO,EAAE,qBAC3E;EAEH,MAAM,EAAE,kBAAkBC,4CACzB;GACC;GACA;GACA,UAAU,KAAK;GACf,cAAc,KAAK;GACnB;GACA,OAAO;EACR,GACA;GAAE,QAAQ;GAAM,GAAG;EAAW,CAC/B;EAEA,KAAK,gBAAgB;EAErB,KAAK,gBAAgB,EAAE,eAAe,KAAK,cAAc,CAAC;CAC3D;AACD"}
@@ -1 +1 @@
1
- {"version":3,"file":"deploy.d.cts","names":[],"sources":["../../src/railway/deploy.ts"],"mappings":";;;;;;;;UAWiB,mBAAA;EAChB,OAAA,GAAU,cAAc;EACxB,YAAA;EACA,gBAAA;AAAA;AAAA,UAGgB,iBAAA;EALQ;EAOxB,QAAA,EAAU,MAAA,CAAO,KAAA,CAAM,MAAA,CAAO,KAAA;EAPpB;EASV,YAAA;EAPA;EASA,cAAA,GAAiB,MAAA;AAAA;AAAA,KAGb,oBAAA,GAAuB,IAAA,CAC3B,MAAA,CAAO,wBAAA;EAGP,QAAA,EAAU,eAAA;EACV,OAAA,EAAS,cAAA;EACT,WAAA,EAAa,kBAAA;EACb,OAAA,EAAS,cAAA,EAVQ;EAYjB,YAAA,EAAc,MAAA,CAAO,KAAA;AAAA;;;;;;;;;;AAZE;cAmCX,aAAA,SAAsB,MAAA,CAAO,iBAAA;EAhCjB;EAAA,SAkCR,aAAA,EAAe,MAAA,CAAO,MAAA;cAGrC,IAAA,UACA,IAAA,EAAM,iBAAA,EACN,IAAA,EAAM,oBAAA;AAAA"}
1
+ {"version":3,"file":"deploy.d.cts","names":[],"sources":["../../src/railway/deploy.ts"],"mappings":";;;;;;;;UAUiB,mBAAA;EAChB,OAAA,GAAU,cAAc;EACxB,YAAA;EACA,gBAAA;AAAA;AAAA,UAGgB,iBAAA;EALQ;EAOxB,QAAA,EAAU,MAAA,CAAO,KAAA,CAAM,MAAA,CAAO,KAAA;EAPpB;EASV,YAAA;EAPA;EASA,cAAA,GAAiB,MAAA;AAAA;AAAA,KAGb,oBAAA,GAAuB,IAAA,CAC3B,MAAA,CAAO,wBAAA;EAGP,QAAA,EAAU,eAAA;EACV,OAAA,EAAS,cAAA;EACT,WAAA,EAAa,kBAAA;EACb,OAAA,EAAS,cAAA,EAVQ;EAYjB,YAAA,EAAc,MAAA,CAAO,KAAA;AAAA;;;;;;;;;;AAZE;cAmCX,aAAA,SAAsB,MAAA,CAAO,iBAAA;EAhCjB;EAAA,SAkCR,aAAA,EAAe,MAAA,CAAO,MAAA;cAGrC,IAAA,UACA,IAAA,EAAM,iBAAA,EACN,IAAA,EAAM,oBAAA;AAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"deploy.d.mts","names":[],"sources":["../../src/railway/deploy.ts"],"mappings":";;;;;;;;UAWiB,mBAAA;EAChB,OAAA,GAAU,cAAc;EACxB,YAAA;EACA,gBAAA;AAAA;AAAA,UAGgB,iBAAA;EALQ;EAOxB,QAAA,EAAU,MAAA,CAAO,KAAA,CAAM,MAAA,CAAO,KAAA;EAPpB;EASV,YAAA;EAPA;EASA,cAAA,GAAiB,MAAA;AAAA;AAAA,KAGb,oBAAA,GAAuB,IAAA,CAC3B,MAAA,CAAO,wBAAA;EAGP,QAAA,EAAU,eAAA;EACV,OAAA,EAAS,cAAA;EACT,WAAA,EAAa,kBAAA;EACb,OAAA,EAAS,cAAA,EAVQ;EAYjB,YAAA,EAAc,MAAA,CAAO,KAAA;AAAA;;;;;;;;;;AAZE;cAmCX,aAAA,SAAsB,MAAA,CAAO,iBAAA;EAhCjB;EAAA,SAkCR,aAAA,EAAe,MAAA,CAAO,MAAA;cAGrC,IAAA,UACA,IAAA,EAAM,iBAAA,EACN,IAAA,EAAM,oBAAA;AAAA"}
1
+ {"version":3,"file":"deploy.d.mts","names":[],"sources":["../../src/railway/deploy.ts"],"mappings":";;;;;;;;UAUiB,mBAAA;EAChB,OAAA,GAAU,cAAc;EACxB,YAAA;EACA,gBAAA;AAAA;AAAA,UAGgB,iBAAA;EALQ;EAOxB,QAAA,EAAU,MAAA,CAAO,KAAA,CAAM,MAAA,CAAO,KAAA;EAPpB;EASV,YAAA;EAPA;EASA,cAAA,GAAiB,MAAA;AAAA;AAAA,KAGb,oBAAA,GAAuB,IAAA,CAC3B,MAAA,CAAO,wBAAA;EAGP,QAAA,EAAU,eAAA;EACV,OAAA,EAAS,cAAA;EACT,WAAA,EAAa,kBAAA;EACb,OAAA,EAAS,cAAA,EAVQ;EAYjB,YAAA,EAAc,MAAA,CAAO,KAAA;AAAA;;;;;;;;;;AAZE;cAmCX,aAAA,SAAsB,MAAA,CAAO,iBAAA;EAhCjB;EAAA,SAkCR,aAAA,EAAe,MAAA,CAAO,MAAA;cAGrC,IAAA,UACA,IAAA,EAAM,iBAAA,EACN,IAAA,EAAM,oBAAA;AAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"deploy.mjs","names":[],"sources":["../../src/railway/deploy.ts"],"sourcesContent":["// src/railway/deploy.ts (replace entire file)\nimport { fileURLToPath } from \"node:url\";\n\nimport * as pulumi from \"@pulumi/pulumi\";\n\nimport { createDeployCommand } from \"../commands/deploy\";\nimport type { RailwayEnvironment } from \"./environment\";\nimport type { RailwayProject } from \"./project\";\nimport type { RailwayProvider } from \"./provider\";\nimport { RailwayBuilder, type RailwayService } from \"./service\";\n\nexport interface RailwayDeployConfig {\n\tbuilder?: RailwayBuilder;\n\tstartCommand?: string;\n\tpreDeployCommand?: string;\n}\n\nexport interface RailwayDeployArgs {\n\t/** Redeploy triggers (e.g. source hashes, env hashes). */\n\ttriggers: pulumi.Input<pulumi.Input<string>[]>;\n\t/** Paths excluded from the upload when running with `DeploySandbox` + `GitGuard`. */\n\texcludePaths?: string[];\n\t/** Railpack configuration written to `railpack.json` before deploy. */\n\trailpackConfig?: Record<string, unknown>;\n}\n\ntype RailwayDeployOptions = Omit<\n\tpulumi.ComponentResourceOptions,\n\t\"provider\"\n> & {\n\tprovider: RailwayProvider;\n\tproject: RailwayProject;\n\tenvironment: RailwayEnvironment;\n\tservice: RailwayService;\n\t/** Environment-scoped Railway deploy token (provision via RailwayProjectToken). */\n\tprojectToken: pulumi.Input<string>;\n};\n\n/**\n * Absolute path to the runnable deploy monitor, resolved next to this module in `dist`.\n * `railway up` only uploads + triggers; this bin makes the Railway GraphQL API — not the\n * CLI's exit code — the source of truth for pass/fail, and dumps build + deploy logs on\n * failure. Its logic lives in the unit-tested `deployment-monitor` module.\n */\nconst MONITOR_BIN = fileURLToPath(\n\tnew URL(\"./bin/monitor-deployment.mjs\", import.meta.url),\n);\n\n/**\n * Deploys a Railway service and waits for a terminal status. Isolation/git are the\n * seam's job (list a `DeploySandbox` and optionally a `GitGuard` in `dependsOn`).\n *\n * @example\n * ```typescript\n * new RailwayDeploy(\"mesh\", { triggers: [sourceHash], railpackConfig: { apt: [\"libatomic1\"] } },\n * { provider, project, environment, service, projectToken: token.token, dependsOn: [sandbox, gitGuard] });\n * ```\n */\nexport class RailwayDeploy extends pulumi.ComponentResource {\n\t/** The deploy CLI's final stdout line (Railway service URL when emitted). */\n\tpublic readonly deploymentUrl: pulumi.Output<string>;\n\n\tconstructor(\n\t\tname: string,\n\t\targs: RailwayDeployArgs,\n\t\topts: RailwayDeployOptions,\n\t) {\n\t\tconst {\n\t\t\tprovider,\n\t\t\tproject,\n\t\t\tenvironment,\n\t\t\tservice,\n\t\t\tprojectToken,\n\t\t\t...pulumiOpts\n\t\t} = opts;\n\n\t\tsuper(\"infracraft:railway:Deploy\", name, {}, pulumiOpts);\n\n\t\t// `railway up --detach` uploads + triggers WITHOUT attaching to the build-log\n\t\t// stream — that long-lived stream is what intermittently times out and makes the\n\t\t// CLI exit non-zero even when the deploy actually succeeds. We capture its `--json`\n\t\t// output (for the exact deployment id) and exit code, re-emit it for visibility,\n\t\t// then hand off to the monitor bin which polls the Railway API to a terminal status.\n\t\t// The API — not the CLI exit code — decides pass/fail.\n\t\t//\n\t\t// The token travels via the command's STDIN, never in the script text: on failure\n\t\t// pulumi-command embeds the executed command verbatim in its error message, and\n\t\t// Pulumi does not scrub secrets from provider diagnostics — an inlined token prints\n\t\t// in plaintext exactly when a deploy fails. The `environment` map is no alternative:\n\t\t// an unknown secret there makes `pulumi preview` fail (the token is a resource\n\t\t// output, unknown on first preview). `|| true` keeps `set -e` alive when the stdin\n\t\t// payload has no trailing newline (read then exits 1 but still fills IC_TOK).\n\t\t// IC_SINCE is captured just before `railway up` as a createdAt fallback for id\n\t\t// resolution.\n\t\t// The capture is guarded with if/else because the whole script runs under\n\t\t// `set -e`: a bare `VAR=$(cmd); EXIT=$?` DIES AT THE ASSIGNMENT when cmd\n\t\t// fails, before the exit code is saved and before the output is re-emitted\n\t\t// — swallowing the CLI's error entirely (live incident: a failed production\n\t\t// `railway up` left zero diagnostics).\n\t\tconst cli = pulumi.interpolate`IFS= read -r IC_TOK || true; IC_SINCE=$(node -e \"process.stdout.write(String(Date.now()))\"); if IC_UP_OUT=$(RAILWAY_TOKEN=\"$IC_TOK\" railway up --detach --json --project ${project.id} --service ${service.id} --environment ${environment.id} 2>&1); then IC_UP_EXIT=0; else IC_UP_EXIT=$?; fi; printf '%s\\\\n' \"$IC_UP_OUT\"; if [ -n \"$INFRACRAFT_SKIP_DEPLOY_WAIT\" ]; then exit \"$IC_UP_EXIT\"; fi; IC_UP_OUT=\"$IC_UP_OUT\" IC_UP_EXIT=$IC_UP_EXIT IC_TOK=\"$IC_TOK\" IC_PROJ=${project.id} IC_ENV=${environment.id} IC_SVC=${service.id} IC_SINCE=$IC_SINCE node \"${MONITOR_BIN}\"`;\n\n\t\t// `printf '%s'` (not a bare format string) so railpack values containing %\n\t\t// are literal; the JSON is single-quote-escaped the POSIX way (' -> '\\'').\n\t\tconst setup = args.railpackConfig\n\t\t\t? `printf '%s' '${JSON.stringify(args.railpackConfig).replace(/'/g, \"'\\\\''\")}' > railpack.json`\n\t\t\t: undefined;\n\n\t\tconst { deploymentUrl } = createDeployCommand(\n\t\t\t{\n\t\t\t\tname,\n\t\t\t\tcli,\n\t\t\t\ttriggers: args.triggers,\n\t\t\t\texcludePaths: args.excludePaths,\n\t\t\t\tsetup,\n\t\t\t\tstdin: projectToken,\n\t\t\t},\n\t\t\t{ parent: this, ...pulumiOpts },\n\t\t);\n\n\t\tthis.deploymentUrl = deploymentUrl;\n\n\t\tthis.registerOutputs({ deploymentUrl: this.deploymentUrl });\n\t}\n}\n"],"mappings":";;;;;;;;;;;;AA4CA,MAAM,cAAc,cACnB,IAAI,IAAI,gCAAgC,OAAO,KAAK,GAAG,CACxD;;;;;;;;;;;AAYA,IAAa,gBAAb,cAAmC,OAAO,kBAAkB;CAI3D,YACC,MACA,MACA,MACC;EACD,MAAM,EACL,UACA,SACA,aACA,SACA,cACA,GAAG,eACA;EAEJ,MAAM,6BAA6B,MAAM,CAAC,GAAG,UAAU;EAuBvD,MAAM,MAAM,OAAO,WAAW,4KAA4K,QAAQ,GAAG,aAAa,QAAQ,GAAG,iBAAiB,YAAY,GAAG,gOAAgO,QAAQ,GAAG,UAAU,YAAY,GAAG,UAAU,QAAQ,GAAG,4BAA4B,YAAY;EAI9kB,MAAM,QAAQ,KAAK,iBAChB,gBAAgB,KAAK,UAAU,KAAK,cAAc,EAAE,QAAQ,MAAM,OAAO,EAAE,qBAC3E;EAEH,MAAM,EAAE,kBAAkB,oBACzB;GACC;GACA;GACA,UAAU,KAAK;GACf,cAAc,KAAK;GACnB;GACA,OAAO;EACR,GACA;GAAE,QAAQ;GAAM,GAAG;EAAW,CAC/B;EAEA,KAAK,gBAAgB;EAErB,KAAK,gBAAgB,EAAE,eAAe,KAAK,cAAc,CAAC;CAC3D;AACD"}
1
+ {"version":3,"file":"deploy.mjs","names":[],"sources":["../../src/railway/deploy.ts"],"sourcesContent":["import { fileURLToPath } from \"node:url\";\n\nimport * as pulumi from \"@pulumi/pulumi\";\n\nimport { createDeployCommand } from \"../commands/deploy\";\nimport type { RailwayEnvironment } from \"./environment\";\nimport type { RailwayProject } from \"./project\";\nimport type { RailwayProvider } from \"./provider\";\nimport { RailwayBuilder, type RailwayService } from \"./service\";\n\nexport interface RailwayDeployConfig {\n\tbuilder?: RailwayBuilder;\n\tstartCommand?: string;\n\tpreDeployCommand?: string;\n}\n\nexport interface RailwayDeployArgs {\n\t/** Redeploy triggers (e.g. source hashes, env hashes). */\n\ttriggers: pulumi.Input<pulumi.Input<string>[]>;\n\t/** Paths excluded from the upload when running with `DeploySandbox` + `GitGuard`. */\n\texcludePaths?: string[];\n\t/** Railpack configuration written to `railpack.json` before deploy. */\n\trailpackConfig?: Record<string, unknown>;\n}\n\ntype RailwayDeployOptions = Omit<\n\tpulumi.ComponentResourceOptions,\n\t\"provider\"\n> & {\n\tprovider: RailwayProvider;\n\tproject: RailwayProject;\n\tenvironment: RailwayEnvironment;\n\tservice: RailwayService;\n\t/** Environment-scoped Railway deploy token (provision via RailwayProjectToken). */\n\tprojectToken: pulumi.Input<string>;\n};\n\n/**\n * Absolute path to the runnable deploy monitor, resolved next to this module in `dist`.\n * `railway up` only uploads + triggers; this bin makes the Railway GraphQL API — not the\n * CLI's exit code — the source of truth for pass/fail, and dumps build + deploy logs on\n * failure. Its logic lives in the unit-tested `deployment-monitor` module.\n */\nconst MONITOR_BIN = fileURLToPath(\n\tnew URL(\"./bin/monitor-deployment.mjs\", import.meta.url),\n);\n\n/**\n * Deploys a Railway service and waits for a terminal status. Isolation/git are the\n * seam's job (list a `DeploySandbox` and optionally a `GitGuard` in `dependsOn`).\n *\n * @example\n * ```typescript\n * new RailwayDeploy(\"mesh\", { triggers: [sourceHash], railpackConfig: { apt: [\"libatomic1\"] } },\n * { provider, project, environment, service, projectToken: token.token, dependsOn: [sandbox, gitGuard] });\n * ```\n */\nexport class RailwayDeploy extends pulumi.ComponentResource {\n\t/** The deploy CLI's final stdout line (Railway service URL when emitted). */\n\tpublic readonly deploymentUrl: pulumi.Output<string>;\n\n\tconstructor(\n\t\tname: string,\n\t\targs: RailwayDeployArgs,\n\t\topts: RailwayDeployOptions,\n\t) {\n\t\tconst {\n\t\t\tprovider,\n\t\t\tproject,\n\t\t\tenvironment,\n\t\t\tservice,\n\t\t\tprojectToken,\n\t\t\t...pulumiOpts\n\t\t} = opts;\n\n\t\tsuper(\"infracraft:railway:Deploy\", name, {}, pulumiOpts);\n\n\t\t// `railway up --detach` uploads + triggers WITHOUT attaching to the build-log\n\t\t// stream — that long-lived stream is what intermittently times out and makes the\n\t\t// CLI exit non-zero even when the deploy actually succeeds. We capture its `--json`\n\t\t// output (for the exact deployment id) and exit code, re-emit it for visibility,\n\t\t// then hand off to the monitor bin which polls the Railway API to a terminal status.\n\t\t// The API — not the CLI exit code — decides pass/fail.\n\t\t//\n\t\t// The token travels via the command's STDIN, never in the script text: on failure\n\t\t// pulumi-command embeds the executed command verbatim in its error message, and\n\t\t// Pulumi does not scrub secrets from provider diagnostics — an inlined token prints\n\t\t// in plaintext exactly when a deploy fails. The `environment` map is no alternative:\n\t\t// an unknown secret there makes `pulumi preview` fail (the token is a resource\n\t\t// output, unknown on first preview). `|| true` keeps `set -e` alive when the stdin\n\t\t// payload has no trailing newline (read then exits 1 but still fills IC_TOK).\n\t\t// IC_SINCE is captured just before `railway up` as a createdAt fallback for id\n\t\t// resolution.\n\t\t// The capture is guarded with if/else because the whole script runs under\n\t\t// `set -e`: a bare `VAR=$(cmd); EXIT=$?` DIES AT THE ASSIGNMENT when cmd\n\t\t// fails, before the exit code is saved and before the output is re-emitted\n\t\t// — swallowing the CLI's error entirely (live incident: a failed production\n\t\t// `railway up` left zero diagnostics).\n\t\tconst cli = pulumi.interpolate`IFS= read -r IC_TOK || true; IC_SINCE=$(node -e \"process.stdout.write(String(Date.now()))\"); if IC_UP_OUT=$(RAILWAY_TOKEN=\"$IC_TOK\" railway up --detach --json --project ${project.id} --service ${service.id} --environment ${environment.id} 2>&1); then IC_UP_EXIT=0; else IC_UP_EXIT=$?; fi; printf '%s\\\\n' \"$IC_UP_OUT\"; if [ -n \"$INFRACRAFT_SKIP_DEPLOY_WAIT\" ]; then exit \"$IC_UP_EXIT\"; fi; IC_UP_OUT=\"$IC_UP_OUT\" IC_UP_EXIT=$IC_UP_EXIT IC_TOK=\"$IC_TOK\" IC_PROJ=${project.id} IC_ENV=${environment.id} IC_SVC=${service.id} IC_SINCE=$IC_SINCE node \"${MONITOR_BIN}\"`;\n\n\t\t// `printf '%s'` (not a bare format string) so railpack values containing %\n\t\t// are literal; the JSON is single-quote-escaped the POSIX way (' -> '\\'').\n\t\tconst setup = args.railpackConfig\n\t\t\t? `printf '%s' '${JSON.stringify(args.railpackConfig).replace(/'/g, \"'\\\\''\")}' > railpack.json`\n\t\t\t: undefined;\n\n\t\tconst { deploymentUrl } = createDeployCommand(\n\t\t\t{\n\t\t\t\tname,\n\t\t\t\tcli,\n\t\t\t\ttriggers: args.triggers,\n\t\t\t\texcludePaths: args.excludePaths,\n\t\t\t\tsetup,\n\t\t\t\tstdin: projectToken,\n\t\t\t},\n\t\t\t{ parent: this, ...pulumiOpts },\n\t\t);\n\n\t\tthis.deploymentUrl = deploymentUrl;\n\n\t\tthis.registerOutputs({ deploymentUrl: this.deploymentUrl });\n\t}\n}\n"],"mappings":";;;;;;;;;;;;AA2CA,MAAM,cAAc,cACnB,IAAI,IAAI,gCAAgC,OAAO,KAAK,GAAG,CACxD;;;;;;;;;;;AAYA,IAAa,gBAAb,cAAmC,OAAO,kBAAkB;CAI3D,YACC,MACA,MACA,MACC;EACD,MAAM,EACL,UACA,SACA,aACA,SACA,cACA,GAAG,eACA;EAEJ,MAAM,6BAA6B,MAAM,CAAC,GAAG,UAAU;EAuBvD,MAAM,MAAM,OAAO,WAAW,4KAA4K,QAAQ,GAAG,aAAa,QAAQ,GAAG,iBAAiB,YAAY,GAAG,gOAAgO,QAAQ,GAAG,UAAU,YAAY,GAAG,UAAU,QAAQ,GAAG,4BAA4B,YAAY;EAI9kB,MAAM,QAAQ,KAAK,iBAChB,gBAAgB,KAAK,UAAU,KAAK,cAAc,EAAE,QAAQ,MAAM,OAAO,EAAE,qBAC3E;EAEH,MAAM,EAAE,kBAAkB,oBACzB;GACC;GACA;GACA,UAAU,KAAK;GACf,cAAc,KAAK;GACnB;GACA,OAAO;EACR,GACA;GAAE,QAAQ;GAAM,GAAG;EAAW,CAC/B;EAEA,KAAK,gBAAgB;EAErB,KAAK,gBAAgB,EAAE,eAAe,KAAK,cAAc,CAAC;CAC3D;AACD"}
@@ -162,7 +162,6 @@ var RailwayProjectTokenResource = class extends _pulumi_pulumi.dynamic.Resource
162
162
  * }, { provider, project, environment: staging });
163
163
  *
164
164
  * new RailwayDeploy("api-deploy", {
165
- * directory: monorepoRoot,
166
165
  * triggers: [sourceHash],
167
166
  * }, { provider, project, environment: staging, service, projectToken: stagingToken.token });
168
167
  * ```
@@ -1 +1 @@
1
- {"version":3,"file":"project-token.cjs","names":["isResolvedString","RailwayClient","pulumi"],"sources":["../../src/railway/project-token.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\nimport { isResolvedString } from \"../dynamic/is-resolved-string\";\nimport { RailwayClient } from \"./client\";\nimport type { RailwayEnvironment } from \"./environment\";\nimport type { RailwayProject } from \"./project\";\nimport type { RailwayProvider } from \"./provider\";\n\n/** Resolved inputs for the Railway project token dynamic provider. */\ninterface RailwayProjectTokenInputs {\n\t/** Railway API bearer token (account-scoped, used for API calls). */\n\ttoken: string;\n\n\t/** Railway project UUID. */\n\tprojectId: string;\n\n\t/** Railway environment UUID this deploy token is scoped to. */\n\tenvironmentId: string;\n\n\t/** Distinct token name, e.g. `\"pulumi-staging\"`. Must be unique per environment. */\n\tname: string;\n\n\t/**\n\t * Rotation handle: bumping this number replaces the token on the next `up` —\n\t * the new token is minted BEFORE the old one is revoked (create-before-delete,\n\t * unlike identity changes), so there is never a tokenless window. Leave unset\n\t * until the first rotation is needed.\n\t */\n\ttokenVersion?: number;\n}\n\n/** Persisted state for the Railway project token. */\ninterface RailwayProjectTokenOutputs extends RailwayProjectTokenInputs {\n\t/** Minted deploy token secret value. */\n\tvalue: string;\n\n\t/** Railway-assigned token UUID (used for clean teardown on delete). */\n\ttokenId: string;\n}\n\nconst PROJECT_TOKENS_QUERY = `\n query($projectId: String!) {\n projectTokens(projectId: $projectId) {\n edges { node { id name } }\n }\n }\n`;\n\nconst PROJECT_TOKEN_CREATE = `\n mutation($input: ProjectTokenCreateInput!) {\n projectTokenCreate(input: $input)\n }\n`;\n\nconst PROJECT_TOKEN_DELETE = `\n mutation($id: String!) { projectTokenDelete(id: $id) }\n`;\n\n/**\n * Dynamic provider that mints a Railway environment-scoped deploy token.\n *\n * On create, it deletes any existing tokens with the same name (stale tokens\n * from previous runs), mints a fresh one scoped to the target environment, then\n * re-lists tokens to capture the Railway-assigned ID for later teardown.\n *\n * @internal Exported only for unit testing; not part of the public API surface.\n */\nexport class RailwayProjectTokenResourceProvider\n\timplements pulumi.dynamic.ResourceProvider\n{\n\t/**\n\t * Validates inputs at plan time. An empty token name would otherwise fail\n\t * deep inside `projectTokenCreate` — and worse, silently match every token\n\t * in the stale-name cleanup sweep.\n\t */\n\tasync check(\n\t\t_olds: RailwayProjectTokenInputs,\n\t\tnews: RailwayProjectTokenInputs,\n\t): Promise<pulumi.dynamic.CheckResult<RailwayProjectTokenInputs>> {\n\t\tconst failures: pulumi.dynamic.CheckFailure[] = [];\n\n\t\tif (isResolvedString(news.name) && news.name.trim().length === 0) {\n\t\t\tfailures.push({\n\t\t\t\tproperty: \"name\",\n\t\t\t\treason: 'name must be a non-empty token name (e.g. \"pulumi-staging\")',\n\t\t\t});\n\t\t}\n\n\t\treturn { inputs: news, failures };\n\t}\n\n\t/**\n\t * Deletes stale same-named tokens, mints a new environment-scoped token,\n\t * and captures its ID via a follow-up list query.\n\t *\n\t * @param inputs Resolved provider inputs.\n\t * @returns Pulumi dynamic create result with `value` (secret) and `tokenId`.\n\t */\n\tasync create(\n\t\tinputs: RailwayProjectTokenInputs,\n\t): Promise<pulumi.dynamic.CreateResult> {\n\t\tconst client = new RailwayClient(inputs.token);\n\n\t\tconst tokensResult = await client.query<{\n\t\t\tprojectTokens: {\n\t\t\t\tedges: Array<{ node: { id: string; name: string } }>;\n\t\t\t};\n\t\t}>(PROJECT_TOKENS_QUERY, { projectId: inputs.projectId });\n\n\t\t// During a tokenVersion rotation this cleanup revokes the OLD token (same\n\t\t// name, still tracked by the engine) as a side effect — the engine's own\n\t\t// delete step then finds it already gone, which delete() tolerates. That\n\t\t// tolerance is what makes this create-before-delete race harmless.\n\t\tconst stale = tokensResult.projectTokens.edges.filter(\n\t\t\t(edge) => edge.node.name === inputs.name,\n\t\t);\n\n\t\tfor (const entry of stale) {\n\t\t\tawait client.query(PROJECT_TOKEN_DELETE, { id: entry.node.id });\n\t\t}\n\n\t\tconst createResult = await client.query<{ projectTokenCreate: string }>(\n\t\t\tPROJECT_TOKEN_CREATE,\n\t\t\t{\n\t\t\t\tinput: {\n\t\t\t\t\tprojectId: inputs.projectId,\n\t\t\t\t\tenvironmentId: inputs.environmentId,\n\t\t\t\t\tname: inputs.name,\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\n\t\tconst value = createResult.projectTokenCreate;\n\n\t\t// Re-list tokens to capture the Railway-assigned ID: the create mutation\n\t\t// returns only the token value, not its UUID.\n\t\tconst refreshResult = await client.query<{\n\t\t\tprojectTokens: {\n\t\t\t\tedges: Array<{ node: { id: string; name: string } }>;\n\t\t\t};\n\t\t}>(PROJECT_TOKENS_QUERY, { projectId: inputs.projectId });\n\n\t\t// After the delete sweep above, exactly one token with this name exists (the one we just\n\t\t// created). Resolve its id from the re-list so delete() can revoke it later.\n\t\tconst found = refreshResult.projectTokens.edges.find(\n\t\t\t(edge) => edge.node.name === inputs.name,\n\t\t);\n\n\t\tif (!found) {\n\t\t\tthrow new Error(\n\t\t\t\t`Could not resolve token id for newly created token \"${inputs.name}\" in project ${inputs.projectId}`,\n\t\t\t);\n\t\t}\n\n\t\tconst tokenId = found.node.id;\n\n\t\tconst outs: RailwayProjectTokenOutputs = {\n\t\t\t...inputs,\n\t\t\tvalue,\n\t\t\ttokenId,\n\t\t};\n\n\t\treturn { id: `${inputs.projectId}:${inputs.name}`, outs };\n\t}\n\n\t/**\n\t * Pass-through read — the token value is a write-once secret that Railway\n\t * never re-exposes via API, so the stored state is the only source of truth.\n\t *\n\t * @param id Resource ID.\n\t * @param props Currently stored outputs.\n\t */\n\tasync read(\n\t\tid: string,\n\t\tprops: RailwayProjectTokenOutputs,\n\t): Promise<pulumi.dynamic.ReadResult> {\n\t\treturn { id, props };\n\t}\n\n\t/**\n\t * Deletes the Railway token by its stored UUID for clean teardown.\n\t * Tolerates an already-revoked token (deletion is idempotent): during a\n\t * `tokenVersion` rotation, `create()`'s stale-name cleanup revokes the old\n\t * token before the engine gets to delete it — throwing here would strand a\n\t * pending-delete tombstone in state that fails every subsequent `up`.\n\t *\n\t * @param _id Resource ID (unused).\n\t * @param props Currently stored outputs containing the tokenId.\n\t */\n\tasync delete(_id: string, props: RailwayProjectTokenOutputs): Promise<void> {\n\t\tif (!props.tokenId) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst client = new RailwayClient(props.token);\n\n\t\ttry {\n\t\t\tawait client.query(PROJECT_TOKEN_DELETE, { id: props.tokenId });\n\t\t} catch (error) {\n\t\t\t// Railway reports an already-revoked token as a GraphQL \"not found\"\n\t\t\t// error (no HTTP 404, so no ApiNotFoundError path exists here).\n\t\t\tif (error instanceof Error && /not found/i.test(error.message)) {\n\t\t\t\tpulumi.log.info(\n\t\t\t\t\t`Railway project token \"${props.name}\" already revoked (rotation cleanup) — nothing to delete`,\n\t\t\t\t);\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/**\n\t * Triggers replacement when the project, environment, or token name changes.\n\t *\n\t * @param _id Resource ID (unused).\n\t * @param olds Previously stored outputs.\n\t * @param news Newly resolved inputs.\n\t */\n\tasync diff(\n\t\t_id: string,\n\t\tolds: RailwayProjectTokenOutputs,\n\t\tnews: RailwayProjectTokenInputs,\n\t): Promise<pulumi.dynamic.DiffResult> {\n\t\tconst identityReplaces: string[] = [];\n\n\t\tif (olds.projectId !== news.projectId) {\n\t\t\tidentityReplaces.push(\"projectId\");\n\t\t}\n\n\t\tif (olds.environmentId !== news.environmentId) {\n\t\t\tidentityReplaces.push(\"environmentId\");\n\t\t}\n\n\t\tif (olds.name !== news.name) {\n\t\t\tidentityReplaces.push(\"name\");\n\t\t}\n\n\t\tconst rotates = olds.tokenVersion !== news.tokenVersion;\n\n\t\treturn {\n\t\t\tchanges: identityReplaces.length > 0 || rotates,\n\t\t\treplaces: rotates\n\t\t\t\t? [...identityReplaces, \"tokenVersion\"]\n\t\t\t\t: identityReplaces,\n\t\t\t// Identity changes revoke the old token first (names must stay unique);\n\t\t\t// a pure rotation mints the new token BEFORE revoking the old so there\n\t\t\t// is never a tokenless window — `create()` already cleans up stale\n\t\t\t// same-name tokens, so the transient name collision is handled.\n\t\t\tdeleteBeforeReplace: identityReplaces.length > 0,\n\t\t};\n\t}\n}\n\n/** Internal dynamic resource — not part of the public API. */\nclass RailwayProjectTokenResource extends pulumi.dynamic.Resource {\n\tpublic declare readonly value: pulumi.Output<string>;\n\tpublic declare readonly tokenId: pulumi.Output<string>;\n\n\tconstructor(\n\t\tname: string,\n\t\targs: {\n\t\t\ttoken: pulumi.Input<string>;\n\t\t\tprojectId: pulumi.Input<string>;\n\t\t\tenvironmentId: pulumi.Input<string>;\n\t\t\tname: pulumi.Input<string>;\n\t\t\ttokenVersion?: pulumi.Input<number>;\n\t\t},\n\t\topts?: pulumi.CustomResourceOptions,\n\t) {\n\t\t// `value` is an output-only secret. Declaring it via additionalSecretOutputs\n\t\t// (rather than a pulumi.secret(undefined) input placeholder) is the only reliable\n\t\t// way to mark a dynamic-provider output secret: the placeholder gives the property\n\t\t// both a secret-undefined input and a real output, so a downstream dynamic resource\n\t\t// consuming the token can race onto the undefined (\"malformed RPC secret: missing\n\t\t// value\" / \"Unexpected struct type\"). `tokenId` is a plain (non-secret) output. See\n\t\t// Pulumi #16041, #3012. `token` (the account API credential) rides along in state\n\t\t// with the outputs, so it is marked secret too.\n\t\tsuper(\n\t\t\tnew RailwayProjectTokenResourceProvider(),\n\t\t\tname,\n\t\t\t{\n\t\t\t\t...args,\n\t\t\t\tvalue: undefined,\n\t\t\t\ttokenId: undefined,\n\t\t\t},\n\t\t\t{ ...opts, additionalSecretOutputs: [\"value\", \"token\"] },\n\t\t);\n\t}\n}\n\n/** Options type for RailwayProjectToken — replaces Pulumi's native `provider` field. */\ntype RailwayProjectTokenOptions = Omit<\n\tpulumi.ComponentResourceOptions,\n\t\"provider\"\n> & {\n\t/** Railway authentication context. */\n\tprovider: RailwayProvider;\n\n\t/** Railway project this token belongs to. */\n\tproject: RailwayProject;\n\n\t/** Railway environment this deploy token is scoped to. */\n\tenvironment: RailwayEnvironment;\n};\n\n/** Args for RailwayProjectToken. */\nexport interface RailwayProjectTokenArgs {\n\t/**\n\t * Distinct token name, e.g. `\"pulumi-staging\"`.\n\t * Each environment should use a unique name so multiple stacks sharing\n\t * a project never collide on token ownership.\n\t */\n\tname: pulumi.Input<string>;\n\n\t/**\n\t * Rotation handle: bump to mint a fresh token on the next `up` (the old one\n\t * is revoked only after the new one exists). Consumers of `token` cascade\n\t * automatically.\n\t */\n\ttokenVersion?: pulumi.Input<number>;\n}\n\n/**\n * Provisions an environment-scoped Railway deploy token.\n *\n * Each environment gets its own correctly-scoped token with a distinct name,\n * so multiple stacks sharing the same Railway project never collide.\n * The token value is exposed as a secret output for use in {@link RailwayDeploy}.\n *\n * @example\n * ```typescript\n * const project = new RailwayProject(\"my-project\", { name: \"my-app\" }, { provider });\n * const staging = new RailwayEnvironment(\"staging\", { name: \"staging\" }, { provider, project });\n *\n * const stagingToken = new RailwayProjectToken(\"staging-token\", {\n * name: \"pulumi-staging\",\n * }, { provider, project, environment: staging });\n *\n * new RailwayDeploy(\"api-deploy\", {\n * directory: monorepoRoot,\n * triggers: [sourceHash],\n * }, { provider, project, environment: staging, service, projectToken: stagingToken.token });\n * ```\n */\nexport class RailwayProjectToken extends pulumi.ComponentResource {\n\t/**\n\t * Environment-scoped Railway deploy token value (secret).\n\t * Pass this to `RailwayDeployOptions.projectToken`.\n\t */\n\tpublic readonly token: pulumi.Output<string>;\n\n\tconstructor(\n\t\tname: string,\n\t\targs: RailwayProjectTokenArgs,\n\t\topts: RailwayProjectTokenOptions,\n\t) {\n\t\tconst { provider, project, environment, ...pulumiOpts } = opts;\n\n\t\tsuper(\"infracraft:railway:ProjectToken\", name, {}, pulumiOpts);\n\n\t\tconst resource = new RailwayProjectTokenResource(\n\t\t\t`${name}-resource`,\n\t\t\t{\n\t\t\t\ttoken: provider.token,\n\t\t\t\tprojectId: project.id,\n\t\t\t\tenvironmentId: environment.id,\n\t\t\t\tname: args.name,\n\t\t\t\ttokenVersion: args.tokenVersion,\n\t\t\t},\n\t\t\t{ parent: this },\n\t\t);\n\n\t\tthis.token = resource.value;\n\n\t\tthis.registerOutputs({ token: this.token });\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"project-token.cjs","names":["isResolvedString","RailwayClient","pulumi"],"sources":["../../src/railway/project-token.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\nimport { isResolvedString } from \"../dynamic/is-resolved-string\";\nimport { RailwayClient } from \"./client\";\nimport type { RailwayEnvironment } from \"./environment\";\nimport type { RailwayProject } from \"./project\";\nimport type { RailwayProvider } from \"./provider\";\n\n/** Resolved inputs for the Railway project token dynamic provider. */\ninterface RailwayProjectTokenInputs {\n\t/** Railway API bearer token (account-scoped, used for API calls). */\n\ttoken: string;\n\n\t/** Railway project UUID. */\n\tprojectId: string;\n\n\t/** Railway environment UUID this deploy token is scoped to. */\n\tenvironmentId: string;\n\n\t/** Distinct token name, e.g. `\"pulumi-staging\"`. Must be unique per environment. */\n\tname: string;\n\n\t/**\n\t * Rotation handle: bumping this number replaces the token on the next `up` —\n\t * the new token is minted BEFORE the old one is revoked (create-before-delete,\n\t * unlike identity changes), so there is never a tokenless window. Leave unset\n\t * until the first rotation is needed.\n\t */\n\ttokenVersion?: number;\n}\n\n/** Persisted state for the Railway project token. */\ninterface RailwayProjectTokenOutputs extends RailwayProjectTokenInputs {\n\t/** Minted deploy token secret value. */\n\tvalue: string;\n\n\t/** Railway-assigned token UUID (used for clean teardown on delete). */\n\ttokenId: string;\n}\n\nconst PROJECT_TOKENS_QUERY = `\n query($projectId: String!) {\n projectTokens(projectId: $projectId) {\n edges { node { id name } }\n }\n }\n`;\n\nconst PROJECT_TOKEN_CREATE = `\n mutation($input: ProjectTokenCreateInput!) {\n projectTokenCreate(input: $input)\n }\n`;\n\nconst PROJECT_TOKEN_DELETE = `\n mutation($id: String!) { projectTokenDelete(id: $id) }\n`;\n\n/**\n * Dynamic provider that mints a Railway environment-scoped deploy token.\n *\n * On create, it deletes any existing tokens with the same name (stale tokens\n * from previous runs), mints a fresh one scoped to the target environment, then\n * re-lists tokens to capture the Railway-assigned ID for later teardown.\n *\n * @internal Exported only for unit testing; not part of the public API surface.\n */\nexport class RailwayProjectTokenResourceProvider\n\timplements pulumi.dynamic.ResourceProvider\n{\n\t/**\n\t * Validates inputs at plan time. An empty token name would otherwise fail\n\t * deep inside `projectTokenCreate` — and worse, silently match every token\n\t * in the stale-name cleanup sweep.\n\t */\n\tasync check(\n\t\t_olds: RailwayProjectTokenInputs,\n\t\tnews: RailwayProjectTokenInputs,\n\t): Promise<pulumi.dynamic.CheckResult<RailwayProjectTokenInputs>> {\n\t\tconst failures: pulumi.dynamic.CheckFailure[] = [];\n\n\t\tif (isResolvedString(news.name) && news.name.trim().length === 0) {\n\t\t\tfailures.push({\n\t\t\t\tproperty: \"name\",\n\t\t\t\treason: 'name must be a non-empty token name (e.g. \"pulumi-staging\")',\n\t\t\t});\n\t\t}\n\n\t\treturn { inputs: news, failures };\n\t}\n\n\t/**\n\t * Deletes stale same-named tokens, mints a new environment-scoped token,\n\t * and captures its ID via a follow-up list query.\n\t *\n\t * @param inputs Resolved provider inputs.\n\t * @returns Pulumi dynamic create result with `value` (secret) and `tokenId`.\n\t */\n\tasync create(\n\t\tinputs: RailwayProjectTokenInputs,\n\t): Promise<pulumi.dynamic.CreateResult> {\n\t\tconst client = new RailwayClient(inputs.token);\n\n\t\tconst tokensResult = await client.query<{\n\t\t\tprojectTokens: {\n\t\t\t\tedges: Array<{ node: { id: string; name: string } }>;\n\t\t\t};\n\t\t}>(PROJECT_TOKENS_QUERY, { projectId: inputs.projectId });\n\n\t\t// During a tokenVersion rotation this cleanup revokes the OLD token (same\n\t\t// name, still tracked by the engine) as a side effect — the engine's own\n\t\t// delete step then finds it already gone, which delete() tolerates. That\n\t\t// tolerance is what makes this create-before-delete race harmless.\n\t\tconst stale = tokensResult.projectTokens.edges.filter(\n\t\t\t(edge) => edge.node.name === inputs.name,\n\t\t);\n\n\t\tfor (const entry of stale) {\n\t\t\tawait client.query(PROJECT_TOKEN_DELETE, { id: entry.node.id });\n\t\t}\n\n\t\tconst createResult = await client.query<{ projectTokenCreate: string }>(\n\t\t\tPROJECT_TOKEN_CREATE,\n\t\t\t{\n\t\t\t\tinput: {\n\t\t\t\t\tprojectId: inputs.projectId,\n\t\t\t\t\tenvironmentId: inputs.environmentId,\n\t\t\t\t\tname: inputs.name,\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\n\t\tconst value = createResult.projectTokenCreate;\n\n\t\t// Re-list tokens to capture the Railway-assigned ID: the create mutation\n\t\t// returns only the token value, not its UUID.\n\t\tconst refreshResult = await client.query<{\n\t\t\tprojectTokens: {\n\t\t\t\tedges: Array<{ node: { id: string; name: string } }>;\n\t\t\t};\n\t\t}>(PROJECT_TOKENS_QUERY, { projectId: inputs.projectId });\n\n\t\t// After the delete sweep above, exactly one token with this name exists (the one we just\n\t\t// created). Resolve its id from the re-list so delete() can revoke it later.\n\t\tconst found = refreshResult.projectTokens.edges.find(\n\t\t\t(edge) => edge.node.name === inputs.name,\n\t\t);\n\n\t\tif (!found) {\n\t\t\tthrow new Error(\n\t\t\t\t`Could not resolve token id for newly created token \"${inputs.name}\" in project ${inputs.projectId}`,\n\t\t\t);\n\t\t}\n\n\t\tconst tokenId = found.node.id;\n\n\t\tconst outs: RailwayProjectTokenOutputs = {\n\t\t\t...inputs,\n\t\t\tvalue,\n\t\t\ttokenId,\n\t\t};\n\n\t\treturn { id: `${inputs.projectId}:${inputs.name}`, outs };\n\t}\n\n\t/**\n\t * Pass-through read — the token value is a write-once secret that Railway\n\t * never re-exposes via API, so the stored state is the only source of truth.\n\t *\n\t * @param id Resource ID.\n\t * @param props Currently stored outputs.\n\t */\n\tasync read(\n\t\tid: string,\n\t\tprops: RailwayProjectTokenOutputs,\n\t): Promise<pulumi.dynamic.ReadResult> {\n\t\treturn { id, props };\n\t}\n\n\t/**\n\t * Deletes the Railway token by its stored UUID for clean teardown.\n\t * Tolerates an already-revoked token (deletion is idempotent): during a\n\t * `tokenVersion` rotation, `create()`'s stale-name cleanup revokes the old\n\t * token before the engine gets to delete it — throwing here would strand a\n\t * pending-delete tombstone in state that fails every subsequent `up`.\n\t *\n\t * @param _id Resource ID (unused).\n\t * @param props Currently stored outputs containing the tokenId.\n\t */\n\tasync delete(_id: string, props: RailwayProjectTokenOutputs): Promise<void> {\n\t\tif (!props.tokenId) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst client = new RailwayClient(props.token);\n\n\t\ttry {\n\t\t\tawait client.query(PROJECT_TOKEN_DELETE, { id: props.tokenId });\n\t\t} catch (error) {\n\t\t\t// Railway reports an already-revoked token as a GraphQL \"not found\"\n\t\t\t// error (no HTTP 404, so no ApiNotFoundError path exists here).\n\t\t\tif (error instanceof Error && /not found/i.test(error.message)) {\n\t\t\t\tpulumi.log.info(\n\t\t\t\t\t`Railway project token \"${props.name}\" already revoked (rotation cleanup) — nothing to delete`,\n\t\t\t\t);\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/**\n\t * Triggers replacement when the project, environment, or token name changes.\n\t *\n\t * @param _id Resource ID (unused).\n\t * @param olds Previously stored outputs.\n\t * @param news Newly resolved inputs.\n\t */\n\tasync diff(\n\t\t_id: string,\n\t\tolds: RailwayProjectTokenOutputs,\n\t\tnews: RailwayProjectTokenInputs,\n\t): Promise<pulumi.dynamic.DiffResult> {\n\t\tconst identityReplaces: string[] = [];\n\n\t\tif (olds.projectId !== news.projectId) {\n\t\t\tidentityReplaces.push(\"projectId\");\n\t\t}\n\n\t\tif (olds.environmentId !== news.environmentId) {\n\t\t\tidentityReplaces.push(\"environmentId\");\n\t\t}\n\n\t\tif (olds.name !== news.name) {\n\t\t\tidentityReplaces.push(\"name\");\n\t\t}\n\n\t\tconst rotates = olds.tokenVersion !== news.tokenVersion;\n\n\t\treturn {\n\t\t\tchanges: identityReplaces.length > 0 || rotates,\n\t\t\treplaces: rotates\n\t\t\t\t? [...identityReplaces, \"tokenVersion\"]\n\t\t\t\t: identityReplaces,\n\t\t\t// Identity changes revoke the old token first (names must stay unique);\n\t\t\t// a pure rotation mints the new token BEFORE revoking the old so there\n\t\t\t// is never a tokenless window — `create()` already cleans up stale\n\t\t\t// same-name tokens, so the transient name collision is handled.\n\t\t\tdeleteBeforeReplace: identityReplaces.length > 0,\n\t\t};\n\t}\n}\n\n/** Internal dynamic resource — not part of the public API. */\nclass RailwayProjectTokenResource extends pulumi.dynamic.Resource {\n\tpublic declare readonly value: pulumi.Output<string>;\n\tpublic declare readonly tokenId: pulumi.Output<string>;\n\n\tconstructor(\n\t\tname: string,\n\t\targs: {\n\t\t\ttoken: pulumi.Input<string>;\n\t\t\tprojectId: pulumi.Input<string>;\n\t\t\tenvironmentId: pulumi.Input<string>;\n\t\t\tname: pulumi.Input<string>;\n\t\t\ttokenVersion?: pulumi.Input<number>;\n\t\t},\n\t\topts?: pulumi.CustomResourceOptions,\n\t) {\n\t\t// `value` is an output-only secret. Declaring it via additionalSecretOutputs\n\t\t// (rather than a pulumi.secret(undefined) input placeholder) is the only reliable\n\t\t// way to mark a dynamic-provider output secret: the placeholder gives the property\n\t\t// both a secret-undefined input and a real output, so a downstream dynamic resource\n\t\t// consuming the token can race onto the undefined (\"malformed RPC secret: missing\n\t\t// value\" / \"Unexpected struct type\"). `tokenId` is a plain (non-secret) output. See\n\t\t// Pulumi #16041, #3012. `token` (the account API credential) rides along in state\n\t\t// with the outputs, so it is marked secret too.\n\t\tsuper(\n\t\t\tnew RailwayProjectTokenResourceProvider(),\n\t\t\tname,\n\t\t\t{\n\t\t\t\t...args,\n\t\t\t\tvalue: undefined,\n\t\t\t\ttokenId: undefined,\n\t\t\t},\n\t\t\t{ ...opts, additionalSecretOutputs: [\"value\", \"token\"] },\n\t\t);\n\t}\n}\n\n/** Options type for RailwayProjectToken — replaces Pulumi's native `provider` field. */\ntype RailwayProjectTokenOptions = Omit<\n\tpulumi.ComponentResourceOptions,\n\t\"provider\"\n> & {\n\t/** Railway authentication context. */\n\tprovider: RailwayProvider;\n\n\t/** Railway project this token belongs to. */\n\tproject: RailwayProject;\n\n\t/** Railway environment this deploy token is scoped to. */\n\tenvironment: RailwayEnvironment;\n};\n\n/** Args for RailwayProjectToken. */\nexport interface RailwayProjectTokenArgs {\n\t/**\n\t * Distinct token name, e.g. `\"pulumi-staging\"`.\n\t * Each environment should use a unique name so multiple stacks sharing\n\t * a project never collide on token ownership.\n\t */\n\tname: pulumi.Input<string>;\n\n\t/**\n\t * Rotation handle: bump to mint a fresh token on the next `up` (the old one\n\t * is revoked only after the new one exists). Consumers of `token` cascade\n\t * automatically.\n\t */\n\ttokenVersion?: pulumi.Input<number>;\n}\n\n/**\n * Provisions an environment-scoped Railway deploy token.\n *\n * Each environment gets its own correctly-scoped token with a distinct name,\n * so multiple stacks sharing the same Railway project never collide.\n * The token value is exposed as a secret output for use in {@link RailwayDeploy}.\n *\n * @example\n * ```typescript\n * const project = new RailwayProject(\"my-project\", { name: \"my-app\" }, { provider });\n * const staging = new RailwayEnvironment(\"staging\", { name: \"staging\" }, { provider, project });\n *\n * const stagingToken = new RailwayProjectToken(\"staging-token\", {\n * name: \"pulumi-staging\",\n * }, { provider, project, environment: staging });\n *\n * new RailwayDeploy(\"api-deploy\", {\n * triggers: [sourceHash],\n * }, { provider, project, environment: staging, service, projectToken: stagingToken.token });\n * ```\n */\nexport class RailwayProjectToken extends pulumi.ComponentResource {\n\t/**\n\t * Environment-scoped Railway deploy token value (secret).\n\t * Pass this to `RailwayDeployOptions.projectToken`.\n\t */\n\tpublic readonly token: pulumi.Output<string>;\n\n\tconstructor(\n\t\tname: string,\n\t\targs: RailwayProjectTokenArgs,\n\t\topts: RailwayProjectTokenOptions,\n\t) {\n\t\tconst { provider, project, environment, ...pulumiOpts } = opts;\n\n\t\tsuper(\"infracraft:railway:ProjectToken\", name, {}, pulumiOpts);\n\n\t\tconst resource = new RailwayProjectTokenResource(\n\t\t\t`${name}-resource`,\n\t\t\t{\n\t\t\t\ttoken: provider.token,\n\t\t\t\tprojectId: project.id,\n\t\t\t\tenvironmentId: environment.id,\n\t\t\t\tname: args.name,\n\t\t\t\ttokenVersion: args.tokenVersion,\n\t\t\t},\n\t\t\t{ parent: this },\n\t\t);\n\n\t\tthis.token = resource.value;\n\n\t\tthis.registerOutputs({ token: this.token });\n\t}\n}\n"],"mappings":"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"}
@@ -120,7 +120,6 @@ interface RailwayProjectTokenArgs {
120
120
  * }, { provider, project, environment: staging });
121
121
  *
122
122
  * new RailwayDeploy("api-deploy", {
123
- * directory: monorepoRoot,
124
123
  * triggers: [sourceHash],
125
124
  * }, { provider, project, environment: staging, service, projectToken: stagingToken.token });
126
125
  * ```
@@ -1 +1 @@
1
- {"version":3,"file":"project-token.d.cts","names":[],"sources":["../../src/railway/project-token.ts"],"mappings":";;;;;;;;UAQU,yBAAA;;EAET,KAAA;EAFkC;EAKlC,SAAA;EALkC;EAQlC,aAAA;EAHA;EAMA,IAAA;EAAA;;;AAQY;AAAA;;EAAZ,YAAA;AAAA;;UAIS,0BAAA,SAAmC,yBAAyB;EAKrE;EAHA,KAAA;EAGO;EAAP,OAAA;AAAA;;;;;;;;;;cA8BY,mCAAA,YACD,MAAA,CAAO,OAAA,CAAQ,gBAAA;EA2Gf;;;;;EApGL,KAAA,CACL,KAAA,EAAO,yBAAA,EACP,IAAA,EAAM,yBAAA,GACJ,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,WAAA,CAAY,yBAAA;EAkJ3B;;;;;;;EA9HL,MAAA,CACL,MAAA,EAAQ,yBAAA,GACN,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,YAAA;EAzBpB;;;;;;;EAiGA,IAAA,CACL,EAAA,UACA,KAAA,EAAO,0BAAA,GACL,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,UAAA;EAjGA;;;;;;;;;;EA+GpB,MAAA,CAAO,GAAA,UAAa,KAAA,EAAO,0BAAA,GAA6B,OAAA;EAftD;;;;;;;EA8CF,IAAA,CACL,GAAA,UACA,IAAA,EAAM,0BAAA,EACN,IAAA,EAAM,yBAAA,GACJ,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,UAAA;AAAA;;KAqEtB,0BAAA,GAA6B,IAAA,CACjC,MAAA,CAAO,wBAAA;EA1ED,sCA8EN,QAAA,EAAU,eAAA,EA5EH;EA+EP,OAAA,EAAS,cAAA,EA9EF;EAiFP,WAAA,EAAa,kBAAA;AAAA;;UAIG,uBAAA;EApFU;;AAAU;AA6BpC;;EA6DA,IAAA,EAAM,MAAA,CAAO,KAAA;EApBb;;;;;EA2BA,YAAA,GAAe,MAAA,CAAO,KAAK;AAAA;;;;;;;;;;;AAjBI;AAIhC;;;;;;;;;;;cAsCa,mBAAA,SAA4B,MAAA,CAAO,iBAAA;EAAnC;;;;EAAA,SAKI,KAAA,EAAO,MAAA,CAAO,MAAA;cAG7B,IAAA,UACA,IAAA,EAAM,uBAAA,EACN,IAAA,EAAM,0BAAA;AAAA"}
1
+ {"version":3,"file":"project-token.d.cts","names":[],"sources":["../../src/railway/project-token.ts"],"mappings":";;;;;;;;UAQU,yBAAA;;EAET,KAAA;EAFkC;EAKlC,SAAA;EALkC;EAQlC,aAAA;EAHA;EAMA,IAAA;EAAA;;;AAQY;AAAA;;EAAZ,YAAA;AAAA;;UAIS,0BAAA,SAAmC,yBAAyB;EAKrE;EAHA,KAAA;EAGO;EAAP,OAAA;AAAA;;;;;;;;;;cA8BY,mCAAA,YACD,MAAA,CAAO,OAAA,CAAQ,gBAAA;EA2Gf;;;;;EApGL,KAAA,CACL,KAAA,EAAO,yBAAA,EACP,IAAA,EAAM,yBAAA,GACJ,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,WAAA,CAAY,yBAAA;EAkJ3B;;;;;;;EA9HL,MAAA,CACL,MAAA,EAAQ,yBAAA,GACN,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,YAAA;EAzBpB;;;;;;;EAiGA,IAAA,CACL,EAAA,UACA,KAAA,EAAO,0BAAA,GACL,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,UAAA;EAjGA;;;;;;;;;;EA+GpB,MAAA,CAAO,GAAA,UAAa,KAAA,EAAO,0BAAA,GAA6B,OAAA;EAftD;;;;;;;EA8CF,IAAA,CACL,GAAA,UACA,IAAA,EAAM,0BAAA,EACN,IAAA,EAAM,yBAAA,GACJ,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,UAAA;AAAA;;KAqEtB,0BAAA,GAA6B,IAAA,CACjC,MAAA,CAAO,wBAAA;EA1ED,sCA8EN,QAAA,EAAU,eAAA,EA5EH;EA+EP,OAAA,EAAS,cAAA,EA9EF;EAiFP,WAAA,EAAa,kBAAA;AAAA;;UAIG,uBAAA;EApFU;;AAAU;AA6BpC;;EA6DA,IAAA,EAAM,MAAA,CAAO,KAAA;EApBb;;;;;EA2BA,YAAA,GAAe,MAAA,CAAO,KAAK;AAAA;;;;;;;;;;;AAjBI;AAIhC;;;;;;;;;;cAqCa,mBAAA,SAA4B,MAAA,CAAO,iBAAA;EAxBpB;AAwB5B;;;EAxB4B,SA6BX,KAAA,EAAO,MAAA,CAAO,MAAA;cAG7B,IAAA,UACA,IAAA,EAAM,uBAAA,EACN,IAAA,EAAM,0BAAA;AAAA"}
@@ -120,7 +120,6 @@ interface RailwayProjectTokenArgs {
120
120
  * }, { provider, project, environment: staging });
121
121
  *
122
122
  * new RailwayDeploy("api-deploy", {
123
- * directory: monorepoRoot,
124
123
  * triggers: [sourceHash],
125
124
  * }, { provider, project, environment: staging, service, projectToken: stagingToken.token });
126
125
  * ```