@infracraft/pulumi 1.17.4 → 1.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -97,7 +97,7 @@ new RailwayDeploy("api-deploy", {
97
97
  | `RailwayVariable` | — | Batch upsert; uses `skipDeploys` to avoid snapshot errors |
98
98
  | `RailwayVolume` | `.id` | Persistent volume; `mountPath` must be absolute |
99
99
  | `RailwayProjectToken` | `.token` (secret) | Environment-scoped deploy token; feed into `RailwayDeploy` |
100
- | `RailwayDeploy` | | Runs `railway up --detach`, then monitors the deployment via the Railway API |
100
+ | `RailwayDeploy` | `.deploymentUrl` | Runs `railway up --detach`, then monitors the deployment via the Railway API |
101
101
 
102
102
  **Enums:** `RailwayBuilder` (`RAILPACK`, `NIXPACKS`, `DOCKERFILE`, `HEROKU`, `PAKETO`), `RailwayRestartPolicy` (`ON_FAILURE`, `ALWAYS`, `NEVER`)
103
103
 
@@ -182,7 +182,7 @@ const project = new VercelProject("web", {
182
182
  rootDirectory: "apps/web",
183
183
  }, { provider })
184
184
 
185
- // project.url resolves to the custom domain or <name>.vercel.app
185
+ // project.url is a full https:// URL — custom domain or <name>.vercel.app
186
186
  export const url = project.url
187
187
 
188
188
  const vars = new VercelVariable("web-vars", {
@@ -217,9 +217,9 @@ new VercelResourceConnection("kv-conn", {
217
217
  | Class | Key outputs | Notes |
218
218
  |---|---|---|
219
219
  | `VercelProvider` | — | `token` + `teamId` |
220
- | `VercelProject` | `.id`, `.url` | `.url` prefers custom domain over `*.vercel.app` |
220
+ | `VercelProject` | `.id`, `.url` | `.url` is a full `https://` URL; prefers the custom production domain over `<name>.vercel.app` |
221
221
  | `VercelVariable` | `.contentHash` | Use as a deploy trigger to redeploy on env var changes |
222
- | `VercelDeploy` | | Runs `vercel deploy --prod --yes` |
222
+ | `VercelDeploy` | `.deploymentUrl` | Runs `vercel deploy --prod --yes` |
223
223
  | `VercelIntegration` | `.configurationId` (`icfg_…`) | Resolves an installed marketplace integration by slug |
224
224
  | `VercelMarketplaceResource` | `.id`, `.externalResourceId`, `.status` | Provisions a marketplace store |
225
225
  | `VercelResourceConnection` | — | Wires a store to a project; injects env vars into target environments |
@@ -298,12 +298,13 @@ new FlyDeploy("api-deploy", {
298
298
 
299
299
  | Class | Key outputs | Notes |
300
300
  |---|---|---|
301
+ | `FlyProvider` | — | Pass as `provider` option to every Fly resource |
301
302
  | `FlyApp` | `.id` (app name) | Adopt-or-create |
302
303
  | `FlySecret` | `.version` | Feed into `FlyDeploy` triggers to redeploy on secret changes |
303
304
  | `FlyVolume` | `.id` (`vol_…`) | `sizeGb` can only grow |
304
305
  | `FlyCertificate` | `.id` (hostname), `.configured`, `.dnsRequirements` | `.dnsRequirements` contains ACME validation records |
305
306
  | `FlyIp` | `.id` (IP address) | `type`: `FlyIpType.V4`, `V6`, `SHARED_V4`, `PRIVATE_V6` |
306
- | `FlyDeploy` | | Writes fly.toml at deploy time; triggers on config + source hash |
307
+ | `FlyDeploy` | `.deploymentUrl` | Writes fly.toml at deploy time; triggers on config + source hash |
307
308
 
308
309
  **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`)
309
310
 
@@ -124,6 +124,8 @@ async function applyInstanceConfig(client, serviceId, environmentId, inputs) {
124
124
  * Uses adopt-or-create on `create()`: queries services by project ID and name
125
125
  * before creating a new one. Service instance configuration (builder, commands,
126
126
  * healthcheck) is applied via `serviceInstanceUpdate` after create or update.
127
+ *
128
+ * @internal Exported only for unit testing; not part of the public API surface.
127
129
  */
128
130
  var RailwayServiceResourceProvider = class {
129
131
  /**
@@ -136,6 +138,7 @@ var RailwayServiceResourceProvider = class {
136
138
  else {
137
139
  const createInput = {
138
140
  projectId: inputs.projectId,
141
+ environmentId: inputs.environmentId,
139
142
  name: inputs.name
140
143
  };
141
144
  if (inputs.source) createInput.source = { image: inputs.source.image };
@@ -277,4 +280,5 @@ var RailwayService = class extends _pulumi_pulumi.ComponentResource {
277
280
  exports.RailwayBuilder = RailwayBuilder;
278
281
  exports.RailwayRestartPolicy = RailwayRestartPolicy;
279
282
  exports.RailwayService = RailwayService;
283
+ exports.RailwayServiceResourceProvider = RailwayServiceResourceProvider;
280
284
  //# sourceMappingURL=service.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"service.cjs","names":["RailwayClient","pulumi"],"sources":["../../src/railway/service.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\nimport { RailwayClient } from \"./client\";\nimport type { RailwayEnvironment } from \"./environment\";\nimport type { RailwayProject } from \"./project\";\nimport type { RailwayProvider } from \"./provider\";\n\n/**\n * Railway build system. Enum keys are UPPERCASE per convention; values are\n * Railway's required UPPERCASE wire literals.\n * Note: HEROKU and PAKETO were deprecated Feb 21 2025 and auto-migrated to\n * NIXPACKS by Railway, but remain in the schema and are accepted by the API.\n */\nexport enum RailwayBuilder {\n\tRAILPACK = \"RAILPACK\",\n\tNIXPACKS = \"NIXPACKS\",\n\tDOCKERFILE = \"DOCKERFILE\",\n\tHEROKU = \"HEROKU\",\n\tPAKETO = \"PAKETO\",\n}\n\n/**\n * Railway service restart policy. Controls when Railway restarts the service\n * container after it exits. Default is ON_FAILURE.\n */\nexport enum RailwayRestartPolicy {\n\tON_FAILURE = \"ON_FAILURE\",\n\tALWAYS = \"ALWAYS\",\n\tNEVER = \"NEVER\",\n}\n\n/** Docker image source for a Railway service (e.g. `redis:8-alpine`). */\ninterface RailwayServiceSource {\n\t/** Full Docker image reference including tag. */\n\timage: string;\n}\n\n/** Resolved inputs for the Railway service dynamic provider. */\ninterface RailwayServiceInputs {\n\t/** Railway API bearer token. */\n\ttoken: string;\n\n\t/** Railway project UUID. */\n\tprojectId: string;\n\n\t/** Railway environment UUID (e.g. production). */\n\tenvironmentId: string;\n\n\t/** Human-readable service name used for adopt-or-create matching. */\n\tname: string;\n\n\t/** SVG icon URL displayed in the Railway dashboard. */\n\ticon?: string;\n\n\t/** Docker image source for image-based services. */\n\tsource?: RailwayServiceSource;\n\n\t/** Build system to use when building the service. */\n\tbuilder?: RailwayBuilder;\n\n\t/** Shell command executed during the build phase. */\n\tbuildCommand?: string;\n\n\t/** Shell command executed to start the service at runtime. */\n\tstartCommand?: string;\n\n\t/** Restart behavior for the service container. */\n\trestartPolicyType?: RailwayRestartPolicy;\n\n\t/** HTTP path polled for health checks (e.g. `\"/health-check\"`). */\n\thealthcheckPath?: string;\n\n\t/** Seconds to wait for a healthy response before marking unhealthy. */\n\thealthcheckTimeout?: number;\n\n\t/** Shell command executed before the main deploy (e.g. migrations). */\n\tpreDeployCommand?: string;\n}\n\n/** Persisted state for the Railway service, extending inputs with the Railway-assigned ID. */\ninterface RailwayServiceOutputs extends RailwayServiceInputs {\n\t/** Railway-assigned service UUID. */\n\tserviceId: string;\n}\n\nconst SERVICES_QUERY = `\n query($projectId: String!) {\n project(id: $projectId) {\n services {\n edges {\n node {\n id\n name\n }\n }\n }\n }\n }\n`;\n\nconst SERVICE_QUERY = `\n query($serviceId: String!) {\n service(id: $serviceId) {\n id\n name\n }\n }\n`;\n\nconst SERVICE_CREATE = `\n mutation($input: ServiceCreateInput!) {\n serviceCreate(input: $input) {\n id\n name\n }\n }\n`;\n\nconst SERVICE_UPDATE = `\n mutation($id: String!, $input: ServiceUpdateInput!) {\n serviceUpdate(id: $id, input: $input) {\n id\n name\n }\n }\n`;\n\nconst SERVICE_INSTANCE_UPDATE = `\n mutation(\n $serviceId: String!\n $environmentId: String!\n $input: ServiceInstanceUpdateInput!\n ) {\n serviceInstanceUpdate(\n serviceId: $serviceId\n environmentId: $environmentId\n input: $input\n )\n }\n`;\n\nconst SERVICE_CONNECT = `\n mutation($id: String!, $input: ServiceConnectInput!) {\n serviceConnect(id: $id, input: $input) {\n id\n }\n }\n`;\n\n/**\n * Applies service instance configuration (builder, commands, healthcheck).\n * Retries without healthcheck fields if the first call fails.\n */\nasync function applyInstanceConfig(\n\tclient: RailwayClient,\n\tserviceId: string,\n\tenvironmentId: string,\n\tinputs: RailwayServiceInputs,\n): Promise<void> {\n\tconst instanceInput: Record<string, unknown> = {};\n\n\tif (inputs.builder) {\n\t\tinstanceInput.builder = inputs.builder;\n\t}\n\n\tif (inputs.buildCommand) {\n\t\tinstanceInput.buildCommand = inputs.buildCommand;\n\t}\n\n\tif (inputs.startCommand) {\n\t\tinstanceInput.startCommand = inputs.startCommand;\n\t}\n\n\tif (inputs.restartPolicyType) {\n\t\tinstanceInput.restartPolicyType = inputs.restartPolicyType;\n\t}\n\n\tif (inputs.healthcheckPath) {\n\t\tinstanceInput.healthcheckPath = inputs.healthcheckPath;\n\t}\n\n\tif (inputs.healthcheckTimeout) {\n\t\tinstanceInput.healthcheckTimeout = inputs.healthcheckTimeout;\n\t}\n\n\tif (inputs.preDeployCommand) {\n\t\tinstanceInput.preDeployCommand = inputs.preDeployCommand;\n\t}\n\n\tif (Object.keys(instanceInput).length === 0) {\n\t\treturn;\n\t}\n\n\ttry {\n\t\tawait client.query(SERVICE_INSTANCE_UPDATE, {\n\t\t\tserviceId,\n\t\t\tenvironmentId,\n\t\t\tinput: instanceInput,\n\t\t});\n\t} catch (error) {\n\t\tpulumi.log.warn(\n\t\t\t`serviceInstanceUpdate failed, retrying without healthcheck fields: ${error}`,\n\t\t);\n\n\t\tdelete instanceInput.healthcheckPath;\n\t\tdelete instanceInput.healthcheckTimeout;\n\n\t\tif (Object.keys(instanceInput).length > 0) {\n\t\t\tawait client.query(SERVICE_INSTANCE_UPDATE, {\n\t\t\t\tserviceId,\n\t\t\t\tenvironmentId,\n\t\t\t\tinput: instanceInput,\n\t\t\t});\n\t\t}\n\t}\n}\n\n/**\n * Dynamic provider implementing CRUD for Railway services.\n *\n * Uses adopt-or-create on `create()`: queries services by project ID and name\n * before creating a new one. Service instance configuration (builder, commands,\n * healthcheck) is applied via `serviceInstanceUpdate` after create or update.\n */\nclass RailwayServiceResourceProvider\n\timplements pulumi.dynamic.ResourceProvider\n{\n\t/**\n\t * Creates or adopts a Railway service by name, then applies instance config.\n\t */\n\tasync create(\n\t\tinputs: RailwayServiceInputs,\n\t): Promise<pulumi.dynamic.CreateResult> {\n\t\tconst client = new RailwayClient(inputs.token);\n\n\t\tconst result = await client.query<{\n\t\t\tproject: {\n\t\t\t\tservices: { edges: Array<{ node: { id: string; name: string } }> };\n\t\t\t};\n\t\t}>(SERVICES_QUERY, { projectId: inputs.projectId });\n\n\t\tlet serviceId = result.project.services.edges.find(\n\t\t\t(edge) => edge.node.name === inputs.name,\n\t\t)?.node.id;\n\n\t\tif (serviceId) {\n\t\t\tpulumi.log.info(\n\t\t\t\t`Adopted existing Railway service \"${inputs.name}\" (${serviceId})`,\n\t\t\t);\n\t\t} else {\n\t\t\tconst createInput: Record<string, unknown> = {\n\t\t\t\tprojectId: inputs.projectId,\n\t\t\t\tname: inputs.name,\n\t\t\t};\n\n\t\t\tif (inputs.source) {\n\t\t\t\tcreateInput.source = { image: inputs.source.image };\n\t\t\t}\n\n\t\t\tconst created = await client.query<{\n\t\t\t\tserviceCreate: { id: string; name: string };\n\t\t\t}>(SERVICE_CREATE, { input: createInput });\n\n\t\t\tserviceId = created.serviceCreate.id;\n\n\t\t\tpulumi.log.info(\n\t\t\t\t`Created Railway service \"${inputs.name}\" (${serviceId})`,\n\t\t\t);\n\n\t\t\tif (inputs.source) {\n\t\t\t\tawait client.query(SERVICE_CONNECT, {\n\t\t\t\t\tid: serviceId,\n\t\t\t\t\tinput: { image: inputs.source.image },\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (inputs.icon) {\n\t\t\t\tawait client.query(SERVICE_UPDATE, {\n\t\t\t\t\tid: serviceId,\n\t\t\t\t\tinput: { icon: inputs.icon },\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tawait applyInstanceConfig(client, serviceId, inputs.environmentId, inputs);\n\n\t\tconst outs: RailwayServiceOutputs = { ...inputs, serviceId };\n\n\t\treturn { id: serviceId, outs };\n\t}\n\n\t/**\n\t * Updates service name/icon and re-applies instance configuration.\n\t */\n\tasync update(\n\t\tid: string,\n\t\tolds: RailwayServiceOutputs,\n\t\tnews: RailwayServiceInputs,\n\t): Promise<pulumi.dynamic.UpdateResult> {\n\t\tconst client = new RailwayClient(news.token);\n\n\t\tconst updateInput: Record<string, unknown> = {};\n\n\t\tif (olds.name !== news.name) {\n\t\t\tupdateInput.name = news.name;\n\t\t}\n\n\t\tif (news.icon && olds.icon !== news.icon) {\n\t\t\tupdateInput.icon = news.icon;\n\t\t}\n\n\t\tif (Object.keys(updateInput).length > 0) {\n\t\t\tawait client.query(SERVICE_UPDATE, { id, input: updateInput });\n\t\t}\n\n\t\tawait applyInstanceConfig(client, id, news.environmentId, news);\n\n\t\tconst outs: RailwayServiceOutputs = { ...news, serviceId: id };\n\n\t\treturn { outs };\n\t}\n\n\t/**\n\t * Reads current state for `pulumi refresh` by querying the service by ID.\n\t */\n\tasync read(\n\t\tid: string,\n\t\tprops: RailwayServiceOutputs,\n\t): Promise<pulumi.dynamic.ReadResult> {\n\t\tconst client = new RailwayClient(props.token);\n\n\t\ttry {\n\t\t\tawait client.query<{ service: { id: string; name: string } }>(\n\t\t\t\tSERVICE_QUERY,\n\t\t\t\t{ serviceId: id },\n\t\t\t);\n\t\t} catch {\n\t\t\tthrow new Error(`Railway service \"${props.name}\" (${id}) not found`);\n\t\t}\n\n\t\treturn { id, props: { ...props, serviceId: id } };\n\t}\n\n\t/**\n\t * Deletion is a no-op. A Railway service is a project-level resource shared\n\t * across environments (forked environments adopt it by name), so a single\n\t * environment's destroy must never delete it. Deleting the *environment*\n\t * removes that environment's service instances instead.\n\t */\n\tasync delete(): Promise<void> {\n\t\tpulumi.log.warn(\n\t\t\t\"Railway service deletion skipped — services are project-level; delete the environment to remove its instances\",\n\t\t);\n\t}\n\n\t/**\n\t * Compares old and new inputs to determine what changed.\n\t *\n\t * ProjectId and environmentId changes trigger replacement.\n\t */\n\tasync diff(\n\t\t_id: string,\n\t\tolds: RailwayServiceOutputs,\n\t\tnews: RailwayServiceInputs,\n\t): Promise<pulumi.dynamic.DiffResult> {\n\t\tconst replaces: string[] = [];\n\t\tconst changes: string[] = [];\n\n\t\tif (olds.name !== news.name) {\n\t\t\tchanges.push(\"name\");\n\t\t}\n\n\t\tif (olds.projectId !== news.projectId) {\n\t\t\treplaces.push(\"projectId\");\n\t\t}\n\n\t\tif (olds.environmentId !== news.environmentId) {\n\t\t\treplaces.push(\"environmentId\");\n\t\t}\n\n\t\tif (olds.builder !== news.builder) {\n\t\t\tchanges.push(\"builder\");\n\t\t}\n\n\t\tif (olds.buildCommand !== news.buildCommand) {\n\t\t\tchanges.push(\"buildCommand\");\n\t\t}\n\n\t\tif (olds.startCommand !== news.startCommand) {\n\t\t\tchanges.push(\"startCommand\");\n\t\t}\n\n\t\tif (olds.restartPolicyType !== news.restartPolicyType) {\n\t\t\tchanges.push(\"restartPolicyType\");\n\t\t}\n\n\t\tif (olds.healthcheckPath !== news.healthcheckPath) {\n\t\t\tchanges.push(\"healthcheckPath\");\n\t\t}\n\n\t\tif (olds.healthcheckTimeout !== news.healthcheckTimeout) {\n\t\t\tchanges.push(\"healthcheckTimeout\");\n\t\t}\n\n\t\tif (olds.preDeployCommand !== news.preDeployCommand) {\n\t\t\tchanges.push(\"preDeployCommand\");\n\t\t}\n\n\t\tif (olds.icon !== news.icon) {\n\t\t\tchanges.push(\"icon\");\n\t\t}\n\n\t\treturn {\n\t\t\tchanges: replaces.length > 0 || changes.length > 0,\n\t\t\treplaces,\n\t\t\tdeleteBeforeReplace: true,\n\t\t};\n\t}\n}\n\n/** Internal dynamic resource — not part of the public API. */\nclass RailwayServiceResource extends pulumi.dynamic.Resource {\n\tpublic declare readonly serviceId: 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\ticon?: pulumi.Input<string>;\n\t\t\tsource?: pulumi.Input<{ image: pulumi.Input<string> }>;\n\t\t\tbuilder?: pulumi.Input<RailwayBuilder>;\n\t\t\tbuildCommand?: pulumi.Input<string>;\n\t\t\tstartCommand?: pulumi.Input<string>;\n\t\t\trestartPolicyType?: pulumi.Input<RailwayRestartPolicy>;\n\t\t\thealthcheckPath?: pulumi.Input<string>;\n\t\t\thealthcheckTimeout?: pulumi.Input<number>;\n\t\t\tpreDeployCommand?: pulumi.Input<string>;\n\t\t},\n\t\topts?: pulumi.CustomResourceOptions,\n\t) {\n\t\tsuper(\n\t\t\tnew RailwayServiceResourceProvider(),\n\t\t\tname,\n\t\t\t{ ...args, serviceId: undefined },\n\t\t\topts,\n\t\t);\n\t}\n}\n\n/** Options type for RailwayService — replaces Pulumi's native `provider` field. */\ntype RailwayServiceOptions = Omit<\n\tpulumi.ComponentResourceOptions,\n\t\"provider\"\n> & {\n\t/** Railway authentication context. */\n\tprovider: RailwayProvider;\n\n\t/** Railway project context. */\n\tproject: RailwayProject;\n\n\t/** Railway environment context. */\n\tenvironment: RailwayEnvironment;\n};\n\n/** Args for RailwayService. */\nexport interface RailwayServiceArgs {\n\t/** Human-readable service name used for adopt-or-create matching. */\n\tname: pulumi.Input<string>;\n\n\t/** SVG icon URL displayed in the Railway dashboard. */\n\ticon?: pulumi.Input<string>;\n\n\t/** Docker image source for image-based services. */\n\tsource?: pulumi.Input<{ image: pulumi.Input<string> }>;\n\n\t/** Build system to use when building the service. */\n\tbuilder?: pulumi.Input<RailwayBuilder>;\n\n\t/** Shell command executed during the build phase. */\n\tbuildCommand?: pulumi.Input<string>;\n\n\t/** Shell command executed to start the service at runtime. */\n\tstartCommand?: pulumi.Input<string>;\n\n\t/** Restart behavior for the service container. */\n\trestartPolicyType?: pulumi.Input<RailwayRestartPolicy>;\n\n\t/** HTTP path polled for health checks (e.g. `\"/health-check\"`). */\n\thealthcheckPath?: pulumi.Input<string>;\n\n\t/** Seconds to wait for a healthy response before marking unhealthy. */\n\thealthcheckTimeout?: pulumi.Input<number>;\n\n\t/** Shell command executed before the main deploy (e.g. migrations). */\n\tpreDeployCommand?: pulumi.Input<string>;\n}\n\n/**\n * Manages a Railway service with adopt-or-create semantics.\n *\n * @example\n * ```typescript\n * const service = new RailwayService(\"api\", {\n * name: \"api\",\n * builder: RailwayBuilder.RAILPACK,\n * startCommand: \"node dist/index.js\",\n * healthcheckPath: \"/health\",\n * }, { provider, project, environment });\n *\n * // Use serviceId downstream\n * new RailwayVariable(\"api-vars\", {\n * variables: { DATABASE_URL: dbUrl },\n * }, { provider, project, environment, service });\n * ```\n */\nexport class RailwayService extends pulumi.ComponentResource {\n\t/** Railway service UUID. */\n\tpublic readonly id: pulumi.Output<string>;\n\n\tconstructor(\n\t\tname: string,\n\t\targs: RailwayServiceArgs,\n\t\topts: RailwayServiceOptions,\n\t) {\n\t\tconst { provider, project, environment, ...pulumiOpts } = opts;\n\n\t\tsuper(\"infracraft:railway:Service\", name, {}, pulumiOpts);\n\n\t\tconst resource = new RailwayServiceResource(\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\t...args,\n\t\t\t},\n\t\t\t{ parent: this },\n\t\t);\n\n\t\tthis.id = resource.serviceId;\n\n\t\tthis.registerOutputs({ id: this.id });\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"service.cjs","names":["RailwayClient","pulumi"],"sources":["../../src/railway/service.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\nimport { RailwayClient } from \"./client\";\nimport type { RailwayEnvironment } from \"./environment\";\nimport type { RailwayProject } from \"./project\";\nimport type { RailwayProvider } from \"./provider\";\n\n/**\n * Railway build system. Enum keys are UPPERCASE per convention; values are\n * Railway's required UPPERCASE wire literals.\n * Note: HEROKU and PAKETO were deprecated Feb 21 2025 and auto-migrated to\n * NIXPACKS by Railway, but remain in the schema and are accepted by the API.\n */\nexport enum RailwayBuilder {\n\tRAILPACK = \"RAILPACK\",\n\tNIXPACKS = \"NIXPACKS\",\n\tDOCKERFILE = \"DOCKERFILE\",\n\tHEROKU = \"HEROKU\",\n\tPAKETO = \"PAKETO\",\n}\n\n/**\n * Railway service restart policy. Controls when Railway restarts the service\n * container after it exits. Default is ON_FAILURE.\n */\nexport enum RailwayRestartPolicy {\n\tON_FAILURE = \"ON_FAILURE\",\n\tALWAYS = \"ALWAYS\",\n\tNEVER = \"NEVER\",\n}\n\n/** Docker image source for a Railway service (e.g. `redis:8-alpine`). */\ninterface RailwayServiceSource {\n\t/** Full Docker image reference including tag. */\n\timage: string;\n}\n\n/** Resolved inputs for the Railway service dynamic provider. */\ninterface RailwayServiceInputs {\n\t/** Railway API bearer token. */\n\ttoken: string;\n\n\t/** Railway project UUID. */\n\tprojectId: string;\n\n\t/** Railway environment UUID (e.g. production). */\n\tenvironmentId: string;\n\n\t/** Human-readable service name used for adopt-or-create matching. */\n\tname: string;\n\n\t/** SVG icon URL displayed in the Railway dashboard. */\n\ticon?: string;\n\n\t/** Docker image source for image-based services. */\n\tsource?: RailwayServiceSource;\n\n\t/** Build system to use when building the service. */\n\tbuilder?: RailwayBuilder;\n\n\t/** Shell command executed during the build phase. */\n\tbuildCommand?: string;\n\n\t/** Shell command executed to start the service at runtime. */\n\tstartCommand?: string;\n\n\t/** Restart behavior for the service container. */\n\trestartPolicyType?: RailwayRestartPolicy;\n\n\t/** HTTP path polled for health checks (e.g. `\"/health-check\"`). */\n\thealthcheckPath?: string;\n\n\t/** Seconds to wait for a healthy response before marking unhealthy. */\n\thealthcheckTimeout?: number;\n\n\t/** Shell command executed before the main deploy (e.g. migrations). */\n\tpreDeployCommand?: string;\n}\n\n/** Persisted state for the Railway service, extending inputs with the Railway-assigned ID. */\ninterface RailwayServiceOutputs extends RailwayServiceInputs {\n\t/** Railway-assigned service UUID. */\n\tserviceId: string;\n}\n\nconst SERVICES_QUERY = `\n query($projectId: String!) {\n project(id: $projectId) {\n services {\n edges {\n node {\n id\n name\n }\n }\n }\n }\n }\n`;\n\nconst SERVICE_QUERY = `\n query($serviceId: String!) {\n service(id: $serviceId) {\n id\n name\n }\n }\n`;\n\nconst SERVICE_CREATE = `\n mutation($input: ServiceCreateInput!) {\n serviceCreate(input: $input) {\n id\n name\n }\n }\n`;\n\nconst SERVICE_UPDATE = `\n mutation($id: String!, $input: ServiceUpdateInput!) {\n serviceUpdate(id: $id, input: $input) {\n id\n name\n }\n }\n`;\n\nconst SERVICE_INSTANCE_UPDATE = `\n mutation(\n $serviceId: String!\n $environmentId: String!\n $input: ServiceInstanceUpdateInput!\n ) {\n serviceInstanceUpdate(\n serviceId: $serviceId\n environmentId: $environmentId\n input: $input\n )\n }\n`;\n\nconst SERVICE_CONNECT = `\n mutation($id: String!, $input: ServiceConnectInput!) {\n serviceConnect(id: $id, input: $input) {\n id\n }\n }\n`;\n\n/**\n * Applies service instance configuration (builder, commands, healthcheck).\n * Retries without healthcheck fields if the first call fails.\n */\nasync function applyInstanceConfig(\n\tclient: RailwayClient,\n\tserviceId: string,\n\tenvironmentId: string,\n\tinputs: RailwayServiceInputs,\n): Promise<void> {\n\tconst instanceInput: Record<string, unknown> = {};\n\n\tif (inputs.builder) {\n\t\tinstanceInput.builder = inputs.builder;\n\t}\n\n\tif (inputs.buildCommand) {\n\t\tinstanceInput.buildCommand = inputs.buildCommand;\n\t}\n\n\tif (inputs.startCommand) {\n\t\tinstanceInput.startCommand = inputs.startCommand;\n\t}\n\n\tif (inputs.restartPolicyType) {\n\t\tinstanceInput.restartPolicyType = inputs.restartPolicyType;\n\t}\n\n\tif (inputs.healthcheckPath) {\n\t\tinstanceInput.healthcheckPath = inputs.healthcheckPath;\n\t}\n\n\tif (inputs.healthcheckTimeout) {\n\t\tinstanceInput.healthcheckTimeout = inputs.healthcheckTimeout;\n\t}\n\n\tif (inputs.preDeployCommand) {\n\t\tinstanceInput.preDeployCommand = inputs.preDeployCommand;\n\t}\n\n\tif (Object.keys(instanceInput).length === 0) {\n\t\treturn;\n\t}\n\n\ttry {\n\t\tawait client.query(SERVICE_INSTANCE_UPDATE, {\n\t\t\tserviceId,\n\t\t\tenvironmentId,\n\t\t\tinput: instanceInput,\n\t\t});\n\t} catch (error) {\n\t\tpulumi.log.warn(\n\t\t\t`serviceInstanceUpdate failed, retrying without healthcheck fields: ${error}`,\n\t\t);\n\n\t\tdelete instanceInput.healthcheckPath;\n\t\tdelete instanceInput.healthcheckTimeout;\n\n\t\tif (Object.keys(instanceInput).length > 0) {\n\t\t\tawait client.query(SERVICE_INSTANCE_UPDATE, {\n\t\t\t\tserviceId,\n\t\t\t\tenvironmentId,\n\t\t\t\tinput: instanceInput,\n\t\t\t});\n\t\t}\n\t}\n}\n\n/**\n * Dynamic provider implementing CRUD for Railway services.\n *\n * Uses adopt-or-create on `create()`: queries services by project ID and name\n * before creating a new one. Service instance configuration (builder, commands,\n * healthcheck) is applied via `serviceInstanceUpdate` after create or update.\n *\n * @internal Exported only for unit testing; not part of the public API surface.\n */\nexport class RailwayServiceResourceProvider\n\timplements pulumi.dynamic.ResourceProvider\n{\n\t/**\n\t * Creates or adopts a Railway service by name, then applies instance config.\n\t */\n\tasync create(\n\t\tinputs: RailwayServiceInputs,\n\t): Promise<pulumi.dynamic.CreateResult> {\n\t\tconst client = new RailwayClient(inputs.token);\n\n\t\tconst result = await client.query<{\n\t\t\tproject: {\n\t\t\t\tservices: { edges: Array<{ node: { id: string; name: string } }> };\n\t\t\t};\n\t\t}>(SERVICES_QUERY, { projectId: inputs.projectId });\n\n\t\tlet serviceId = result.project.services.edges.find(\n\t\t\t(edge) => edge.node.name === inputs.name,\n\t\t)?.node.id;\n\n\t\tif (serviceId) {\n\t\t\tpulumi.log.info(\n\t\t\t\t`Adopted existing Railway service \"${inputs.name}\" (${serviceId})`,\n\t\t\t);\n\t\t} else {\n\t\t\tconst createInput: Record<string, unknown> = {\n\t\t\t\tprojectId: inputs.projectId,\n\t\t\t\tenvironmentId: inputs.environmentId,\n\t\t\t\tname: inputs.name,\n\t\t\t};\n\n\t\t\tif (inputs.source) {\n\t\t\t\tcreateInput.source = { image: inputs.source.image };\n\t\t\t}\n\n\t\t\tconst created = await client.query<{\n\t\t\t\tserviceCreate: { id: string; name: string };\n\t\t\t}>(SERVICE_CREATE, { input: createInput });\n\n\t\t\tserviceId = created.serviceCreate.id;\n\n\t\t\tpulumi.log.info(\n\t\t\t\t`Created Railway service \"${inputs.name}\" (${serviceId})`,\n\t\t\t);\n\n\t\t\tif (inputs.source) {\n\t\t\t\tawait client.query(SERVICE_CONNECT, {\n\t\t\t\t\tid: serviceId,\n\t\t\t\t\tinput: { image: inputs.source.image },\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (inputs.icon) {\n\t\t\t\tawait client.query(SERVICE_UPDATE, {\n\t\t\t\t\tid: serviceId,\n\t\t\t\t\tinput: { icon: inputs.icon },\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tawait applyInstanceConfig(client, serviceId, inputs.environmentId, inputs);\n\n\t\tconst outs: RailwayServiceOutputs = { ...inputs, serviceId };\n\n\t\treturn { id: serviceId, outs };\n\t}\n\n\t/**\n\t * Updates service name/icon and re-applies instance configuration.\n\t */\n\tasync update(\n\t\tid: string,\n\t\tolds: RailwayServiceOutputs,\n\t\tnews: RailwayServiceInputs,\n\t): Promise<pulumi.dynamic.UpdateResult> {\n\t\tconst client = new RailwayClient(news.token);\n\n\t\tconst updateInput: Record<string, unknown> = {};\n\n\t\tif (olds.name !== news.name) {\n\t\t\tupdateInput.name = news.name;\n\t\t}\n\n\t\tif (news.icon && olds.icon !== news.icon) {\n\t\t\tupdateInput.icon = news.icon;\n\t\t}\n\n\t\tif (Object.keys(updateInput).length > 0) {\n\t\t\tawait client.query(SERVICE_UPDATE, { id, input: updateInput });\n\t\t}\n\n\t\tawait applyInstanceConfig(client, id, news.environmentId, news);\n\n\t\tconst outs: RailwayServiceOutputs = { ...news, serviceId: id };\n\n\t\treturn { outs };\n\t}\n\n\t/**\n\t * Reads current state for `pulumi refresh` by querying the service by ID.\n\t */\n\tasync read(\n\t\tid: string,\n\t\tprops: RailwayServiceOutputs,\n\t): Promise<pulumi.dynamic.ReadResult> {\n\t\tconst client = new RailwayClient(props.token);\n\n\t\ttry {\n\t\t\tawait client.query<{ service: { id: string; name: string } }>(\n\t\t\t\tSERVICE_QUERY,\n\t\t\t\t{ serviceId: id },\n\t\t\t);\n\t\t} catch {\n\t\t\tthrow new Error(`Railway service \"${props.name}\" (${id}) not found`);\n\t\t}\n\n\t\treturn { id, props: { ...props, serviceId: id } };\n\t}\n\n\t/**\n\t * Deletion is a no-op. A Railway service is a project-level resource shared\n\t * across environments (forked environments adopt it by name), so a single\n\t * environment's destroy must never delete it. Deleting the *environment*\n\t * removes that environment's service instances instead.\n\t */\n\tasync delete(): Promise<void> {\n\t\tpulumi.log.warn(\n\t\t\t\"Railway service deletion skipped — services are project-level; delete the environment to remove its instances\",\n\t\t);\n\t}\n\n\t/**\n\t * Compares old and new inputs to determine what changed.\n\t *\n\t * ProjectId and environmentId changes trigger replacement.\n\t */\n\tasync diff(\n\t\t_id: string,\n\t\tolds: RailwayServiceOutputs,\n\t\tnews: RailwayServiceInputs,\n\t): Promise<pulumi.dynamic.DiffResult> {\n\t\tconst replaces: string[] = [];\n\t\tconst changes: string[] = [];\n\n\t\tif (olds.name !== news.name) {\n\t\t\tchanges.push(\"name\");\n\t\t}\n\n\t\tif (olds.projectId !== news.projectId) {\n\t\t\treplaces.push(\"projectId\");\n\t\t}\n\n\t\tif (olds.environmentId !== news.environmentId) {\n\t\t\treplaces.push(\"environmentId\");\n\t\t}\n\n\t\tif (olds.builder !== news.builder) {\n\t\t\tchanges.push(\"builder\");\n\t\t}\n\n\t\tif (olds.buildCommand !== news.buildCommand) {\n\t\t\tchanges.push(\"buildCommand\");\n\t\t}\n\n\t\tif (olds.startCommand !== news.startCommand) {\n\t\t\tchanges.push(\"startCommand\");\n\t\t}\n\n\t\tif (olds.restartPolicyType !== news.restartPolicyType) {\n\t\t\tchanges.push(\"restartPolicyType\");\n\t\t}\n\n\t\tif (olds.healthcheckPath !== news.healthcheckPath) {\n\t\t\tchanges.push(\"healthcheckPath\");\n\t\t}\n\n\t\tif (olds.healthcheckTimeout !== news.healthcheckTimeout) {\n\t\t\tchanges.push(\"healthcheckTimeout\");\n\t\t}\n\n\t\tif (olds.preDeployCommand !== news.preDeployCommand) {\n\t\t\tchanges.push(\"preDeployCommand\");\n\t\t}\n\n\t\tif (olds.icon !== news.icon) {\n\t\t\tchanges.push(\"icon\");\n\t\t}\n\n\t\treturn {\n\t\t\tchanges: replaces.length > 0 || changes.length > 0,\n\t\t\treplaces,\n\t\t\tdeleteBeforeReplace: true,\n\t\t};\n\t}\n}\n\n/** Internal dynamic resource — not part of the public API. */\nclass RailwayServiceResource extends pulumi.dynamic.Resource {\n\tpublic declare readonly serviceId: 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\ticon?: pulumi.Input<string>;\n\t\t\tsource?: pulumi.Input<{ image: pulumi.Input<string> }>;\n\t\t\tbuilder?: pulumi.Input<RailwayBuilder>;\n\t\t\tbuildCommand?: pulumi.Input<string>;\n\t\t\tstartCommand?: pulumi.Input<string>;\n\t\t\trestartPolicyType?: pulumi.Input<RailwayRestartPolicy>;\n\t\t\thealthcheckPath?: pulumi.Input<string>;\n\t\t\thealthcheckTimeout?: pulumi.Input<number>;\n\t\t\tpreDeployCommand?: pulumi.Input<string>;\n\t\t},\n\t\topts?: pulumi.CustomResourceOptions,\n\t) {\n\t\tsuper(\n\t\t\tnew RailwayServiceResourceProvider(),\n\t\t\tname,\n\t\t\t{ ...args, serviceId: undefined },\n\t\t\topts,\n\t\t);\n\t}\n}\n\n/** Options type for RailwayService — replaces Pulumi's native `provider` field. */\ntype RailwayServiceOptions = Omit<\n\tpulumi.ComponentResourceOptions,\n\t\"provider\"\n> & {\n\t/** Railway authentication context. */\n\tprovider: RailwayProvider;\n\n\t/** Railway project context. */\n\tproject: RailwayProject;\n\n\t/** Railway environment context. */\n\tenvironment: RailwayEnvironment;\n};\n\n/** Args for RailwayService. */\nexport interface RailwayServiceArgs {\n\t/** Human-readable service name used for adopt-or-create matching. */\n\tname: pulumi.Input<string>;\n\n\t/** SVG icon URL displayed in the Railway dashboard. */\n\ticon?: pulumi.Input<string>;\n\n\t/** Docker image source for image-based services. */\n\tsource?: pulumi.Input<{ image: pulumi.Input<string> }>;\n\n\t/** Build system to use when building the service. */\n\tbuilder?: pulumi.Input<RailwayBuilder>;\n\n\t/** Shell command executed during the build phase. */\n\tbuildCommand?: pulumi.Input<string>;\n\n\t/** Shell command executed to start the service at runtime. */\n\tstartCommand?: pulumi.Input<string>;\n\n\t/** Restart behavior for the service container. */\n\trestartPolicyType?: pulumi.Input<RailwayRestartPolicy>;\n\n\t/** HTTP path polled for health checks (e.g. `\"/health-check\"`). */\n\thealthcheckPath?: pulumi.Input<string>;\n\n\t/** Seconds to wait for a healthy response before marking unhealthy. */\n\thealthcheckTimeout?: pulumi.Input<number>;\n\n\t/** Shell command executed before the main deploy (e.g. migrations). */\n\tpreDeployCommand?: pulumi.Input<string>;\n}\n\n/**\n * Manages a Railway service with adopt-or-create semantics.\n *\n * @example\n * ```typescript\n * const service = new RailwayService(\"api\", {\n * name: \"api\",\n * builder: RailwayBuilder.RAILPACK,\n * startCommand: \"node dist/index.js\",\n * healthcheckPath: \"/health\",\n * }, { provider, project, environment });\n *\n * // Use serviceId downstream\n * new RailwayVariable(\"api-vars\", {\n * variables: { DATABASE_URL: dbUrl },\n * }, { provider, project, environment, service });\n * ```\n */\nexport class RailwayService extends pulumi.ComponentResource {\n\t/** Railway service UUID. */\n\tpublic readonly id: pulumi.Output<string>;\n\n\tconstructor(\n\t\tname: string,\n\t\targs: RailwayServiceArgs,\n\t\topts: RailwayServiceOptions,\n\t) {\n\t\tconst { provider, project, environment, ...pulumiOpts } = opts;\n\n\t\tsuper(\"infracraft:railway:Service\", name, {}, pulumiOpts);\n\n\t\tconst resource = new RailwayServiceResource(\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\t...args,\n\t\t\t},\n\t\t\t{ parent: this },\n\t\t);\n\n\t\tthis.id = resource.serviceId;\n\n\t\tthis.registerOutputs({ id: this.id });\n\t}\n}\n"],"mappings":"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"}
@@ -27,6 +27,81 @@ declare enum RailwayRestartPolicy {
27
27
  ALWAYS = "ALWAYS",
28
28
  NEVER = "NEVER"
29
29
  }
30
+ /** Docker image source for a Railway service (e.g. `redis:8-alpine`). */
31
+ interface RailwayServiceSource {
32
+ /** Full Docker image reference including tag. */
33
+ image: string;
34
+ }
35
+ /** Resolved inputs for the Railway service dynamic provider. */
36
+ interface RailwayServiceInputs {
37
+ /** Railway API bearer token. */
38
+ token: string;
39
+ /** Railway project UUID. */
40
+ projectId: string;
41
+ /** Railway environment UUID (e.g. production). */
42
+ environmentId: string;
43
+ /** Human-readable service name used for adopt-or-create matching. */
44
+ name: string;
45
+ /** SVG icon URL displayed in the Railway dashboard. */
46
+ icon?: string;
47
+ /** Docker image source for image-based services. */
48
+ source?: RailwayServiceSource;
49
+ /** Build system to use when building the service. */
50
+ builder?: RailwayBuilder;
51
+ /** Shell command executed during the build phase. */
52
+ buildCommand?: string;
53
+ /** Shell command executed to start the service at runtime. */
54
+ startCommand?: string;
55
+ /** Restart behavior for the service container. */
56
+ restartPolicyType?: RailwayRestartPolicy;
57
+ /** HTTP path polled for health checks (e.g. `"/health-check"`). */
58
+ healthcheckPath?: string;
59
+ /** Seconds to wait for a healthy response before marking unhealthy. */
60
+ healthcheckTimeout?: number;
61
+ /** Shell command executed before the main deploy (e.g. migrations). */
62
+ preDeployCommand?: string;
63
+ }
64
+ /** Persisted state for the Railway service, extending inputs with the Railway-assigned ID. */
65
+ interface RailwayServiceOutputs extends RailwayServiceInputs {
66
+ /** Railway-assigned service UUID. */
67
+ serviceId: string;
68
+ }
69
+ /**
70
+ * Dynamic provider implementing CRUD for Railway services.
71
+ *
72
+ * Uses adopt-or-create on `create()`: queries services by project ID and name
73
+ * before creating a new one. Service instance configuration (builder, commands,
74
+ * healthcheck) is applied via `serviceInstanceUpdate` after create or update.
75
+ *
76
+ * @internal Exported only for unit testing; not part of the public API surface.
77
+ */
78
+ declare class RailwayServiceResourceProvider implements pulumi.dynamic.ResourceProvider {
79
+ /**
80
+ * Creates or adopts a Railway service by name, then applies instance config.
81
+ */
82
+ create(inputs: RailwayServiceInputs): Promise<pulumi.dynamic.CreateResult>;
83
+ /**
84
+ * Updates service name/icon and re-applies instance configuration.
85
+ */
86
+ update(id: string, olds: RailwayServiceOutputs, news: RailwayServiceInputs): Promise<pulumi.dynamic.UpdateResult>;
87
+ /**
88
+ * Reads current state for `pulumi refresh` by querying the service by ID.
89
+ */
90
+ read(id: string, props: RailwayServiceOutputs): Promise<pulumi.dynamic.ReadResult>;
91
+ /**
92
+ * Deletion is a no-op. A Railway service is a project-level resource shared
93
+ * across environments (forked environments adopt it by name), so a single
94
+ * environment's destroy must never delete it. Deleting the *environment*
95
+ * removes that environment's service instances instead.
96
+ */
97
+ delete(): Promise<void>;
98
+ /**
99
+ * Compares old and new inputs to determine what changed.
100
+ *
101
+ * ProjectId and environmentId changes trigger replacement.
102
+ */
103
+ diff(_id: string, olds: RailwayServiceOutputs, news: RailwayServiceInputs): Promise<pulumi.dynamic.DiffResult>;
104
+ }
30
105
  /** Options type for RailwayService — replaces Pulumi's native `provider` field. */
31
106
  type RailwayServiceOptions = Omit<pulumi.ComponentResourceOptions, "provider"> & {
32
107
  /** Railway authentication context. */provider: RailwayProvider; /** Railway project context. */
@@ -82,5 +157,5 @@ declare class RailwayService extends pulumi.ComponentResource {
82
157
  constructor(name: string, args: RailwayServiceArgs, opts: RailwayServiceOptions);
83
158
  }
84
159
  //#endregion
85
- export { RailwayBuilder, RailwayRestartPolicy, RailwayService, RailwayServiceArgs };
160
+ export { RailwayBuilder, RailwayRestartPolicy, RailwayService, RailwayServiceArgs, RailwayServiceResourceProvider };
86
161
  //# sourceMappingURL=service.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"service.d.cts","names":[],"sources":["../../src/railway/service.ts"],"mappings":";;;;;;;;;;AAYA;;;aAAY,cAAA;EACX,QAAA;EACA,QAAA;EACA,UAAA;EACA,MAAA;EACA,MAAA;AAAA;AAAM;AAOP;;;AAPO,aAOK,oBAAA;EACX,UAAA;EACA,MAAA;EACA,KAAA;AAAA;AAAK;AAAA,KAyaD,qBAAA,GAAwB,IAAA,CAC5B,MAAA,CAAO,wBAAA;EADkB,sCAKzB,QAAA,EAAU,eAAA,EAJV;EAOA,OAAA,EAAS,cAAA,EAHC;EAMV,WAAA,EAAa,kBAAA;AAAA;;UAIG,kBAAA;EAfY;EAiB5B,IAAA,EAAM,MAAA,CAAO,KAAA;EAhBN;EAmBP,IAAA,GAAO,MAAA,CAAO,KAAA;EAfJ;EAkBV,MAAA,GAAS,MAAA,CAAO,KAAA;IAAQ,KAAA,EAAO,MAAA,CAAO,KAAA;EAAA;EAZzB;EAeb,OAAA,GAAU,MAAA,CAAO,KAAA,CAAM,cAAA;EAfQ;EAkB/B,YAAA,GAAe,MAAA,CAAO,KAAA;EAdY;EAiBlC,YAAA,GAAe,MAAA,CAAO,KAAA;EAfhB;EAkBN,iBAAA,GAAoB,MAAA,CAAO,KAAA,CAAM,oBAAA;EAZF;EAe/B,eAAA,GAAkB,MAAA,CAAO,KAAA;EAZF;EAevB,kBAAA,GAAqB,MAAA,CAAO,KAAA;EAZb;EAef,gBAAA,GAAmB,MAAA,CAAO,KAAA;AAAA;;;;;;;;;;;;;;;;;;;cAqBd,cAAA,SAAuB,MAAA,CAAO,iBAAA;EAvChC;EAAA,SAyCM,EAAA,EAAI,MAAA,CAAO,MAAA;cAG1B,IAAA,UACA,IAAA,EAAM,kBAAA,EACN,IAAA,EAAM,qBAAA;AAAA"}
1
+ {"version":3,"file":"service.d.cts","names":[],"sources":["../../src/railway/service.ts"],"mappings":";;;;;;;;;;AAYA;;;aAAY,cAAA;EACX,QAAA;EACA,QAAA;EACA,UAAA;EACA,MAAA;EACA,MAAA;AAAA;AAAM;AAOP;;;AAPO,aAOK,oBAAA;EACX,UAAA;EACA,MAAA;EACA,KAAA;AAAA;AAAK;AAAA,UAII,oBAAA;EAAoB;EAE7B,KAAK;AAAA;AAAA;AAAA,UAII,oBAAA;EAAoB;EAE7B,KAAA;EAeS;EAZT,SAAA;EAwBoB;EArBpB,aAAA;EAqBwC;EAlBxC,IAAA;EANA;EASA,IAAA;EAHA;EAMA,MAAA,GAAS,oBAAA;EAAT;EAGA,OAAA,GAAU,cAAA;EAAV;EAGA,YAAA;EAAA;EAGA,YAAA;EAGA;EAAA,iBAAA,GAAoB,oBAAA;EAGpB;EAAA,eAAA;EAMA;EAHA,kBAAA;EAGgB;EAAhB,gBAAA;AAAA;;UAIS,qBAAA,SAA8B,oBAAoB;EAElD;EAAT,SAAS;AAAA;;;;;;;;;;cAgJG,8BAAA,YACD,MAAA,CAAO,OAAA,CAAQ,gBAAA;EAwGvB;;;EAnGG,MAAA,CACL,MAAA,EAAQ,oBAAA,GACN,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,YAAA;EAqIf;;;EAtEL,MAAA,CACL,EAAA,UACA,IAAA,EAAM,qBAAA,EACN,IAAA,EAAM,oBAAA,GACJ,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,YAAA;EA1EgB;;;EAqGpC,IAAA,CACL,EAAA,UACA,KAAA,EAAO,qBAAA,GACL,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,UAAA;EAnGpB;;;;;;EAwHA,MAAA,CAAA,GAAU,OAAA;EAvDV;;;;;EAkEA,IAAA,CACL,GAAA,UACA,IAAA,EAAM,qBAAA,EACN,IAAA,EAAM,oBAAA,GACJ,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,UAAA;AAAA;;KAyFtB,qBAAA,GAAwB,IAAA,CAC5B,MAAA,CAAO,wBAAA;EA5JmB,sCAgK1B,QAAA,EAAU,eAAA,EApIT;EAuID,OAAA,EAAS,cAAA,EAtIR;EAyID,WAAA,EAAa,kBAAA;AAAA;;UAIG,kBAAA;EAvHV;EAyHN,IAAA,EAAM,MAAA,CAAO,KAAA;EA9GP;EAiHN,IAAA,GAAO,MAAA,CAAO,KAAA;EA/GP;EAkHP,MAAA,GAAS,MAAA,CAAO,KAAA;IAAQ,KAAA,EAAO,MAAA,CAAO,KAAA;EAAA;EAhHnC;EAmHH,OAAA,GAAU,MAAA,CAAO,KAAA,CAAM,cAAA;EAnHL;EAsHlB,YAAA,GAAe,MAAA,CAAO,KAAA;EAtHc;EAyHpC,YAAA,GAAe,MAAA,CAAO,KAAA;EAhClB;EAmCJ,iBAAA,GAAoB,MAAA,CAAO,KAAA,CAAM,oBAAA;;EAGjC,eAAA,GAAkB,MAAA,CAAO,KAAA;EAtCG;EAyC5B,kBAAA,GAAqB,MAAA,CAAO,KAAA;EAjCnB;EAoCT,gBAAA,GAAmB,MAAA,CAAO,KAAA;AAAA;;;;;;;;;;;;AAjCK;AAIhC;;;;;;cAkDa,cAAA,SAAuB,MAAA,CAAO,iBAAA;EAvCnB;EAAA,SAyCP,EAAA,EAAI,MAAA,CAAO,MAAA;cAG1B,IAAA,UACA,IAAA,EAAM,kBAAA,EACN,IAAA,EAAM,qBAAA;AAAA"}
@@ -27,6 +27,81 @@ declare enum RailwayRestartPolicy {
27
27
  ALWAYS = "ALWAYS",
28
28
  NEVER = "NEVER"
29
29
  }
30
+ /** Docker image source for a Railway service (e.g. `redis:8-alpine`). */
31
+ interface RailwayServiceSource {
32
+ /** Full Docker image reference including tag. */
33
+ image: string;
34
+ }
35
+ /** Resolved inputs for the Railway service dynamic provider. */
36
+ interface RailwayServiceInputs {
37
+ /** Railway API bearer token. */
38
+ token: string;
39
+ /** Railway project UUID. */
40
+ projectId: string;
41
+ /** Railway environment UUID (e.g. production). */
42
+ environmentId: string;
43
+ /** Human-readable service name used for adopt-or-create matching. */
44
+ name: string;
45
+ /** SVG icon URL displayed in the Railway dashboard. */
46
+ icon?: string;
47
+ /** Docker image source for image-based services. */
48
+ source?: RailwayServiceSource;
49
+ /** Build system to use when building the service. */
50
+ builder?: RailwayBuilder;
51
+ /** Shell command executed during the build phase. */
52
+ buildCommand?: string;
53
+ /** Shell command executed to start the service at runtime. */
54
+ startCommand?: string;
55
+ /** Restart behavior for the service container. */
56
+ restartPolicyType?: RailwayRestartPolicy;
57
+ /** HTTP path polled for health checks (e.g. `"/health-check"`). */
58
+ healthcheckPath?: string;
59
+ /** Seconds to wait for a healthy response before marking unhealthy. */
60
+ healthcheckTimeout?: number;
61
+ /** Shell command executed before the main deploy (e.g. migrations). */
62
+ preDeployCommand?: string;
63
+ }
64
+ /** Persisted state for the Railway service, extending inputs with the Railway-assigned ID. */
65
+ interface RailwayServiceOutputs extends RailwayServiceInputs {
66
+ /** Railway-assigned service UUID. */
67
+ serviceId: string;
68
+ }
69
+ /**
70
+ * Dynamic provider implementing CRUD for Railway services.
71
+ *
72
+ * Uses adopt-or-create on `create()`: queries services by project ID and name
73
+ * before creating a new one. Service instance configuration (builder, commands,
74
+ * healthcheck) is applied via `serviceInstanceUpdate` after create or update.
75
+ *
76
+ * @internal Exported only for unit testing; not part of the public API surface.
77
+ */
78
+ declare class RailwayServiceResourceProvider implements pulumi.dynamic.ResourceProvider {
79
+ /**
80
+ * Creates or adopts a Railway service by name, then applies instance config.
81
+ */
82
+ create(inputs: RailwayServiceInputs): Promise<pulumi.dynamic.CreateResult>;
83
+ /**
84
+ * Updates service name/icon and re-applies instance configuration.
85
+ */
86
+ update(id: string, olds: RailwayServiceOutputs, news: RailwayServiceInputs): Promise<pulumi.dynamic.UpdateResult>;
87
+ /**
88
+ * Reads current state for `pulumi refresh` by querying the service by ID.
89
+ */
90
+ read(id: string, props: RailwayServiceOutputs): Promise<pulumi.dynamic.ReadResult>;
91
+ /**
92
+ * Deletion is a no-op. A Railway service is a project-level resource shared
93
+ * across environments (forked environments adopt it by name), so a single
94
+ * environment's destroy must never delete it. Deleting the *environment*
95
+ * removes that environment's service instances instead.
96
+ */
97
+ delete(): Promise<void>;
98
+ /**
99
+ * Compares old and new inputs to determine what changed.
100
+ *
101
+ * ProjectId and environmentId changes trigger replacement.
102
+ */
103
+ diff(_id: string, olds: RailwayServiceOutputs, news: RailwayServiceInputs): Promise<pulumi.dynamic.DiffResult>;
104
+ }
30
105
  /** Options type for RailwayService — replaces Pulumi's native `provider` field. */
31
106
  type RailwayServiceOptions = Omit<pulumi.ComponentResourceOptions, "provider"> & {
32
107
  /** Railway authentication context. */provider: RailwayProvider; /** Railway project context. */
@@ -82,5 +157,5 @@ declare class RailwayService extends pulumi.ComponentResource {
82
157
  constructor(name: string, args: RailwayServiceArgs, opts: RailwayServiceOptions);
83
158
  }
84
159
  //#endregion
85
- export { RailwayBuilder, RailwayRestartPolicy, RailwayService, RailwayServiceArgs };
160
+ export { RailwayBuilder, RailwayRestartPolicy, RailwayService, RailwayServiceArgs, RailwayServiceResourceProvider };
86
161
  //# sourceMappingURL=service.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"service.d.mts","names":[],"sources":["../../src/railway/service.ts"],"mappings":";;;;;;;;;;AAYA;;;aAAY,cAAA;EACX,QAAA;EACA,QAAA;EACA,UAAA;EACA,MAAA;EACA,MAAA;AAAA;AAAM;AAOP;;;AAPO,aAOK,oBAAA;EACX,UAAA;EACA,MAAA;EACA,KAAA;AAAA;AAAK;AAAA,KAyaD,qBAAA,GAAwB,IAAA,CAC5B,MAAA,CAAO,wBAAA;EADkB,sCAKzB,QAAA,EAAU,eAAA,EAJV;EAOA,OAAA,EAAS,cAAA,EAHC;EAMV,WAAA,EAAa,kBAAA;AAAA;;UAIG,kBAAA;EAfY;EAiB5B,IAAA,EAAM,MAAA,CAAO,KAAA;EAhBN;EAmBP,IAAA,GAAO,MAAA,CAAO,KAAA;EAfJ;EAkBV,MAAA,GAAS,MAAA,CAAO,KAAA;IAAQ,KAAA,EAAO,MAAA,CAAO,KAAA;EAAA;EAZzB;EAeb,OAAA,GAAU,MAAA,CAAO,KAAA,CAAM,cAAA;EAfQ;EAkB/B,YAAA,GAAe,MAAA,CAAO,KAAA;EAdY;EAiBlC,YAAA,GAAe,MAAA,CAAO,KAAA;EAfhB;EAkBN,iBAAA,GAAoB,MAAA,CAAO,KAAA,CAAM,oBAAA;EAZF;EAe/B,eAAA,GAAkB,MAAA,CAAO,KAAA;EAZF;EAevB,kBAAA,GAAqB,MAAA,CAAO,KAAA;EAZb;EAef,gBAAA,GAAmB,MAAA,CAAO,KAAA;AAAA;;;;;;;;;;;;;;;;;;;cAqBd,cAAA,SAAuB,MAAA,CAAO,iBAAA;EAvChC;EAAA,SAyCM,EAAA,EAAI,MAAA,CAAO,MAAA;cAG1B,IAAA,UACA,IAAA,EAAM,kBAAA,EACN,IAAA,EAAM,qBAAA;AAAA"}
1
+ {"version":3,"file":"service.d.mts","names":[],"sources":["../../src/railway/service.ts"],"mappings":";;;;;;;;;;AAYA;;;aAAY,cAAA;EACX,QAAA;EACA,QAAA;EACA,UAAA;EACA,MAAA;EACA,MAAA;AAAA;AAAM;AAOP;;;AAPO,aAOK,oBAAA;EACX,UAAA;EACA,MAAA;EACA,KAAA;AAAA;AAAK;AAAA,UAII,oBAAA;EAAoB;EAE7B,KAAK;AAAA;AAAA;AAAA,UAII,oBAAA;EAAoB;EAE7B,KAAA;EAeS;EAZT,SAAA;EAwBoB;EArBpB,aAAA;EAqBwC;EAlBxC,IAAA;EANA;EASA,IAAA;EAHA;EAMA,MAAA,GAAS,oBAAA;EAAT;EAGA,OAAA,GAAU,cAAA;EAAV;EAGA,YAAA;EAAA;EAGA,YAAA;EAGA;EAAA,iBAAA,GAAoB,oBAAA;EAGpB;EAAA,eAAA;EAMA;EAHA,kBAAA;EAGgB;EAAhB,gBAAA;AAAA;;UAIS,qBAAA,SAA8B,oBAAoB;EAElD;EAAT,SAAS;AAAA;;;;;;;;;;cAgJG,8BAAA,YACD,MAAA,CAAO,OAAA,CAAQ,gBAAA;EAwGvB;;;EAnGG,MAAA,CACL,MAAA,EAAQ,oBAAA,GACN,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,YAAA;EAqIf;;;EAtEL,MAAA,CACL,EAAA,UACA,IAAA,EAAM,qBAAA,EACN,IAAA,EAAM,oBAAA,GACJ,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,YAAA;EA1EgB;;;EAqGpC,IAAA,CACL,EAAA,UACA,KAAA,EAAO,qBAAA,GACL,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,UAAA;EAnGpB;;;;;;EAwHA,MAAA,CAAA,GAAU,OAAA;EAvDV;;;;;EAkEA,IAAA,CACL,GAAA,UACA,IAAA,EAAM,qBAAA,EACN,IAAA,EAAM,oBAAA,GACJ,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,UAAA;AAAA;;KAyFtB,qBAAA,GAAwB,IAAA,CAC5B,MAAA,CAAO,wBAAA;EA5JmB,sCAgK1B,QAAA,EAAU,eAAA,EApIT;EAuID,OAAA,EAAS,cAAA,EAtIR;EAyID,WAAA,EAAa,kBAAA;AAAA;;UAIG,kBAAA;EAvHV;EAyHN,IAAA,EAAM,MAAA,CAAO,KAAA;EA9GP;EAiHN,IAAA,GAAO,MAAA,CAAO,KAAA;EA/GP;EAkHP,MAAA,GAAS,MAAA,CAAO,KAAA;IAAQ,KAAA,EAAO,MAAA,CAAO,KAAA;EAAA;EAhHnC;EAmHH,OAAA,GAAU,MAAA,CAAO,KAAA,CAAM,cAAA;EAnHL;EAsHlB,YAAA,GAAe,MAAA,CAAO,KAAA;EAtHc;EAyHpC,YAAA,GAAe,MAAA,CAAO,KAAA;EAhClB;EAmCJ,iBAAA,GAAoB,MAAA,CAAO,KAAA,CAAM,oBAAA;;EAGjC,eAAA,GAAkB,MAAA,CAAO,KAAA;EAtCG;EAyC5B,kBAAA,GAAqB,MAAA,CAAO,KAAA;EAjCnB;EAoCT,gBAAA,GAAmB,MAAA,CAAO,KAAA;AAAA;;;;;;;;;;;;AAjCK;AAIhC;;;;;;cAkDa,cAAA,SAAuB,MAAA,CAAO,iBAAA;EAvCnB;EAAA,SAyCP,EAAA,EAAI,MAAA,CAAO,MAAA;cAG1B,IAAA,UACA,IAAA,EAAM,kBAAA,EACN,IAAA,EAAM,qBAAA;AAAA"}
@@ -122,6 +122,8 @@ async function applyInstanceConfig(client, serviceId, environmentId, inputs) {
122
122
  * Uses adopt-or-create on `create()`: queries services by project ID and name
123
123
  * before creating a new one. Service instance configuration (builder, commands,
124
124
  * healthcheck) is applied via `serviceInstanceUpdate` after create or update.
125
+ *
126
+ * @internal Exported only for unit testing; not part of the public API surface.
125
127
  */
126
128
  var RailwayServiceResourceProvider = class {
127
129
  /**
@@ -134,6 +136,7 @@ var RailwayServiceResourceProvider = class {
134
136
  else {
135
137
  const createInput = {
136
138
  projectId: inputs.projectId,
139
+ environmentId: inputs.environmentId,
137
140
  name: inputs.name
138
141
  };
139
142
  if (inputs.source) createInput.source = { image: inputs.source.image };
@@ -272,5 +275,5 @@ var RailwayService = class extends pulumi.ComponentResource {
272
275
  };
273
276
 
274
277
  //#endregion
275
- export { RailwayBuilder, RailwayRestartPolicy, RailwayService };
278
+ export { RailwayBuilder, RailwayRestartPolicy, RailwayService, RailwayServiceResourceProvider };
276
279
  //# sourceMappingURL=service.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"service.mjs","names":[],"sources":["../../src/railway/service.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\nimport { RailwayClient } from \"./client\";\nimport type { RailwayEnvironment } from \"./environment\";\nimport type { RailwayProject } from \"./project\";\nimport type { RailwayProvider } from \"./provider\";\n\n/**\n * Railway build system. Enum keys are UPPERCASE per convention; values are\n * Railway's required UPPERCASE wire literals.\n * Note: HEROKU and PAKETO were deprecated Feb 21 2025 and auto-migrated to\n * NIXPACKS by Railway, but remain in the schema and are accepted by the API.\n */\nexport enum RailwayBuilder {\n\tRAILPACK = \"RAILPACK\",\n\tNIXPACKS = \"NIXPACKS\",\n\tDOCKERFILE = \"DOCKERFILE\",\n\tHEROKU = \"HEROKU\",\n\tPAKETO = \"PAKETO\",\n}\n\n/**\n * Railway service restart policy. Controls when Railway restarts the service\n * container after it exits. Default is ON_FAILURE.\n */\nexport enum RailwayRestartPolicy {\n\tON_FAILURE = \"ON_FAILURE\",\n\tALWAYS = \"ALWAYS\",\n\tNEVER = \"NEVER\",\n}\n\n/** Docker image source for a Railway service (e.g. `redis:8-alpine`). */\ninterface RailwayServiceSource {\n\t/** Full Docker image reference including tag. */\n\timage: string;\n}\n\n/** Resolved inputs for the Railway service dynamic provider. */\ninterface RailwayServiceInputs {\n\t/** Railway API bearer token. */\n\ttoken: string;\n\n\t/** Railway project UUID. */\n\tprojectId: string;\n\n\t/** Railway environment UUID (e.g. production). */\n\tenvironmentId: string;\n\n\t/** Human-readable service name used for adopt-or-create matching. */\n\tname: string;\n\n\t/** SVG icon URL displayed in the Railway dashboard. */\n\ticon?: string;\n\n\t/** Docker image source for image-based services. */\n\tsource?: RailwayServiceSource;\n\n\t/** Build system to use when building the service. */\n\tbuilder?: RailwayBuilder;\n\n\t/** Shell command executed during the build phase. */\n\tbuildCommand?: string;\n\n\t/** Shell command executed to start the service at runtime. */\n\tstartCommand?: string;\n\n\t/** Restart behavior for the service container. */\n\trestartPolicyType?: RailwayRestartPolicy;\n\n\t/** HTTP path polled for health checks (e.g. `\"/health-check\"`). */\n\thealthcheckPath?: string;\n\n\t/** Seconds to wait for a healthy response before marking unhealthy. */\n\thealthcheckTimeout?: number;\n\n\t/** Shell command executed before the main deploy (e.g. migrations). */\n\tpreDeployCommand?: string;\n}\n\n/** Persisted state for the Railway service, extending inputs with the Railway-assigned ID. */\ninterface RailwayServiceOutputs extends RailwayServiceInputs {\n\t/** Railway-assigned service UUID. */\n\tserviceId: string;\n}\n\nconst SERVICES_QUERY = `\n query($projectId: String!) {\n project(id: $projectId) {\n services {\n edges {\n node {\n id\n name\n }\n }\n }\n }\n }\n`;\n\nconst SERVICE_QUERY = `\n query($serviceId: String!) {\n service(id: $serviceId) {\n id\n name\n }\n }\n`;\n\nconst SERVICE_CREATE = `\n mutation($input: ServiceCreateInput!) {\n serviceCreate(input: $input) {\n id\n name\n }\n }\n`;\n\nconst SERVICE_UPDATE = `\n mutation($id: String!, $input: ServiceUpdateInput!) {\n serviceUpdate(id: $id, input: $input) {\n id\n name\n }\n }\n`;\n\nconst SERVICE_INSTANCE_UPDATE = `\n mutation(\n $serviceId: String!\n $environmentId: String!\n $input: ServiceInstanceUpdateInput!\n ) {\n serviceInstanceUpdate(\n serviceId: $serviceId\n environmentId: $environmentId\n input: $input\n )\n }\n`;\n\nconst SERVICE_CONNECT = `\n mutation($id: String!, $input: ServiceConnectInput!) {\n serviceConnect(id: $id, input: $input) {\n id\n }\n }\n`;\n\n/**\n * Applies service instance configuration (builder, commands, healthcheck).\n * Retries without healthcheck fields if the first call fails.\n */\nasync function applyInstanceConfig(\n\tclient: RailwayClient,\n\tserviceId: string,\n\tenvironmentId: string,\n\tinputs: RailwayServiceInputs,\n): Promise<void> {\n\tconst instanceInput: Record<string, unknown> = {};\n\n\tif (inputs.builder) {\n\t\tinstanceInput.builder = inputs.builder;\n\t}\n\n\tif (inputs.buildCommand) {\n\t\tinstanceInput.buildCommand = inputs.buildCommand;\n\t}\n\n\tif (inputs.startCommand) {\n\t\tinstanceInput.startCommand = inputs.startCommand;\n\t}\n\n\tif (inputs.restartPolicyType) {\n\t\tinstanceInput.restartPolicyType = inputs.restartPolicyType;\n\t}\n\n\tif (inputs.healthcheckPath) {\n\t\tinstanceInput.healthcheckPath = inputs.healthcheckPath;\n\t}\n\n\tif (inputs.healthcheckTimeout) {\n\t\tinstanceInput.healthcheckTimeout = inputs.healthcheckTimeout;\n\t}\n\n\tif (inputs.preDeployCommand) {\n\t\tinstanceInput.preDeployCommand = inputs.preDeployCommand;\n\t}\n\n\tif (Object.keys(instanceInput).length === 0) {\n\t\treturn;\n\t}\n\n\ttry {\n\t\tawait client.query(SERVICE_INSTANCE_UPDATE, {\n\t\t\tserviceId,\n\t\t\tenvironmentId,\n\t\t\tinput: instanceInput,\n\t\t});\n\t} catch (error) {\n\t\tpulumi.log.warn(\n\t\t\t`serviceInstanceUpdate failed, retrying without healthcheck fields: ${error}`,\n\t\t);\n\n\t\tdelete instanceInput.healthcheckPath;\n\t\tdelete instanceInput.healthcheckTimeout;\n\n\t\tif (Object.keys(instanceInput).length > 0) {\n\t\t\tawait client.query(SERVICE_INSTANCE_UPDATE, {\n\t\t\t\tserviceId,\n\t\t\t\tenvironmentId,\n\t\t\t\tinput: instanceInput,\n\t\t\t});\n\t\t}\n\t}\n}\n\n/**\n * Dynamic provider implementing CRUD for Railway services.\n *\n * Uses adopt-or-create on `create()`: queries services by project ID and name\n * before creating a new one. Service instance configuration (builder, commands,\n * healthcheck) is applied via `serviceInstanceUpdate` after create or update.\n */\nclass RailwayServiceResourceProvider\n\timplements pulumi.dynamic.ResourceProvider\n{\n\t/**\n\t * Creates or adopts a Railway service by name, then applies instance config.\n\t */\n\tasync create(\n\t\tinputs: RailwayServiceInputs,\n\t): Promise<pulumi.dynamic.CreateResult> {\n\t\tconst client = new RailwayClient(inputs.token);\n\n\t\tconst result = await client.query<{\n\t\t\tproject: {\n\t\t\t\tservices: { edges: Array<{ node: { id: string; name: string } }> };\n\t\t\t};\n\t\t}>(SERVICES_QUERY, { projectId: inputs.projectId });\n\n\t\tlet serviceId = result.project.services.edges.find(\n\t\t\t(edge) => edge.node.name === inputs.name,\n\t\t)?.node.id;\n\n\t\tif (serviceId) {\n\t\t\tpulumi.log.info(\n\t\t\t\t`Adopted existing Railway service \"${inputs.name}\" (${serviceId})`,\n\t\t\t);\n\t\t} else {\n\t\t\tconst createInput: Record<string, unknown> = {\n\t\t\t\tprojectId: inputs.projectId,\n\t\t\t\tname: inputs.name,\n\t\t\t};\n\n\t\t\tif (inputs.source) {\n\t\t\t\tcreateInput.source = { image: inputs.source.image };\n\t\t\t}\n\n\t\t\tconst created = await client.query<{\n\t\t\t\tserviceCreate: { id: string; name: string };\n\t\t\t}>(SERVICE_CREATE, { input: createInput });\n\n\t\t\tserviceId = created.serviceCreate.id;\n\n\t\t\tpulumi.log.info(\n\t\t\t\t`Created Railway service \"${inputs.name}\" (${serviceId})`,\n\t\t\t);\n\n\t\t\tif (inputs.source) {\n\t\t\t\tawait client.query(SERVICE_CONNECT, {\n\t\t\t\t\tid: serviceId,\n\t\t\t\t\tinput: { image: inputs.source.image },\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (inputs.icon) {\n\t\t\t\tawait client.query(SERVICE_UPDATE, {\n\t\t\t\t\tid: serviceId,\n\t\t\t\t\tinput: { icon: inputs.icon },\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tawait applyInstanceConfig(client, serviceId, inputs.environmentId, inputs);\n\n\t\tconst outs: RailwayServiceOutputs = { ...inputs, serviceId };\n\n\t\treturn { id: serviceId, outs };\n\t}\n\n\t/**\n\t * Updates service name/icon and re-applies instance configuration.\n\t */\n\tasync update(\n\t\tid: string,\n\t\tolds: RailwayServiceOutputs,\n\t\tnews: RailwayServiceInputs,\n\t): Promise<pulumi.dynamic.UpdateResult> {\n\t\tconst client = new RailwayClient(news.token);\n\n\t\tconst updateInput: Record<string, unknown> = {};\n\n\t\tif (olds.name !== news.name) {\n\t\t\tupdateInput.name = news.name;\n\t\t}\n\n\t\tif (news.icon && olds.icon !== news.icon) {\n\t\t\tupdateInput.icon = news.icon;\n\t\t}\n\n\t\tif (Object.keys(updateInput).length > 0) {\n\t\t\tawait client.query(SERVICE_UPDATE, { id, input: updateInput });\n\t\t}\n\n\t\tawait applyInstanceConfig(client, id, news.environmentId, news);\n\n\t\tconst outs: RailwayServiceOutputs = { ...news, serviceId: id };\n\n\t\treturn { outs };\n\t}\n\n\t/**\n\t * Reads current state for `pulumi refresh` by querying the service by ID.\n\t */\n\tasync read(\n\t\tid: string,\n\t\tprops: RailwayServiceOutputs,\n\t): Promise<pulumi.dynamic.ReadResult> {\n\t\tconst client = new RailwayClient(props.token);\n\n\t\ttry {\n\t\t\tawait client.query<{ service: { id: string; name: string } }>(\n\t\t\t\tSERVICE_QUERY,\n\t\t\t\t{ serviceId: id },\n\t\t\t);\n\t\t} catch {\n\t\t\tthrow new Error(`Railway service \"${props.name}\" (${id}) not found`);\n\t\t}\n\n\t\treturn { id, props: { ...props, serviceId: id } };\n\t}\n\n\t/**\n\t * Deletion is a no-op. A Railway service is a project-level resource shared\n\t * across environments (forked environments adopt it by name), so a single\n\t * environment's destroy must never delete it. Deleting the *environment*\n\t * removes that environment's service instances instead.\n\t */\n\tasync delete(): Promise<void> {\n\t\tpulumi.log.warn(\n\t\t\t\"Railway service deletion skipped — services are project-level; delete the environment to remove its instances\",\n\t\t);\n\t}\n\n\t/**\n\t * Compares old and new inputs to determine what changed.\n\t *\n\t * ProjectId and environmentId changes trigger replacement.\n\t */\n\tasync diff(\n\t\t_id: string,\n\t\tolds: RailwayServiceOutputs,\n\t\tnews: RailwayServiceInputs,\n\t): Promise<pulumi.dynamic.DiffResult> {\n\t\tconst replaces: string[] = [];\n\t\tconst changes: string[] = [];\n\n\t\tif (olds.name !== news.name) {\n\t\t\tchanges.push(\"name\");\n\t\t}\n\n\t\tif (olds.projectId !== news.projectId) {\n\t\t\treplaces.push(\"projectId\");\n\t\t}\n\n\t\tif (olds.environmentId !== news.environmentId) {\n\t\t\treplaces.push(\"environmentId\");\n\t\t}\n\n\t\tif (olds.builder !== news.builder) {\n\t\t\tchanges.push(\"builder\");\n\t\t}\n\n\t\tif (olds.buildCommand !== news.buildCommand) {\n\t\t\tchanges.push(\"buildCommand\");\n\t\t}\n\n\t\tif (olds.startCommand !== news.startCommand) {\n\t\t\tchanges.push(\"startCommand\");\n\t\t}\n\n\t\tif (olds.restartPolicyType !== news.restartPolicyType) {\n\t\t\tchanges.push(\"restartPolicyType\");\n\t\t}\n\n\t\tif (olds.healthcheckPath !== news.healthcheckPath) {\n\t\t\tchanges.push(\"healthcheckPath\");\n\t\t}\n\n\t\tif (olds.healthcheckTimeout !== news.healthcheckTimeout) {\n\t\t\tchanges.push(\"healthcheckTimeout\");\n\t\t}\n\n\t\tif (olds.preDeployCommand !== news.preDeployCommand) {\n\t\t\tchanges.push(\"preDeployCommand\");\n\t\t}\n\n\t\tif (olds.icon !== news.icon) {\n\t\t\tchanges.push(\"icon\");\n\t\t}\n\n\t\treturn {\n\t\t\tchanges: replaces.length > 0 || changes.length > 0,\n\t\t\treplaces,\n\t\t\tdeleteBeforeReplace: true,\n\t\t};\n\t}\n}\n\n/** Internal dynamic resource — not part of the public API. */\nclass RailwayServiceResource extends pulumi.dynamic.Resource {\n\tpublic declare readonly serviceId: 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\ticon?: pulumi.Input<string>;\n\t\t\tsource?: pulumi.Input<{ image: pulumi.Input<string> }>;\n\t\t\tbuilder?: pulumi.Input<RailwayBuilder>;\n\t\t\tbuildCommand?: pulumi.Input<string>;\n\t\t\tstartCommand?: pulumi.Input<string>;\n\t\t\trestartPolicyType?: pulumi.Input<RailwayRestartPolicy>;\n\t\t\thealthcheckPath?: pulumi.Input<string>;\n\t\t\thealthcheckTimeout?: pulumi.Input<number>;\n\t\t\tpreDeployCommand?: pulumi.Input<string>;\n\t\t},\n\t\topts?: pulumi.CustomResourceOptions,\n\t) {\n\t\tsuper(\n\t\t\tnew RailwayServiceResourceProvider(),\n\t\t\tname,\n\t\t\t{ ...args, serviceId: undefined },\n\t\t\topts,\n\t\t);\n\t}\n}\n\n/** Options type for RailwayService — replaces Pulumi's native `provider` field. */\ntype RailwayServiceOptions = Omit<\n\tpulumi.ComponentResourceOptions,\n\t\"provider\"\n> & {\n\t/** Railway authentication context. */\n\tprovider: RailwayProvider;\n\n\t/** Railway project context. */\n\tproject: RailwayProject;\n\n\t/** Railway environment context. */\n\tenvironment: RailwayEnvironment;\n};\n\n/** Args for RailwayService. */\nexport interface RailwayServiceArgs {\n\t/** Human-readable service name used for adopt-or-create matching. */\n\tname: pulumi.Input<string>;\n\n\t/** SVG icon URL displayed in the Railway dashboard. */\n\ticon?: pulumi.Input<string>;\n\n\t/** Docker image source for image-based services. */\n\tsource?: pulumi.Input<{ image: pulumi.Input<string> }>;\n\n\t/** Build system to use when building the service. */\n\tbuilder?: pulumi.Input<RailwayBuilder>;\n\n\t/** Shell command executed during the build phase. */\n\tbuildCommand?: pulumi.Input<string>;\n\n\t/** Shell command executed to start the service at runtime. */\n\tstartCommand?: pulumi.Input<string>;\n\n\t/** Restart behavior for the service container. */\n\trestartPolicyType?: pulumi.Input<RailwayRestartPolicy>;\n\n\t/** HTTP path polled for health checks (e.g. `\"/health-check\"`). */\n\thealthcheckPath?: pulumi.Input<string>;\n\n\t/** Seconds to wait for a healthy response before marking unhealthy. */\n\thealthcheckTimeout?: pulumi.Input<number>;\n\n\t/** Shell command executed before the main deploy (e.g. migrations). */\n\tpreDeployCommand?: pulumi.Input<string>;\n}\n\n/**\n * Manages a Railway service with adopt-or-create semantics.\n *\n * @example\n * ```typescript\n * const service = new RailwayService(\"api\", {\n * name: \"api\",\n * builder: RailwayBuilder.RAILPACK,\n * startCommand: \"node dist/index.js\",\n * healthcheckPath: \"/health\",\n * }, { provider, project, environment });\n *\n * // Use serviceId downstream\n * new RailwayVariable(\"api-vars\", {\n * variables: { DATABASE_URL: dbUrl },\n * }, { provider, project, environment, service });\n * ```\n */\nexport class RailwayService extends pulumi.ComponentResource {\n\t/** Railway service UUID. */\n\tpublic readonly id: pulumi.Output<string>;\n\n\tconstructor(\n\t\tname: string,\n\t\targs: RailwayServiceArgs,\n\t\topts: RailwayServiceOptions,\n\t) {\n\t\tconst { provider, project, environment, ...pulumiOpts } = opts;\n\n\t\tsuper(\"infracraft:railway:Service\", name, {}, pulumiOpts);\n\n\t\tconst resource = new RailwayServiceResource(\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\t...args,\n\t\t\t},\n\t\t\t{ parent: this },\n\t\t);\n\n\t\tthis.id = resource.serviceId;\n\n\t\tthis.registerOutputs({ id: this.id });\n\t}\n}\n"],"mappings":";;;;;;;;;;;AAYA,IAAY,iBAAL;CACN;CACA;CACA;CACA;CACA;;AACD;;;;;AAMA,IAAY,uBAAL;CACN;CACA;CACA;;AACD;AAwDA,MAAM,iBAAiB;;;;;;;;;;;;;;AAevB,MAAM,gBAAgB;;;;;;;;AAStB,MAAM,iBAAiB;;;;;;;;AASvB,MAAM,iBAAiB;;;;;;;;AASvB,MAAM,0BAA0B;;;;;;;;;;;;;AAchC,MAAM,kBAAkB;;;;;;;;;;;AAYxB,eAAe,oBACd,QACA,WACA,eACA,QACgB;CAChB,MAAM,gBAAyC,CAAC;CAEhD,IAAI,OAAO,SACV,cAAc,UAAU,OAAO;CAGhC,IAAI,OAAO,cACV,cAAc,eAAe,OAAO;CAGrC,IAAI,OAAO,cACV,cAAc,eAAe,OAAO;CAGrC,IAAI,OAAO,mBACV,cAAc,oBAAoB,OAAO;CAG1C,IAAI,OAAO,iBACV,cAAc,kBAAkB,OAAO;CAGxC,IAAI,OAAO,oBACV,cAAc,qBAAqB,OAAO;CAG3C,IAAI,OAAO,kBACV,cAAc,mBAAmB,OAAO;CAGzC,IAAI,OAAO,KAAK,aAAa,EAAE,WAAW,GACzC;CAGD,IAAI;EACH,MAAM,OAAO,MAAM,yBAAyB;GAC3C;GACA;GACA,OAAO;EACR,CAAC;CACF,SAAS,OAAO;EACf,OAAO,IAAI,KACV,sEAAsE,OACvE;EAEA,OAAO,cAAc;EACrB,OAAO,cAAc;EAErB,IAAI,OAAO,KAAK,aAAa,EAAE,SAAS,GACvC,MAAM,OAAO,MAAM,yBAAyB;GAC3C;GACA;GACA,OAAO;EACR,CAAC;CAEH;AACD;;;;;;;;AASA,IAAM,iCAAN,MAEA;;;;CAIC,MAAM,OACL,QACuC;EACvC,MAAM,SAAS,IAAI,cAAc,OAAO,KAAK;EAQ7C,IAAI,aAAY,MANK,OAAO,MAIzB,gBAAgB,EAAE,WAAW,OAAO,UAAU,CAAC,GAE3B,QAAQ,SAAS,MAAM,MAC5C,SAAS,KAAK,KAAK,SAAS,OAAO,IACrC,GAAG,KAAK;EAER,IAAI,WACH,OAAO,IAAI,KACV,qCAAqC,OAAO,KAAK,KAAK,UAAU,EACjE;OACM;GACN,MAAM,cAAuC;IAC5C,WAAW,OAAO;IAClB,MAAM,OAAO;GACd;GAEA,IAAI,OAAO,QACV,YAAY,SAAS,EAAE,OAAO,OAAO,OAAO,MAAM;GAOnD,aAAY,MAJU,OAAO,MAE1B,gBAAgB,EAAE,OAAO,YAAY,CAAC,GAErB,cAAc;GAElC,OAAO,IAAI,KACV,4BAA4B,OAAO,KAAK,KAAK,UAAU,EACxD;GAEA,IAAI,OAAO,QACV,MAAM,OAAO,MAAM,iBAAiB;IACnC,IAAI;IACJ,OAAO,EAAE,OAAO,OAAO,OAAO,MAAM;GACrC,CAAC;GAGF,IAAI,OAAO,MACV,MAAM,OAAO,MAAM,gBAAgB;IAClC,IAAI;IACJ,OAAO,EAAE,MAAM,OAAO,KAAK;GAC5B,CAAC;EAEH;EAEA,MAAM,oBAAoB,QAAQ,WAAW,OAAO,eAAe,MAAM;EAEzE,MAAM,OAA8B;GAAE,GAAG;GAAQ;EAAU;EAE3D,OAAO;GAAE,IAAI;GAAW;EAAK;CAC9B;;;;CAKA,MAAM,OACL,IACA,MACA,MACuC;EACvC,MAAM,SAAS,IAAI,cAAc,KAAK,KAAK;EAE3C,MAAM,cAAuC,CAAC;EAE9C,IAAI,KAAK,SAAS,KAAK,MACtB,YAAY,OAAO,KAAK;EAGzB,IAAI,KAAK,QAAQ,KAAK,SAAS,KAAK,MACnC,YAAY,OAAO,KAAK;EAGzB,IAAI,OAAO,KAAK,WAAW,EAAE,SAAS,GACrC,MAAM,OAAO,MAAM,gBAAgB;GAAE;GAAI,OAAO;EAAY,CAAC;EAG9D,MAAM,oBAAoB,QAAQ,IAAI,KAAK,eAAe,IAAI;EAI9D,OAAO,EAAE;GAF6B,GAAG;GAAM,WAAW;EAE9C,EAAE;CACf;;;;CAKA,MAAM,KACL,IACA,OACqC;EACrC,MAAM,SAAS,IAAI,cAAc,MAAM,KAAK;EAE5C,IAAI;GACH,MAAM,OAAO,MACZ,eACA,EAAE,WAAW,GAAG,CACjB;EACD,QAAQ;GACP,MAAM,IAAI,MAAM,oBAAoB,MAAM,KAAK,KAAK,GAAG,YAAY;EACpE;EAEA,OAAO;GAAE;GAAI,OAAO;IAAE,GAAG;IAAO,WAAW;GAAG;EAAE;CACjD;;;;;;;CAQA,MAAM,SAAwB;EAC7B,OAAO,IAAI,KACV,+GACD;CACD;;;;;;CAOA,MAAM,KACL,KACA,MACA,MACqC;EACrC,MAAM,WAAqB,CAAC;EAC5B,MAAM,UAAoB,CAAC;EAE3B,IAAI,KAAK,SAAS,KAAK,MACtB,QAAQ,KAAK,MAAM;EAGpB,IAAI,KAAK,cAAc,KAAK,WAC3B,SAAS,KAAK,WAAW;EAG1B,IAAI,KAAK,kBAAkB,KAAK,eAC/B,SAAS,KAAK,eAAe;EAG9B,IAAI,KAAK,YAAY,KAAK,SACzB,QAAQ,KAAK,SAAS;EAGvB,IAAI,KAAK,iBAAiB,KAAK,cAC9B,QAAQ,KAAK,cAAc;EAG5B,IAAI,KAAK,iBAAiB,KAAK,cAC9B,QAAQ,KAAK,cAAc;EAG5B,IAAI,KAAK,sBAAsB,KAAK,mBACnC,QAAQ,KAAK,mBAAmB;EAGjC,IAAI,KAAK,oBAAoB,KAAK,iBACjC,QAAQ,KAAK,iBAAiB;EAG/B,IAAI,KAAK,uBAAuB,KAAK,oBACpC,QAAQ,KAAK,oBAAoB;EAGlC,IAAI,KAAK,qBAAqB,KAAK,kBAClC,QAAQ,KAAK,kBAAkB;EAGhC,IAAI,KAAK,SAAS,KAAK,MACtB,QAAQ,KAAK,MAAM;EAGpB,OAAO;GACN,SAAS,SAAS,SAAS,KAAK,QAAQ,SAAS;GACjD;GACA,qBAAqB;EACtB;CACD;AACD;;AAGA,IAAM,yBAAN,cAAqC,OAAO,QAAQ,SAAS;CAG5D,YACC,MACA,MAeA,MACC;EACD,MACC,IAAI,+BAA+B,GACnC,MACA;GAAE,GAAG;GAAM,WAAW;EAAU,GAChC,IACD;CACD;AACD;;;;;;;;;;;;;;;;;;;AAoEA,IAAa,iBAAb,cAAoC,OAAO,kBAAkB;CAI5D,YACC,MACA,MACA,MACC;EACD,MAAM,EAAE,UAAU,SAAS,aAAa,GAAG,eAAe;EAE1D,MAAM,8BAA8B,MAAM,CAAC,GAAG,UAAU;EAExD,MAAM,WAAW,IAAI,uBACpB,GAAG,KAAK,YACR;GACC,OAAO,SAAS;GAChB,WAAW,QAAQ;GACnB,eAAe,YAAY;GAC3B,GAAG;EACJ,GACA,EAAE,QAAQ,KAAK,CAChB;EAEA,KAAK,KAAK,SAAS;EAEnB,KAAK,gBAAgB,EAAE,IAAI,KAAK,GAAG,CAAC;CACrC;AACD"}
1
+ {"version":3,"file":"service.mjs","names":[],"sources":["../../src/railway/service.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\nimport { RailwayClient } from \"./client\";\nimport type { RailwayEnvironment } from \"./environment\";\nimport type { RailwayProject } from \"./project\";\nimport type { RailwayProvider } from \"./provider\";\n\n/**\n * Railway build system. Enum keys are UPPERCASE per convention; values are\n * Railway's required UPPERCASE wire literals.\n * Note: HEROKU and PAKETO were deprecated Feb 21 2025 and auto-migrated to\n * NIXPACKS by Railway, but remain in the schema and are accepted by the API.\n */\nexport enum RailwayBuilder {\n\tRAILPACK = \"RAILPACK\",\n\tNIXPACKS = \"NIXPACKS\",\n\tDOCKERFILE = \"DOCKERFILE\",\n\tHEROKU = \"HEROKU\",\n\tPAKETO = \"PAKETO\",\n}\n\n/**\n * Railway service restart policy. Controls when Railway restarts the service\n * container after it exits. Default is ON_FAILURE.\n */\nexport enum RailwayRestartPolicy {\n\tON_FAILURE = \"ON_FAILURE\",\n\tALWAYS = \"ALWAYS\",\n\tNEVER = \"NEVER\",\n}\n\n/** Docker image source for a Railway service (e.g. `redis:8-alpine`). */\ninterface RailwayServiceSource {\n\t/** Full Docker image reference including tag. */\n\timage: string;\n}\n\n/** Resolved inputs for the Railway service dynamic provider. */\ninterface RailwayServiceInputs {\n\t/** Railway API bearer token. */\n\ttoken: string;\n\n\t/** Railway project UUID. */\n\tprojectId: string;\n\n\t/** Railway environment UUID (e.g. production). */\n\tenvironmentId: string;\n\n\t/** Human-readable service name used for adopt-or-create matching. */\n\tname: string;\n\n\t/** SVG icon URL displayed in the Railway dashboard. */\n\ticon?: string;\n\n\t/** Docker image source for image-based services. */\n\tsource?: RailwayServiceSource;\n\n\t/** Build system to use when building the service. */\n\tbuilder?: RailwayBuilder;\n\n\t/** Shell command executed during the build phase. */\n\tbuildCommand?: string;\n\n\t/** Shell command executed to start the service at runtime. */\n\tstartCommand?: string;\n\n\t/** Restart behavior for the service container. */\n\trestartPolicyType?: RailwayRestartPolicy;\n\n\t/** HTTP path polled for health checks (e.g. `\"/health-check\"`). */\n\thealthcheckPath?: string;\n\n\t/** Seconds to wait for a healthy response before marking unhealthy. */\n\thealthcheckTimeout?: number;\n\n\t/** Shell command executed before the main deploy (e.g. migrations). */\n\tpreDeployCommand?: string;\n}\n\n/** Persisted state for the Railway service, extending inputs with the Railway-assigned ID. */\ninterface RailwayServiceOutputs extends RailwayServiceInputs {\n\t/** Railway-assigned service UUID. */\n\tserviceId: string;\n}\n\nconst SERVICES_QUERY = `\n query($projectId: String!) {\n project(id: $projectId) {\n services {\n edges {\n node {\n id\n name\n }\n }\n }\n }\n }\n`;\n\nconst SERVICE_QUERY = `\n query($serviceId: String!) {\n service(id: $serviceId) {\n id\n name\n }\n }\n`;\n\nconst SERVICE_CREATE = `\n mutation($input: ServiceCreateInput!) {\n serviceCreate(input: $input) {\n id\n name\n }\n }\n`;\n\nconst SERVICE_UPDATE = `\n mutation($id: String!, $input: ServiceUpdateInput!) {\n serviceUpdate(id: $id, input: $input) {\n id\n name\n }\n }\n`;\n\nconst SERVICE_INSTANCE_UPDATE = `\n mutation(\n $serviceId: String!\n $environmentId: String!\n $input: ServiceInstanceUpdateInput!\n ) {\n serviceInstanceUpdate(\n serviceId: $serviceId\n environmentId: $environmentId\n input: $input\n )\n }\n`;\n\nconst SERVICE_CONNECT = `\n mutation($id: String!, $input: ServiceConnectInput!) {\n serviceConnect(id: $id, input: $input) {\n id\n }\n }\n`;\n\n/**\n * Applies service instance configuration (builder, commands, healthcheck).\n * Retries without healthcheck fields if the first call fails.\n */\nasync function applyInstanceConfig(\n\tclient: RailwayClient,\n\tserviceId: string,\n\tenvironmentId: string,\n\tinputs: RailwayServiceInputs,\n): Promise<void> {\n\tconst instanceInput: Record<string, unknown> = {};\n\n\tif (inputs.builder) {\n\t\tinstanceInput.builder = inputs.builder;\n\t}\n\n\tif (inputs.buildCommand) {\n\t\tinstanceInput.buildCommand = inputs.buildCommand;\n\t}\n\n\tif (inputs.startCommand) {\n\t\tinstanceInput.startCommand = inputs.startCommand;\n\t}\n\n\tif (inputs.restartPolicyType) {\n\t\tinstanceInput.restartPolicyType = inputs.restartPolicyType;\n\t}\n\n\tif (inputs.healthcheckPath) {\n\t\tinstanceInput.healthcheckPath = inputs.healthcheckPath;\n\t}\n\n\tif (inputs.healthcheckTimeout) {\n\t\tinstanceInput.healthcheckTimeout = inputs.healthcheckTimeout;\n\t}\n\n\tif (inputs.preDeployCommand) {\n\t\tinstanceInput.preDeployCommand = inputs.preDeployCommand;\n\t}\n\n\tif (Object.keys(instanceInput).length === 0) {\n\t\treturn;\n\t}\n\n\ttry {\n\t\tawait client.query(SERVICE_INSTANCE_UPDATE, {\n\t\t\tserviceId,\n\t\t\tenvironmentId,\n\t\t\tinput: instanceInput,\n\t\t});\n\t} catch (error) {\n\t\tpulumi.log.warn(\n\t\t\t`serviceInstanceUpdate failed, retrying without healthcheck fields: ${error}`,\n\t\t);\n\n\t\tdelete instanceInput.healthcheckPath;\n\t\tdelete instanceInput.healthcheckTimeout;\n\n\t\tif (Object.keys(instanceInput).length > 0) {\n\t\t\tawait client.query(SERVICE_INSTANCE_UPDATE, {\n\t\t\t\tserviceId,\n\t\t\t\tenvironmentId,\n\t\t\t\tinput: instanceInput,\n\t\t\t});\n\t\t}\n\t}\n}\n\n/**\n * Dynamic provider implementing CRUD for Railway services.\n *\n * Uses adopt-or-create on `create()`: queries services by project ID and name\n * before creating a new one. Service instance configuration (builder, commands,\n * healthcheck) is applied via `serviceInstanceUpdate` after create or update.\n *\n * @internal Exported only for unit testing; not part of the public API surface.\n */\nexport class RailwayServiceResourceProvider\n\timplements pulumi.dynamic.ResourceProvider\n{\n\t/**\n\t * Creates or adopts a Railway service by name, then applies instance config.\n\t */\n\tasync create(\n\t\tinputs: RailwayServiceInputs,\n\t): Promise<pulumi.dynamic.CreateResult> {\n\t\tconst client = new RailwayClient(inputs.token);\n\n\t\tconst result = await client.query<{\n\t\t\tproject: {\n\t\t\t\tservices: { edges: Array<{ node: { id: string; name: string } }> };\n\t\t\t};\n\t\t}>(SERVICES_QUERY, { projectId: inputs.projectId });\n\n\t\tlet serviceId = result.project.services.edges.find(\n\t\t\t(edge) => edge.node.name === inputs.name,\n\t\t)?.node.id;\n\n\t\tif (serviceId) {\n\t\t\tpulumi.log.info(\n\t\t\t\t`Adopted existing Railway service \"${inputs.name}\" (${serviceId})`,\n\t\t\t);\n\t\t} else {\n\t\t\tconst createInput: Record<string, unknown> = {\n\t\t\t\tprojectId: inputs.projectId,\n\t\t\t\tenvironmentId: inputs.environmentId,\n\t\t\t\tname: inputs.name,\n\t\t\t};\n\n\t\t\tif (inputs.source) {\n\t\t\t\tcreateInput.source = { image: inputs.source.image };\n\t\t\t}\n\n\t\t\tconst created = await client.query<{\n\t\t\t\tserviceCreate: { id: string; name: string };\n\t\t\t}>(SERVICE_CREATE, { input: createInput });\n\n\t\t\tserviceId = created.serviceCreate.id;\n\n\t\t\tpulumi.log.info(\n\t\t\t\t`Created Railway service \"${inputs.name}\" (${serviceId})`,\n\t\t\t);\n\n\t\t\tif (inputs.source) {\n\t\t\t\tawait client.query(SERVICE_CONNECT, {\n\t\t\t\t\tid: serviceId,\n\t\t\t\t\tinput: { image: inputs.source.image },\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (inputs.icon) {\n\t\t\t\tawait client.query(SERVICE_UPDATE, {\n\t\t\t\t\tid: serviceId,\n\t\t\t\t\tinput: { icon: inputs.icon },\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tawait applyInstanceConfig(client, serviceId, inputs.environmentId, inputs);\n\n\t\tconst outs: RailwayServiceOutputs = { ...inputs, serviceId };\n\n\t\treturn { id: serviceId, outs };\n\t}\n\n\t/**\n\t * Updates service name/icon and re-applies instance configuration.\n\t */\n\tasync update(\n\t\tid: string,\n\t\tolds: RailwayServiceOutputs,\n\t\tnews: RailwayServiceInputs,\n\t): Promise<pulumi.dynamic.UpdateResult> {\n\t\tconst client = new RailwayClient(news.token);\n\n\t\tconst updateInput: Record<string, unknown> = {};\n\n\t\tif (olds.name !== news.name) {\n\t\t\tupdateInput.name = news.name;\n\t\t}\n\n\t\tif (news.icon && olds.icon !== news.icon) {\n\t\t\tupdateInput.icon = news.icon;\n\t\t}\n\n\t\tif (Object.keys(updateInput).length > 0) {\n\t\t\tawait client.query(SERVICE_UPDATE, { id, input: updateInput });\n\t\t}\n\n\t\tawait applyInstanceConfig(client, id, news.environmentId, news);\n\n\t\tconst outs: RailwayServiceOutputs = { ...news, serviceId: id };\n\n\t\treturn { outs };\n\t}\n\n\t/**\n\t * Reads current state for `pulumi refresh` by querying the service by ID.\n\t */\n\tasync read(\n\t\tid: string,\n\t\tprops: RailwayServiceOutputs,\n\t): Promise<pulumi.dynamic.ReadResult> {\n\t\tconst client = new RailwayClient(props.token);\n\n\t\ttry {\n\t\t\tawait client.query<{ service: { id: string; name: string } }>(\n\t\t\t\tSERVICE_QUERY,\n\t\t\t\t{ serviceId: id },\n\t\t\t);\n\t\t} catch {\n\t\t\tthrow new Error(`Railway service \"${props.name}\" (${id}) not found`);\n\t\t}\n\n\t\treturn { id, props: { ...props, serviceId: id } };\n\t}\n\n\t/**\n\t * Deletion is a no-op. A Railway service is a project-level resource shared\n\t * across environments (forked environments adopt it by name), so a single\n\t * environment's destroy must never delete it. Deleting the *environment*\n\t * removes that environment's service instances instead.\n\t */\n\tasync delete(): Promise<void> {\n\t\tpulumi.log.warn(\n\t\t\t\"Railway service deletion skipped — services are project-level; delete the environment to remove its instances\",\n\t\t);\n\t}\n\n\t/**\n\t * Compares old and new inputs to determine what changed.\n\t *\n\t * ProjectId and environmentId changes trigger replacement.\n\t */\n\tasync diff(\n\t\t_id: string,\n\t\tolds: RailwayServiceOutputs,\n\t\tnews: RailwayServiceInputs,\n\t): Promise<pulumi.dynamic.DiffResult> {\n\t\tconst replaces: string[] = [];\n\t\tconst changes: string[] = [];\n\n\t\tif (olds.name !== news.name) {\n\t\t\tchanges.push(\"name\");\n\t\t}\n\n\t\tif (olds.projectId !== news.projectId) {\n\t\t\treplaces.push(\"projectId\");\n\t\t}\n\n\t\tif (olds.environmentId !== news.environmentId) {\n\t\t\treplaces.push(\"environmentId\");\n\t\t}\n\n\t\tif (olds.builder !== news.builder) {\n\t\t\tchanges.push(\"builder\");\n\t\t}\n\n\t\tif (olds.buildCommand !== news.buildCommand) {\n\t\t\tchanges.push(\"buildCommand\");\n\t\t}\n\n\t\tif (olds.startCommand !== news.startCommand) {\n\t\t\tchanges.push(\"startCommand\");\n\t\t}\n\n\t\tif (olds.restartPolicyType !== news.restartPolicyType) {\n\t\t\tchanges.push(\"restartPolicyType\");\n\t\t}\n\n\t\tif (olds.healthcheckPath !== news.healthcheckPath) {\n\t\t\tchanges.push(\"healthcheckPath\");\n\t\t}\n\n\t\tif (olds.healthcheckTimeout !== news.healthcheckTimeout) {\n\t\t\tchanges.push(\"healthcheckTimeout\");\n\t\t}\n\n\t\tif (olds.preDeployCommand !== news.preDeployCommand) {\n\t\t\tchanges.push(\"preDeployCommand\");\n\t\t}\n\n\t\tif (olds.icon !== news.icon) {\n\t\t\tchanges.push(\"icon\");\n\t\t}\n\n\t\treturn {\n\t\t\tchanges: replaces.length > 0 || changes.length > 0,\n\t\t\treplaces,\n\t\t\tdeleteBeforeReplace: true,\n\t\t};\n\t}\n}\n\n/** Internal dynamic resource — not part of the public API. */\nclass RailwayServiceResource extends pulumi.dynamic.Resource {\n\tpublic declare readonly serviceId: 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\ticon?: pulumi.Input<string>;\n\t\t\tsource?: pulumi.Input<{ image: pulumi.Input<string> }>;\n\t\t\tbuilder?: pulumi.Input<RailwayBuilder>;\n\t\t\tbuildCommand?: pulumi.Input<string>;\n\t\t\tstartCommand?: pulumi.Input<string>;\n\t\t\trestartPolicyType?: pulumi.Input<RailwayRestartPolicy>;\n\t\t\thealthcheckPath?: pulumi.Input<string>;\n\t\t\thealthcheckTimeout?: pulumi.Input<number>;\n\t\t\tpreDeployCommand?: pulumi.Input<string>;\n\t\t},\n\t\topts?: pulumi.CustomResourceOptions,\n\t) {\n\t\tsuper(\n\t\t\tnew RailwayServiceResourceProvider(),\n\t\t\tname,\n\t\t\t{ ...args, serviceId: undefined },\n\t\t\topts,\n\t\t);\n\t}\n}\n\n/** Options type for RailwayService — replaces Pulumi's native `provider` field. */\ntype RailwayServiceOptions = Omit<\n\tpulumi.ComponentResourceOptions,\n\t\"provider\"\n> & {\n\t/** Railway authentication context. */\n\tprovider: RailwayProvider;\n\n\t/** Railway project context. */\n\tproject: RailwayProject;\n\n\t/** Railway environment context. */\n\tenvironment: RailwayEnvironment;\n};\n\n/** Args for RailwayService. */\nexport interface RailwayServiceArgs {\n\t/** Human-readable service name used for adopt-or-create matching. */\n\tname: pulumi.Input<string>;\n\n\t/** SVG icon URL displayed in the Railway dashboard. */\n\ticon?: pulumi.Input<string>;\n\n\t/** Docker image source for image-based services. */\n\tsource?: pulumi.Input<{ image: pulumi.Input<string> }>;\n\n\t/** Build system to use when building the service. */\n\tbuilder?: pulumi.Input<RailwayBuilder>;\n\n\t/** Shell command executed during the build phase. */\n\tbuildCommand?: pulumi.Input<string>;\n\n\t/** Shell command executed to start the service at runtime. */\n\tstartCommand?: pulumi.Input<string>;\n\n\t/** Restart behavior for the service container. */\n\trestartPolicyType?: pulumi.Input<RailwayRestartPolicy>;\n\n\t/** HTTP path polled for health checks (e.g. `\"/health-check\"`). */\n\thealthcheckPath?: pulumi.Input<string>;\n\n\t/** Seconds to wait for a healthy response before marking unhealthy. */\n\thealthcheckTimeout?: pulumi.Input<number>;\n\n\t/** Shell command executed before the main deploy (e.g. migrations). */\n\tpreDeployCommand?: pulumi.Input<string>;\n}\n\n/**\n * Manages a Railway service with adopt-or-create semantics.\n *\n * @example\n * ```typescript\n * const service = new RailwayService(\"api\", {\n * name: \"api\",\n * builder: RailwayBuilder.RAILPACK,\n * startCommand: \"node dist/index.js\",\n * healthcheckPath: \"/health\",\n * }, { provider, project, environment });\n *\n * // Use serviceId downstream\n * new RailwayVariable(\"api-vars\", {\n * variables: { DATABASE_URL: dbUrl },\n * }, { provider, project, environment, service });\n * ```\n */\nexport class RailwayService extends pulumi.ComponentResource {\n\t/** Railway service UUID. */\n\tpublic readonly id: pulumi.Output<string>;\n\n\tconstructor(\n\t\tname: string,\n\t\targs: RailwayServiceArgs,\n\t\topts: RailwayServiceOptions,\n\t) {\n\t\tconst { provider, project, environment, ...pulumiOpts } = opts;\n\n\t\tsuper(\"infracraft:railway:Service\", name, {}, pulumiOpts);\n\n\t\tconst resource = new RailwayServiceResource(\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\t...args,\n\t\t\t},\n\t\t\t{ parent: this },\n\t\t);\n\n\t\tthis.id = resource.serviceId;\n\n\t\tthis.registerOutputs({ id: this.id });\n\t}\n}\n"],"mappings":"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"}
@@ -0,0 +1,149 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ const require_chunk = require('../chunk-BVYJZCqc.cjs');
3
+ let _pulumi_pulumi = require("@pulumi/pulumi");
4
+ _pulumi_pulumi = require_chunk.__toESM(_pulumi_pulumi, 1);
5
+
6
+ //#region src/vercel/domain.ts
7
+ const VERCEL_API_URL = "https://api.vercel.com";
8
+ /**
9
+ * Vercel's documented CNAME target for attaching a subdomain to a project.
10
+ * https://vercel.com/docs/domains/working-with-domains/add-a-domain
11
+ */
12
+ const VERCEL_CNAME_TARGET = "cname.vercel-dns.com";
13
+ /**
14
+ * Fetches a project domain by name. Returns `null` if not attached (404).
15
+ */
16
+ async function fetchDomain(token, teamId, projectId, name) {
17
+ const response = await fetch(`${VERCEL_API_URL}/v9/projects/${encodeURIComponent(projectId)}/domains/${encodeURIComponent(name)}?teamId=${teamId}`, { headers: { Authorization: `Bearer ${token}` } });
18
+ if (response.status === 404) return null;
19
+ if (!response.ok) throw new Error(`Vercel API error fetching domain "${name}" (${response.status}): ${await response.text()}`);
20
+ return await response.json();
21
+ }
22
+ /**
23
+ * Dynamic provider implementing adopt-or-create for a Vercel project domain.
24
+ *
25
+ * On `create()`, calls `GET /v9/projects/{projectId}/domains/{name}`. If found, adopts
26
+ * the existing attachment. If 404, attaches it via `POST /v10/projects/{projectId}/domains`.
27
+ *
28
+ * @internal Exported only for unit testing; not part of the public API surface.
29
+ */
30
+ var VercelDomainResourceProvider = class {
31
+ async create(inputs) {
32
+ const existing = await fetchDomain(inputs.token, inputs.teamId, inputs.projectId, inputs.name);
33
+ if (existing) {
34
+ _pulumi_pulumi.log.info(`Adopted existing Vercel domain "${inputs.name}" on project ${inputs.projectId}`);
35
+ const outs = {
36
+ ...inputs,
37
+ verified: existing.verified
38
+ };
39
+ return {
40
+ id: `${inputs.projectId}/${inputs.name}`,
41
+ outs
42
+ };
43
+ }
44
+ const response = await fetch(`${VERCEL_API_URL}/v10/projects/${encodeURIComponent(inputs.projectId)}/domains?teamId=${inputs.teamId}`, {
45
+ method: "POST",
46
+ headers: {
47
+ Authorization: `Bearer ${inputs.token}`,
48
+ "Content-Type": "application/json"
49
+ },
50
+ body: JSON.stringify({ name: inputs.name })
51
+ });
52
+ if (!response.ok) throw new Error(`Vercel API error creating domain "${inputs.name}" (${response.status}): ${await response.text()}`);
53
+ const created = await response.json();
54
+ const outs = {
55
+ ...inputs,
56
+ verified: created.verified
57
+ };
58
+ return {
59
+ id: `${inputs.projectId}/${inputs.name}`,
60
+ outs
61
+ };
62
+ }
63
+ async read(_id, props) {
64
+ const domain = await fetchDomain(props.token, props.teamId, props.projectId, props.name);
65
+ if (!domain) throw new Error(`Vercel domain "${props.name}" not found on project ${props.projectId}`);
66
+ return {
67
+ id: `${props.projectId}/${props.name}`,
68
+ props: {
69
+ ...props,
70
+ verified: domain.verified
71
+ }
72
+ };
73
+ }
74
+ /** All fields replace (see `diff`) — this is never actually invoked. */
75
+ async update(_id, _olds, news) {
76
+ return { outs: {
77
+ ...news,
78
+ verified: false
79
+ } };
80
+ }
81
+ async delete(_id, props) {
82
+ const response = await fetch(`${VERCEL_API_URL}/v9/projects/${encodeURIComponent(props.projectId)}/domains/${encodeURIComponent(props.name)}?teamId=${props.teamId}`, {
83
+ method: "DELETE",
84
+ headers: { Authorization: `Bearer ${props.token}` }
85
+ });
86
+ if (response.status === 404) {
87
+ _pulumi_pulumi.log.warn(`Vercel domain "${props.name}" already gone from project ${props.projectId}`);
88
+ return;
89
+ }
90
+ if (!response.ok) throw new Error(`Vercel API error deleting domain "${props.name}" (${response.status}): ${await response.text()}`);
91
+ _pulumi_pulumi.log.info(`Deleted Vercel domain "${props.name}" from project ${props.projectId}`);
92
+ }
93
+ /**
94
+ * A domain can only be attached to one project, so every field change replaces
95
+ * rather than updates.
96
+ */
97
+ async diff(_id, olds, news) {
98
+ const replaces = [];
99
+ if (olds.name !== news.name) replaces.push("name");
100
+ if (olds.projectId !== news.projectId) replaces.push("projectId");
101
+ if (olds.teamId !== news.teamId) replaces.push("teamId");
102
+ return {
103
+ changes: replaces.length > 0,
104
+ replaces,
105
+ deleteBeforeReplace: true
106
+ };
107
+ }
108
+ };
109
+ /** Internal dynamic resource — not part of the public API. */
110
+ var VercelDomainResource = class extends _pulumi_pulumi.dynamic.Resource {
111
+ constructor(name, args, opts) {
112
+ super(new VercelDomainResourceProvider(), name, {
113
+ ...args,
114
+ verified: void 0
115
+ }, opts);
116
+ }
117
+ };
118
+ /**
119
+ * Attaches a custom domain to a Vercel project, with adopt-or-create semantics.
120
+ *
121
+ * @example
122
+ * ```typescript
123
+ * new VercelDomain("aura-domain", { name: "app.example.com" }, { provider, project });
124
+ * ```
125
+ */
126
+ var VercelDomain = class extends _pulumi_pulumi.ComponentResource {
127
+ constructor(name, args, opts) {
128
+ const { provider, project, ...pulumiOpts } = opts;
129
+ super("infracraft:vercel:Domain", name, {}, pulumiOpts);
130
+ const resource = new VercelDomainResource(`${name}-resource`, {
131
+ token: provider.token,
132
+ teamId: provider.teamId,
133
+ projectId: project.id,
134
+ ...args
135
+ }, { parent: this });
136
+ this.verified = resource.verified;
137
+ this.name = resource.name;
138
+ this.registerOutputs({
139
+ verified: this.verified,
140
+ name: this.name
141
+ });
142
+ }
143
+ };
144
+
145
+ //#endregion
146
+ exports.VERCEL_CNAME_TARGET = VERCEL_CNAME_TARGET;
147
+ exports.VercelDomain = VercelDomain;
148
+ exports.VercelDomainResourceProvider = VercelDomainResourceProvider;
149
+ //# sourceMappingURL=domain.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"domain.cjs","names":["pulumi"],"sources":["../../src/vercel/domain.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\nimport type { VercelProject } from \"./project\";\nimport type { VercelProvider } from \"./provider\";\n\nconst VERCEL_API_URL = \"https://api.vercel.com\";\n\n/**\n * Vercel's documented CNAME target for attaching a subdomain to a project.\n * https://vercel.com/docs/domains/working-with-domains/add-a-domain\n */\nexport const VERCEL_CNAME_TARGET = \"cname.vercel-dns.com\";\n\n/** Resolved inputs for the Vercel domain dynamic provider. */\nexport interface VercelDomainInputs {\n\t/** Vercel API bearer token. */\n\ttoken: string;\n\n\t/** Vercel team/org ID. */\n\tteamId: string;\n\n\t/** Vercel project ID to attach the domain to. */\n\tprojectId: string;\n\n\t/** Domain name (e.g. `\"app.example.com\"`). */\n\tname: string;\n}\n\n/** Persisted state for the Vercel domain, extending inputs with verification status. */\ninterface VercelDomainOutputs extends VercelDomainInputs {\n\t/** `true` once the domain's DNS has been verified by Vercel. */\n\tverified: boolean;\n}\n\n/** Vercel API response shape for a project domain. */\ninterface VercelDomainResponse {\n\tname: string;\n\tprojectId: string;\n\tverified: boolean;\n}\n\n/**\n * Fetches a project domain by name. Returns `null` if not attached (404).\n */\nasync function fetchDomain(\n\ttoken: string,\n\tteamId: string,\n\tprojectId: string,\n\tname: string,\n): Promise<VercelDomainResponse | null> {\n\tconst response = await fetch(\n\t\t`${VERCEL_API_URL}/v9/projects/${encodeURIComponent(projectId)}/domains/${encodeURIComponent(name)}?teamId=${teamId}`,\n\t\t{ headers: { Authorization: `Bearer ${token}` } },\n\t);\n\n\tif (response.status === 404) {\n\t\treturn null;\n\t}\n\n\tif (!response.ok) {\n\t\tthrow new Error(\n\t\t\t`Vercel API error fetching domain \"${name}\" (${response.status}): ${await response.text()}`,\n\t\t);\n\t}\n\n\treturn (await response.json()) as VercelDomainResponse;\n}\n\n/**\n * Dynamic provider implementing adopt-or-create for a Vercel project domain.\n *\n * On `create()`, calls `GET /v9/projects/{projectId}/domains/{name}`. If found, adopts\n * the existing attachment. If 404, attaches it via `POST /v10/projects/{projectId}/domains`.\n *\n * @internal Exported only for unit testing; not part of the public API surface.\n */\nexport class VercelDomainResourceProvider\n\timplements pulumi.dynamic.ResourceProvider\n{\n\tasync create(\n\t\tinputs: VercelDomainInputs,\n\t): Promise<pulumi.dynamic.CreateResult> {\n\t\tconst existing = await fetchDomain(\n\t\t\tinputs.token,\n\t\t\tinputs.teamId,\n\t\t\tinputs.projectId,\n\t\t\tinputs.name,\n\t\t);\n\n\t\tif (existing) {\n\t\t\tpulumi.log.info(\n\t\t\t\t`Adopted existing Vercel domain \"${inputs.name}\" on project ${inputs.projectId}`,\n\t\t\t);\n\n\t\t\tconst outs: VercelDomainOutputs = {\n\t\t\t\t...inputs,\n\t\t\t\tverified: existing.verified,\n\t\t\t};\n\n\t\t\treturn { id: `${inputs.projectId}/${inputs.name}`, outs };\n\t\t}\n\n\t\tconst response = await fetch(\n\t\t\t`${VERCEL_API_URL}/v10/projects/${encodeURIComponent(inputs.projectId)}/domains?teamId=${inputs.teamId}`,\n\t\t\t{\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\tAuthorization: `Bearer ${inputs.token}`,\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify({ name: inputs.name }),\n\t\t\t},\n\t\t);\n\n\t\tif (!response.ok) {\n\t\t\tthrow new Error(\n\t\t\t\t`Vercel API error creating domain \"${inputs.name}\" (${response.status}): ${await response.text()}`,\n\t\t\t);\n\t\t}\n\n\t\tconst created = (await response.json()) as VercelDomainResponse;\n\n\t\tconst outs: VercelDomainOutputs = { ...inputs, verified: created.verified };\n\n\t\treturn { id: `${inputs.projectId}/${inputs.name}`, outs };\n\t}\n\n\tasync read(\n\t\t_id: string,\n\t\tprops: VercelDomainOutputs,\n\t): Promise<pulumi.dynamic.ReadResult> {\n\t\tconst domain = await fetchDomain(\n\t\t\tprops.token,\n\t\t\tprops.teamId,\n\t\t\tprops.projectId,\n\t\t\tprops.name,\n\t\t);\n\n\t\tif (!domain) {\n\t\t\tthrow new Error(\n\t\t\t\t`Vercel domain \"${props.name}\" not found on project ${props.projectId}`,\n\t\t\t);\n\t\t}\n\n\t\treturn {\n\t\t\tid: `${props.projectId}/${props.name}`,\n\t\t\tprops: { ...props, verified: domain.verified },\n\t\t};\n\t}\n\n\t/** All fields replace (see `diff`) — this is never actually invoked. */\n\tasync update(\n\t\t_id: string,\n\t\t_olds: VercelDomainOutputs,\n\t\tnews: VercelDomainInputs,\n\t): Promise<pulumi.dynamic.UpdateResult> {\n\t\treturn { outs: { ...news, verified: false } };\n\t}\n\n\tasync delete(_id: string, props: VercelDomainOutputs): Promise<void> {\n\t\tconst response = await fetch(\n\t\t\t`${VERCEL_API_URL}/v9/projects/${encodeURIComponent(props.projectId)}/domains/${encodeURIComponent(props.name)}?teamId=${props.teamId}`,\n\t\t\t{\n\t\t\t\tmethod: \"DELETE\",\n\t\t\t\theaders: { Authorization: `Bearer ${props.token}` },\n\t\t\t},\n\t\t);\n\n\t\tif (response.status === 404) {\n\t\t\tpulumi.log.warn(\n\t\t\t\t`Vercel domain \"${props.name}\" already gone from project ${props.projectId}`,\n\t\t\t);\n\n\t\t\treturn;\n\t\t}\n\n\t\tif (!response.ok) {\n\t\t\tthrow new Error(\n\t\t\t\t`Vercel API error deleting domain \"${props.name}\" (${response.status}): ${await response.text()}`,\n\t\t\t);\n\t\t}\n\n\t\tpulumi.log.info(\n\t\t\t`Deleted Vercel domain \"${props.name}\" from project ${props.projectId}`,\n\t\t);\n\t}\n\n\t/**\n\t * A domain can only be attached to one project, so every field change replaces\n\t * rather than updates.\n\t */\n\tasync diff(\n\t\t_id: string,\n\t\tolds: VercelDomainOutputs,\n\t\tnews: VercelDomainInputs,\n\t): Promise<pulumi.dynamic.DiffResult> {\n\t\tconst replaces: string[] = [];\n\n\t\tif (olds.name !== news.name) {\n\t\t\treplaces.push(\"name\");\n\t\t}\n\n\t\tif (olds.projectId !== news.projectId) {\n\t\t\treplaces.push(\"projectId\");\n\t\t}\n\n\t\tif (olds.teamId !== news.teamId) {\n\t\t\treplaces.push(\"teamId\");\n\t\t}\n\n\t\treturn {\n\t\t\tchanges: replaces.length > 0,\n\t\t\treplaces,\n\t\t\tdeleteBeforeReplace: true,\n\t\t};\n\t}\n}\n\n/** Internal dynamic resource — not part of the public API. */\nclass VercelDomainResource extends pulumi.dynamic.Resource {\n\tpublic declare readonly verified: pulumi.Output<boolean>;\n\tpublic declare readonly name: pulumi.Output<string>;\n\n\tconstructor(\n\t\tname: string,\n\t\targs: {\n\t\t\ttoken: pulumi.Input<string>;\n\t\t\tteamId: pulumi.Input<string>;\n\t\t\tprojectId: pulumi.Input<string>;\n\t\t\tname: pulumi.Input<string>;\n\t\t},\n\t\topts?: pulumi.CustomResourceOptions,\n\t) {\n\t\tsuper(\n\t\t\tnew VercelDomainResourceProvider(),\n\t\t\tname,\n\t\t\t{ ...args, verified: undefined },\n\t\t\topts,\n\t\t);\n\t}\n}\n\n/** Options type for VercelDomain — replaces Pulumi's native `provider` field. */\ntype VercelDomainOptions = Omit<pulumi.ComponentResourceOptions, \"provider\"> & {\n\t/** Vercel authentication context. */\n\tprovider: VercelProvider;\n\n\t/** Vercel project to attach the domain to. */\n\tproject: VercelProject;\n};\n\n/** Args for VercelDomain. */\nexport interface VercelDomainArgs {\n\t/** Domain name (e.g. `\"app.example.com\"`). */\n\tname: pulumi.Input<string>;\n}\n\n/**\n * Attaches a custom domain to a Vercel project, with adopt-or-create semantics.\n *\n * @example\n * ```typescript\n * new VercelDomain(\"aura-domain\", { name: \"app.example.com\" }, { provider, project });\n * ```\n */\nexport class VercelDomain extends pulumi.ComponentResource {\n\t/** `true` once the domain's DNS has been verified by Vercel. */\n\tpublic readonly verified: pulumi.Output<boolean>;\n\n\t/** The attached domain name. */\n\tpublic readonly name: pulumi.Output<string>;\n\n\tconstructor(name: string, args: VercelDomainArgs, opts: VercelDomainOptions) {\n\t\tconst { provider, project, ...pulumiOpts } = opts;\n\n\t\tsuper(\"infracraft:vercel:Domain\", name, {}, pulumiOpts);\n\n\t\tconst resource = new VercelDomainResource(\n\t\t\t`${name}-resource`,\n\t\t\t{\n\t\t\t\ttoken: provider.token,\n\t\t\t\tteamId: provider.teamId,\n\t\t\t\tprojectId: project.id,\n\t\t\t\t...args,\n\t\t\t},\n\t\t\t{ parent: this },\n\t\t);\n\n\t\tthis.verified = resource.verified;\n\t\tthis.name = resource.name;\n\n\t\tthis.registerOutputs({ verified: this.verified, name: this.name });\n\t}\n}\n"],"mappings":"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"}
@@ -0,0 +1,75 @@
1
+ import { t as __name } from "../chunk-OPjESj5l.cjs";
2
+ import { VercelProvider } from "./provider.cjs";
3
+ import { VercelProject } from "./project.cjs";
4
+ import * as pulumi from "@pulumi/pulumi";
5
+
6
+ //#region src/vercel/domain.d.ts
7
+ /**
8
+ * Vercel's documented CNAME target for attaching a subdomain to a project.
9
+ * https://vercel.com/docs/domains/working-with-domains/add-a-domain
10
+ */
11
+ declare const VERCEL_CNAME_TARGET = "cname.vercel-dns.com";
12
+ /** Resolved inputs for the Vercel domain dynamic provider. */
13
+ interface VercelDomainInputs {
14
+ /** Vercel API bearer token. */
15
+ token: string;
16
+ /** Vercel team/org ID. */
17
+ teamId: string;
18
+ /** Vercel project ID to attach the domain to. */
19
+ projectId: string;
20
+ /** Domain name (e.g. `"app.example.com"`). */
21
+ name: string;
22
+ }
23
+ /** Persisted state for the Vercel domain, extending inputs with verification status. */
24
+ interface VercelDomainOutputs extends VercelDomainInputs {
25
+ /** `true` once the domain's DNS has been verified by Vercel. */
26
+ verified: boolean;
27
+ }
28
+ /**
29
+ * Dynamic provider implementing adopt-or-create for a Vercel project domain.
30
+ *
31
+ * On `create()`, calls `GET /v9/projects/{projectId}/domains/{name}`. If found, adopts
32
+ * the existing attachment. If 404, attaches it via `POST /v10/projects/{projectId}/domains`.
33
+ *
34
+ * @internal Exported only for unit testing; not part of the public API surface.
35
+ */
36
+ declare class VercelDomainResourceProvider implements pulumi.dynamic.ResourceProvider {
37
+ create(inputs: VercelDomainInputs): Promise<pulumi.dynamic.CreateResult>;
38
+ read(_id: string, props: VercelDomainOutputs): Promise<pulumi.dynamic.ReadResult>;
39
+ /** All fields replace (see `diff`) — this is never actually invoked. */
40
+ update(_id: string, _olds: VercelDomainOutputs, news: VercelDomainInputs): Promise<pulumi.dynamic.UpdateResult>;
41
+ delete(_id: string, props: VercelDomainOutputs): Promise<void>;
42
+ /**
43
+ * A domain can only be attached to one project, so every field change replaces
44
+ * rather than updates.
45
+ */
46
+ diff(_id: string, olds: VercelDomainOutputs, news: VercelDomainInputs): Promise<pulumi.dynamic.DiffResult>;
47
+ }
48
+ /** Options type for VercelDomain — replaces Pulumi's native `provider` field. */
49
+ type VercelDomainOptions = Omit<pulumi.ComponentResourceOptions, "provider"> & {
50
+ /** Vercel authentication context. */provider: VercelProvider; /** Vercel project to attach the domain to. */
51
+ project: VercelProject;
52
+ };
53
+ /** Args for VercelDomain. */
54
+ interface VercelDomainArgs {
55
+ /** Domain name (e.g. `"app.example.com"`). */
56
+ name: pulumi.Input<string>;
57
+ }
58
+ /**
59
+ * Attaches a custom domain to a Vercel project, with adopt-or-create semantics.
60
+ *
61
+ * @example
62
+ * ```typescript
63
+ * new VercelDomain("aura-domain", { name: "app.example.com" }, { provider, project });
64
+ * ```
65
+ */
66
+ declare class VercelDomain extends pulumi.ComponentResource {
67
+ /** `true` once the domain's DNS has been verified by Vercel. */
68
+ readonly verified: pulumi.Output<boolean>;
69
+ /** The attached domain name. */
70
+ readonly name: pulumi.Output<string>;
71
+ constructor(name: string, args: VercelDomainArgs, opts: VercelDomainOptions);
72
+ }
73
+ //#endregion
74
+ export { VERCEL_CNAME_TARGET, VercelDomain, VercelDomainArgs, VercelDomainInputs, VercelDomainResourceProvider };
75
+ //# sourceMappingURL=domain.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"domain.d.cts","names":[],"sources":["../../src/vercel/domain.ts"],"mappings":";;;;;;;;;AAUA;cAAa,mBAAA;;UAGI,kBAAA;EAHe;EAK/B,KAAA;EAFkC;EAKlC,MAAA;EALkC;EAQlC,SAAA;EAHA;EAMA,IAAA;AAAA;;UAIS,mBAAA,SAA4B,kBAAkB;EAA9C;EAET,QAAQ;AAAA;;AAAA;AA6CT;;;;;;cAAa,4BAAA,YACD,MAAA,CAAO,OAAA,CAAQ,gBAAA;EAEpB,MAAA,CACL,MAAA,EAAQ,kBAAA,GACN,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,YAAA;EA8CpB,IAAA,CACL,GAAA,UACA,KAAA,EAAO,mBAAA,GACL,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,UAAA;EAuBlB;EAFF,MAAA,CACL,GAAA,UACA,KAAA,EAAO,mBAAA,EACP,IAAA,EAAM,kBAAA,GACJ,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,YAAA;EAIpB,MAAA,CAAO,GAAA,UAAa,KAAA,EAAO,mBAAA,GAAsB,OAAA;EAJpD;;;;EAoCG,IAAA,CACL,GAAA,UACA,IAAA,EAAM,mBAAA,EACN,IAAA,EAAM,kBAAA,GACJ,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,UAAA;AAAA;;KAgDtB,mBAAA,GAAsB,IAAA,CAAK,MAAA,CAAO,wBAAA;EAtKI,qCAwK1C,QAAA,EAAU,cAAA,EAxKC;EA2KX,OAAA,EAAS,aAAA;AAAA;;UAIO,gBAAA;EA5Kf;EA8KD,IAAA,EAAM,MAAA,CAAO,KAAK;AAAA;;;;;;;;;cAWN,YAAA,SAAqB,MAAA,CAAO,iBAAA;EAvId;EAAA,SAyIV,QAAA,EAAU,MAAA,CAAO,MAAA;EAnHhC;EAAA,SAsHe,IAAA,EAAM,MAAA,CAAO,MAAA;cAEjB,IAAA,UAAc,IAAA,EAAM,gBAAA,EAAkB,IAAA,EAAM,mBAAA;AAAA"}
@@ -0,0 +1,75 @@
1
+ import { t as __name } from "../chunk-OPjESj5l.mjs";
2
+ import { VercelProvider } from "./provider.mjs";
3
+ import { VercelProject } from "./project.mjs";
4
+ import * as pulumi from "@pulumi/pulumi";
5
+
6
+ //#region src/vercel/domain.d.ts
7
+ /**
8
+ * Vercel's documented CNAME target for attaching a subdomain to a project.
9
+ * https://vercel.com/docs/domains/working-with-domains/add-a-domain
10
+ */
11
+ declare const VERCEL_CNAME_TARGET = "cname.vercel-dns.com";
12
+ /** Resolved inputs for the Vercel domain dynamic provider. */
13
+ interface VercelDomainInputs {
14
+ /** Vercel API bearer token. */
15
+ token: string;
16
+ /** Vercel team/org ID. */
17
+ teamId: string;
18
+ /** Vercel project ID to attach the domain to. */
19
+ projectId: string;
20
+ /** Domain name (e.g. `"app.example.com"`). */
21
+ name: string;
22
+ }
23
+ /** Persisted state for the Vercel domain, extending inputs with verification status. */
24
+ interface VercelDomainOutputs extends VercelDomainInputs {
25
+ /** `true` once the domain's DNS has been verified by Vercel. */
26
+ verified: boolean;
27
+ }
28
+ /**
29
+ * Dynamic provider implementing adopt-or-create for a Vercel project domain.
30
+ *
31
+ * On `create()`, calls `GET /v9/projects/{projectId}/domains/{name}`. If found, adopts
32
+ * the existing attachment. If 404, attaches it via `POST /v10/projects/{projectId}/domains`.
33
+ *
34
+ * @internal Exported only for unit testing; not part of the public API surface.
35
+ */
36
+ declare class VercelDomainResourceProvider implements pulumi.dynamic.ResourceProvider {
37
+ create(inputs: VercelDomainInputs): Promise<pulumi.dynamic.CreateResult>;
38
+ read(_id: string, props: VercelDomainOutputs): Promise<pulumi.dynamic.ReadResult>;
39
+ /** All fields replace (see `diff`) — this is never actually invoked. */
40
+ update(_id: string, _olds: VercelDomainOutputs, news: VercelDomainInputs): Promise<pulumi.dynamic.UpdateResult>;
41
+ delete(_id: string, props: VercelDomainOutputs): Promise<void>;
42
+ /**
43
+ * A domain can only be attached to one project, so every field change replaces
44
+ * rather than updates.
45
+ */
46
+ diff(_id: string, olds: VercelDomainOutputs, news: VercelDomainInputs): Promise<pulumi.dynamic.DiffResult>;
47
+ }
48
+ /** Options type for VercelDomain — replaces Pulumi's native `provider` field. */
49
+ type VercelDomainOptions = Omit<pulumi.ComponentResourceOptions, "provider"> & {
50
+ /** Vercel authentication context. */provider: VercelProvider; /** Vercel project to attach the domain to. */
51
+ project: VercelProject;
52
+ };
53
+ /** Args for VercelDomain. */
54
+ interface VercelDomainArgs {
55
+ /** Domain name (e.g. `"app.example.com"`). */
56
+ name: pulumi.Input<string>;
57
+ }
58
+ /**
59
+ * Attaches a custom domain to a Vercel project, with adopt-or-create semantics.
60
+ *
61
+ * @example
62
+ * ```typescript
63
+ * new VercelDomain("aura-domain", { name: "app.example.com" }, { provider, project });
64
+ * ```
65
+ */
66
+ declare class VercelDomain extends pulumi.ComponentResource {
67
+ /** `true` once the domain's DNS has been verified by Vercel. */
68
+ readonly verified: pulumi.Output<boolean>;
69
+ /** The attached domain name. */
70
+ readonly name: pulumi.Output<string>;
71
+ constructor(name: string, args: VercelDomainArgs, opts: VercelDomainOptions);
72
+ }
73
+ //#endregion
74
+ export { VERCEL_CNAME_TARGET, VercelDomain, VercelDomainArgs, VercelDomainInputs, VercelDomainResourceProvider };
75
+ //# sourceMappingURL=domain.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"domain.d.mts","names":[],"sources":["../../src/vercel/domain.ts"],"mappings":";;;;;;;;;AAUA;cAAa,mBAAA;;UAGI,kBAAA;EAHe;EAK/B,KAAA;EAFkC;EAKlC,MAAA;EALkC;EAQlC,SAAA;EAHA;EAMA,IAAA;AAAA;;UAIS,mBAAA,SAA4B,kBAAkB;EAA9C;EAET,QAAQ;AAAA;;AAAA;AA6CT;;;;;;cAAa,4BAAA,YACD,MAAA,CAAO,OAAA,CAAQ,gBAAA;EAEpB,MAAA,CACL,MAAA,EAAQ,kBAAA,GACN,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,YAAA;EA8CpB,IAAA,CACL,GAAA,UACA,KAAA,EAAO,mBAAA,GACL,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,UAAA;EAuBlB;EAFF,MAAA,CACL,GAAA,UACA,KAAA,EAAO,mBAAA,EACP,IAAA,EAAM,kBAAA,GACJ,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,YAAA;EAIpB,MAAA,CAAO,GAAA,UAAa,KAAA,EAAO,mBAAA,GAAsB,OAAA;EAJpD;;;;EAoCG,IAAA,CACL,GAAA,UACA,IAAA,EAAM,mBAAA,EACN,IAAA,EAAM,kBAAA,GACJ,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,UAAA;AAAA;;KAgDtB,mBAAA,GAAsB,IAAA,CAAK,MAAA,CAAO,wBAAA;EAtKI,qCAwK1C,QAAA,EAAU,cAAA,EAxKC;EA2KX,OAAA,EAAS,aAAA;AAAA;;UAIO,gBAAA;EA5Kf;EA8KD,IAAA,EAAM,MAAA,CAAO,KAAK;AAAA;;;;;;;;;cAWN,YAAA,SAAqB,MAAA,CAAO,iBAAA;EAvId;EAAA,SAyIV,QAAA,EAAU,MAAA,CAAO,MAAA;EAnHhC;EAAA,SAsHe,IAAA,EAAM,MAAA,CAAO,MAAA;cAEjB,IAAA,UAAc,IAAA,EAAM,gBAAA,EAAkB,IAAA,EAAM,mBAAA;AAAA"}
@@ -0,0 +1,145 @@
1
+ import { t as __name } from "../chunk-OPjESj5l.mjs";
2
+ import * as pulumi from "@pulumi/pulumi";
3
+
4
+ //#region src/vercel/domain.ts
5
+ const VERCEL_API_URL = "https://api.vercel.com";
6
+ /**
7
+ * Vercel's documented CNAME target for attaching a subdomain to a project.
8
+ * https://vercel.com/docs/domains/working-with-domains/add-a-domain
9
+ */
10
+ const VERCEL_CNAME_TARGET = "cname.vercel-dns.com";
11
+ /**
12
+ * Fetches a project domain by name. Returns `null` if not attached (404).
13
+ */
14
+ async function fetchDomain(token, teamId, projectId, name) {
15
+ const response = await fetch(`${VERCEL_API_URL}/v9/projects/${encodeURIComponent(projectId)}/domains/${encodeURIComponent(name)}?teamId=${teamId}`, { headers: { Authorization: `Bearer ${token}` } });
16
+ if (response.status === 404) return null;
17
+ if (!response.ok) throw new Error(`Vercel API error fetching domain "${name}" (${response.status}): ${await response.text()}`);
18
+ return await response.json();
19
+ }
20
+ /**
21
+ * Dynamic provider implementing adopt-or-create for a Vercel project domain.
22
+ *
23
+ * On `create()`, calls `GET /v9/projects/{projectId}/domains/{name}`. If found, adopts
24
+ * the existing attachment. If 404, attaches it via `POST /v10/projects/{projectId}/domains`.
25
+ *
26
+ * @internal Exported only for unit testing; not part of the public API surface.
27
+ */
28
+ var VercelDomainResourceProvider = class {
29
+ async create(inputs) {
30
+ const existing = await fetchDomain(inputs.token, inputs.teamId, inputs.projectId, inputs.name);
31
+ if (existing) {
32
+ pulumi.log.info(`Adopted existing Vercel domain "${inputs.name}" on project ${inputs.projectId}`);
33
+ const outs = {
34
+ ...inputs,
35
+ verified: existing.verified
36
+ };
37
+ return {
38
+ id: `${inputs.projectId}/${inputs.name}`,
39
+ outs
40
+ };
41
+ }
42
+ const response = await fetch(`${VERCEL_API_URL}/v10/projects/${encodeURIComponent(inputs.projectId)}/domains?teamId=${inputs.teamId}`, {
43
+ method: "POST",
44
+ headers: {
45
+ Authorization: `Bearer ${inputs.token}`,
46
+ "Content-Type": "application/json"
47
+ },
48
+ body: JSON.stringify({ name: inputs.name })
49
+ });
50
+ if (!response.ok) throw new Error(`Vercel API error creating domain "${inputs.name}" (${response.status}): ${await response.text()}`);
51
+ const created = await response.json();
52
+ const outs = {
53
+ ...inputs,
54
+ verified: created.verified
55
+ };
56
+ return {
57
+ id: `${inputs.projectId}/${inputs.name}`,
58
+ outs
59
+ };
60
+ }
61
+ async read(_id, props) {
62
+ const domain = await fetchDomain(props.token, props.teamId, props.projectId, props.name);
63
+ if (!domain) throw new Error(`Vercel domain "${props.name}" not found on project ${props.projectId}`);
64
+ return {
65
+ id: `${props.projectId}/${props.name}`,
66
+ props: {
67
+ ...props,
68
+ verified: domain.verified
69
+ }
70
+ };
71
+ }
72
+ /** All fields replace (see `diff`) — this is never actually invoked. */
73
+ async update(_id, _olds, news) {
74
+ return { outs: {
75
+ ...news,
76
+ verified: false
77
+ } };
78
+ }
79
+ async delete(_id, props) {
80
+ const response = await fetch(`${VERCEL_API_URL}/v9/projects/${encodeURIComponent(props.projectId)}/domains/${encodeURIComponent(props.name)}?teamId=${props.teamId}`, {
81
+ method: "DELETE",
82
+ headers: { Authorization: `Bearer ${props.token}` }
83
+ });
84
+ if (response.status === 404) {
85
+ pulumi.log.warn(`Vercel domain "${props.name}" already gone from project ${props.projectId}`);
86
+ return;
87
+ }
88
+ if (!response.ok) throw new Error(`Vercel API error deleting domain "${props.name}" (${response.status}): ${await response.text()}`);
89
+ pulumi.log.info(`Deleted Vercel domain "${props.name}" from project ${props.projectId}`);
90
+ }
91
+ /**
92
+ * A domain can only be attached to one project, so every field change replaces
93
+ * rather than updates.
94
+ */
95
+ async diff(_id, olds, news) {
96
+ const replaces = [];
97
+ if (olds.name !== news.name) replaces.push("name");
98
+ if (olds.projectId !== news.projectId) replaces.push("projectId");
99
+ if (olds.teamId !== news.teamId) replaces.push("teamId");
100
+ return {
101
+ changes: replaces.length > 0,
102
+ replaces,
103
+ deleteBeforeReplace: true
104
+ };
105
+ }
106
+ };
107
+ /** Internal dynamic resource — not part of the public API. */
108
+ var VercelDomainResource = class extends pulumi.dynamic.Resource {
109
+ constructor(name, args, opts) {
110
+ super(new VercelDomainResourceProvider(), name, {
111
+ ...args,
112
+ verified: void 0
113
+ }, opts);
114
+ }
115
+ };
116
+ /**
117
+ * Attaches a custom domain to a Vercel project, with adopt-or-create semantics.
118
+ *
119
+ * @example
120
+ * ```typescript
121
+ * new VercelDomain("aura-domain", { name: "app.example.com" }, { provider, project });
122
+ * ```
123
+ */
124
+ var VercelDomain = class extends pulumi.ComponentResource {
125
+ constructor(name, args, opts) {
126
+ const { provider, project, ...pulumiOpts } = opts;
127
+ super("infracraft:vercel:Domain", name, {}, pulumiOpts);
128
+ const resource = new VercelDomainResource(`${name}-resource`, {
129
+ token: provider.token,
130
+ teamId: provider.teamId,
131
+ projectId: project.id,
132
+ ...args
133
+ }, { parent: this });
134
+ this.verified = resource.verified;
135
+ this.name = resource.name;
136
+ this.registerOutputs({
137
+ verified: this.verified,
138
+ name: this.name
139
+ });
140
+ }
141
+ };
142
+
143
+ //#endregion
144
+ export { VERCEL_CNAME_TARGET, VercelDomain, VercelDomainResourceProvider };
145
+ //# sourceMappingURL=domain.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"domain.mjs","names":[],"sources":["../../src/vercel/domain.ts"],"sourcesContent":["import * as pulumi from \"@pulumi/pulumi\";\nimport type { VercelProject } from \"./project\";\nimport type { VercelProvider } from \"./provider\";\n\nconst VERCEL_API_URL = \"https://api.vercel.com\";\n\n/**\n * Vercel's documented CNAME target for attaching a subdomain to a project.\n * https://vercel.com/docs/domains/working-with-domains/add-a-domain\n */\nexport const VERCEL_CNAME_TARGET = \"cname.vercel-dns.com\";\n\n/** Resolved inputs for the Vercel domain dynamic provider. */\nexport interface VercelDomainInputs {\n\t/** Vercel API bearer token. */\n\ttoken: string;\n\n\t/** Vercel team/org ID. */\n\tteamId: string;\n\n\t/** Vercel project ID to attach the domain to. */\n\tprojectId: string;\n\n\t/** Domain name (e.g. `\"app.example.com\"`). */\n\tname: string;\n}\n\n/** Persisted state for the Vercel domain, extending inputs with verification status. */\ninterface VercelDomainOutputs extends VercelDomainInputs {\n\t/** `true` once the domain's DNS has been verified by Vercel. */\n\tverified: boolean;\n}\n\n/** Vercel API response shape for a project domain. */\ninterface VercelDomainResponse {\n\tname: string;\n\tprojectId: string;\n\tverified: boolean;\n}\n\n/**\n * Fetches a project domain by name. Returns `null` if not attached (404).\n */\nasync function fetchDomain(\n\ttoken: string,\n\tteamId: string,\n\tprojectId: string,\n\tname: string,\n): Promise<VercelDomainResponse | null> {\n\tconst response = await fetch(\n\t\t`${VERCEL_API_URL}/v9/projects/${encodeURIComponent(projectId)}/domains/${encodeURIComponent(name)}?teamId=${teamId}`,\n\t\t{ headers: { Authorization: `Bearer ${token}` } },\n\t);\n\n\tif (response.status === 404) {\n\t\treturn null;\n\t}\n\n\tif (!response.ok) {\n\t\tthrow new Error(\n\t\t\t`Vercel API error fetching domain \"${name}\" (${response.status}): ${await response.text()}`,\n\t\t);\n\t}\n\n\treturn (await response.json()) as VercelDomainResponse;\n}\n\n/**\n * Dynamic provider implementing adopt-or-create for a Vercel project domain.\n *\n * On `create()`, calls `GET /v9/projects/{projectId}/domains/{name}`. If found, adopts\n * the existing attachment. If 404, attaches it via `POST /v10/projects/{projectId}/domains`.\n *\n * @internal Exported only for unit testing; not part of the public API surface.\n */\nexport class VercelDomainResourceProvider\n\timplements pulumi.dynamic.ResourceProvider\n{\n\tasync create(\n\t\tinputs: VercelDomainInputs,\n\t): Promise<pulumi.dynamic.CreateResult> {\n\t\tconst existing = await fetchDomain(\n\t\t\tinputs.token,\n\t\t\tinputs.teamId,\n\t\t\tinputs.projectId,\n\t\t\tinputs.name,\n\t\t);\n\n\t\tif (existing) {\n\t\t\tpulumi.log.info(\n\t\t\t\t`Adopted existing Vercel domain \"${inputs.name}\" on project ${inputs.projectId}`,\n\t\t\t);\n\n\t\t\tconst outs: VercelDomainOutputs = {\n\t\t\t\t...inputs,\n\t\t\t\tverified: existing.verified,\n\t\t\t};\n\n\t\t\treturn { id: `${inputs.projectId}/${inputs.name}`, outs };\n\t\t}\n\n\t\tconst response = await fetch(\n\t\t\t`${VERCEL_API_URL}/v10/projects/${encodeURIComponent(inputs.projectId)}/domains?teamId=${inputs.teamId}`,\n\t\t\t{\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\tAuthorization: `Bearer ${inputs.token}`,\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify({ name: inputs.name }),\n\t\t\t},\n\t\t);\n\n\t\tif (!response.ok) {\n\t\t\tthrow new Error(\n\t\t\t\t`Vercel API error creating domain \"${inputs.name}\" (${response.status}): ${await response.text()}`,\n\t\t\t);\n\t\t}\n\n\t\tconst created = (await response.json()) as VercelDomainResponse;\n\n\t\tconst outs: VercelDomainOutputs = { ...inputs, verified: created.verified };\n\n\t\treturn { id: `${inputs.projectId}/${inputs.name}`, outs };\n\t}\n\n\tasync read(\n\t\t_id: string,\n\t\tprops: VercelDomainOutputs,\n\t): Promise<pulumi.dynamic.ReadResult> {\n\t\tconst domain = await fetchDomain(\n\t\t\tprops.token,\n\t\t\tprops.teamId,\n\t\t\tprops.projectId,\n\t\t\tprops.name,\n\t\t);\n\n\t\tif (!domain) {\n\t\t\tthrow new Error(\n\t\t\t\t`Vercel domain \"${props.name}\" not found on project ${props.projectId}`,\n\t\t\t);\n\t\t}\n\n\t\treturn {\n\t\t\tid: `${props.projectId}/${props.name}`,\n\t\t\tprops: { ...props, verified: domain.verified },\n\t\t};\n\t}\n\n\t/** All fields replace (see `diff`) — this is never actually invoked. */\n\tasync update(\n\t\t_id: string,\n\t\t_olds: VercelDomainOutputs,\n\t\tnews: VercelDomainInputs,\n\t): Promise<pulumi.dynamic.UpdateResult> {\n\t\treturn { outs: { ...news, verified: false } };\n\t}\n\n\tasync delete(_id: string, props: VercelDomainOutputs): Promise<void> {\n\t\tconst response = await fetch(\n\t\t\t`${VERCEL_API_URL}/v9/projects/${encodeURIComponent(props.projectId)}/domains/${encodeURIComponent(props.name)}?teamId=${props.teamId}`,\n\t\t\t{\n\t\t\t\tmethod: \"DELETE\",\n\t\t\t\theaders: { Authorization: `Bearer ${props.token}` },\n\t\t\t},\n\t\t);\n\n\t\tif (response.status === 404) {\n\t\t\tpulumi.log.warn(\n\t\t\t\t`Vercel domain \"${props.name}\" already gone from project ${props.projectId}`,\n\t\t\t);\n\n\t\t\treturn;\n\t\t}\n\n\t\tif (!response.ok) {\n\t\t\tthrow new Error(\n\t\t\t\t`Vercel API error deleting domain \"${props.name}\" (${response.status}): ${await response.text()}`,\n\t\t\t);\n\t\t}\n\n\t\tpulumi.log.info(\n\t\t\t`Deleted Vercel domain \"${props.name}\" from project ${props.projectId}`,\n\t\t);\n\t}\n\n\t/**\n\t * A domain can only be attached to one project, so every field change replaces\n\t * rather than updates.\n\t */\n\tasync diff(\n\t\t_id: string,\n\t\tolds: VercelDomainOutputs,\n\t\tnews: VercelDomainInputs,\n\t): Promise<pulumi.dynamic.DiffResult> {\n\t\tconst replaces: string[] = [];\n\n\t\tif (olds.name !== news.name) {\n\t\t\treplaces.push(\"name\");\n\t\t}\n\n\t\tif (olds.projectId !== news.projectId) {\n\t\t\treplaces.push(\"projectId\");\n\t\t}\n\n\t\tif (olds.teamId !== news.teamId) {\n\t\t\treplaces.push(\"teamId\");\n\t\t}\n\n\t\treturn {\n\t\t\tchanges: replaces.length > 0,\n\t\t\treplaces,\n\t\t\tdeleteBeforeReplace: true,\n\t\t};\n\t}\n}\n\n/** Internal dynamic resource — not part of the public API. */\nclass VercelDomainResource extends pulumi.dynamic.Resource {\n\tpublic declare readonly verified: pulumi.Output<boolean>;\n\tpublic declare readonly name: pulumi.Output<string>;\n\n\tconstructor(\n\t\tname: string,\n\t\targs: {\n\t\t\ttoken: pulumi.Input<string>;\n\t\t\tteamId: pulumi.Input<string>;\n\t\t\tprojectId: pulumi.Input<string>;\n\t\t\tname: pulumi.Input<string>;\n\t\t},\n\t\topts?: pulumi.CustomResourceOptions,\n\t) {\n\t\tsuper(\n\t\t\tnew VercelDomainResourceProvider(),\n\t\t\tname,\n\t\t\t{ ...args, verified: undefined },\n\t\t\topts,\n\t\t);\n\t}\n}\n\n/** Options type for VercelDomain — replaces Pulumi's native `provider` field. */\ntype VercelDomainOptions = Omit<pulumi.ComponentResourceOptions, \"provider\"> & {\n\t/** Vercel authentication context. */\n\tprovider: VercelProvider;\n\n\t/** Vercel project to attach the domain to. */\n\tproject: VercelProject;\n};\n\n/** Args for VercelDomain. */\nexport interface VercelDomainArgs {\n\t/** Domain name (e.g. `\"app.example.com\"`). */\n\tname: pulumi.Input<string>;\n}\n\n/**\n * Attaches a custom domain to a Vercel project, with adopt-or-create semantics.\n *\n * @example\n * ```typescript\n * new VercelDomain(\"aura-domain\", { name: \"app.example.com\" }, { provider, project });\n * ```\n */\nexport class VercelDomain extends pulumi.ComponentResource {\n\t/** `true` once the domain's DNS has been verified by Vercel. */\n\tpublic readonly verified: pulumi.Output<boolean>;\n\n\t/** The attached domain name. */\n\tpublic readonly name: pulumi.Output<string>;\n\n\tconstructor(name: string, args: VercelDomainArgs, opts: VercelDomainOptions) {\n\t\tconst { provider, project, ...pulumiOpts } = opts;\n\n\t\tsuper(\"infracraft:vercel:Domain\", name, {}, pulumiOpts);\n\n\t\tconst resource = new VercelDomainResource(\n\t\t\t`${name}-resource`,\n\t\t\t{\n\t\t\t\ttoken: provider.token,\n\t\t\t\tteamId: provider.teamId,\n\t\t\t\tprojectId: project.id,\n\t\t\t\t...args,\n\t\t\t},\n\t\t\t{ parent: this },\n\t\t);\n\n\t\tthis.verified = resource.verified;\n\t\tthis.name = resource.name;\n\n\t\tthis.registerOutputs({ verified: this.verified, name: this.name });\n\t}\n}\n"],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
  const require_vercel_deploy = require('./deploy.cjs');
3
+ const require_vercel_domain = require('./domain.cjs');
3
4
  const require_vercel_integration = require('./integration.cjs');
4
5
  const require_vercel_marketplace_resource = require('./marketplace-resource.cjs');
5
6
  const require_vercel_project = require('./project.cjs');
@@ -7,8 +8,10 @@ const require_vercel_provider = require('./provider.cjs');
7
8
  const require_vercel_resource_connection = require('./resource-connection.cjs');
8
9
  const require_vercel_variable = require('./variable.cjs');
9
10
 
11
+ exports.VERCEL_CNAME_TARGET = require_vercel_domain.VERCEL_CNAME_TARGET;
10
12
  exports.VERCEL_FRAMEWORKS = require_vercel_project.VERCEL_FRAMEWORKS;
11
13
  exports.VercelDeploy = require_vercel_deploy.VercelDeploy;
14
+ exports.VercelDomain = require_vercel_domain.VercelDomain;
12
15
  exports.VercelIntegration = require_vercel_integration.VercelIntegration;
13
16
  exports.VercelMarketplaceResource = require_vercel_marketplace_resource.VercelMarketplaceResource;
14
17
  exports.VercelProject = require_vercel_project.VercelProject;
@@ -1,8 +1,9 @@
1
1
  import { VercelProvider, VercelProviderArgs } from "./provider.cjs";
2
2
  import { VERCEL_FRAMEWORKS, VercelFramework, VercelProject, VercelProjectArgs } from "./project.cjs";
3
3
  import { VercelDeploy, VercelDeployArgs } from "./deploy.cjs";
4
+ import { VERCEL_CNAME_TARGET, VercelDomain, VercelDomainArgs } from "./domain.cjs";
4
5
  import { VercelIntegration, VercelIntegrationArgs } from "./integration.cjs";
5
6
  import { VercelMarketplaceResource, VercelMarketplaceResourceArgs } from "./marketplace-resource.cjs";
6
7
  import { VercelResourceConnection, VercelResourceConnectionArgs } from "./resource-connection.cjs";
7
8
  import { VercelVariable, VercelVariableArgs } from "./variable.cjs";
8
- export { VERCEL_FRAMEWORKS, VercelDeploy, type VercelDeployArgs, type VercelFramework, VercelIntegration, type VercelIntegrationArgs, VercelMarketplaceResource, type VercelMarketplaceResourceArgs, VercelProject, type VercelProjectArgs, VercelProvider, type VercelProviderArgs, VercelResourceConnection, type VercelResourceConnectionArgs, VercelVariable, type VercelVariableArgs };
9
+ export { VERCEL_CNAME_TARGET, VERCEL_FRAMEWORKS, VercelDeploy, type VercelDeployArgs, VercelDomain, type VercelDomainArgs, type VercelFramework, VercelIntegration, type VercelIntegrationArgs, VercelMarketplaceResource, type VercelMarketplaceResourceArgs, VercelProject, type VercelProjectArgs, VercelProvider, type VercelProviderArgs, VercelResourceConnection, type VercelResourceConnectionArgs, VercelVariable, type VercelVariableArgs };
@@ -1,8 +1,9 @@
1
1
  import { VercelProvider, VercelProviderArgs } from "./provider.mjs";
2
2
  import { VERCEL_FRAMEWORKS, VercelFramework, VercelProject, VercelProjectArgs } from "./project.mjs";
3
3
  import { VercelDeploy, VercelDeployArgs } from "./deploy.mjs";
4
+ import { VERCEL_CNAME_TARGET, VercelDomain, VercelDomainArgs } from "./domain.mjs";
4
5
  import { VercelIntegration, VercelIntegrationArgs } from "./integration.mjs";
5
6
  import { VercelMarketplaceResource, VercelMarketplaceResourceArgs } from "./marketplace-resource.mjs";
6
7
  import { VercelResourceConnection, VercelResourceConnectionArgs } from "./resource-connection.mjs";
7
8
  import { VercelVariable, VercelVariableArgs } from "./variable.mjs";
8
- export { VERCEL_FRAMEWORKS, VercelDeploy, type VercelDeployArgs, type VercelFramework, VercelIntegration, type VercelIntegrationArgs, VercelMarketplaceResource, type VercelMarketplaceResourceArgs, VercelProject, type VercelProjectArgs, VercelProvider, type VercelProviderArgs, VercelResourceConnection, type VercelResourceConnectionArgs, VercelVariable, type VercelVariableArgs };
9
+ export { VERCEL_CNAME_TARGET, VERCEL_FRAMEWORKS, VercelDeploy, type VercelDeployArgs, VercelDomain, type VercelDomainArgs, type VercelFramework, VercelIntegration, type VercelIntegrationArgs, VercelMarketplaceResource, type VercelMarketplaceResourceArgs, VercelProject, type VercelProjectArgs, VercelProvider, type VercelProviderArgs, VercelResourceConnection, type VercelResourceConnectionArgs, VercelVariable, type VercelVariableArgs };
@@ -1,4 +1,5 @@
1
1
  import { VercelDeploy } from "./deploy.mjs";
2
+ import { VERCEL_CNAME_TARGET, VercelDomain } from "./domain.mjs";
2
3
  import { VercelIntegration } from "./integration.mjs";
3
4
  import { VercelMarketplaceResource } from "./marketplace-resource.mjs";
4
5
  import { VERCEL_FRAMEWORKS, VercelProject } from "./project.mjs";
@@ -6,4 +7,4 @@ import { VercelProvider } from "./provider.mjs";
6
7
  import { VercelResourceConnection } from "./resource-connection.mjs";
7
8
  import { VercelVariable } from "./variable.mjs";
8
9
 
9
- export { VERCEL_FRAMEWORKS, VercelDeploy, VercelIntegration, VercelMarketplaceResource, VercelProject, VercelProvider, VercelResourceConnection, VercelVariable };
10
+ export { VERCEL_CNAME_TARGET, VERCEL_FRAMEWORKS, VercelDeploy, VercelDomain, VercelIntegration, VercelMarketplaceResource, VercelProject, VercelProvider, VercelResourceConnection, VercelVariable };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@infracraft/pulumi",
3
- "version": "1.17.4",
3
+ "version": "1.18.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "Native Pulumi providers for Railway, Neon, Vercel, and Fly.io with adopt-or-create semantics and deploy orchestration. No Terraform bridge.",