@infracraft/pulumi 1.18.0 → 1.20.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/dist/vercel/domain.cjs +39 -11
- package/dist/vercel/domain.cjs.map +1 -1
- package/dist/vercel/domain.d.cts +14 -8
- package/dist/vercel/domain.d.cts.map +1 -1
- package/dist/vercel/domain.d.mts +14 -8
- package/dist/vercel/domain.d.mts.map +1 -1
- package/dist/vercel/domain.mjs +40 -11
- package/dist/vercel/domain.mjs.map +1 -1
- package/dist/vercel/index.cjs +0 -1
- package/dist/vercel/index.d.cts +2 -2
- package/dist/vercel/index.d.mts +2 -2
- package/dist/vercel/index.mjs +2 -2
- 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":"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"}
|
package/dist/vercel/domain.cjs
CHANGED
|
@@ -6,10 +6,29 @@ _pulumi_pulumi = require_chunk.__toESM(_pulumi_pulumi, 1);
|
|
|
6
6
|
//#region src/vercel/domain.ts
|
|
7
7
|
const VERCEL_API_URL = "https://api.vercel.com";
|
|
8
8
|
/**
|
|
9
|
-
*
|
|
9
|
+
* Fallback CNAME target, used only when Vercel's domain-config endpoint returns no
|
|
10
|
+
* recommendation (see {@link fetchCnameTarget}). Not the primary source of truth —
|
|
11
|
+
* `VercelDomain.cnameTarget` queries Vercel's per-domain recommendation dynamically,
|
|
12
|
+
* since the actual target can vary by account/domain.
|
|
10
13
|
* https://vercel.com/docs/domains/working-with-domains/add-a-domain
|
|
11
14
|
*/
|
|
12
|
-
const
|
|
15
|
+
const VERCEL_CNAME_TARGET_FALLBACK = "cname.vercel-dns.com";
|
|
16
|
+
/**
|
|
17
|
+
* Fetches Vercel's recommended CNAME target for a domain from its DNS config endpoint.
|
|
18
|
+
* Falls back to {@link VERCEL_CNAME_TARGET_FALLBACK} if Vercel returns no recommendation
|
|
19
|
+
* (logged, since that's an unusual state worth noticing rather than silently accepting).
|
|
20
|
+
*/
|
|
21
|
+
async function fetchCnameTarget(token, teamId, name) {
|
|
22
|
+
const response = await fetch(`${VERCEL_API_URL}/v6/domains/${encodeURIComponent(name)}/config?teamId=${teamId}`, { headers: { Authorization: `Bearer ${token}` } });
|
|
23
|
+
if (!response.ok) throw new Error(`Vercel API error fetching domain config for "${name}" (${response.status}): ${await response.text()}`);
|
|
24
|
+
const { recommendedCNAME = [] } = await response.json();
|
|
25
|
+
const preferred = [...recommendedCNAME].sort((a, b) => a.rank - b.rank)[0];
|
|
26
|
+
if (!preferred) {
|
|
27
|
+
_pulumi_pulumi.log.info(`Vercel returned no recommended CNAME for "${name}" — falling back to "${VERCEL_CNAME_TARGET_FALLBACK}"`);
|
|
28
|
+
return VERCEL_CNAME_TARGET_FALLBACK;
|
|
29
|
+
}
|
|
30
|
+
return preferred.value;
|
|
31
|
+
}
|
|
13
32
|
/**
|
|
14
33
|
* Fetches a project domain by name. Returns `null` if not attached (404).
|
|
15
34
|
*/
|
|
@@ -34,7 +53,8 @@ var VercelDomainResourceProvider = class {
|
|
|
34
53
|
_pulumi_pulumi.log.info(`Adopted existing Vercel domain "${inputs.name}" on project ${inputs.projectId}`);
|
|
35
54
|
const outs = {
|
|
36
55
|
...inputs,
|
|
37
|
-
verified: existing.verified
|
|
56
|
+
verified: existing.verified,
|
|
57
|
+
cnameTarget: await fetchCnameTarget(inputs.token, inputs.teamId, inputs.name)
|
|
38
58
|
};
|
|
39
59
|
return {
|
|
40
60
|
id: `${inputs.projectId}/${inputs.name}`,
|
|
@@ -53,7 +73,8 @@ var VercelDomainResourceProvider = class {
|
|
|
53
73
|
const created = await response.json();
|
|
54
74
|
const outs = {
|
|
55
75
|
...inputs,
|
|
56
|
-
verified: created.verified
|
|
76
|
+
verified: created.verified,
|
|
77
|
+
cnameTarget: await fetchCnameTarget(inputs.token, inputs.teamId, inputs.name)
|
|
57
78
|
};
|
|
58
79
|
return {
|
|
59
80
|
id: `${inputs.projectId}/${inputs.name}`,
|
|
@@ -67,15 +88,17 @@ var VercelDomainResourceProvider = class {
|
|
|
67
88
|
id: `${props.projectId}/${props.name}`,
|
|
68
89
|
props: {
|
|
69
90
|
...props,
|
|
70
|
-
verified: domain.verified
|
|
91
|
+
verified: domain.verified,
|
|
92
|
+
cnameTarget: await fetchCnameTarget(props.token, props.teamId, props.name)
|
|
71
93
|
}
|
|
72
94
|
};
|
|
73
95
|
}
|
|
74
96
|
/** All fields replace (see `diff`) — this is never actually invoked. */
|
|
75
|
-
async update(_id,
|
|
97
|
+
async update(_id, olds, news) {
|
|
76
98
|
return { outs: {
|
|
77
99
|
...news,
|
|
78
|
-
verified: false
|
|
100
|
+
verified: false,
|
|
101
|
+
cnameTarget: olds.cnameTarget
|
|
79
102
|
} };
|
|
80
103
|
}
|
|
81
104
|
async delete(_id, props) {
|
|
@@ -111,7 +134,8 @@ var VercelDomainResource = class extends _pulumi_pulumi.dynamic.Resource {
|
|
|
111
134
|
constructor(name, args, opts) {
|
|
112
135
|
super(new VercelDomainResourceProvider(), name, {
|
|
113
136
|
...args,
|
|
114
|
-
verified: void 0
|
|
137
|
+
verified: void 0,
|
|
138
|
+
cnameTarget: void 0
|
|
115
139
|
}, opts);
|
|
116
140
|
}
|
|
117
141
|
};
|
|
@@ -120,7 +144,10 @@ var VercelDomainResource = class extends _pulumi_pulumi.dynamic.Resource {
|
|
|
120
144
|
*
|
|
121
145
|
* @example
|
|
122
146
|
* ```typescript
|
|
123
|
-
* new VercelDomain("aura-domain", { name: "app.example.com" }, { provider, project });
|
|
147
|
+
* const domain = new VercelDomain("aura-domain", { name: "app.example.com" }, { provider, project });
|
|
148
|
+
*
|
|
149
|
+
* // Point app.example.com's DNS CNAME at this value.
|
|
150
|
+
* const cnameTarget = domain.cnameTarget;
|
|
124
151
|
* ```
|
|
125
152
|
*/
|
|
126
153
|
var VercelDomain = class extends _pulumi_pulumi.ComponentResource {
|
|
@@ -135,15 +162,16 @@ var VercelDomain = class extends _pulumi_pulumi.ComponentResource {
|
|
|
135
162
|
}, { parent: this });
|
|
136
163
|
this.verified = resource.verified;
|
|
137
164
|
this.name = resource.name;
|
|
165
|
+
this.cnameTarget = resource.cnameTarget;
|
|
138
166
|
this.registerOutputs({
|
|
139
167
|
verified: this.verified,
|
|
140
|
-
name: this.name
|
|
168
|
+
name: this.name,
|
|
169
|
+
cnameTarget: this.cnameTarget
|
|
141
170
|
});
|
|
142
171
|
}
|
|
143
172
|
};
|
|
144
173
|
|
|
145
174
|
//#endregion
|
|
146
|
-
exports.VERCEL_CNAME_TARGET = VERCEL_CNAME_TARGET;
|
|
147
175
|
exports.VercelDomain = VercelDomain;
|
|
148
176
|
exports.VercelDomainResourceProvider = VercelDomainResourceProvider;
|
|
149
177
|
//# sourceMappingURL=domain.cjs.map
|
|
@@ -1 +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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"}
|
|
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 * Fallback CNAME target, used only when Vercel's domain-config endpoint returns no\n * recommendation (see {@link fetchCnameTarget}). Not the primary source of truth —\n * `VercelDomain.cnameTarget` queries Vercel's per-domain recommendation dynamically,\n * since the actual target can vary by account/domain.\n * https://vercel.com/docs/domains/working-with-domains/add-a-domain\n */\nconst VERCEL_CNAME_TARGET_FALLBACK = \"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\t/** The value to CNAME `name` to, per Vercel's own per-domain recommendation. */\n\tcnameTarget: string;\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/** A single ranked CNAME recommendation from `GET /v6/domains/{domain}/config`. */\ninterface VercelRecommendedCname {\n\trank: number;\n\tvalue: string;\n}\n\n/**\n * Fetches Vercel's recommended CNAME target for a domain from its DNS config endpoint.\n * Falls back to {@link VERCEL_CNAME_TARGET_FALLBACK} if Vercel returns no recommendation\n * (logged, since that's an unusual state worth noticing rather than silently accepting).\n */\nasync function fetchCnameTarget(\n\ttoken: string,\n\tteamId: string,\n\tname: string,\n): Promise<string> {\n\tconst response = await fetch(\n\t\t`${VERCEL_API_URL}/v6/domains/${encodeURIComponent(name)}/config?teamId=${teamId}`,\n\t\t{ headers: { Authorization: `Bearer ${token}` } },\n\t);\n\n\tif (!response.ok) {\n\t\tthrow new Error(\n\t\t\t`Vercel API error fetching domain config for \"${name}\" (${response.status}): ${await response.text()}`,\n\t\t);\n\t}\n\n\tconst { recommendedCNAME = [] } = (await response.json()) as {\n\t\trecommendedCNAME?: VercelRecommendedCname[];\n\t};\n\n\tconst preferred = [...recommendedCNAME].sort((a, b) => a.rank - b.rank)[0];\n\n\tif (!preferred) {\n\t\tpulumi.log.info(\n\t\t\t`Vercel returned no recommended CNAME for \"${name}\" — falling back to \"${VERCEL_CNAME_TARGET_FALLBACK}\"`,\n\t\t);\n\n\t\treturn VERCEL_CNAME_TARGET_FALLBACK;\n\t}\n\n\treturn preferred.value;\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\tcnameTarget: await fetchCnameTarget(\n\t\t\t\t\tinputs.token,\n\t\t\t\t\tinputs.teamId,\n\t\t\t\t\tinputs.name,\n\t\t\t\t),\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 = {\n\t\t\t...inputs,\n\t\t\tverified: created.verified,\n\t\t\tcnameTarget: await fetchCnameTarget(\n\t\t\t\tinputs.token,\n\t\t\t\tinputs.teamId,\n\t\t\t\tinputs.name,\n\t\t\t),\n\t\t};\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: {\n\t\t\t\t...props,\n\t\t\t\tverified: domain.verified,\n\t\t\t\tcnameTarget: await fetchCnameTarget(\n\t\t\t\t\tprops.token,\n\t\t\t\t\tprops.teamId,\n\t\t\t\t\tprops.name,\n\t\t\t\t),\n\t\t\t},\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\tolds: VercelDomainOutputs,\n\t\tnews: VercelDomainInputs,\n\t): Promise<pulumi.dynamic.UpdateResult> {\n\t\treturn {\n\t\t\touts: { ...news, verified: false, cnameTarget: olds.cnameTarget },\n\t\t};\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\tpublic declare readonly cnameTarget: 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, cnameTarget: 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 * const domain = new VercelDomain(\"aura-domain\", { name: \"app.example.com\" }, { provider, project });\n *\n * // Point app.example.com's DNS CNAME at this value.\n * const cnameTarget = domain.cnameTarget;\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\t/**\n\t * The value to CNAME `name` to — Vercel's own recommendation for this specific\n\t * domain, fetched fresh from its DNS config endpoint (falls back to a static default\n\t * only if Vercel returns no recommendation).\n\t */\n\tpublic readonly cnameTarget: 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\t\tthis.cnameTarget = resource.cnameTarget;\n\n\t\tthis.registerOutputs({\n\t\t\tverified: this.verified,\n\t\t\tname: this.name,\n\t\t\tcnameTarget: this.cnameTarget,\n\t\t});\n\t}\n}\n"],"mappings":"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"}
|
package/dist/vercel/domain.d.cts
CHANGED
|
@@ -4,11 +4,6 @@ import { VercelProject } from "./project.cjs";
|
|
|
4
4
|
import * as pulumi from "@pulumi/pulumi";
|
|
5
5
|
|
|
6
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
7
|
/** Resolved inputs for the Vercel domain dynamic provider. */
|
|
13
8
|
interface VercelDomainInputs {
|
|
14
9
|
/** Vercel API bearer token. */
|
|
@@ -24,6 +19,8 @@ interface VercelDomainInputs {
|
|
|
24
19
|
interface VercelDomainOutputs extends VercelDomainInputs {
|
|
25
20
|
/** `true` once the domain's DNS has been verified by Vercel. */
|
|
26
21
|
verified: boolean;
|
|
22
|
+
/** The value to CNAME `name` to, per Vercel's own per-domain recommendation. */
|
|
23
|
+
cnameTarget: string;
|
|
27
24
|
}
|
|
28
25
|
/**
|
|
29
26
|
* Dynamic provider implementing adopt-or-create for a Vercel project domain.
|
|
@@ -37,7 +34,7 @@ declare class VercelDomainResourceProvider implements pulumi.dynamic.ResourcePro
|
|
|
37
34
|
create(inputs: VercelDomainInputs): Promise<pulumi.dynamic.CreateResult>;
|
|
38
35
|
read(_id: string, props: VercelDomainOutputs): Promise<pulumi.dynamic.ReadResult>;
|
|
39
36
|
/** All fields replace (see `diff`) — this is never actually invoked. */
|
|
40
|
-
update(_id: string,
|
|
37
|
+
update(_id: string, olds: VercelDomainOutputs, news: VercelDomainInputs): Promise<pulumi.dynamic.UpdateResult>;
|
|
41
38
|
delete(_id: string, props: VercelDomainOutputs): Promise<void>;
|
|
42
39
|
/**
|
|
43
40
|
* A domain can only be attached to one project, so every field change replaces
|
|
@@ -60,7 +57,10 @@ interface VercelDomainArgs {
|
|
|
60
57
|
*
|
|
61
58
|
* @example
|
|
62
59
|
* ```typescript
|
|
63
|
-
* new VercelDomain("aura-domain", { name: "app.example.com" }, { provider, project });
|
|
60
|
+
* const domain = new VercelDomain("aura-domain", { name: "app.example.com" }, { provider, project });
|
|
61
|
+
*
|
|
62
|
+
* // Point app.example.com's DNS CNAME at this value.
|
|
63
|
+
* const cnameTarget = domain.cnameTarget;
|
|
64
64
|
* ```
|
|
65
65
|
*/
|
|
66
66
|
declare class VercelDomain extends pulumi.ComponentResource {
|
|
@@ -68,8 +68,14 @@ declare class VercelDomain extends pulumi.ComponentResource {
|
|
|
68
68
|
readonly verified: pulumi.Output<boolean>;
|
|
69
69
|
/** The attached domain name. */
|
|
70
70
|
readonly name: pulumi.Output<string>;
|
|
71
|
+
/**
|
|
72
|
+
* The value to CNAME `name` to — Vercel's own recommendation for this specific
|
|
73
|
+
* domain, fetched fresh from its DNS config endpoint (falls back to a static default
|
|
74
|
+
* only if Vercel returns no recommendation).
|
|
75
|
+
*/
|
|
76
|
+
readonly cnameTarget: pulumi.Output<string>;
|
|
71
77
|
constructor(name: string, args: VercelDomainArgs, opts: VercelDomainOptions);
|
|
72
78
|
}
|
|
73
79
|
//#endregion
|
|
74
|
-
export {
|
|
80
|
+
export { VercelDomain, VercelDomainArgs, VercelDomainInputs, VercelDomainResourceProvider };
|
|
75
81
|
//# sourceMappingURL=domain.d.cts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"domain.d.cts","names":[],"sources":["../../src/vercel/domain.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"domain.d.cts","names":[],"sources":["../../src/vercel/domain.ts"],"mappings":";;;;;;;UAgBiB,kBAAA;;EAEhB,KAAA;EAFkC;EAKlC,MAAA;EALkC;EAQlC,SAAA;EAHA;EAMA,IAAA;AAAA;;UAIS,mBAAA,SAA4B,kBAAkB;EAA9C;EAET,QAAA;;EAGA,WAAA;AAAA;;;;AAAW;AAyFZ;;;;cAAa,4BAAA,YACD,MAAA,CAAO,OAAA,CAAQ,gBAAA;EAEpB,MAAA,CACL,MAAA,EAAQ,kBAAA,GACN,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,YAAA;EA2DpB,IAAA,CACL,GAAA,UACA,KAAA,EAAO,mBAAA,GACL,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,UAAA;EAAf;EA6BL,MAAA,CACL,GAAA,UACA,IAAA,EAAM,mBAAA,EACN,IAAA,EAAM,kBAAA,GACJ,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,YAAA;EAMpB,MAAA,CAAO,GAAA,UAAa,KAAA,EAAO,mBAAA,GAAsB,OAAA;EAPhD;;;;EAuCD,IAAA,CACL,GAAA,UACA,IAAA,EAAM,mBAAA,EACN,IAAA,EAAM,kBAAA,GACJ,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,UAAA;AAAA;;KAiDtB,mBAAA,GAAsB,IAAA,CAAK,MAAA,CAAO,wBAAA;EAjDnC,qCAmDH,QAAA,EAAU,cAAA,EAhMgC;EAmM1C,OAAA,EAAS,aAAA;AAAA;;UAIO,gBAAA;EArMV;EAuMN,IAAA,EAAM,MAAA,CAAO,KAAK;AAAA;;;;;;;;;;;;cAcN,YAAA,SAAqB,MAAA,CAAO,iBAAA;EAxHlC;EAAA,SA0HU,QAAA,EAAU,MAAA,CAAO,MAAA;EAxH1B;EAAA,SA2HS,IAAA,EAAM,MAAA,CAAO,MAAA;EA1HtB;;;;;EAAA,SAiIS,WAAA,EAAa,MAAA,CAAO,MAAA;cAExB,IAAA,UAAc,IAAA,EAAM,gBAAA,EAAkB,IAAA,EAAM,mBAAA;AAAA"}
|
package/dist/vercel/domain.d.mts
CHANGED
|
@@ -4,11 +4,6 @@ import { VercelProject } from "./project.mjs";
|
|
|
4
4
|
import * as pulumi from "@pulumi/pulumi";
|
|
5
5
|
|
|
6
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
7
|
/** Resolved inputs for the Vercel domain dynamic provider. */
|
|
13
8
|
interface VercelDomainInputs {
|
|
14
9
|
/** Vercel API bearer token. */
|
|
@@ -24,6 +19,8 @@ interface VercelDomainInputs {
|
|
|
24
19
|
interface VercelDomainOutputs extends VercelDomainInputs {
|
|
25
20
|
/** `true` once the domain's DNS has been verified by Vercel. */
|
|
26
21
|
verified: boolean;
|
|
22
|
+
/** The value to CNAME `name` to, per Vercel's own per-domain recommendation. */
|
|
23
|
+
cnameTarget: string;
|
|
27
24
|
}
|
|
28
25
|
/**
|
|
29
26
|
* Dynamic provider implementing adopt-or-create for a Vercel project domain.
|
|
@@ -37,7 +34,7 @@ declare class VercelDomainResourceProvider implements pulumi.dynamic.ResourcePro
|
|
|
37
34
|
create(inputs: VercelDomainInputs): Promise<pulumi.dynamic.CreateResult>;
|
|
38
35
|
read(_id: string, props: VercelDomainOutputs): Promise<pulumi.dynamic.ReadResult>;
|
|
39
36
|
/** All fields replace (see `diff`) — this is never actually invoked. */
|
|
40
|
-
update(_id: string,
|
|
37
|
+
update(_id: string, olds: VercelDomainOutputs, news: VercelDomainInputs): Promise<pulumi.dynamic.UpdateResult>;
|
|
41
38
|
delete(_id: string, props: VercelDomainOutputs): Promise<void>;
|
|
42
39
|
/**
|
|
43
40
|
* A domain can only be attached to one project, so every field change replaces
|
|
@@ -60,7 +57,10 @@ interface VercelDomainArgs {
|
|
|
60
57
|
*
|
|
61
58
|
* @example
|
|
62
59
|
* ```typescript
|
|
63
|
-
* new VercelDomain("aura-domain", { name: "app.example.com" }, { provider, project });
|
|
60
|
+
* const domain = new VercelDomain("aura-domain", { name: "app.example.com" }, { provider, project });
|
|
61
|
+
*
|
|
62
|
+
* // Point app.example.com's DNS CNAME at this value.
|
|
63
|
+
* const cnameTarget = domain.cnameTarget;
|
|
64
64
|
* ```
|
|
65
65
|
*/
|
|
66
66
|
declare class VercelDomain extends pulumi.ComponentResource {
|
|
@@ -68,8 +68,14 @@ declare class VercelDomain extends pulumi.ComponentResource {
|
|
|
68
68
|
readonly verified: pulumi.Output<boolean>;
|
|
69
69
|
/** The attached domain name. */
|
|
70
70
|
readonly name: pulumi.Output<string>;
|
|
71
|
+
/**
|
|
72
|
+
* The value to CNAME `name` to — Vercel's own recommendation for this specific
|
|
73
|
+
* domain, fetched fresh from its DNS config endpoint (falls back to a static default
|
|
74
|
+
* only if Vercel returns no recommendation).
|
|
75
|
+
*/
|
|
76
|
+
readonly cnameTarget: pulumi.Output<string>;
|
|
71
77
|
constructor(name: string, args: VercelDomainArgs, opts: VercelDomainOptions);
|
|
72
78
|
}
|
|
73
79
|
//#endregion
|
|
74
|
-
export {
|
|
80
|
+
export { VercelDomain, VercelDomainArgs, VercelDomainInputs, VercelDomainResourceProvider };
|
|
75
81
|
//# sourceMappingURL=domain.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"domain.d.mts","names":[],"sources":["../../src/vercel/domain.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"domain.d.mts","names":[],"sources":["../../src/vercel/domain.ts"],"mappings":";;;;;;;UAgBiB,kBAAA;;EAEhB,KAAA;EAFkC;EAKlC,MAAA;EALkC;EAQlC,SAAA;EAHA;EAMA,IAAA;AAAA;;UAIS,mBAAA,SAA4B,kBAAkB;EAA9C;EAET,QAAA;;EAGA,WAAA;AAAA;;;;AAAW;AAyFZ;;;;cAAa,4BAAA,YACD,MAAA,CAAO,OAAA,CAAQ,gBAAA;EAEpB,MAAA,CACL,MAAA,EAAQ,kBAAA,GACN,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,YAAA;EA2DpB,IAAA,CACL,GAAA,UACA,KAAA,EAAO,mBAAA,GACL,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,UAAA;EAAf;EA6BL,MAAA,CACL,GAAA,UACA,IAAA,EAAM,mBAAA,EACN,IAAA,EAAM,kBAAA,GACJ,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,YAAA;EAMpB,MAAA,CAAO,GAAA,UAAa,KAAA,EAAO,mBAAA,GAAsB,OAAA;EAPhD;;;;EAuCD,IAAA,CACL,GAAA,UACA,IAAA,EAAM,mBAAA,EACN,IAAA,EAAM,kBAAA,GACJ,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,UAAA;AAAA;;KAiDtB,mBAAA,GAAsB,IAAA,CAAK,MAAA,CAAO,wBAAA;EAjDnC,qCAmDH,QAAA,EAAU,cAAA,EAhMgC;EAmM1C,OAAA,EAAS,aAAA;AAAA;;UAIO,gBAAA;EArMV;EAuMN,IAAA,EAAM,MAAA,CAAO,KAAK;AAAA;;;;;;;;;;;;cAcN,YAAA,SAAqB,MAAA,CAAO,iBAAA;EAxHlC;EAAA,SA0HU,QAAA,EAAU,MAAA,CAAO,MAAA;EAxH1B;EAAA,SA2HS,IAAA,EAAM,MAAA,CAAO,MAAA;EA1HtB;;;;;EAAA,SAiIS,WAAA,EAAa,MAAA,CAAO,MAAA;cAExB,IAAA,UAAc,IAAA,EAAM,gBAAA,EAAkB,IAAA,EAAM,mBAAA;AAAA"}
|
package/dist/vercel/domain.mjs
CHANGED
|
@@ -4,10 +4,29 @@ import * as pulumi from "@pulumi/pulumi";
|
|
|
4
4
|
//#region src/vercel/domain.ts
|
|
5
5
|
const VERCEL_API_URL = "https://api.vercel.com";
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
7
|
+
* Fallback CNAME target, used only when Vercel's domain-config endpoint returns no
|
|
8
|
+
* recommendation (see {@link fetchCnameTarget}). Not the primary source of truth —
|
|
9
|
+
* `VercelDomain.cnameTarget` queries Vercel's per-domain recommendation dynamically,
|
|
10
|
+
* since the actual target can vary by account/domain.
|
|
8
11
|
* https://vercel.com/docs/domains/working-with-domains/add-a-domain
|
|
9
12
|
*/
|
|
10
|
-
const
|
|
13
|
+
const VERCEL_CNAME_TARGET_FALLBACK = "cname.vercel-dns.com";
|
|
14
|
+
/**
|
|
15
|
+
* Fetches Vercel's recommended CNAME target for a domain from its DNS config endpoint.
|
|
16
|
+
* Falls back to {@link VERCEL_CNAME_TARGET_FALLBACK} if Vercel returns no recommendation
|
|
17
|
+
* (logged, since that's an unusual state worth noticing rather than silently accepting).
|
|
18
|
+
*/
|
|
19
|
+
async function fetchCnameTarget(token, teamId, name) {
|
|
20
|
+
const response = await fetch(`${VERCEL_API_URL}/v6/domains/${encodeURIComponent(name)}/config?teamId=${teamId}`, { headers: { Authorization: `Bearer ${token}` } });
|
|
21
|
+
if (!response.ok) throw new Error(`Vercel API error fetching domain config for "${name}" (${response.status}): ${await response.text()}`);
|
|
22
|
+
const { recommendedCNAME = [] } = await response.json();
|
|
23
|
+
const preferred = [...recommendedCNAME].sort((a, b) => a.rank - b.rank)[0];
|
|
24
|
+
if (!preferred) {
|
|
25
|
+
pulumi.log.info(`Vercel returned no recommended CNAME for "${name}" — falling back to "${VERCEL_CNAME_TARGET_FALLBACK}"`);
|
|
26
|
+
return VERCEL_CNAME_TARGET_FALLBACK;
|
|
27
|
+
}
|
|
28
|
+
return preferred.value;
|
|
29
|
+
}
|
|
11
30
|
/**
|
|
12
31
|
* Fetches a project domain by name. Returns `null` if not attached (404).
|
|
13
32
|
*/
|
|
@@ -32,7 +51,8 @@ var VercelDomainResourceProvider = class {
|
|
|
32
51
|
pulumi.log.info(`Adopted existing Vercel domain "${inputs.name}" on project ${inputs.projectId}`);
|
|
33
52
|
const outs = {
|
|
34
53
|
...inputs,
|
|
35
|
-
verified: existing.verified
|
|
54
|
+
verified: existing.verified,
|
|
55
|
+
cnameTarget: await fetchCnameTarget(inputs.token, inputs.teamId, inputs.name)
|
|
36
56
|
};
|
|
37
57
|
return {
|
|
38
58
|
id: `${inputs.projectId}/${inputs.name}`,
|
|
@@ -51,7 +71,8 @@ var VercelDomainResourceProvider = class {
|
|
|
51
71
|
const created = await response.json();
|
|
52
72
|
const outs = {
|
|
53
73
|
...inputs,
|
|
54
|
-
verified: created.verified
|
|
74
|
+
verified: created.verified,
|
|
75
|
+
cnameTarget: await fetchCnameTarget(inputs.token, inputs.teamId, inputs.name)
|
|
55
76
|
};
|
|
56
77
|
return {
|
|
57
78
|
id: `${inputs.projectId}/${inputs.name}`,
|
|
@@ -65,15 +86,17 @@ var VercelDomainResourceProvider = class {
|
|
|
65
86
|
id: `${props.projectId}/${props.name}`,
|
|
66
87
|
props: {
|
|
67
88
|
...props,
|
|
68
|
-
verified: domain.verified
|
|
89
|
+
verified: domain.verified,
|
|
90
|
+
cnameTarget: await fetchCnameTarget(props.token, props.teamId, props.name)
|
|
69
91
|
}
|
|
70
92
|
};
|
|
71
93
|
}
|
|
72
94
|
/** All fields replace (see `diff`) — this is never actually invoked. */
|
|
73
|
-
async update(_id,
|
|
95
|
+
async update(_id, olds, news) {
|
|
74
96
|
return { outs: {
|
|
75
97
|
...news,
|
|
76
|
-
verified: false
|
|
98
|
+
verified: false,
|
|
99
|
+
cnameTarget: olds.cnameTarget
|
|
77
100
|
} };
|
|
78
101
|
}
|
|
79
102
|
async delete(_id, props) {
|
|
@@ -109,7 +132,8 @@ var VercelDomainResource = class extends pulumi.dynamic.Resource {
|
|
|
109
132
|
constructor(name, args, opts) {
|
|
110
133
|
super(new VercelDomainResourceProvider(), name, {
|
|
111
134
|
...args,
|
|
112
|
-
verified: void 0
|
|
135
|
+
verified: void 0,
|
|
136
|
+
cnameTarget: void 0
|
|
113
137
|
}, opts);
|
|
114
138
|
}
|
|
115
139
|
};
|
|
@@ -118,7 +142,10 @@ var VercelDomainResource = class extends pulumi.dynamic.Resource {
|
|
|
118
142
|
*
|
|
119
143
|
* @example
|
|
120
144
|
* ```typescript
|
|
121
|
-
* new VercelDomain("aura-domain", { name: "app.example.com" }, { provider, project });
|
|
145
|
+
* const domain = new VercelDomain("aura-domain", { name: "app.example.com" }, { provider, project });
|
|
146
|
+
*
|
|
147
|
+
* // Point app.example.com's DNS CNAME at this value.
|
|
148
|
+
* const cnameTarget = domain.cnameTarget;
|
|
122
149
|
* ```
|
|
123
150
|
*/
|
|
124
151
|
var VercelDomain = class extends pulumi.ComponentResource {
|
|
@@ -133,13 +160,15 @@ var VercelDomain = class extends pulumi.ComponentResource {
|
|
|
133
160
|
}, { parent: this });
|
|
134
161
|
this.verified = resource.verified;
|
|
135
162
|
this.name = resource.name;
|
|
163
|
+
this.cnameTarget = resource.cnameTarget;
|
|
136
164
|
this.registerOutputs({
|
|
137
165
|
verified: this.verified,
|
|
138
|
-
name: this.name
|
|
166
|
+
name: this.name,
|
|
167
|
+
cnameTarget: this.cnameTarget
|
|
139
168
|
});
|
|
140
169
|
}
|
|
141
170
|
};
|
|
142
171
|
|
|
143
172
|
//#endregion
|
|
144
|
-
export {
|
|
173
|
+
export { VercelDomain, VercelDomainResourceProvider };
|
|
145
174
|
//# sourceMappingURL=domain.mjs.map
|
|
@@ -1 +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
|
+
{"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 * Fallback CNAME target, used only when Vercel's domain-config endpoint returns no\n * recommendation (see {@link fetchCnameTarget}). Not the primary source of truth —\n * `VercelDomain.cnameTarget` queries Vercel's per-domain recommendation dynamically,\n * since the actual target can vary by account/domain.\n * https://vercel.com/docs/domains/working-with-domains/add-a-domain\n */\nconst VERCEL_CNAME_TARGET_FALLBACK = \"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\t/** The value to CNAME `name` to, per Vercel's own per-domain recommendation. */\n\tcnameTarget: string;\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/** A single ranked CNAME recommendation from `GET /v6/domains/{domain}/config`. */\ninterface VercelRecommendedCname {\n\trank: number;\n\tvalue: string;\n}\n\n/**\n * Fetches Vercel's recommended CNAME target for a domain from its DNS config endpoint.\n * Falls back to {@link VERCEL_CNAME_TARGET_FALLBACK} if Vercel returns no recommendation\n * (logged, since that's an unusual state worth noticing rather than silently accepting).\n */\nasync function fetchCnameTarget(\n\ttoken: string,\n\tteamId: string,\n\tname: string,\n): Promise<string> {\n\tconst response = await fetch(\n\t\t`${VERCEL_API_URL}/v6/domains/${encodeURIComponent(name)}/config?teamId=${teamId}`,\n\t\t{ headers: { Authorization: `Bearer ${token}` } },\n\t);\n\n\tif (!response.ok) {\n\t\tthrow new Error(\n\t\t\t`Vercel API error fetching domain config for \"${name}\" (${response.status}): ${await response.text()}`,\n\t\t);\n\t}\n\n\tconst { recommendedCNAME = [] } = (await response.json()) as {\n\t\trecommendedCNAME?: VercelRecommendedCname[];\n\t};\n\n\tconst preferred = [...recommendedCNAME].sort((a, b) => a.rank - b.rank)[0];\n\n\tif (!preferred) {\n\t\tpulumi.log.info(\n\t\t\t`Vercel returned no recommended CNAME for \"${name}\" — falling back to \"${VERCEL_CNAME_TARGET_FALLBACK}\"`,\n\t\t);\n\n\t\treturn VERCEL_CNAME_TARGET_FALLBACK;\n\t}\n\n\treturn preferred.value;\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\tcnameTarget: await fetchCnameTarget(\n\t\t\t\t\tinputs.token,\n\t\t\t\t\tinputs.teamId,\n\t\t\t\t\tinputs.name,\n\t\t\t\t),\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 = {\n\t\t\t...inputs,\n\t\t\tverified: created.verified,\n\t\t\tcnameTarget: await fetchCnameTarget(\n\t\t\t\tinputs.token,\n\t\t\t\tinputs.teamId,\n\t\t\t\tinputs.name,\n\t\t\t),\n\t\t};\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: {\n\t\t\t\t...props,\n\t\t\t\tverified: domain.verified,\n\t\t\t\tcnameTarget: await fetchCnameTarget(\n\t\t\t\t\tprops.token,\n\t\t\t\t\tprops.teamId,\n\t\t\t\t\tprops.name,\n\t\t\t\t),\n\t\t\t},\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\tolds: VercelDomainOutputs,\n\t\tnews: VercelDomainInputs,\n\t): Promise<pulumi.dynamic.UpdateResult> {\n\t\treturn {\n\t\t\touts: { ...news, verified: false, cnameTarget: olds.cnameTarget },\n\t\t};\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\tpublic declare readonly cnameTarget: 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, cnameTarget: 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 * const domain = new VercelDomain(\"aura-domain\", { name: \"app.example.com\" }, { provider, project });\n *\n * // Point app.example.com's DNS CNAME at this value.\n * const cnameTarget = domain.cnameTarget;\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\t/**\n\t * The value to CNAME `name` to — Vercel's own recommendation for this specific\n\t * domain, fetched fresh from its DNS config endpoint (falls back to a static default\n\t * only if Vercel returns no recommendation).\n\t */\n\tpublic readonly cnameTarget: 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\t\tthis.cnameTarget = resource.cnameTarget;\n\n\t\tthis.registerOutputs({\n\t\t\tverified: this.verified,\n\t\t\tname: this.name,\n\t\t\tcnameTarget: this.cnameTarget,\n\t\t});\n\t}\n}\n"],"mappings":"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"}
|
package/dist/vercel/index.cjs
CHANGED
|
@@ -8,7 +8,6 @@ const require_vercel_provider = require('./provider.cjs');
|
|
|
8
8
|
const require_vercel_resource_connection = require('./resource-connection.cjs');
|
|
9
9
|
const require_vercel_variable = require('./variable.cjs');
|
|
10
10
|
|
|
11
|
-
exports.VERCEL_CNAME_TARGET = require_vercel_domain.VERCEL_CNAME_TARGET;
|
|
12
11
|
exports.VERCEL_FRAMEWORKS = require_vercel_project.VERCEL_FRAMEWORKS;
|
|
13
12
|
exports.VercelDeploy = require_vercel_deploy.VercelDeploy;
|
|
14
13
|
exports.VercelDomain = require_vercel_domain.VercelDomain;
|
package/dist/vercel/index.d.cts
CHANGED
|
@@ -1,9 +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 {
|
|
4
|
+
import { VercelDomain, VercelDomainArgs } from "./domain.cjs";
|
|
5
5
|
import { VercelIntegration, VercelIntegrationArgs } from "./integration.cjs";
|
|
6
6
|
import { VercelMarketplaceResource, VercelMarketplaceResourceArgs } from "./marketplace-resource.cjs";
|
|
7
7
|
import { VercelResourceConnection, VercelResourceConnectionArgs } from "./resource-connection.cjs";
|
|
8
8
|
import { VercelVariable, VercelVariableArgs } from "./variable.cjs";
|
|
9
|
-
export {
|
|
9
|
+
export { 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 };
|
package/dist/vercel/index.d.mts
CHANGED
|
@@ -1,9 +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 {
|
|
4
|
+
import { VercelDomain, VercelDomainArgs } from "./domain.mjs";
|
|
5
5
|
import { VercelIntegration, VercelIntegrationArgs } from "./integration.mjs";
|
|
6
6
|
import { VercelMarketplaceResource, VercelMarketplaceResourceArgs } from "./marketplace-resource.mjs";
|
|
7
7
|
import { VercelResourceConnection, VercelResourceConnectionArgs } from "./resource-connection.mjs";
|
|
8
8
|
import { VercelVariable, VercelVariableArgs } from "./variable.mjs";
|
|
9
|
-
export {
|
|
9
|
+
export { 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 };
|
package/dist/vercel/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { VercelDeploy } from "./deploy.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { VercelDomain } from "./domain.mjs";
|
|
3
3
|
import { VercelIntegration } from "./integration.mjs";
|
|
4
4
|
import { VercelMarketplaceResource } from "./marketplace-resource.mjs";
|
|
5
5
|
import { VERCEL_FRAMEWORKS, VercelProject } from "./project.mjs";
|
|
@@ -7,4 +7,4 @@ import { VercelProvider } from "./provider.mjs";
|
|
|
7
7
|
import { VercelResourceConnection } from "./resource-connection.mjs";
|
|
8
8
|
import { VercelVariable } from "./variable.mjs";
|
|
9
9
|
|
|
10
|
-
export {
|
|
10
|
+
export { 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.
|
|
3
|
+
"version": "1.20.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.",
|