@infracraft/pulumi 1.18.0 → 1.19.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.
Potentially problematic release.
This version of @infracraft/pulumi might be problematic. Click here for more details.
- package/dist/railway/domain.cjs +40 -9
- package/dist/railway/domain.cjs.map +1 -1
- package/dist/railway/domain.d.cts +57 -3
- package/dist/railway/domain.d.cts.map +1 -1
- package/dist/railway/domain.d.mts +57 -3
- package/dist/railway/domain.d.mts.map +1 -1
- package/dist/railway/domain.mjs +40 -10
- package/dist/railway/domain.mjs.map +1 -1
- package/package.json +1 -1
package/dist/railway/domain.cjs
CHANGED
|
@@ -5,6 +5,18 @@ let _pulumi_pulumi = require("@pulumi/pulumi");
|
|
|
5
5
|
_pulumi_pulumi = require_chunk.__toESM(_pulumi_pulumi, 1);
|
|
6
6
|
|
|
7
7
|
//#region src/railway/domain.ts
|
|
8
|
+
/**
|
|
9
|
+
* Picks the CNAME record Railway expects for routing traffic to a custom domain out
|
|
10
|
+
* of its full DNS record list (which also includes e.g. an ACME challenge TXT record).
|
|
11
|
+
*/
|
|
12
|
+
function extractCnameTarget(dnsRecords) {
|
|
13
|
+
return dnsRecords?.find((record) => record.recordType === "DNS_RECORD_TYPE_CNAME" && record.purpose === "DNS_RECORD_PURPOSE_TRAFFIC_ROUTE")?.requiredValue;
|
|
14
|
+
}
|
|
15
|
+
const DOMAIN_STATUS_FIELDS = `
|
|
16
|
+
status {
|
|
17
|
+
dnsRecords { recordType purpose requiredValue }
|
|
18
|
+
}
|
|
19
|
+
`;
|
|
8
20
|
const SERVICE_DOMAINS_QUERY = `
|
|
9
21
|
query($projectId: String!, $serviceId: String!, $environmentId: String!) {
|
|
10
22
|
domains(
|
|
@@ -13,7 +25,7 @@ const SERVICE_DOMAINS_QUERY = `
|
|
|
13
25
|
environmentId: $environmentId
|
|
14
26
|
) {
|
|
15
27
|
serviceDomains { id domain }
|
|
16
|
-
customDomains { id domain }
|
|
28
|
+
customDomains { id domain ${DOMAIN_STATUS_FIELDS} }
|
|
17
29
|
}
|
|
18
30
|
}
|
|
19
31
|
`;
|
|
@@ -24,7 +36,7 @@ const SERVICE_DOMAIN_CREATE = `
|
|
|
24
36
|
`;
|
|
25
37
|
const CUSTOM_DOMAIN_CREATE = `
|
|
26
38
|
mutation($input: CustomDomainCreateInput!) {
|
|
27
|
-
customDomainCreate(input: $input) { id domain }
|
|
39
|
+
customDomainCreate(input: $input) { id domain ${DOMAIN_STATUS_FIELDS} }
|
|
28
40
|
}
|
|
29
41
|
`;
|
|
30
42
|
const CUSTOM_DOMAIN_DELETE = `
|
|
@@ -48,6 +60,8 @@ async function findExistingDomains(client, projectId, serviceId, environmentId)
|
|
|
48
60
|
*
|
|
49
61
|
* Uses adopt-or-create: queries existing domains before creating new ones.
|
|
50
62
|
* Uses the FQDN as the Pulumi resource ID.
|
|
63
|
+
*
|
|
64
|
+
* @internal Exported only for unit testing; not part of the public API surface.
|
|
51
65
|
*/
|
|
52
66
|
var RailwayDomainResourceProvider = class {
|
|
53
67
|
async create(inputs) {
|
|
@@ -62,7 +76,8 @@ var RailwayDomainResourceProvider = class {
|
|
|
62
76
|
outs: {
|
|
63
77
|
...inputs,
|
|
64
78
|
domainId: found.id,
|
|
65
|
-
fqdn: found.domain
|
|
79
|
+
fqdn: found.domain,
|
|
80
|
+
cnameTarget: extractCnameTarget(found.status?.dnsRecords)
|
|
66
81
|
}
|
|
67
82
|
};
|
|
68
83
|
}
|
|
@@ -77,7 +92,8 @@ var RailwayDomainResourceProvider = class {
|
|
|
77
92
|
outs: {
|
|
78
93
|
...inputs,
|
|
79
94
|
domainId: result.customDomainCreate.id,
|
|
80
|
-
fqdn: result.customDomainCreate.domain
|
|
95
|
+
fqdn: result.customDomainCreate.domain,
|
|
96
|
+
cnameTarget: extractCnameTarget(result.customDomainCreate.status?.dnsRecords)
|
|
81
97
|
}
|
|
82
98
|
};
|
|
83
99
|
}
|
|
@@ -116,7 +132,8 @@ var RailwayDomainResourceProvider = class {
|
|
|
116
132
|
props: {
|
|
117
133
|
...props,
|
|
118
134
|
domainId: found.id,
|
|
119
|
-
fqdn: found.domain
|
|
135
|
+
fqdn: found.domain,
|
|
136
|
+
cnameTarget: extractCnameTarget(found.status?.dnsRecords)
|
|
120
137
|
}
|
|
121
138
|
};
|
|
122
139
|
}
|
|
@@ -160,20 +177,29 @@ var RailwayDomainResource = class extends _pulumi_pulumi.dynamic.Resource {
|
|
|
160
177
|
super(new RailwayDomainResourceProvider(), name, {
|
|
161
178
|
...args,
|
|
162
179
|
domainId: void 0,
|
|
163
|
-
fqdn: void 0
|
|
180
|
+
fqdn: void 0,
|
|
181
|
+
cnameTarget: void 0
|
|
164
182
|
}, opts);
|
|
165
183
|
}
|
|
166
184
|
};
|
|
167
185
|
/**
|
|
168
186
|
* Manages a Railway domain (service or custom) with adopt-or-create semantics.
|
|
169
187
|
*
|
|
188
|
+
* A service can carry more than one custom domain — declare one `RailwayDomain` per
|
|
189
|
+
* domain; each instance adopts, creates, and deletes only its own domain.
|
|
190
|
+
*
|
|
170
191
|
* @example
|
|
171
192
|
* ```typescript
|
|
172
|
-
* const
|
|
193
|
+
* const apiDomain = new RailwayDomain("api-domain", { customDomain: "api.example.com" }, {
|
|
194
|
+
* provider, project, environment, service,
|
|
195
|
+
* });
|
|
196
|
+
* const wwwDomain = new RailwayDomain("www-domain", { customDomain: "www.example.com" }, {
|
|
173
197
|
* provider, project, environment, service,
|
|
174
198
|
* });
|
|
175
199
|
*
|
|
176
|
-
*
|
|
200
|
+
* // Point each domain's DNS CNAME at its own target.
|
|
201
|
+
* const apiCnameTarget = apiDomain.cnameTarget;
|
|
202
|
+
* const wwwCnameTarget = wwwDomain.cnameTarget;
|
|
177
203
|
* ```
|
|
178
204
|
*/
|
|
179
205
|
var RailwayDomain = class extends _pulumi_pulumi.ComponentResource {
|
|
@@ -188,10 +214,15 @@ var RailwayDomain = class extends _pulumi_pulumi.ComponentResource {
|
|
|
188
214
|
customDomain: args.customDomain
|
|
189
215
|
}, { parent: this });
|
|
190
216
|
this.fqdn = resource.fqdn;
|
|
191
|
-
this.
|
|
217
|
+
this.cnameTarget = resource.cnameTarget;
|
|
218
|
+
this.registerOutputs({
|
|
219
|
+
fqdn: this.fqdn,
|
|
220
|
+
cnameTarget: this.cnameTarget
|
|
221
|
+
});
|
|
192
222
|
}
|
|
193
223
|
};
|
|
194
224
|
|
|
195
225
|
//#endregion
|
|
196
226
|
exports.RailwayDomain = RailwayDomain;
|
|
227
|
+
exports.RailwayDomainResourceProvider = RailwayDomainResourceProvider;
|
|
197
228
|
//# sourceMappingURL=domain.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"domain.cjs","names":["RailwayClient","pulumi"],"sources":["../../src/railway/domain.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\";\nimport type { RailwayService } from \"./service\";\n\n/** Resolved inputs for the Railway domain dynamic provider. */\ninterface RailwayDomainInputs {\n\t/** Railway API bearer token. */\n\ttoken: string;\n\n\t/** Railway project UUID. */\n\tprojectId: string;\n\n\t/** Railway service UUID to attach the domain to. */\n\tserviceId: string;\n\n\t/** Railway environment UUID (e.g. production). */\n\tenvironmentId: string;\n\n\t/** Custom domain FQDN (e.g. `\"api.example.com\"`). Omit for auto-generated Railway domain. */\n\tcustomDomain?: string;\n}\n\n/** Persisted state for the Railway domain, extending inputs with Railway-assigned identifiers. */\ninterface RailwayDomainOutputs extends RailwayDomainInputs {\n\t/** Railway-assigned domain UUID (used for deletion API calls). */\n\tdomainId: string;\n\n\t/** Fully qualified domain name (e.g. `\"api-production-abc.up.railway.app\"`). */\n\tfqdn: string;\n}\n\nconst SERVICE_DOMAINS_QUERY = `\n query($projectId: String!, $serviceId: String!, $environmentId: String!) {\n domains(\n projectId: $projectId\n serviceId: $serviceId\n environmentId: $environmentId\n ) {\n serviceDomains { id domain }\n customDomains { id domain }\n }\n }\n`;\n\nconst SERVICE_DOMAIN_CREATE = `\n mutation($input: ServiceDomainCreateInput!) {\n serviceDomainCreate(input: $input) { id domain }\n }\n`;\n\nconst CUSTOM_DOMAIN_CREATE = `\n mutation($input: CustomDomainCreateInput!) {\n customDomainCreate(input: $input) { id domain }\n }\n`;\n\nconst CUSTOM_DOMAIN_DELETE = `\n mutation($id: String!) { customDomainDelete(id: $id) }\n`;\n\nconst SERVICE_DOMAIN_DELETE = `\n mutation($id: String!) { serviceDomainDelete(id: $id) }\n`;\n\n/**\n * Queries all existing domains (service and custom) for a Railway service.\n */\nasync function findExistingDomains(\n\tclient: RailwayClient,\n\tprojectId: string,\n\tserviceId: string,\n\tenvironmentId: string,\n): Promise<{\n\tserviceDomains: Array<{ id: string; domain: string }>;\n\tcustomDomains: Array<{ id: string; domain: string }>;\n}> {\n\tconst result = await client.query<{\n\t\tdomains: {\n\t\t\tserviceDomains: Array<{ id: string; domain: string }>;\n\t\t\tcustomDomains: Array<{ id: string; domain: string }>;\n\t\t};\n\t}>(SERVICE_DOMAINS_QUERY, { projectId, serviceId, environmentId });\n\n\treturn result.domains;\n}\n\n/**\n * Dynamic provider implementing CRUD for Railway domains.\n *\n * Uses adopt-or-create: queries existing domains before creating new ones.\n * Uses the FQDN as the Pulumi resource ID.\n */\nclass RailwayDomainResourceProvider implements pulumi.dynamic.ResourceProvider {\n\tasync create(\n\t\tinputs: RailwayDomainInputs,\n\t): Promise<pulumi.dynamic.CreateResult> {\n\t\tconst client = new RailwayClient(inputs.token);\n\n\t\tconst existing = await findExistingDomains(\n\t\t\tclient,\n\t\t\tinputs.projectId,\n\t\t\tinputs.serviceId,\n\t\t\tinputs.environmentId,\n\t\t);\n\n\t\tif (inputs.customDomain) {\n\t\t\tconst found = existing.customDomains.find(\n\t\t\t\t(d) => d.domain === inputs.customDomain,\n\t\t\t);\n\n\t\t\tif (found) {\n\t\t\t\tpulumi.log.info(`Adopting existing custom domain \"${found.domain}\"`);\n\n\t\t\t\treturn {\n\t\t\t\t\tid: found.domain,\n\t\t\t\t\touts: { ...inputs, domainId: found.id, fqdn: found.domain },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst result = await client.query<{\n\t\t\t\tcustomDomainCreate: { id: string; domain: string };\n\t\t\t}>(CUSTOM_DOMAIN_CREATE, {\n\t\t\t\tinput: {\n\t\t\t\t\tprojectId: inputs.projectId,\n\t\t\t\t\tserviceId: inputs.serviceId,\n\t\t\t\t\tenvironmentId: inputs.environmentId,\n\t\t\t\t\tdomain: inputs.customDomain,\n\t\t\t\t},\n\t\t\t});\n\n\t\t\treturn {\n\t\t\t\tid: result.customDomainCreate.domain,\n\t\t\t\touts: {\n\t\t\t\t\t...inputs,\n\t\t\t\t\tdomainId: result.customDomainCreate.id,\n\t\t\t\t\tfqdn: result.customDomainCreate.domain,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tif (existing.serviceDomains.length > 0) {\n\t\t\tconst found = existing.serviceDomains[0];\n\n\t\t\tpulumi.log.info(`Adopting existing service domain \"${found.domain}\"`);\n\n\t\t\treturn {\n\t\t\t\tid: found.domain,\n\t\t\t\touts: { ...inputs, domainId: found.id, fqdn: found.domain },\n\t\t\t};\n\t\t}\n\n\t\tconst result = await client.query<{\n\t\t\tserviceDomainCreate: { id: string; domain: string };\n\t\t}>(SERVICE_DOMAIN_CREATE, {\n\t\t\tinput: {\n\t\t\t\tserviceId: inputs.serviceId,\n\t\t\t\tenvironmentId: inputs.environmentId,\n\t\t\t},\n\t\t});\n\n\t\treturn {\n\t\t\tid: result.serviceDomainCreate.domain,\n\t\t\touts: {\n\t\t\t\t...inputs,\n\t\t\t\tdomainId: result.serviceDomainCreate.id,\n\t\t\t\tfqdn: result.serviceDomainCreate.domain,\n\t\t\t},\n\t\t};\n\t}\n\n\tasync read(\n\t\t_id: string,\n\t\tprops: RailwayDomainOutputs,\n\t): Promise<pulumi.dynamic.ReadResult> {\n\t\tconst client = new RailwayClient(props.token);\n\n\t\tconst existing = await findExistingDomains(\n\t\t\tclient,\n\t\t\tprops.projectId,\n\t\t\tprops.serviceId,\n\t\t\tprops.environmentId,\n\t\t);\n\n\t\tif (props.customDomain) {\n\t\t\tconst found = existing.customDomains.find(\n\t\t\t\t(d) => d.domain === props.customDomain,\n\t\t\t);\n\n\t\t\tif (!found) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Custom domain \"${props.customDomain}\" not found during refresh`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tid: found.domain,\n\t\t\t\tprops: { ...props, domainId: found.id, fqdn: found.domain },\n\t\t\t};\n\t\t}\n\n\t\tif (existing.serviceDomains.length > 0) {\n\t\t\tconst found = existing.serviceDomains[0];\n\n\t\t\treturn {\n\t\t\t\tid: found.domain,\n\t\t\t\tprops: { ...props, domainId: found.id, fqdn: found.domain },\n\t\t\t};\n\t\t}\n\n\t\tthrow new Error(\"Railway domain not found during refresh\");\n\t}\n\n\tasync delete(_id: string, props: RailwayDomainOutputs): Promise<void> {\n\t\tconst client = new RailwayClient(props.token);\n\n\t\tconst mutation = props.customDomain\n\t\t\t? CUSTOM_DOMAIN_DELETE\n\t\t\t: SERVICE_DOMAIN_DELETE;\n\n\t\ttry {\n\t\t\tawait client.query(mutation, { id: props.domainId });\n\t\t} catch {\n\t\t\tpulumi.log.warn(\n\t\t\t\t\"Failed to delete Railway domain (may already be deleted)\",\n\t\t\t);\n\t\t}\n\t}\n\n\tasync diff(\n\t\t_id: string,\n\t\tolds: RailwayDomainOutputs,\n\t\tnews: RailwayDomainInputs,\n\t): Promise<pulumi.dynamic.DiffResult> {\n\t\tconst replaces: string[] = [];\n\n\t\tif (olds.serviceId !== news.serviceId) {\n\t\t\treplaces.push(\"serviceId\");\n\t\t}\n\n\t\tif (olds.customDomain !== news.customDomain) {\n\t\t\treplaces.push(\"customDomain\");\n\t\t}\n\n\t\tif (olds.environmentId !== news.environmentId) {\n\t\t\treplaces.push(\"environmentId\");\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 RailwayDomainResource extends pulumi.dynamic.Resource {\n\tpublic declare readonly fqdn: 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\tserviceId: pulumi.Input<string>;\n\t\t\tenvironmentId: pulumi.Input<string>;\n\t\t\tcustomDomain?: pulumi.Input<string>;\n\t\t},\n\t\topts?: pulumi.CustomResourceOptions,\n\t) {\n\t\tsuper(\n\t\t\tnew RailwayDomainResourceProvider(),\n\t\t\tname,\n\t\t\t{ ...args, domainId: undefined, fqdn: undefined },\n\t\t\topts,\n\t\t);\n\t}\n}\n\n/** Options type for RailwayDomain — replaces Pulumi's native `provider` field. */\ntype RailwayDomainOptions = 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\t/** Railway service context. */\n\tservice: RailwayService;\n};\n\n/** Args for RailwayDomain. */\nexport interface RailwayDomainArgs {\n\t/** Custom domain FQDN. Omit to create an auto-generated Railway service domain. */\n\tcustomDomain?: pulumi.Input<string>;\n}\n\n/**\n * Manages a Railway domain (service or custom) with adopt-or-create semantics.\n *\n * @example\n * ```typescript\n * const domain = new RailwayDomain(\"api-domain\", {}, {\n * provider, project, environment, service,\n * });\n *\n * const url = pulumi.interpolate`https://${domain.fqdn}`;\n * ```\n */\nexport class RailwayDomain extends pulumi.ComponentResource {\n\t/** Fully qualified domain name. */\n\tpublic readonly fqdn: pulumi.Output<string>;\n\n\tconstructor(\n\t\tname: string,\n\t\targs: RailwayDomainArgs,\n\t\topts: RailwayDomainOptions,\n\t) {\n\t\tconst { provider, project, environment, service, ...pulumiOpts } = opts;\n\n\t\tsuper(\"infracraft:railway:Domain\", name, {}, pulumiOpts);\n\n\t\tconst resource = new RailwayDomainResource(\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\tserviceId: service.id,\n\t\t\t\tenvironmentId: environment.id,\n\t\t\t\tcustomDomain: args.customDomain,\n\t\t\t},\n\t\t\t{ parent: this },\n\t\t);\n\n\t\tthis.fqdn = resource.fqdn;\n\n\t\tthis.registerOutputs({ fqdn: this.fqdn });\n\t}\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"domain.cjs","names":["RailwayClient","pulumi"],"sources":["../../src/railway/domain.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\";\nimport type { RailwayService } from \"./service\";\n\n/** Resolved inputs for the Railway domain dynamic provider. */\ninterface RailwayDomainInputs {\n\t/** Railway API bearer token. */\n\ttoken: string;\n\n\t/** Railway project UUID. */\n\tprojectId: string;\n\n\t/** Railway service UUID to attach the domain to. */\n\tserviceId: string;\n\n\t/** Railway environment UUID (e.g. production). */\n\tenvironmentId: string;\n\n\t/** Custom domain FQDN (e.g. `\"api.example.com\"`). Omit for auto-generated Railway domain. */\n\tcustomDomain?: string;\n}\n\n/** Persisted state for the Railway domain, extending inputs with Railway-assigned identifiers. */\ninterface RailwayDomainOutputs extends RailwayDomainInputs {\n\t/** Railway-assigned domain UUID (used for deletion API calls). */\n\tdomainId: string;\n\n\t/** Fully qualified domain name (e.g. `\"api-production-abc.up.railway.app\"`). */\n\tfqdn: string;\n\n\t/**\n\t * The value to CNAME `customDomain` to. Only present for custom domains — a\n\t * service's auto-generated `*.up.railway.app` domain needs no DNS record of its\n\t * own. `undefined` if Railway hasn't returned a traffic-routing CNAME record yet.\n\t */\n\tcnameTarget?: string;\n}\n\n/** A single DNS record Railway expects for a custom domain (CNAME target, ACME challenge, ...). */\ninterface DomainDnsRecord {\n\trecordType: string;\n\tpurpose: string;\n\trequiredValue: string;\n}\n\n/** Shape returned for a custom domain, including its required DNS records. */\ninterface CustomDomainEntry {\n\tid: string;\n\tdomain: string;\n\tstatus?: { dnsRecords: DomainDnsRecord[] };\n}\n\n/**\n * Picks the CNAME record Railway expects for routing traffic to a custom domain out\n * of its full DNS record list (which also includes e.g. an ACME challenge TXT record).\n */\nfunction extractCnameTarget(\n\tdnsRecords: DomainDnsRecord[] | undefined,\n): string | undefined {\n\treturn dnsRecords?.find(\n\t\t(record) =>\n\t\t\trecord.recordType === \"DNS_RECORD_TYPE_CNAME\" &&\n\t\t\trecord.purpose === \"DNS_RECORD_PURPOSE_TRAFFIC_ROUTE\",\n\t)?.requiredValue;\n}\n\nconst DOMAIN_STATUS_FIELDS = `\n status {\n dnsRecords { recordType purpose requiredValue }\n }\n`;\n\nconst SERVICE_DOMAINS_QUERY = `\n query($projectId: String!, $serviceId: String!, $environmentId: String!) {\n domains(\n projectId: $projectId\n serviceId: $serviceId\n environmentId: $environmentId\n ) {\n serviceDomains { id domain }\n customDomains { id domain ${DOMAIN_STATUS_FIELDS} }\n }\n }\n`;\n\nconst SERVICE_DOMAIN_CREATE = `\n mutation($input: ServiceDomainCreateInput!) {\n serviceDomainCreate(input: $input) { id domain }\n }\n`;\n\nconst CUSTOM_DOMAIN_CREATE = `\n mutation($input: CustomDomainCreateInput!) {\n customDomainCreate(input: $input) { id domain ${DOMAIN_STATUS_FIELDS} }\n }\n`;\n\nconst CUSTOM_DOMAIN_DELETE = `\n mutation($id: String!) { customDomainDelete(id: $id) }\n`;\n\nconst SERVICE_DOMAIN_DELETE = `\n mutation($id: String!) { serviceDomainDelete(id: $id) }\n`;\n\n/**\n * Queries all existing domains (service and custom) for a Railway service.\n */\nasync function findExistingDomains(\n\tclient: RailwayClient,\n\tprojectId: string,\n\tserviceId: string,\n\tenvironmentId: string,\n): Promise<{\n\tserviceDomains: Array<{ id: string; domain: string }>;\n\tcustomDomains: CustomDomainEntry[];\n}> {\n\tconst result = await client.query<{\n\t\tdomains: {\n\t\t\tserviceDomains: Array<{ id: string; domain: string }>;\n\t\t\tcustomDomains: CustomDomainEntry[];\n\t\t};\n\t}>(SERVICE_DOMAINS_QUERY, { projectId, serviceId, environmentId });\n\n\treturn result.domains;\n}\n\n/**\n * Dynamic provider implementing CRUD for Railway domains.\n *\n * Uses adopt-or-create: queries existing domains before creating new ones.\n * Uses the FQDN as the Pulumi resource ID.\n *\n * @internal Exported only for unit testing; not part of the public API surface.\n */\nexport class RailwayDomainResourceProvider\n\timplements pulumi.dynamic.ResourceProvider\n{\n\tasync create(\n\t\tinputs: RailwayDomainInputs,\n\t): Promise<pulumi.dynamic.CreateResult> {\n\t\tconst client = new RailwayClient(inputs.token);\n\n\t\tconst existing = await findExistingDomains(\n\t\t\tclient,\n\t\t\tinputs.projectId,\n\t\t\tinputs.serviceId,\n\t\t\tinputs.environmentId,\n\t\t);\n\n\t\tif (inputs.customDomain) {\n\t\t\tconst found = existing.customDomains.find(\n\t\t\t\t(d) => d.domain === inputs.customDomain,\n\t\t\t);\n\n\t\t\tif (found) {\n\t\t\t\tpulumi.log.info(`Adopting existing custom domain \"${found.domain}\"`);\n\n\t\t\t\treturn {\n\t\t\t\t\tid: found.domain,\n\t\t\t\t\touts: {\n\t\t\t\t\t\t...inputs,\n\t\t\t\t\t\tdomainId: found.id,\n\t\t\t\t\t\tfqdn: found.domain,\n\t\t\t\t\t\tcnameTarget: extractCnameTarget(found.status?.dnsRecords),\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst result = await client.query<{\n\t\t\t\tcustomDomainCreate: CustomDomainEntry;\n\t\t\t}>(CUSTOM_DOMAIN_CREATE, {\n\t\t\t\tinput: {\n\t\t\t\t\tprojectId: inputs.projectId,\n\t\t\t\t\tserviceId: inputs.serviceId,\n\t\t\t\t\tenvironmentId: inputs.environmentId,\n\t\t\t\t\tdomain: inputs.customDomain,\n\t\t\t\t},\n\t\t\t});\n\n\t\t\treturn {\n\t\t\t\tid: result.customDomainCreate.domain,\n\t\t\t\touts: {\n\t\t\t\t\t...inputs,\n\t\t\t\t\tdomainId: result.customDomainCreate.id,\n\t\t\t\t\tfqdn: result.customDomainCreate.domain,\n\t\t\t\t\tcnameTarget: extractCnameTarget(\n\t\t\t\t\t\tresult.customDomainCreate.status?.dnsRecords,\n\t\t\t\t\t),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tif (existing.serviceDomains.length > 0) {\n\t\t\tconst found = existing.serviceDomains[0];\n\n\t\t\tpulumi.log.info(`Adopting existing service domain \"${found.domain}\"`);\n\n\t\t\treturn {\n\t\t\t\tid: found.domain,\n\t\t\t\touts: { ...inputs, domainId: found.id, fqdn: found.domain },\n\t\t\t};\n\t\t}\n\n\t\tconst result = await client.query<{\n\t\t\tserviceDomainCreate: { id: string; domain: string };\n\t\t}>(SERVICE_DOMAIN_CREATE, {\n\t\t\tinput: {\n\t\t\t\tserviceId: inputs.serviceId,\n\t\t\t\tenvironmentId: inputs.environmentId,\n\t\t\t},\n\t\t});\n\n\t\treturn {\n\t\t\tid: result.serviceDomainCreate.domain,\n\t\t\touts: {\n\t\t\t\t...inputs,\n\t\t\t\tdomainId: result.serviceDomainCreate.id,\n\t\t\t\tfqdn: result.serviceDomainCreate.domain,\n\t\t\t},\n\t\t};\n\t}\n\n\tasync read(\n\t\t_id: string,\n\t\tprops: RailwayDomainOutputs,\n\t): Promise<pulumi.dynamic.ReadResult> {\n\t\tconst client = new RailwayClient(props.token);\n\n\t\tconst existing = await findExistingDomains(\n\t\t\tclient,\n\t\t\tprops.projectId,\n\t\t\tprops.serviceId,\n\t\t\tprops.environmentId,\n\t\t);\n\n\t\tif (props.customDomain) {\n\t\t\tconst found = existing.customDomains.find(\n\t\t\t\t(d) => d.domain === props.customDomain,\n\t\t\t);\n\n\t\t\tif (!found) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Custom domain \"${props.customDomain}\" not found during refresh`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tid: found.domain,\n\t\t\t\tprops: {\n\t\t\t\t\t...props,\n\t\t\t\t\tdomainId: found.id,\n\t\t\t\t\tfqdn: found.domain,\n\t\t\t\t\tcnameTarget: extractCnameTarget(found.status?.dnsRecords),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tif (existing.serviceDomains.length > 0) {\n\t\t\tconst found = existing.serviceDomains[0];\n\n\t\t\treturn {\n\t\t\t\tid: found.domain,\n\t\t\t\tprops: { ...props, domainId: found.id, fqdn: found.domain },\n\t\t\t};\n\t\t}\n\n\t\tthrow new Error(\"Railway domain not found during refresh\");\n\t}\n\n\tasync delete(_id: string, props: RailwayDomainOutputs): Promise<void> {\n\t\tconst client = new RailwayClient(props.token);\n\n\t\tconst mutation = props.customDomain\n\t\t\t? CUSTOM_DOMAIN_DELETE\n\t\t\t: SERVICE_DOMAIN_DELETE;\n\n\t\ttry {\n\t\t\tawait client.query(mutation, { id: props.domainId });\n\t\t} catch {\n\t\t\tpulumi.log.warn(\n\t\t\t\t\"Failed to delete Railway domain (may already be deleted)\",\n\t\t\t);\n\t\t}\n\t}\n\n\tasync diff(\n\t\t_id: string,\n\t\tolds: RailwayDomainOutputs,\n\t\tnews: RailwayDomainInputs,\n\t): Promise<pulumi.dynamic.DiffResult> {\n\t\tconst replaces: string[] = [];\n\n\t\tif (olds.serviceId !== news.serviceId) {\n\t\t\treplaces.push(\"serviceId\");\n\t\t}\n\n\t\tif (olds.customDomain !== news.customDomain) {\n\t\t\treplaces.push(\"customDomain\");\n\t\t}\n\n\t\tif (olds.environmentId !== news.environmentId) {\n\t\t\treplaces.push(\"environmentId\");\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 RailwayDomainResource extends pulumi.dynamic.Resource {\n\tpublic declare readonly fqdn: pulumi.Output<string>;\n\tpublic declare readonly cnameTarget: pulumi.Output<string | undefined>;\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\tserviceId: pulumi.Input<string>;\n\t\t\tenvironmentId: pulumi.Input<string>;\n\t\t\tcustomDomain?: pulumi.Input<string>;\n\t\t},\n\t\topts?: pulumi.CustomResourceOptions,\n\t) {\n\t\tsuper(\n\t\t\tnew RailwayDomainResourceProvider(),\n\t\t\tname,\n\t\t\t{ ...args, domainId: undefined, fqdn: undefined, cnameTarget: undefined },\n\t\t\topts,\n\t\t);\n\t}\n}\n\n/** Options type for RailwayDomain — replaces Pulumi's native `provider` field. */\ntype RailwayDomainOptions = 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\t/** Railway service context. */\n\tservice: RailwayService;\n};\n\n/** Args for RailwayDomain. */\nexport interface RailwayDomainArgs {\n\t/** Custom domain FQDN. Omit to create an auto-generated Railway service domain. */\n\tcustomDomain?: pulumi.Input<string>;\n}\n\n/**\n * Manages a Railway domain (service or custom) with adopt-or-create semantics.\n *\n * A service can carry more than one custom domain — declare one `RailwayDomain` per\n * domain; each instance adopts, creates, and deletes only its own domain.\n *\n * @example\n * ```typescript\n * const apiDomain = new RailwayDomain(\"api-domain\", { customDomain: \"api.example.com\" }, {\n * provider, project, environment, service,\n * });\n * const wwwDomain = new RailwayDomain(\"www-domain\", { customDomain: \"www.example.com\" }, {\n * provider, project, environment, service,\n * });\n *\n * // Point each domain's DNS CNAME at its own target.\n * const apiCnameTarget = apiDomain.cnameTarget;\n * const wwwCnameTarget = wwwDomain.cnameTarget;\n * ```\n */\nexport class RailwayDomain extends pulumi.ComponentResource {\n\t/** Fully qualified domain name. */\n\tpublic readonly fqdn: pulumi.Output<string>;\n\n\t/**\n\t * The value to CNAME `customDomain` to. Only set for custom domains — `undefined`\n\t * for a service's auto-generated `*.up.railway.app` domain, or if Railway hasn't\n\t * returned a traffic-routing CNAME record for it yet.\n\t */\n\tpublic readonly cnameTarget: pulumi.Output<string | undefined>;\n\n\tconstructor(\n\t\tname: string,\n\t\targs: RailwayDomainArgs,\n\t\topts: RailwayDomainOptions,\n\t) {\n\t\tconst { provider, project, environment, service, ...pulumiOpts } = opts;\n\n\t\tsuper(\"infracraft:railway:Domain\", name, {}, pulumiOpts);\n\n\t\tconst resource = new RailwayDomainResource(\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\tserviceId: service.id,\n\t\t\t\tenvironmentId: environment.id,\n\t\t\t\tcustomDomain: args.customDomain,\n\t\t\t},\n\t\t\t{ parent: this },\n\t\t);\n\n\t\tthis.fqdn = resource.fqdn;\n\t\tthis.cnameTarget = resource.cnameTarget;\n\n\t\tthis.registerOutputs({ fqdn: this.fqdn, cnameTarget: this.cnameTarget });\n\t}\n}\n"],"mappings":"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"}
|
|
@@ -6,6 +6,46 @@ import { RailwayService } from "./service.cjs";
|
|
|
6
6
|
import * as pulumi from "@pulumi/pulumi";
|
|
7
7
|
|
|
8
8
|
//#region src/railway/domain.d.ts
|
|
9
|
+
/** Resolved inputs for the Railway domain dynamic provider. */
|
|
10
|
+
interface RailwayDomainInputs {
|
|
11
|
+
/** Railway API bearer token. */
|
|
12
|
+
token: string;
|
|
13
|
+
/** Railway project UUID. */
|
|
14
|
+
projectId: string;
|
|
15
|
+
/** Railway service UUID to attach the domain to. */
|
|
16
|
+
serviceId: string;
|
|
17
|
+
/** Railway environment UUID (e.g. production). */
|
|
18
|
+
environmentId: string;
|
|
19
|
+
/** Custom domain FQDN (e.g. `"api.example.com"`). Omit for auto-generated Railway domain. */
|
|
20
|
+
customDomain?: string;
|
|
21
|
+
}
|
|
22
|
+
/** Persisted state for the Railway domain, extending inputs with Railway-assigned identifiers. */
|
|
23
|
+
interface RailwayDomainOutputs extends RailwayDomainInputs {
|
|
24
|
+
/** Railway-assigned domain UUID (used for deletion API calls). */
|
|
25
|
+
domainId: string;
|
|
26
|
+
/** Fully qualified domain name (e.g. `"api-production-abc.up.railway.app"`). */
|
|
27
|
+
fqdn: string;
|
|
28
|
+
/**
|
|
29
|
+
* The value to CNAME `customDomain` to. Only present for custom domains — a
|
|
30
|
+
* service's auto-generated `*.up.railway.app` domain needs no DNS record of its
|
|
31
|
+
* own. `undefined` if Railway hasn't returned a traffic-routing CNAME record yet.
|
|
32
|
+
*/
|
|
33
|
+
cnameTarget?: string;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Dynamic provider implementing CRUD for Railway domains.
|
|
37
|
+
*
|
|
38
|
+
* Uses adopt-or-create: queries existing domains before creating new ones.
|
|
39
|
+
* Uses the FQDN as the Pulumi resource ID.
|
|
40
|
+
*
|
|
41
|
+
* @internal Exported only for unit testing; not part of the public API surface.
|
|
42
|
+
*/
|
|
43
|
+
declare class RailwayDomainResourceProvider implements pulumi.dynamic.ResourceProvider {
|
|
44
|
+
create(inputs: RailwayDomainInputs): Promise<pulumi.dynamic.CreateResult>;
|
|
45
|
+
read(_id: string, props: RailwayDomainOutputs): Promise<pulumi.dynamic.ReadResult>;
|
|
46
|
+
delete(_id: string, props: RailwayDomainOutputs): Promise<void>;
|
|
47
|
+
diff(_id: string, olds: RailwayDomainOutputs, news: RailwayDomainInputs): Promise<pulumi.dynamic.DiffResult>;
|
|
48
|
+
}
|
|
9
49
|
/** Options type for RailwayDomain — replaces Pulumi's native `provider` field. */
|
|
10
50
|
type RailwayDomainOptions = Omit<pulumi.ComponentResourceOptions, "provider"> & {
|
|
11
51
|
/** Railway authentication context. */provider: RailwayProvider; /** Railway project context. */
|
|
@@ -21,20 +61,34 @@ interface RailwayDomainArgs {
|
|
|
21
61
|
/**
|
|
22
62
|
* Manages a Railway domain (service or custom) with adopt-or-create semantics.
|
|
23
63
|
*
|
|
64
|
+
* A service can carry more than one custom domain — declare one `RailwayDomain` per
|
|
65
|
+
* domain; each instance adopts, creates, and deletes only its own domain.
|
|
66
|
+
*
|
|
24
67
|
* @example
|
|
25
68
|
* ```typescript
|
|
26
|
-
* const
|
|
69
|
+
* const apiDomain = new RailwayDomain("api-domain", { customDomain: "api.example.com" }, {
|
|
70
|
+
* provider, project, environment, service,
|
|
71
|
+
* });
|
|
72
|
+
* const wwwDomain = new RailwayDomain("www-domain", { customDomain: "www.example.com" }, {
|
|
27
73
|
* provider, project, environment, service,
|
|
28
74
|
* });
|
|
29
75
|
*
|
|
30
|
-
*
|
|
76
|
+
* // Point each domain's DNS CNAME at its own target.
|
|
77
|
+
* const apiCnameTarget = apiDomain.cnameTarget;
|
|
78
|
+
* const wwwCnameTarget = wwwDomain.cnameTarget;
|
|
31
79
|
* ```
|
|
32
80
|
*/
|
|
33
81
|
declare class RailwayDomain extends pulumi.ComponentResource {
|
|
34
82
|
/** Fully qualified domain name. */
|
|
35
83
|
readonly fqdn: pulumi.Output<string>;
|
|
84
|
+
/**
|
|
85
|
+
* The value to CNAME `customDomain` to. Only set for custom domains — `undefined`
|
|
86
|
+
* for a service's auto-generated `*.up.railway.app` domain, or if Railway hasn't
|
|
87
|
+
* returned a traffic-routing CNAME record for it yet.
|
|
88
|
+
*/
|
|
89
|
+
readonly cnameTarget: pulumi.Output<string | undefined>;
|
|
36
90
|
constructor(name: string, args: RailwayDomainArgs, opts: RailwayDomainOptions);
|
|
37
91
|
}
|
|
38
92
|
//#endregion
|
|
39
|
-
export { RailwayDomain, RailwayDomainArgs };
|
|
93
|
+
export { RailwayDomain, RailwayDomainArgs, RailwayDomainResourceProvider };
|
|
40
94
|
//# sourceMappingURL=domain.d.cts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"domain.d.cts","names":[],"sources":["../../src/railway/domain.ts"],"mappings":";;;;;;;;;
|
|
1
|
+
{"version":3,"file":"domain.d.cts","names":[],"sources":["../../src/railway/domain.ts"],"mappings":";;;;;;;;;UAQU,mBAAA;;EAET,KAAA;EAF4B;EAK5B,SAAA;EAL4B;EAQ5B,SAAA;EAHA;EAMA,aAAA;EAAA;EAGA,YAAA;AAAA;AAAY;AAAA,UAIH,oBAAA,SAA6B,mBAAmB;EAA3B;EAE9B,QAAA;EAFyD;EAKzD,IAAA;EAHA;;;;AAUW;EAAX,WAAA;AAAA;;;;;;;;;cAoGY,6BAAA,YACD,MAAA,CAAO,OAAA,CAAQ,gBAAA;EAEpB,MAAA,CACL,MAAA,EAAQ,mBAAA,GACN,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,YAAA;EAmFpB,IAAA,CACL,GAAA,UACA,KAAA,EAAO,oBAAA,GACL,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,UAAA;EA4CpB,MAAA,CAAO,GAAA,UAAa,KAAA,EAAO,oBAAA,GAAuB,OAAA;EAgBlD,IAAA,CACL,GAAA,UACA,IAAA,EAAM,oBAAA,EACN,IAAA,EAAM,mBAAA,GACJ,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,UAAA;AAAA;;KAiDtB,oBAAA,GAAuB,IAAA,CAC3B,MAAA,CAAO,wBAAA;EA5MI,sCAgNX,QAAA,EAAU,eAAA,EAhNgB;EAmN1B,OAAA,EAAS,cAAA,EAhNA;EAmNT,WAAA,EAAa,kBAAA,EAlNV;EAqNH,OAAA,EAAS,cAAA;AAAA;;UAIO,iBAAA;EArIf;EAuID,YAAA,GAAe,MAAA,CAAO,KAAK;AAAA;;;;;;;;;;;;;;;;;;;;;cAuBf,aAAA,SAAsB,MAAA,CAAO,iBAAA;EA3CrC;EAAA,SA6CY,IAAA,EAAM,MAAA,CAAO,MAAA;;;;;;WAOb,WAAA,EAAa,MAAA,CAAO,MAAA;cAGnC,IAAA,UACA,IAAA,EAAM,iBAAA,EACN,IAAA,EAAM,oBAAA;AAAA"}
|
|
@@ -6,6 +6,46 @@ import { RailwayService } from "./service.mjs";
|
|
|
6
6
|
import * as pulumi from "@pulumi/pulumi";
|
|
7
7
|
|
|
8
8
|
//#region src/railway/domain.d.ts
|
|
9
|
+
/** Resolved inputs for the Railway domain dynamic provider. */
|
|
10
|
+
interface RailwayDomainInputs {
|
|
11
|
+
/** Railway API bearer token. */
|
|
12
|
+
token: string;
|
|
13
|
+
/** Railway project UUID. */
|
|
14
|
+
projectId: string;
|
|
15
|
+
/** Railway service UUID to attach the domain to. */
|
|
16
|
+
serviceId: string;
|
|
17
|
+
/** Railway environment UUID (e.g. production). */
|
|
18
|
+
environmentId: string;
|
|
19
|
+
/** Custom domain FQDN (e.g. `"api.example.com"`). Omit for auto-generated Railway domain. */
|
|
20
|
+
customDomain?: string;
|
|
21
|
+
}
|
|
22
|
+
/** Persisted state for the Railway domain, extending inputs with Railway-assigned identifiers. */
|
|
23
|
+
interface RailwayDomainOutputs extends RailwayDomainInputs {
|
|
24
|
+
/** Railway-assigned domain UUID (used for deletion API calls). */
|
|
25
|
+
domainId: string;
|
|
26
|
+
/** Fully qualified domain name (e.g. `"api-production-abc.up.railway.app"`). */
|
|
27
|
+
fqdn: string;
|
|
28
|
+
/**
|
|
29
|
+
* The value to CNAME `customDomain` to. Only present for custom domains — a
|
|
30
|
+
* service's auto-generated `*.up.railway.app` domain needs no DNS record of its
|
|
31
|
+
* own. `undefined` if Railway hasn't returned a traffic-routing CNAME record yet.
|
|
32
|
+
*/
|
|
33
|
+
cnameTarget?: string;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Dynamic provider implementing CRUD for Railway domains.
|
|
37
|
+
*
|
|
38
|
+
* Uses adopt-or-create: queries existing domains before creating new ones.
|
|
39
|
+
* Uses the FQDN as the Pulumi resource ID.
|
|
40
|
+
*
|
|
41
|
+
* @internal Exported only for unit testing; not part of the public API surface.
|
|
42
|
+
*/
|
|
43
|
+
declare class RailwayDomainResourceProvider implements pulumi.dynamic.ResourceProvider {
|
|
44
|
+
create(inputs: RailwayDomainInputs): Promise<pulumi.dynamic.CreateResult>;
|
|
45
|
+
read(_id: string, props: RailwayDomainOutputs): Promise<pulumi.dynamic.ReadResult>;
|
|
46
|
+
delete(_id: string, props: RailwayDomainOutputs): Promise<void>;
|
|
47
|
+
diff(_id: string, olds: RailwayDomainOutputs, news: RailwayDomainInputs): Promise<pulumi.dynamic.DiffResult>;
|
|
48
|
+
}
|
|
9
49
|
/** Options type for RailwayDomain — replaces Pulumi's native `provider` field. */
|
|
10
50
|
type RailwayDomainOptions = Omit<pulumi.ComponentResourceOptions, "provider"> & {
|
|
11
51
|
/** Railway authentication context. */provider: RailwayProvider; /** Railway project context. */
|
|
@@ -21,20 +61,34 @@ interface RailwayDomainArgs {
|
|
|
21
61
|
/**
|
|
22
62
|
* Manages a Railway domain (service or custom) with adopt-or-create semantics.
|
|
23
63
|
*
|
|
64
|
+
* A service can carry more than one custom domain — declare one `RailwayDomain` per
|
|
65
|
+
* domain; each instance adopts, creates, and deletes only its own domain.
|
|
66
|
+
*
|
|
24
67
|
* @example
|
|
25
68
|
* ```typescript
|
|
26
|
-
* const
|
|
69
|
+
* const apiDomain = new RailwayDomain("api-domain", { customDomain: "api.example.com" }, {
|
|
70
|
+
* provider, project, environment, service,
|
|
71
|
+
* });
|
|
72
|
+
* const wwwDomain = new RailwayDomain("www-domain", { customDomain: "www.example.com" }, {
|
|
27
73
|
* provider, project, environment, service,
|
|
28
74
|
* });
|
|
29
75
|
*
|
|
30
|
-
*
|
|
76
|
+
* // Point each domain's DNS CNAME at its own target.
|
|
77
|
+
* const apiCnameTarget = apiDomain.cnameTarget;
|
|
78
|
+
* const wwwCnameTarget = wwwDomain.cnameTarget;
|
|
31
79
|
* ```
|
|
32
80
|
*/
|
|
33
81
|
declare class RailwayDomain extends pulumi.ComponentResource {
|
|
34
82
|
/** Fully qualified domain name. */
|
|
35
83
|
readonly fqdn: pulumi.Output<string>;
|
|
84
|
+
/**
|
|
85
|
+
* The value to CNAME `customDomain` to. Only set for custom domains — `undefined`
|
|
86
|
+
* for a service's auto-generated `*.up.railway.app` domain, or if Railway hasn't
|
|
87
|
+
* returned a traffic-routing CNAME record for it yet.
|
|
88
|
+
*/
|
|
89
|
+
readonly cnameTarget: pulumi.Output<string | undefined>;
|
|
36
90
|
constructor(name: string, args: RailwayDomainArgs, opts: RailwayDomainOptions);
|
|
37
91
|
}
|
|
38
92
|
//#endregion
|
|
39
|
-
export { RailwayDomain, RailwayDomainArgs };
|
|
93
|
+
export { RailwayDomain, RailwayDomainArgs, RailwayDomainResourceProvider };
|
|
40
94
|
//# sourceMappingURL=domain.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"domain.d.mts","names":[],"sources":["../../src/railway/domain.ts"],"mappings":";;;;;;;;;
|
|
1
|
+
{"version":3,"file":"domain.d.mts","names":[],"sources":["../../src/railway/domain.ts"],"mappings":";;;;;;;;;UAQU,mBAAA;;EAET,KAAA;EAF4B;EAK5B,SAAA;EAL4B;EAQ5B,SAAA;EAHA;EAMA,aAAA;EAAA;EAGA,YAAA;AAAA;AAAY;AAAA,UAIH,oBAAA,SAA6B,mBAAmB;EAA3B;EAE9B,QAAA;EAFyD;EAKzD,IAAA;EAHA;;;;AAUW;EAAX,WAAA;AAAA;;;;;;;;;cAoGY,6BAAA,YACD,MAAA,CAAO,OAAA,CAAQ,gBAAA;EAEpB,MAAA,CACL,MAAA,EAAQ,mBAAA,GACN,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,YAAA;EAmFpB,IAAA,CACL,GAAA,UACA,KAAA,EAAO,oBAAA,GACL,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,UAAA;EA4CpB,MAAA,CAAO,GAAA,UAAa,KAAA,EAAO,oBAAA,GAAuB,OAAA;EAgBlD,IAAA,CACL,GAAA,UACA,IAAA,EAAM,oBAAA,EACN,IAAA,EAAM,mBAAA,GACJ,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,UAAA;AAAA;;KAiDtB,oBAAA,GAAuB,IAAA,CAC3B,MAAA,CAAO,wBAAA;EA5MI,sCAgNX,QAAA,EAAU,eAAA,EAhNgB;EAmN1B,OAAA,EAAS,cAAA,EAhNA;EAmNT,WAAA,EAAa,kBAAA,EAlNV;EAqNH,OAAA,EAAS,cAAA;AAAA;;UAIO,iBAAA;EArIf;EAuID,YAAA,GAAe,MAAA,CAAO,KAAK;AAAA;;;;;;;;;;;;;;;;;;;;;cAuBf,aAAA,SAAsB,MAAA,CAAO,iBAAA;EA3CrC;EAAA,SA6CY,IAAA,EAAM,MAAA,CAAO,MAAA;;;;;;WAOb,WAAA,EAAa,MAAA,CAAO,MAAA;cAGnC,IAAA,UACA,IAAA,EAAM,iBAAA,EACN,IAAA,EAAM,oBAAA;AAAA"}
|
package/dist/railway/domain.mjs
CHANGED
|
@@ -3,6 +3,18 @@ import { RailwayClient } from "./client.mjs";
|
|
|
3
3
|
import * as pulumi from "@pulumi/pulumi";
|
|
4
4
|
|
|
5
5
|
//#region src/railway/domain.ts
|
|
6
|
+
/**
|
|
7
|
+
* Picks the CNAME record Railway expects for routing traffic to a custom domain out
|
|
8
|
+
* of its full DNS record list (which also includes e.g. an ACME challenge TXT record).
|
|
9
|
+
*/
|
|
10
|
+
function extractCnameTarget(dnsRecords) {
|
|
11
|
+
return dnsRecords?.find((record) => record.recordType === "DNS_RECORD_TYPE_CNAME" && record.purpose === "DNS_RECORD_PURPOSE_TRAFFIC_ROUTE")?.requiredValue;
|
|
12
|
+
}
|
|
13
|
+
const DOMAIN_STATUS_FIELDS = `
|
|
14
|
+
status {
|
|
15
|
+
dnsRecords { recordType purpose requiredValue }
|
|
16
|
+
}
|
|
17
|
+
`;
|
|
6
18
|
const SERVICE_DOMAINS_QUERY = `
|
|
7
19
|
query($projectId: String!, $serviceId: String!, $environmentId: String!) {
|
|
8
20
|
domains(
|
|
@@ -11,7 +23,7 @@ const SERVICE_DOMAINS_QUERY = `
|
|
|
11
23
|
environmentId: $environmentId
|
|
12
24
|
) {
|
|
13
25
|
serviceDomains { id domain }
|
|
14
|
-
customDomains { id domain }
|
|
26
|
+
customDomains { id domain ${DOMAIN_STATUS_FIELDS} }
|
|
15
27
|
}
|
|
16
28
|
}
|
|
17
29
|
`;
|
|
@@ -22,7 +34,7 @@ const SERVICE_DOMAIN_CREATE = `
|
|
|
22
34
|
`;
|
|
23
35
|
const CUSTOM_DOMAIN_CREATE = `
|
|
24
36
|
mutation($input: CustomDomainCreateInput!) {
|
|
25
|
-
customDomainCreate(input: $input) { id domain }
|
|
37
|
+
customDomainCreate(input: $input) { id domain ${DOMAIN_STATUS_FIELDS} }
|
|
26
38
|
}
|
|
27
39
|
`;
|
|
28
40
|
const CUSTOM_DOMAIN_DELETE = `
|
|
@@ -46,6 +58,8 @@ async function findExistingDomains(client, projectId, serviceId, environmentId)
|
|
|
46
58
|
*
|
|
47
59
|
* Uses adopt-or-create: queries existing domains before creating new ones.
|
|
48
60
|
* Uses the FQDN as the Pulumi resource ID.
|
|
61
|
+
*
|
|
62
|
+
* @internal Exported only for unit testing; not part of the public API surface.
|
|
49
63
|
*/
|
|
50
64
|
var RailwayDomainResourceProvider = class {
|
|
51
65
|
async create(inputs) {
|
|
@@ -60,7 +74,8 @@ var RailwayDomainResourceProvider = class {
|
|
|
60
74
|
outs: {
|
|
61
75
|
...inputs,
|
|
62
76
|
domainId: found.id,
|
|
63
|
-
fqdn: found.domain
|
|
77
|
+
fqdn: found.domain,
|
|
78
|
+
cnameTarget: extractCnameTarget(found.status?.dnsRecords)
|
|
64
79
|
}
|
|
65
80
|
};
|
|
66
81
|
}
|
|
@@ -75,7 +90,8 @@ var RailwayDomainResourceProvider = class {
|
|
|
75
90
|
outs: {
|
|
76
91
|
...inputs,
|
|
77
92
|
domainId: result.customDomainCreate.id,
|
|
78
|
-
fqdn: result.customDomainCreate.domain
|
|
93
|
+
fqdn: result.customDomainCreate.domain,
|
|
94
|
+
cnameTarget: extractCnameTarget(result.customDomainCreate.status?.dnsRecords)
|
|
79
95
|
}
|
|
80
96
|
};
|
|
81
97
|
}
|
|
@@ -114,7 +130,8 @@ var RailwayDomainResourceProvider = class {
|
|
|
114
130
|
props: {
|
|
115
131
|
...props,
|
|
116
132
|
domainId: found.id,
|
|
117
|
-
fqdn: found.domain
|
|
133
|
+
fqdn: found.domain,
|
|
134
|
+
cnameTarget: extractCnameTarget(found.status?.dnsRecords)
|
|
118
135
|
}
|
|
119
136
|
};
|
|
120
137
|
}
|
|
@@ -158,20 +175,29 @@ var RailwayDomainResource = class extends pulumi.dynamic.Resource {
|
|
|
158
175
|
super(new RailwayDomainResourceProvider(), name, {
|
|
159
176
|
...args,
|
|
160
177
|
domainId: void 0,
|
|
161
|
-
fqdn: void 0
|
|
178
|
+
fqdn: void 0,
|
|
179
|
+
cnameTarget: void 0
|
|
162
180
|
}, opts);
|
|
163
181
|
}
|
|
164
182
|
};
|
|
165
183
|
/**
|
|
166
184
|
* Manages a Railway domain (service or custom) with adopt-or-create semantics.
|
|
167
185
|
*
|
|
186
|
+
* A service can carry more than one custom domain — declare one `RailwayDomain` per
|
|
187
|
+
* domain; each instance adopts, creates, and deletes only its own domain.
|
|
188
|
+
*
|
|
168
189
|
* @example
|
|
169
190
|
* ```typescript
|
|
170
|
-
* const
|
|
191
|
+
* const apiDomain = new RailwayDomain("api-domain", { customDomain: "api.example.com" }, {
|
|
192
|
+
* provider, project, environment, service,
|
|
193
|
+
* });
|
|
194
|
+
* const wwwDomain = new RailwayDomain("www-domain", { customDomain: "www.example.com" }, {
|
|
171
195
|
* provider, project, environment, service,
|
|
172
196
|
* });
|
|
173
197
|
*
|
|
174
|
-
*
|
|
198
|
+
* // Point each domain's DNS CNAME at its own target.
|
|
199
|
+
* const apiCnameTarget = apiDomain.cnameTarget;
|
|
200
|
+
* const wwwCnameTarget = wwwDomain.cnameTarget;
|
|
175
201
|
* ```
|
|
176
202
|
*/
|
|
177
203
|
var RailwayDomain = class extends pulumi.ComponentResource {
|
|
@@ -186,10 +212,14 @@ var RailwayDomain = class extends pulumi.ComponentResource {
|
|
|
186
212
|
customDomain: args.customDomain
|
|
187
213
|
}, { parent: this });
|
|
188
214
|
this.fqdn = resource.fqdn;
|
|
189
|
-
this.
|
|
215
|
+
this.cnameTarget = resource.cnameTarget;
|
|
216
|
+
this.registerOutputs({
|
|
217
|
+
fqdn: this.fqdn,
|
|
218
|
+
cnameTarget: this.cnameTarget
|
|
219
|
+
});
|
|
190
220
|
}
|
|
191
221
|
};
|
|
192
222
|
|
|
193
223
|
//#endregion
|
|
194
|
-
export { RailwayDomain };
|
|
224
|
+
export { RailwayDomain, RailwayDomainResourceProvider };
|
|
195
225
|
//# sourceMappingURL=domain.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"domain.mjs","names":[],"sources":["../../src/railway/domain.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\";\nimport type { RailwayService } from \"./service\";\n\n/** Resolved inputs for the Railway domain dynamic provider. */\ninterface RailwayDomainInputs {\n\t/** Railway API bearer token. */\n\ttoken: string;\n\n\t/** Railway project UUID. */\n\tprojectId: string;\n\n\t/** Railway service UUID to attach the domain to. */\n\tserviceId: string;\n\n\t/** Railway environment UUID (e.g. production). */\n\tenvironmentId: string;\n\n\t/** Custom domain FQDN (e.g. `\"api.example.com\"`). Omit for auto-generated Railway domain. */\n\tcustomDomain?: string;\n}\n\n/** Persisted state for the Railway domain, extending inputs with Railway-assigned identifiers. */\ninterface RailwayDomainOutputs extends RailwayDomainInputs {\n\t/** Railway-assigned domain UUID (used for deletion API calls). */\n\tdomainId: string;\n\n\t/** Fully qualified domain name (e.g. `\"api-production-abc.up.railway.app\"`). */\n\tfqdn: string;\n}\n\nconst SERVICE_DOMAINS_QUERY = `\n query($projectId: String!, $serviceId: String!, $environmentId: String!) {\n domains(\n projectId: $projectId\n serviceId: $serviceId\n environmentId: $environmentId\n ) {\n serviceDomains { id domain }\n customDomains { id domain }\n }\n }\n`;\n\nconst SERVICE_DOMAIN_CREATE = `\n mutation($input: ServiceDomainCreateInput!) {\n serviceDomainCreate(input: $input) { id domain }\n }\n`;\n\nconst CUSTOM_DOMAIN_CREATE = `\n mutation($input: CustomDomainCreateInput!) {\n customDomainCreate(input: $input) { id domain }\n }\n`;\n\nconst CUSTOM_DOMAIN_DELETE = `\n mutation($id: String!) { customDomainDelete(id: $id) }\n`;\n\nconst SERVICE_DOMAIN_DELETE = `\n mutation($id: String!) { serviceDomainDelete(id: $id) }\n`;\n\n/**\n * Queries all existing domains (service and custom) for a Railway service.\n */\nasync function findExistingDomains(\n\tclient: RailwayClient,\n\tprojectId: string,\n\tserviceId: string,\n\tenvironmentId: string,\n): Promise<{\n\tserviceDomains: Array<{ id: string; domain: string }>;\n\tcustomDomains: Array<{ id: string; domain: string }>;\n}> {\n\tconst result = await client.query<{\n\t\tdomains: {\n\t\t\tserviceDomains: Array<{ id: string; domain: string }>;\n\t\t\tcustomDomains: Array<{ id: string; domain: string }>;\n\t\t};\n\t}>(SERVICE_DOMAINS_QUERY, { projectId, serviceId, environmentId });\n\n\treturn result.domains;\n}\n\n/**\n * Dynamic provider implementing CRUD for Railway domains.\n *\n * Uses adopt-or-create: queries existing domains before creating new ones.\n * Uses the FQDN as the Pulumi resource ID.\n */\nclass RailwayDomainResourceProvider implements pulumi.dynamic.ResourceProvider {\n\tasync create(\n\t\tinputs: RailwayDomainInputs,\n\t): Promise<pulumi.dynamic.CreateResult> {\n\t\tconst client = new RailwayClient(inputs.token);\n\n\t\tconst existing = await findExistingDomains(\n\t\t\tclient,\n\t\t\tinputs.projectId,\n\t\t\tinputs.serviceId,\n\t\t\tinputs.environmentId,\n\t\t);\n\n\t\tif (inputs.customDomain) {\n\t\t\tconst found = existing.customDomains.find(\n\t\t\t\t(d) => d.domain === inputs.customDomain,\n\t\t\t);\n\n\t\t\tif (found) {\n\t\t\t\tpulumi.log.info(`Adopting existing custom domain \"${found.domain}\"`);\n\n\t\t\t\treturn {\n\t\t\t\t\tid: found.domain,\n\t\t\t\t\touts: { ...inputs, domainId: found.id, fqdn: found.domain },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst result = await client.query<{\n\t\t\t\tcustomDomainCreate: { id: string; domain: string };\n\t\t\t}>(CUSTOM_DOMAIN_CREATE, {\n\t\t\t\tinput: {\n\t\t\t\t\tprojectId: inputs.projectId,\n\t\t\t\t\tserviceId: inputs.serviceId,\n\t\t\t\t\tenvironmentId: inputs.environmentId,\n\t\t\t\t\tdomain: inputs.customDomain,\n\t\t\t\t},\n\t\t\t});\n\n\t\t\treturn {\n\t\t\t\tid: result.customDomainCreate.domain,\n\t\t\t\touts: {\n\t\t\t\t\t...inputs,\n\t\t\t\t\tdomainId: result.customDomainCreate.id,\n\t\t\t\t\tfqdn: result.customDomainCreate.domain,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tif (existing.serviceDomains.length > 0) {\n\t\t\tconst found = existing.serviceDomains[0];\n\n\t\t\tpulumi.log.info(`Adopting existing service domain \"${found.domain}\"`);\n\n\t\t\treturn {\n\t\t\t\tid: found.domain,\n\t\t\t\touts: { ...inputs, domainId: found.id, fqdn: found.domain },\n\t\t\t};\n\t\t}\n\n\t\tconst result = await client.query<{\n\t\t\tserviceDomainCreate: { id: string; domain: string };\n\t\t}>(SERVICE_DOMAIN_CREATE, {\n\t\t\tinput: {\n\t\t\t\tserviceId: inputs.serviceId,\n\t\t\t\tenvironmentId: inputs.environmentId,\n\t\t\t},\n\t\t});\n\n\t\treturn {\n\t\t\tid: result.serviceDomainCreate.domain,\n\t\t\touts: {\n\t\t\t\t...inputs,\n\t\t\t\tdomainId: result.serviceDomainCreate.id,\n\t\t\t\tfqdn: result.serviceDomainCreate.domain,\n\t\t\t},\n\t\t};\n\t}\n\n\tasync read(\n\t\t_id: string,\n\t\tprops: RailwayDomainOutputs,\n\t): Promise<pulumi.dynamic.ReadResult> {\n\t\tconst client = new RailwayClient(props.token);\n\n\t\tconst existing = await findExistingDomains(\n\t\t\tclient,\n\t\t\tprops.projectId,\n\t\t\tprops.serviceId,\n\t\t\tprops.environmentId,\n\t\t);\n\n\t\tif (props.customDomain) {\n\t\t\tconst found = existing.customDomains.find(\n\t\t\t\t(d) => d.domain === props.customDomain,\n\t\t\t);\n\n\t\t\tif (!found) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Custom domain \"${props.customDomain}\" not found during refresh`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tid: found.domain,\n\t\t\t\tprops: { ...props, domainId: found.id, fqdn: found.domain },\n\t\t\t};\n\t\t}\n\n\t\tif (existing.serviceDomains.length > 0) {\n\t\t\tconst found = existing.serviceDomains[0];\n\n\t\t\treturn {\n\t\t\t\tid: found.domain,\n\t\t\t\tprops: { ...props, domainId: found.id, fqdn: found.domain },\n\t\t\t};\n\t\t}\n\n\t\tthrow new Error(\"Railway domain not found during refresh\");\n\t}\n\n\tasync delete(_id: string, props: RailwayDomainOutputs): Promise<void> {\n\t\tconst client = new RailwayClient(props.token);\n\n\t\tconst mutation = props.customDomain\n\t\t\t? CUSTOM_DOMAIN_DELETE\n\t\t\t: SERVICE_DOMAIN_DELETE;\n\n\t\ttry {\n\t\t\tawait client.query(mutation, { id: props.domainId });\n\t\t} catch {\n\t\t\tpulumi.log.warn(\n\t\t\t\t\"Failed to delete Railway domain (may already be deleted)\",\n\t\t\t);\n\t\t}\n\t}\n\n\tasync diff(\n\t\t_id: string,\n\t\tolds: RailwayDomainOutputs,\n\t\tnews: RailwayDomainInputs,\n\t): Promise<pulumi.dynamic.DiffResult> {\n\t\tconst replaces: string[] = [];\n\n\t\tif (olds.serviceId !== news.serviceId) {\n\t\t\treplaces.push(\"serviceId\");\n\t\t}\n\n\t\tif (olds.customDomain !== news.customDomain) {\n\t\t\treplaces.push(\"customDomain\");\n\t\t}\n\n\t\tif (olds.environmentId !== news.environmentId) {\n\t\t\treplaces.push(\"environmentId\");\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 RailwayDomainResource extends pulumi.dynamic.Resource {\n\tpublic declare readonly fqdn: 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\tserviceId: pulumi.Input<string>;\n\t\t\tenvironmentId: pulumi.Input<string>;\n\t\t\tcustomDomain?: pulumi.Input<string>;\n\t\t},\n\t\topts?: pulumi.CustomResourceOptions,\n\t) {\n\t\tsuper(\n\t\t\tnew RailwayDomainResourceProvider(),\n\t\t\tname,\n\t\t\t{ ...args, domainId: undefined, fqdn: undefined },\n\t\t\topts,\n\t\t);\n\t}\n}\n\n/** Options type for RailwayDomain — replaces Pulumi's native `provider` field. */\ntype RailwayDomainOptions = 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\t/** Railway service context. */\n\tservice: RailwayService;\n};\n\n/** Args for RailwayDomain. */\nexport interface RailwayDomainArgs {\n\t/** Custom domain FQDN. Omit to create an auto-generated Railway service domain. */\n\tcustomDomain?: pulumi.Input<string>;\n}\n\n/**\n * Manages a Railway domain (service or custom) with adopt-or-create semantics.\n *\n * @example\n * ```typescript\n * const domain = new RailwayDomain(\"api-domain\", {}, {\n * provider, project, environment, service,\n * });\n *\n * const url = pulumi.interpolate`https://${domain.fqdn}`;\n * ```\n */\nexport class RailwayDomain extends pulumi.ComponentResource {\n\t/** Fully qualified domain name. */\n\tpublic readonly fqdn: pulumi.Output<string>;\n\n\tconstructor(\n\t\tname: string,\n\t\targs: RailwayDomainArgs,\n\t\topts: RailwayDomainOptions,\n\t) {\n\t\tconst { provider, project, environment, service, ...pulumiOpts } = opts;\n\n\t\tsuper(\"infracraft:railway:Domain\", name, {}, pulumiOpts);\n\n\t\tconst resource = new RailwayDomainResource(\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\tserviceId: service.id,\n\t\t\t\tenvironmentId: environment.id,\n\t\t\t\tcustomDomain: args.customDomain,\n\t\t\t},\n\t\t\t{ parent: this },\n\t\t);\n\n\t\tthis.fqdn = resource.fqdn;\n\n\t\tthis.registerOutputs({ fqdn: this.fqdn });\n\t}\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"domain.mjs","names":[],"sources":["../../src/railway/domain.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\";\nimport type { RailwayService } from \"./service\";\n\n/** Resolved inputs for the Railway domain dynamic provider. */\ninterface RailwayDomainInputs {\n\t/** Railway API bearer token. */\n\ttoken: string;\n\n\t/** Railway project UUID. */\n\tprojectId: string;\n\n\t/** Railway service UUID to attach the domain to. */\n\tserviceId: string;\n\n\t/** Railway environment UUID (e.g. production). */\n\tenvironmentId: string;\n\n\t/** Custom domain FQDN (e.g. `\"api.example.com\"`). Omit for auto-generated Railway domain. */\n\tcustomDomain?: string;\n}\n\n/** Persisted state for the Railway domain, extending inputs with Railway-assigned identifiers. */\ninterface RailwayDomainOutputs extends RailwayDomainInputs {\n\t/** Railway-assigned domain UUID (used for deletion API calls). */\n\tdomainId: string;\n\n\t/** Fully qualified domain name (e.g. `\"api-production-abc.up.railway.app\"`). */\n\tfqdn: string;\n\n\t/**\n\t * The value to CNAME `customDomain` to. Only present for custom domains — a\n\t * service's auto-generated `*.up.railway.app` domain needs no DNS record of its\n\t * own. `undefined` if Railway hasn't returned a traffic-routing CNAME record yet.\n\t */\n\tcnameTarget?: string;\n}\n\n/** A single DNS record Railway expects for a custom domain (CNAME target, ACME challenge, ...). */\ninterface DomainDnsRecord {\n\trecordType: string;\n\tpurpose: string;\n\trequiredValue: string;\n}\n\n/** Shape returned for a custom domain, including its required DNS records. */\ninterface CustomDomainEntry {\n\tid: string;\n\tdomain: string;\n\tstatus?: { dnsRecords: DomainDnsRecord[] };\n}\n\n/**\n * Picks the CNAME record Railway expects for routing traffic to a custom domain out\n * of its full DNS record list (which also includes e.g. an ACME challenge TXT record).\n */\nfunction extractCnameTarget(\n\tdnsRecords: DomainDnsRecord[] | undefined,\n): string | undefined {\n\treturn dnsRecords?.find(\n\t\t(record) =>\n\t\t\trecord.recordType === \"DNS_RECORD_TYPE_CNAME\" &&\n\t\t\trecord.purpose === \"DNS_RECORD_PURPOSE_TRAFFIC_ROUTE\",\n\t)?.requiredValue;\n}\n\nconst DOMAIN_STATUS_FIELDS = `\n status {\n dnsRecords { recordType purpose requiredValue }\n }\n`;\n\nconst SERVICE_DOMAINS_QUERY = `\n query($projectId: String!, $serviceId: String!, $environmentId: String!) {\n domains(\n projectId: $projectId\n serviceId: $serviceId\n environmentId: $environmentId\n ) {\n serviceDomains { id domain }\n customDomains { id domain ${DOMAIN_STATUS_FIELDS} }\n }\n }\n`;\n\nconst SERVICE_DOMAIN_CREATE = `\n mutation($input: ServiceDomainCreateInput!) {\n serviceDomainCreate(input: $input) { id domain }\n }\n`;\n\nconst CUSTOM_DOMAIN_CREATE = `\n mutation($input: CustomDomainCreateInput!) {\n customDomainCreate(input: $input) { id domain ${DOMAIN_STATUS_FIELDS} }\n }\n`;\n\nconst CUSTOM_DOMAIN_DELETE = `\n mutation($id: String!) { customDomainDelete(id: $id) }\n`;\n\nconst SERVICE_DOMAIN_DELETE = `\n mutation($id: String!) { serviceDomainDelete(id: $id) }\n`;\n\n/**\n * Queries all existing domains (service and custom) for a Railway service.\n */\nasync function findExistingDomains(\n\tclient: RailwayClient,\n\tprojectId: string,\n\tserviceId: string,\n\tenvironmentId: string,\n): Promise<{\n\tserviceDomains: Array<{ id: string; domain: string }>;\n\tcustomDomains: CustomDomainEntry[];\n}> {\n\tconst result = await client.query<{\n\t\tdomains: {\n\t\t\tserviceDomains: Array<{ id: string; domain: string }>;\n\t\t\tcustomDomains: CustomDomainEntry[];\n\t\t};\n\t}>(SERVICE_DOMAINS_QUERY, { projectId, serviceId, environmentId });\n\n\treturn result.domains;\n}\n\n/**\n * Dynamic provider implementing CRUD for Railway domains.\n *\n * Uses adopt-or-create: queries existing domains before creating new ones.\n * Uses the FQDN as the Pulumi resource ID.\n *\n * @internal Exported only for unit testing; not part of the public API surface.\n */\nexport class RailwayDomainResourceProvider\n\timplements pulumi.dynamic.ResourceProvider\n{\n\tasync create(\n\t\tinputs: RailwayDomainInputs,\n\t): Promise<pulumi.dynamic.CreateResult> {\n\t\tconst client = new RailwayClient(inputs.token);\n\n\t\tconst existing = await findExistingDomains(\n\t\t\tclient,\n\t\t\tinputs.projectId,\n\t\t\tinputs.serviceId,\n\t\t\tinputs.environmentId,\n\t\t);\n\n\t\tif (inputs.customDomain) {\n\t\t\tconst found = existing.customDomains.find(\n\t\t\t\t(d) => d.domain === inputs.customDomain,\n\t\t\t);\n\n\t\t\tif (found) {\n\t\t\t\tpulumi.log.info(`Adopting existing custom domain \"${found.domain}\"`);\n\n\t\t\t\treturn {\n\t\t\t\t\tid: found.domain,\n\t\t\t\t\touts: {\n\t\t\t\t\t\t...inputs,\n\t\t\t\t\t\tdomainId: found.id,\n\t\t\t\t\t\tfqdn: found.domain,\n\t\t\t\t\t\tcnameTarget: extractCnameTarget(found.status?.dnsRecords),\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst result = await client.query<{\n\t\t\t\tcustomDomainCreate: CustomDomainEntry;\n\t\t\t}>(CUSTOM_DOMAIN_CREATE, {\n\t\t\t\tinput: {\n\t\t\t\t\tprojectId: inputs.projectId,\n\t\t\t\t\tserviceId: inputs.serviceId,\n\t\t\t\t\tenvironmentId: inputs.environmentId,\n\t\t\t\t\tdomain: inputs.customDomain,\n\t\t\t\t},\n\t\t\t});\n\n\t\t\treturn {\n\t\t\t\tid: result.customDomainCreate.domain,\n\t\t\t\touts: {\n\t\t\t\t\t...inputs,\n\t\t\t\t\tdomainId: result.customDomainCreate.id,\n\t\t\t\t\tfqdn: result.customDomainCreate.domain,\n\t\t\t\t\tcnameTarget: extractCnameTarget(\n\t\t\t\t\t\tresult.customDomainCreate.status?.dnsRecords,\n\t\t\t\t\t),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tif (existing.serviceDomains.length > 0) {\n\t\t\tconst found = existing.serviceDomains[0];\n\n\t\t\tpulumi.log.info(`Adopting existing service domain \"${found.domain}\"`);\n\n\t\t\treturn {\n\t\t\t\tid: found.domain,\n\t\t\t\touts: { ...inputs, domainId: found.id, fqdn: found.domain },\n\t\t\t};\n\t\t}\n\n\t\tconst result = await client.query<{\n\t\t\tserviceDomainCreate: { id: string; domain: string };\n\t\t}>(SERVICE_DOMAIN_CREATE, {\n\t\t\tinput: {\n\t\t\t\tserviceId: inputs.serviceId,\n\t\t\t\tenvironmentId: inputs.environmentId,\n\t\t\t},\n\t\t});\n\n\t\treturn {\n\t\t\tid: result.serviceDomainCreate.domain,\n\t\t\touts: {\n\t\t\t\t...inputs,\n\t\t\t\tdomainId: result.serviceDomainCreate.id,\n\t\t\t\tfqdn: result.serviceDomainCreate.domain,\n\t\t\t},\n\t\t};\n\t}\n\n\tasync read(\n\t\t_id: string,\n\t\tprops: RailwayDomainOutputs,\n\t): Promise<pulumi.dynamic.ReadResult> {\n\t\tconst client = new RailwayClient(props.token);\n\n\t\tconst existing = await findExistingDomains(\n\t\t\tclient,\n\t\t\tprops.projectId,\n\t\t\tprops.serviceId,\n\t\t\tprops.environmentId,\n\t\t);\n\n\t\tif (props.customDomain) {\n\t\t\tconst found = existing.customDomains.find(\n\t\t\t\t(d) => d.domain === props.customDomain,\n\t\t\t);\n\n\t\t\tif (!found) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Custom domain \"${props.customDomain}\" not found during refresh`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tid: found.domain,\n\t\t\t\tprops: {\n\t\t\t\t\t...props,\n\t\t\t\t\tdomainId: found.id,\n\t\t\t\t\tfqdn: found.domain,\n\t\t\t\t\tcnameTarget: extractCnameTarget(found.status?.dnsRecords),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tif (existing.serviceDomains.length > 0) {\n\t\t\tconst found = existing.serviceDomains[0];\n\n\t\t\treturn {\n\t\t\t\tid: found.domain,\n\t\t\t\tprops: { ...props, domainId: found.id, fqdn: found.domain },\n\t\t\t};\n\t\t}\n\n\t\tthrow new Error(\"Railway domain not found during refresh\");\n\t}\n\n\tasync delete(_id: string, props: RailwayDomainOutputs): Promise<void> {\n\t\tconst client = new RailwayClient(props.token);\n\n\t\tconst mutation = props.customDomain\n\t\t\t? CUSTOM_DOMAIN_DELETE\n\t\t\t: SERVICE_DOMAIN_DELETE;\n\n\t\ttry {\n\t\t\tawait client.query(mutation, { id: props.domainId });\n\t\t} catch {\n\t\t\tpulumi.log.warn(\n\t\t\t\t\"Failed to delete Railway domain (may already be deleted)\",\n\t\t\t);\n\t\t}\n\t}\n\n\tasync diff(\n\t\t_id: string,\n\t\tolds: RailwayDomainOutputs,\n\t\tnews: RailwayDomainInputs,\n\t): Promise<pulumi.dynamic.DiffResult> {\n\t\tconst replaces: string[] = [];\n\n\t\tif (olds.serviceId !== news.serviceId) {\n\t\t\treplaces.push(\"serviceId\");\n\t\t}\n\n\t\tif (olds.customDomain !== news.customDomain) {\n\t\t\treplaces.push(\"customDomain\");\n\t\t}\n\n\t\tif (olds.environmentId !== news.environmentId) {\n\t\t\treplaces.push(\"environmentId\");\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 RailwayDomainResource extends pulumi.dynamic.Resource {\n\tpublic declare readonly fqdn: pulumi.Output<string>;\n\tpublic declare readonly cnameTarget: pulumi.Output<string | undefined>;\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\tserviceId: pulumi.Input<string>;\n\t\t\tenvironmentId: pulumi.Input<string>;\n\t\t\tcustomDomain?: pulumi.Input<string>;\n\t\t},\n\t\topts?: pulumi.CustomResourceOptions,\n\t) {\n\t\tsuper(\n\t\t\tnew RailwayDomainResourceProvider(),\n\t\t\tname,\n\t\t\t{ ...args, domainId: undefined, fqdn: undefined, cnameTarget: undefined },\n\t\t\topts,\n\t\t);\n\t}\n}\n\n/** Options type for RailwayDomain — replaces Pulumi's native `provider` field. */\ntype RailwayDomainOptions = 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\t/** Railway service context. */\n\tservice: RailwayService;\n};\n\n/** Args for RailwayDomain. */\nexport interface RailwayDomainArgs {\n\t/** Custom domain FQDN. Omit to create an auto-generated Railway service domain. */\n\tcustomDomain?: pulumi.Input<string>;\n}\n\n/**\n * Manages a Railway domain (service or custom) with adopt-or-create semantics.\n *\n * A service can carry more than one custom domain — declare one `RailwayDomain` per\n * domain; each instance adopts, creates, and deletes only its own domain.\n *\n * @example\n * ```typescript\n * const apiDomain = new RailwayDomain(\"api-domain\", { customDomain: \"api.example.com\" }, {\n * provider, project, environment, service,\n * });\n * const wwwDomain = new RailwayDomain(\"www-domain\", { customDomain: \"www.example.com\" }, {\n * provider, project, environment, service,\n * });\n *\n * // Point each domain's DNS CNAME at its own target.\n * const apiCnameTarget = apiDomain.cnameTarget;\n * const wwwCnameTarget = wwwDomain.cnameTarget;\n * ```\n */\nexport class RailwayDomain extends pulumi.ComponentResource {\n\t/** Fully qualified domain name. */\n\tpublic readonly fqdn: pulumi.Output<string>;\n\n\t/**\n\t * The value to CNAME `customDomain` to. Only set for custom domains — `undefined`\n\t * for a service's auto-generated `*.up.railway.app` domain, or if Railway hasn't\n\t * returned a traffic-routing CNAME record for it yet.\n\t */\n\tpublic readonly cnameTarget: pulumi.Output<string | undefined>;\n\n\tconstructor(\n\t\tname: string,\n\t\targs: RailwayDomainArgs,\n\t\topts: RailwayDomainOptions,\n\t) {\n\t\tconst { provider, project, environment, service, ...pulumiOpts } = opts;\n\n\t\tsuper(\"infracraft:railway:Domain\", name, {}, pulumiOpts);\n\n\t\tconst resource = new RailwayDomainResource(\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\tserviceId: service.id,\n\t\t\t\tenvironmentId: environment.id,\n\t\t\t\tcustomDomain: args.customDomain,\n\t\t\t},\n\t\t\t{ parent: this },\n\t\t);\n\n\t\tthis.fqdn = resource.fqdn;\n\t\tthis.cnameTarget = resource.cnameTarget;\n\n\t\tthis.registerOutputs({ fqdn: this.fqdn, cnameTarget: this.cnameTarget });\n\t}\n}\n"],"mappings":";;;;;;;;;AA2DA,SAAS,mBACR,YACqB;CACrB,OAAO,YAAY,MACjB,WACA,OAAO,eAAe,2BACtB,OAAO,YAAY,kCACrB,GAAG;AACJ;AAEA,MAAM,uBAAuB;;;;;AAM7B,MAAM,wBAAwB;;;;;;;;kCAQI,qBAAqB;;;;AAKvD,MAAM,wBAAwB;;;;;AAM9B,MAAM,uBAAuB;;oDAEuB,qBAAqB;;;AAIzE,MAAM,uBAAuB;;;AAI7B,MAAM,wBAAwB;;;;;;AAO9B,eAAe,oBACd,QACA,WACA,WACA,eAIE;CAQF,QAAO,MAPc,OAAO,MAKzB,uBAAuB;EAAE;EAAW;EAAW;CAAc,CAAC,GAEnD;AACf;;;;;;;;;AAUA,IAAa,gCAAb,MAEA;CACC,MAAM,OACL,QACuC;EACvC,MAAM,SAAS,IAAI,cAAc,OAAO,KAAK;EAE7C,MAAM,WAAW,MAAM,oBACtB,QACA,OAAO,WACP,OAAO,WACP,OAAO,aACR;EAEA,IAAI,OAAO,cAAc;GACxB,MAAM,QAAQ,SAAS,cAAc,MACnC,MAAM,EAAE,WAAW,OAAO,YAC5B;GAEA,IAAI,OAAO;IACV,OAAO,IAAI,KAAK,oCAAoC,MAAM,OAAO,EAAE;IAEnE,OAAO;KACN,IAAI,MAAM;KACV,MAAM;MACL,GAAG;MACH,UAAU,MAAM;MAChB,MAAM,MAAM;MACZ,aAAa,mBAAmB,MAAM,QAAQ,UAAU;KACzD;IACD;GACD;GAEA,MAAM,SAAS,MAAM,OAAO,MAEzB,sBAAsB,EACxB,OAAO;IACN,WAAW,OAAO;IAClB,WAAW,OAAO;IAClB,eAAe,OAAO;IACtB,QAAQ,OAAO;GAChB,EACD,CAAC;GAED,OAAO;IACN,IAAI,OAAO,mBAAmB;IAC9B,MAAM;KACL,GAAG;KACH,UAAU,OAAO,mBAAmB;KACpC,MAAM,OAAO,mBAAmB;KAChC,aAAa,mBACZ,OAAO,mBAAmB,QAAQ,UACnC;IACD;GACD;EACD;EAEA,IAAI,SAAS,eAAe,SAAS,GAAG;GACvC,MAAM,QAAQ,SAAS,eAAe;GAEtC,OAAO,IAAI,KAAK,qCAAqC,MAAM,OAAO,EAAE;GAEpE,OAAO;IACN,IAAI,MAAM;IACV,MAAM;KAAE,GAAG;KAAQ,UAAU,MAAM;KAAI,MAAM,MAAM;IAAO;GAC3D;EACD;EAEA,MAAM,SAAS,MAAM,OAAO,MAEzB,uBAAuB,EACzB,OAAO;GACN,WAAW,OAAO;GAClB,eAAe,OAAO;EACvB,EACD,CAAC;EAED,OAAO;GACN,IAAI,OAAO,oBAAoB;GAC/B,MAAM;IACL,GAAG;IACH,UAAU,OAAO,oBAAoB;IACrC,MAAM,OAAO,oBAAoB;GAClC;EACD;CACD;CAEA,MAAM,KACL,KACA,OACqC;EAGrC,MAAM,WAAW,MAAM,oBACtB,IAHkB,cAAc,MAAM,KAGjC,GACL,MAAM,WACN,MAAM,WACN,MAAM,aACP;EAEA,IAAI,MAAM,cAAc;GACvB,MAAM,QAAQ,SAAS,cAAc,MACnC,MAAM,EAAE,WAAW,MAAM,YAC3B;GAEA,IAAI,CAAC,OACJ,MAAM,IAAI,MACT,kBAAkB,MAAM,aAAa,2BACtC;GAGD,OAAO;IACN,IAAI,MAAM;IACV,OAAO;KACN,GAAG;KACH,UAAU,MAAM;KAChB,MAAM,MAAM;KACZ,aAAa,mBAAmB,MAAM,QAAQ,UAAU;IACzD;GACD;EACD;EAEA,IAAI,SAAS,eAAe,SAAS,GAAG;GACvC,MAAM,QAAQ,SAAS,eAAe;GAEtC,OAAO;IACN,IAAI,MAAM;IACV,OAAO;KAAE,GAAG;KAAO,UAAU,MAAM;KAAI,MAAM,MAAM;IAAO;GAC3D;EACD;EAEA,MAAM,IAAI,MAAM,yCAAyC;CAC1D;CAEA,MAAM,OAAO,KAAa,OAA4C;EACrE,MAAM,SAAS,IAAI,cAAc,MAAM,KAAK;EAE5C,MAAM,WAAW,MAAM,eACpB,uBACA;EAEH,IAAI;GACH,MAAM,OAAO,MAAM,UAAU,EAAE,IAAI,MAAM,SAAS,CAAC;EACpD,QAAQ;GACP,OAAO,IAAI,KACV,0DACD;EACD;CACD;CAEA,MAAM,KACL,KACA,MACA,MACqC;EACrC,MAAM,WAAqB,CAAC;EAE5B,IAAI,KAAK,cAAc,KAAK,WAC3B,SAAS,KAAK,WAAW;EAG1B,IAAI,KAAK,iBAAiB,KAAK,cAC9B,SAAS,KAAK,cAAc;EAG7B,IAAI,KAAK,kBAAkB,KAAK,eAC/B,SAAS,KAAK,eAAe;EAG9B,OAAO;GACN,SAAS,SAAS,SAAS;GAC3B;GACA,qBAAqB;EACtB;CACD;AACD;;AAGA,IAAM,wBAAN,cAAoC,OAAO,QAAQ,SAAS;CAI3D,YACC,MACA,MAOA,MACC;EACD,MACC,IAAI,8BAA8B,GAClC,MACA;GAAE,GAAG;GAAM,UAAU;GAAW,MAAM;GAAW,aAAa;EAAU,GACxE,IACD;CACD;AACD;;;;;;;;;;;;;;;;;;;;;AA8CA,IAAa,gBAAb,cAAmC,OAAO,kBAAkB;CAW3D,YACC,MACA,MACA,MACC;EACD,MAAM,EAAE,UAAU,SAAS,aAAa,SAAS,GAAG,eAAe;EAEnE,MAAM,6BAA6B,MAAM,CAAC,GAAG,UAAU;EAEvD,MAAM,WAAW,IAAI,sBACpB,GAAG,KAAK,YACR;GACC,OAAO,SAAS;GAChB,WAAW,QAAQ;GACnB,WAAW,QAAQ;GACnB,eAAe,YAAY;GAC3B,cAAc,KAAK;EACpB,GACA,EAAE,QAAQ,KAAK,CAChB;EAEA,KAAK,OAAO,SAAS;EACrB,KAAK,cAAc,SAAS;EAE5B,KAAK,gBAAgB;GAAE,MAAM,KAAK;GAAM,aAAa,KAAK;EAAY,CAAC;CACxE;AACD"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@infracraft/pulumi",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.19.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.",
|