doomain 0.1.22 → 0.1.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/commands/auth/clerk.js +3 -2
- package/dist/commands/auth/logout/clerk.js +3 -2
- package/dist/commands/auth/logout/vercel.js +3 -2
- package/dist/commands/auth/vercel.js +5 -4
- package/dist/commands/clerk/domains/add.js +3 -2
- package/dist/commands/dns/diagnose.js +3 -2
- package/dist/commands/dns/point.js +7 -3
- package/dist/commands/dns/remove.js +3 -2
- package/dist/commands/domains/find.js +3 -2
- package/dist/commands/domains/list.js +46 -33
- package/dist/commands/link.js +4 -3
- package/dist/commands/projects/list.js +5 -2
- package/dist/commands/providers/connect.js +14 -23
- package/dist/commands/providers/disconnect.js +3 -2
- package/dist/commands/providers/status.js +2 -1
- package/dist/commands/providers/verify.js +3 -2
- package/dist/commands/schema.js +2 -1
- package/dist/commands/update.js +2 -1
- package/dist/commands/verify.js +5 -2
- package/dist/commands/version.d.ts +5 -0
- package/dist/commands/version.js +8 -0
- package/dist/commands/wizard.js +45 -46
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/lib/clerk-domain.d.ts +2 -1
- package/dist/lib/clerk-domain.js +122 -110
- package/dist/lib/clerk.d.ts +9 -5
- package/dist/lib/clerk.js +45 -36
- package/dist/lib/command-schema.d.ts +2 -1
- package/dist/lib/command-schema.js +16 -9
- package/dist/lib/config.d.ts +6 -4
- package/dist/lib/config.js +29 -27
- package/dist/lib/diagnose-dns.d.ts +8 -5
- package/dist/lib/diagnose-dns.js +90 -92
- package/dist/lib/dns-propagation.d.ts +5 -4
- package/dist/lib/dns-propagation.js +80 -54
- package/dist/lib/dns-reconciliation.d.ts +4 -3
- package/dist/lib/dns-reconciliation.js +52 -47
- package/dist/lib/domain-provider.d.ts +5 -1
- package/dist/lib/domain-provider.js +136 -131
- package/dist/lib/effect.d.ts +6 -0
- package/dist/lib/effect.js +20 -0
- package/dist/lib/link-domain.d.ts +3 -2
- package/dist/lib/link-domain.js +239 -218
- package/dist/lib/point-domain.d.ts +7 -4
- package/dist/lib/point-domain.js +92 -90
- package/dist/lib/providers/cloudflare/index.d.ts +9 -8
- package/dist/lib/providers/cloudflare/index.js +93 -87
- package/dist/lib/providers/core/config.d.ts +4 -1
- package/dist/lib/providers/core/config.js +17 -13
- package/dist/lib/providers/core/http.d.ts +4 -1
- package/dist/lib/providers/core/http.js +28 -18
- package/dist/lib/providers/core/pagination.d.ts +3 -2
- package/dist/lib/providers/core/pagination.js +17 -14
- package/dist/lib/providers/core/planner.d.ts +5 -4
- package/dist/lib/providers/core/planner.js +18 -15
- package/dist/lib/providers/core/types.d.ts +11 -8
- package/dist/lib/providers/hostinger/index.d.ts +9 -8
- package/dist/lib/providers/hostinger/index.js +74 -72
- package/dist/lib/providers/namecheap/index.d.ts +10 -8
- package/dist/lib/providers/namecheap/index.js +134 -109
- package/dist/lib/providers/registry.d.ts +3 -3
- package/dist/lib/providers/registry.js +7 -3
- package/dist/lib/providers/spaceship/index.d.ts +9 -8
- package/dist/lib/providers/spaceship/index.js +48 -47
- package/dist/lib/providers/status.d.ts +2 -1
- package/dist/lib/providers/status.js +41 -34
- package/dist/lib/public-ip.d.ts +2 -0
- package/dist/lib/public-ip.js +14 -0
- package/dist/lib/remove-domain.d.ts +6 -3
- package/dist/lib/remove-domain.js +83 -77
- package/dist/lib/self-update.d.ts +3 -2
- package/dist/lib/self-update.js +15 -23
- package/dist/lib/vercel-auth.d.ts +2 -1
- package/dist/lib/vercel-auth.js +21 -24
- package/dist/lib/vercel.d.ts +16 -12
- package/dist/lib/vercel.js +174 -114
- package/oclif.manifest.json +21 -1
- package/package.json +8 -1
package/dist/lib/link-domain.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { resolve4, resolveCname, resolveTxt } from 'node:dns/promises';
|
|
2
2
|
import { existsSync, readFileSync } from 'node:fs';
|
|
3
3
|
import { dirname, join, parse } from 'node:path';
|
|
4
|
+
import { Effect } from 'effect';
|
|
4
5
|
import { loadConfig } from './config.js';
|
|
5
6
|
import { resolveProviderTarget } from './domain-provider.js';
|
|
7
|
+
import { tryPromise } from './effect.js';
|
|
6
8
|
import { DoomainError } from './errors.js';
|
|
7
9
|
import { detectLocalVercelProject } from './local-vercel.js';
|
|
8
10
|
import { createProvider } from './providers/registry.js';
|
|
@@ -13,12 +15,15 @@ function cleanDnsValue(value) {
|
|
|
13
15
|
function recordFqdn(record, zoneDomain) {
|
|
14
16
|
return record.name === '@' ? zoneDomain : `${record.name}.${zoneDomain}`;
|
|
15
17
|
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
function resolveConfiguredDomain(domain) {
|
|
19
|
+
return Effect.gen(function* () {
|
|
20
|
+
const config = yield* loadConfig();
|
|
21
|
+
const resolved = domain ?? process.env.DOOMAIN_DOMAIN ?? config.defaults?.domain;
|
|
22
|
+
if (!resolved) {
|
|
23
|
+
return yield* Effect.fail(new DoomainError('MISSING_ARGUMENT', 'Domain is required. Use --domain or set a default domain.'));
|
|
24
|
+
}
|
|
25
|
+
return resolved;
|
|
26
|
+
});
|
|
22
27
|
}
|
|
23
28
|
function findPackageProjectName(start = process.cwd()) {
|
|
24
29
|
let current = start;
|
|
@@ -71,48 +76,50 @@ function projectSuggestions(projectName, projects) {
|
|
|
71
76
|
.slice(0, 5)
|
|
72
77
|
.map(({ project }) => ({ id: project.id, name: project.name }));
|
|
73
78
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
project: projectName,
|
|
84
|
-
projectSource: 'packageJson',
|
|
85
|
-
suggestions,
|
|
79
|
+
function resolvePackageProject(projectName) {
|
|
80
|
+
return Effect.gen(function* () {
|
|
81
|
+
const vercel = createVercelClient(yield* resolveVercelConfig());
|
|
82
|
+
const projects = yield* vercel.listProjects(projectName);
|
|
83
|
+
const match = projects.find((project) => project.name === projectName || project.id === projectName);
|
|
84
|
+
if (match)
|
|
85
|
+
return match.name;
|
|
86
|
+
const allProjects = yield* vercel.listProjects().pipe(Effect.catchAll(() => Effect.succeed([])));
|
|
87
|
+
const suggestions = projectSuggestions(projectName, [...projects, ...allProjects]);
|
|
88
|
+
return yield* Effect.fail(new DoomainError('VERCEL_PROJECT_NOT_LINKED', `No Vercel project named ${projectName} was found. Pass --project to choose a project.`, { project: projectName, projectSource: 'packageJson', suggestions }));
|
|
86
89
|
});
|
|
87
90
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
91
|
+
function resolveProject(project) {
|
|
92
|
+
return Effect.gen(function* () {
|
|
93
|
+
if (project)
|
|
94
|
+
return { project, projectSource: 'flag', localProjectDetected: false };
|
|
95
|
+
const config = yield* loadConfig();
|
|
96
|
+
const envProject = process.env.DOOMAIN_PROJECT;
|
|
97
|
+
if (envProject)
|
|
98
|
+
return { project: envProject, projectSource: 'env', localProjectDetected: false };
|
|
99
|
+
if (config.defaults?.project)
|
|
100
|
+
return { project: config.defaults.project, projectSource: 'config', localProjectDetected: false };
|
|
101
|
+
const localProject = detectLocalVercelProject();
|
|
102
|
+
if (localProject)
|
|
103
|
+
return { project: localProject.projectId, projectSource: 'vercelProjectFile', localProjectDetected: true };
|
|
104
|
+
const packageProject = findPackageProjectName();
|
|
105
|
+
if (packageProject) {
|
|
106
|
+
return {
|
|
107
|
+
project: yield* resolvePackageProject(packageProject),
|
|
108
|
+
projectSource: 'packageJson',
|
|
109
|
+
localProjectDetected: false,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
return yield* Effect.fail(new DoomainError('VERCEL_PROJECT_NOT_LINKED', 'No Vercel project could be inferred. Run inside a project with package.json/.vercel/project.json or pass --project.'));
|
|
113
|
+
});
|
|
109
114
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
115
|
+
function resolveZone(provider, zoneDomain) {
|
|
116
|
+
return Effect.gen(function* () {
|
|
117
|
+
const zone = yield* provider.getZone(zoneDomain);
|
|
118
|
+
if (!zone) {
|
|
119
|
+
return yield* Effect.fail(new DoomainError('PROVIDER_ZONE_NOT_FOUND', `${provider.name} does not have a DNS zone for ${zoneDomain}.`));
|
|
120
|
+
}
|
|
121
|
+
return zone;
|
|
122
|
+
});
|
|
116
123
|
}
|
|
117
124
|
export function withProviderRecordOptions(provider, record) {
|
|
118
125
|
if (provider !== 'cloudflare' || !['A', 'AAAA', 'CNAME'].includes(record.type))
|
|
@@ -186,101 +193,104 @@ export function verificationRecords(raw, zoneDomain) {
|
|
|
186
193
|
return [{ type: 'TXT', name: cleanVerificationName(name, zoneDomain), value: record.value, ttl: 3600 }];
|
|
187
194
|
});
|
|
188
195
|
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
setTimeout(resolve, milliseconds);
|
|
192
|
-
});
|
|
196
|
+
function wait(milliseconds) {
|
|
197
|
+
return Effect.sleep(milliseconds);
|
|
193
198
|
}
|
|
194
|
-
|
|
199
|
+
function isRecordPropagated(record, zoneDomain) {
|
|
195
200
|
const fqdn = recordFqdn(record, zoneDomain);
|
|
196
|
-
|
|
201
|
+
return Effect.gen(function* () {
|
|
197
202
|
if (record.type === 'A') {
|
|
198
|
-
const values =
|
|
203
|
+
const values = yield* Effect.tryPromise(() => resolve4(fqdn));
|
|
199
204
|
return values.includes(record.value);
|
|
200
205
|
}
|
|
201
206
|
if (record.type === 'CNAME') {
|
|
202
|
-
const values =
|
|
207
|
+
const values = yield* Effect.tryPromise(() => resolveCname(fqdn));
|
|
203
208
|
return values.map(cleanDnsValue).includes(cleanDnsValue(record.value));
|
|
204
209
|
}
|
|
205
210
|
if (record.type === 'TXT') {
|
|
206
|
-
const values = (
|
|
211
|
+
const values = (yield* Effect.tryPromise(() => resolveTxt(fqdn))).map((chunks) => chunks.join(''));
|
|
207
212
|
return values.includes(record.value);
|
|
208
213
|
}
|
|
209
|
-
}
|
|
210
|
-
catch {
|
|
211
214
|
return false;
|
|
212
|
-
}
|
|
213
|
-
return false;
|
|
215
|
+
}).pipe(Effect.catchAll(() => Effect.succeed(false)));
|
|
214
216
|
}
|
|
215
217
|
function errorMessage(error) {
|
|
216
218
|
if (error instanceof Error)
|
|
217
219
|
return error.message;
|
|
218
220
|
return String(error);
|
|
219
221
|
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
222
|
+
function areRecordsPropagated(records, zoneDomain) {
|
|
223
|
+
return Effect.all(records.map((record) => isRecordPropagated(record, zoneDomain)), {
|
|
224
|
+
concurrency: 'unbounded',
|
|
225
|
+
}).pipe(Effect.map((results) => results.every(Boolean)));
|
|
223
226
|
}
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
227
|
+
function waitForVercelDomainReady(opts) {
|
|
228
|
+
return Effect.gen(function* () {
|
|
229
|
+
const vercel = createVercelClient(yield* resolveVercelConfig());
|
|
230
|
+
const timeoutSeconds = opts.input.timeoutSeconds ?? 300;
|
|
231
|
+
const startedAt = Date.now();
|
|
232
|
+
const deadline = Date.now() + timeoutSeconds * 1000;
|
|
233
|
+
let lastError;
|
|
234
|
+
let lastConfig;
|
|
235
|
+
let propagated = false;
|
|
236
|
+
let attempt = 1;
|
|
237
|
+
let records = opts.records;
|
|
238
|
+
function applyVerificationRecords(raw) {
|
|
239
|
+
return Effect.gen(function* () {
|
|
240
|
+
const nextRecords = planVerificationRecords(opts.providerId, raw, opts.zoneDomain);
|
|
241
|
+
const merged = mergeRecords(records, nextRecords);
|
|
242
|
+
if (merged.added.length === 0)
|
|
243
|
+
return;
|
|
244
|
+
reportProgress(opts.input, 'dns:plan', `Found ${merged.added.length} new Vercel ownership record${merged.added.length === 1 ? '' : 's'}`);
|
|
245
|
+
const dnsPlan = yield* opts.provider.planChanges(opts.zone, merged.added, { force: opts.force });
|
|
246
|
+
reportProgress(opts.input, 'dns:apply', `Updating ownership records in ${opts.provider.name}`);
|
|
247
|
+
yield* opts.provider.applyChanges(opts.zone, dnsPlan, { force: opts.force });
|
|
248
|
+
records = merged.records;
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
function isVercelReady(raw) {
|
|
252
|
+
return Effect.gen(function* () {
|
|
253
|
+
yield* applyVerificationRecords(raw);
|
|
254
|
+
if (raw.verified !== true)
|
|
255
|
+
return false;
|
|
256
|
+
lastConfig = yield* vercel.getDomainConfig(opts.domain);
|
|
257
|
+
return isDomainConfigReady(lastConfig);
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
while (Date.now() <= deadline) {
|
|
261
|
+
const elapsedSeconds = Math.round((Date.now() - startedAt) / 1000);
|
|
262
|
+
reportProgress(opts.input, 'vercel:verify', `Verifying domain in Vercel (attempt ${attempt}, ${elapsedSeconds}s elapsed)`);
|
|
263
|
+
const verification = yield* Effect.gen(function* () {
|
|
264
|
+
const current = yield* vercel.getProjectDomain(opts.project, opts.domain);
|
|
265
|
+
if (yield* isVercelReady(current))
|
|
266
|
+
return true;
|
|
267
|
+
const result = yield* vercel.verifyProjectDomain(opts.project, opts.domain);
|
|
268
|
+
return yield* isVercelReady(result);
|
|
269
|
+
}).pipe(Effect.either);
|
|
270
|
+
if (verification._tag === 'Right' && verification.right)
|
|
261
271
|
return { propagated: true, verified: true };
|
|
272
|
+
if (verification._tag === 'Left') {
|
|
273
|
+
// Vercel returns an error while DNS is still propagating.
|
|
274
|
+
lastError = verification.left;
|
|
275
|
+
yield* applyVerificationRecords(errorDetails(verification.left));
|
|
276
|
+
}
|
|
277
|
+
propagated = yield* areRecordsPropagated(records, opts.zoneDomain);
|
|
278
|
+
reportProgress(opts.input, 'dns:wait', propagated
|
|
279
|
+
? 'DNS is visible publicly; Vercel verification is still pending'
|
|
280
|
+
: 'DNS records were saved; public DNS is still catching up');
|
|
281
|
+
attempt += 1;
|
|
282
|
+
if (Date.now() > deadline)
|
|
283
|
+
break;
|
|
284
|
+
yield* wait(5000);
|
|
262
285
|
}
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
lastError = error;
|
|
266
|
-
await applyVerificationRecords(errorDetails(error));
|
|
286
|
+
if (lastError) {
|
|
287
|
+
return yield* Effect.fail(new DoomainError('DOMAIN_VERIFY_FAILED', `Vercel did not verify ${opts.domain} within ${timeoutSeconds} seconds. Last Vercel response: ${errorMessage(lastError)}`, { domainConfig: lastConfig, error: errorDetails(lastError) }));
|
|
267
288
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
if (Date.now() > deadline)
|
|
274
|
-
break;
|
|
275
|
-
await wait(5000);
|
|
276
|
-
}
|
|
277
|
-
if (lastError) {
|
|
278
|
-
throw new DoomainError('DOMAIN_VERIFY_FAILED', `Vercel did not verify ${opts.domain} within ${timeoutSeconds} seconds. Last Vercel response: ${errorMessage(lastError)}`, { domainConfig: lastConfig, error: errorDetails(lastError) });
|
|
279
|
-
}
|
|
280
|
-
if (lastConfig?.misconfigured === true) {
|
|
281
|
-
throw new DoomainError('DOMAIN_VERIFY_FAILED', `Vercel verified ${opts.domain}, but its DNS configuration is still invalid after ${timeoutSeconds} seconds.`, { domainConfig: lastConfig });
|
|
282
|
-
}
|
|
283
|
-
return { propagated, verified: false };
|
|
289
|
+
if (lastConfig?.misconfigured === true) {
|
|
290
|
+
return yield* Effect.fail(new DoomainError('DOMAIN_VERIFY_FAILED', `Vercel verified ${opts.domain}, but its DNS configuration is still invalid after ${timeoutSeconds} seconds.`, { domainConfig: lastConfig }));
|
|
291
|
+
}
|
|
292
|
+
return { propagated, verified: false };
|
|
293
|
+
});
|
|
284
294
|
}
|
|
285
295
|
function reportProgress(input, stage, message) {
|
|
286
296
|
input.progress?.({ message, stage });
|
|
@@ -299,110 +309,121 @@ function dnsTargetConflictError(warning) {
|
|
|
299
309
|
zoneDomain: warning.zoneDomain,
|
|
300
310
|
});
|
|
301
311
|
}
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
312
|
+
function resolveDnsForce(input, opts) {
|
|
313
|
+
return Effect.gen(function* () {
|
|
314
|
+
reportProgress(input, 'dns:inspect', `Checking existing DNS records in ${opts.provider.name}`);
|
|
315
|
+
const dnsPlan = yield* opts.provider.planChanges(opts.zone, [opts.baseRecord], { force: input.force });
|
|
316
|
+
if (input.force || dnsPlan.conflicts.length === 0)
|
|
317
|
+
return Boolean(input.force);
|
|
318
|
+
const warning = {
|
|
319
|
+
account: opts.plan.account,
|
|
320
|
+
conflicts: dnsPlan.conflicts,
|
|
321
|
+
desired: [opts.baseRecord],
|
|
322
|
+
domain: opts.plan.domain,
|
|
323
|
+
provider: opts.plan.provider,
|
|
324
|
+
providerName: opts.provider.name,
|
|
325
|
+
recordName: opts.plan.recordName,
|
|
326
|
+
zoneDomain: opts.plan.zoneDomain,
|
|
327
|
+
};
|
|
328
|
+
reportProgress(input, 'dns:override-confirm', `Existing DNS records point ${opts.plan.domain} somewhere else`);
|
|
329
|
+
const confirmed = input.confirmDnsOverride
|
|
330
|
+
? yield* tryPromise(() => input.confirmDnsOverride?.(warning) ?? Promise.resolve(false), 'DOMAIN_LINK_FAILED')
|
|
331
|
+
: false;
|
|
332
|
+
if (!confirmed)
|
|
333
|
+
return yield* Effect.fail(dnsTargetConflictError(warning));
|
|
334
|
+
return true;
|
|
335
|
+
});
|
|
322
336
|
}
|
|
323
|
-
export
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
337
|
+
export function createLinkPlan(input) {
|
|
338
|
+
return Effect.gen(function* () {
|
|
339
|
+
const domain = yield* resolveConfiguredDomain(input.domain);
|
|
340
|
+
const project = yield* resolveProject(input.project);
|
|
341
|
+
const resolved = yield* resolveProviderTarget({ ...input, domain }, { transportErrorCode: 'DOMAIN_LINK_FAILED' });
|
|
342
|
+
const { account, accountInferred, isDefaultAccount, provider, providerInferred, target } = resolved;
|
|
343
|
+
const record = planBaseRecord({ isApex: target.isApex, provider, recordName: target.recordName });
|
|
344
|
+
return {
|
|
345
|
+
account,
|
|
346
|
+
accountInferred,
|
|
347
|
+
isDefaultAccount,
|
|
348
|
+
provider,
|
|
349
|
+
providerInferred,
|
|
350
|
+
project: project.project,
|
|
351
|
+
projectSource: project.projectSource,
|
|
352
|
+
recordName: target.recordName,
|
|
353
|
+
zoneDomain: target.zoneDomain,
|
|
354
|
+
domain: target.fullDomain,
|
|
355
|
+
isApex: target.isApex,
|
|
356
|
+
records: [record],
|
|
357
|
+
localProjectDetected: project.localProjectDetected,
|
|
358
|
+
actions: ['vercel:addProjectDomain', 'dns:upsertRecord', 'dns:waitPropagation', 'vercel:verifyProjectDomain'],
|
|
359
|
+
};
|
|
360
|
+
});
|
|
345
361
|
}
|
|
346
|
-
export
|
|
347
|
-
|
|
348
|
-
|
|
362
|
+
export function linkDomain(input) {
|
|
363
|
+
return Effect.gen(function* () {
|
|
364
|
+
const plan = yield* createLinkPlan(input);
|
|
365
|
+
if (input.dryRun) {
|
|
366
|
+
return {
|
|
367
|
+
...plan,
|
|
368
|
+
dryRun: true,
|
|
369
|
+
dns: { updated: false, propagated: false, skipped: [] },
|
|
370
|
+
vercel: { added: false, alreadyAdded: false, verified: false },
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
const vercel = createVercelClient(yield* resolveVercelConfig());
|
|
374
|
+
const provider = yield* createProvider(plan.provider, {
|
|
375
|
+
account: plan.account,
|
|
376
|
+
transportErrorCode: 'DOMAIN_LINK_FAILED',
|
|
377
|
+
});
|
|
378
|
+
reportProgress(input, 'dns:resolve-zone', `Finding ${provider.name} DNS zone`);
|
|
379
|
+
const zone = yield* resolveZone(provider, plan.zoneDomain);
|
|
380
|
+
reportProgress(input, 'vercel:get-target', 'Reading Vercel DNS target');
|
|
381
|
+
const cname = plan.isApex ? undefined : yield* vercel.getRecommendedCname(plan.domain);
|
|
382
|
+
const baseRecord = planBaseRecord({
|
|
383
|
+
isApex: plan.isApex,
|
|
384
|
+
provider: plan.provider,
|
|
385
|
+
recordName: plan.recordName,
|
|
386
|
+
cname,
|
|
387
|
+
});
|
|
388
|
+
const forceDns = yield* resolveDnsForce(input, { baseRecord, plan, provider, zone });
|
|
389
|
+
reportProgress(input, 'vercel:add-domain', 'Adding domain to Vercel');
|
|
390
|
+
const addResult = yield* vercel.addDomainToProject(plan.project, plan.domain, { force: input.force });
|
|
391
|
+
reportProgress(input, 'vercel:get-domain', 'Reading Vercel verification records');
|
|
392
|
+
const projectDomain = yield* vercel.getProjectDomain(plan.project, plan.domain);
|
|
393
|
+
const verificationDnsRecords = uniqueRecords([
|
|
394
|
+
...planVerificationRecords(plan.provider, addResult.raw, plan.zoneDomain),
|
|
395
|
+
...planVerificationRecords(plan.provider, projectDomain, plan.zoneDomain),
|
|
396
|
+
]);
|
|
397
|
+
const records = [baseRecord, ...verificationDnsRecords];
|
|
398
|
+
reportProgress(input, 'dns:plan', `Reading ${provider.name} DNS records`);
|
|
399
|
+
const dnsPlan = yield* provider.planChanges(zone, records, { force: forceDns });
|
|
400
|
+
reportProgress(input, 'dns:apply', `Updating DNS records in ${provider.name}`);
|
|
401
|
+
const dnsResult = yield* provider.applyChanges(zone, dnsPlan, { force: forceDns });
|
|
402
|
+
const shouldWait = input.wait ?? true;
|
|
403
|
+
if (shouldWait) {
|
|
404
|
+
reportProgress(input, 'dns:wait', verificationDnsRecords.length > 0
|
|
405
|
+
? 'DNS records saved; asking Vercel to verify ownership'
|
|
406
|
+
: 'DNS records saved; asking Vercel to verify');
|
|
407
|
+
}
|
|
408
|
+
const waitResult = shouldWait
|
|
409
|
+
? yield* waitForVercelDomainReady({
|
|
410
|
+
domain: plan.domain,
|
|
411
|
+
force: forceDns,
|
|
412
|
+
input,
|
|
413
|
+
project: plan.project,
|
|
414
|
+
provider,
|
|
415
|
+
providerId: plan.provider,
|
|
416
|
+
records,
|
|
417
|
+
zone,
|
|
418
|
+
zoneDomain: plan.zoneDomain,
|
|
419
|
+
})
|
|
420
|
+
: { propagated: false, verified: false };
|
|
349
421
|
return {
|
|
350
422
|
...plan,
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
423
|
+
records,
|
|
424
|
+
dryRun: false,
|
|
425
|
+
dns: { updated: dnsResult.applied.length > 0, propagated: waitResult.propagated, skipped: dnsResult.skipped },
|
|
426
|
+
vercel: { added: !addResult.alreadyAdded, alreadyAdded: addResult.alreadyAdded, verified: waitResult.verified },
|
|
354
427
|
};
|
|
355
|
-
}
|
|
356
|
-
const vercel = createVercelClient(await resolveVercelConfig());
|
|
357
|
-
const provider = await createProvider(plan.provider, { account: plan.account });
|
|
358
|
-
reportProgress(input, 'dns:resolve-zone', `Finding ${provider.name} DNS zone`);
|
|
359
|
-
const zone = await resolveZone(provider, plan.zoneDomain);
|
|
360
|
-
reportProgress(input, 'vercel:get-target', 'Reading Vercel DNS target');
|
|
361
|
-
const cname = plan.isApex ? undefined : await vercel.getRecommendedCname(plan.domain);
|
|
362
|
-
const baseRecord = planBaseRecord({
|
|
363
|
-
isApex: plan.isApex,
|
|
364
|
-
provider: plan.provider,
|
|
365
|
-
recordName: plan.recordName,
|
|
366
|
-
cname,
|
|
367
428
|
});
|
|
368
|
-
const forceDns = await resolveDnsForce(input, { baseRecord, plan, provider, zone });
|
|
369
|
-
reportProgress(input, 'vercel:add-domain', 'Adding domain to Vercel');
|
|
370
|
-
const addResult = await vercel.addDomainToProject(plan.project, plan.domain, { force: input.force });
|
|
371
|
-
reportProgress(input, 'vercel:get-domain', 'Reading Vercel verification records');
|
|
372
|
-
const projectDomain = await vercel.getProjectDomain(plan.project, plan.domain);
|
|
373
|
-
const verificationDnsRecords = uniqueRecords([
|
|
374
|
-
...planVerificationRecords(plan.provider, addResult.raw, plan.zoneDomain),
|
|
375
|
-
...planVerificationRecords(plan.provider, projectDomain, plan.zoneDomain),
|
|
376
|
-
]);
|
|
377
|
-
const records = [baseRecord, ...verificationDnsRecords];
|
|
378
|
-
reportProgress(input, 'dns:plan', `Reading ${provider.name} DNS records`);
|
|
379
|
-
const dnsPlan = await provider.planChanges(zone, records, { force: forceDns });
|
|
380
|
-
reportProgress(input, 'dns:apply', `Updating DNS records in ${provider.name}`);
|
|
381
|
-
const dnsResult = await provider.applyChanges(zone, dnsPlan, { force: forceDns });
|
|
382
|
-
const shouldWait = input.wait ?? true;
|
|
383
|
-
if (shouldWait) {
|
|
384
|
-
reportProgress(input, 'dns:wait', verificationDnsRecords.length > 0
|
|
385
|
-
? 'DNS records saved; asking Vercel to verify ownership'
|
|
386
|
-
: 'DNS records saved; asking Vercel to verify');
|
|
387
|
-
}
|
|
388
|
-
const waitResult = shouldWait
|
|
389
|
-
? await waitForVercelDomainReady({
|
|
390
|
-
domain: plan.domain,
|
|
391
|
-
force: forceDns,
|
|
392
|
-
input,
|
|
393
|
-
project: plan.project,
|
|
394
|
-
provider,
|
|
395
|
-
providerId: plan.provider,
|
|
396
|
-
records,
|
|
397
|
-
zone,
|
|
398
|
-
zoneDomain: plan.zoneDomain,
|
|
399
|
-
})
|
|
400
|
-
: { propagated: false, verified: false };
|
|
401
|
-
return {
|
|
402
|
-
...plan,
|
|
403
|
-
records,
|
|
404
|
-
dryRun: false,
|
|
405
|
-
dns: { updated: dnsResult.applied.length > 0, propagated: waitResult.propagated, skipped: dnsResult.skipped },
|
|
406
|
-
vercel: { added: !addResult.alreadyAdded, alreadyAdded: addResult.alreadyAdded, verified: waitResult.verified },
|
|
407
|
-
};
|
|
408
429
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { type DnsPropagationResult, type DnsResolverObservation } from './dns-propagation.js';
|
|
2
2
|
import { type ResolvedDnsTarget } from './domain-provider.js';
|
|
3
|
+
import { type DoomainEffect } from './effect.js';
|
|
4
|
+
import { type DoomainErrorCode } from './errors.js';
|
|
3
5
|
import { type DnsOverrideWarning } from './link-domain.js';
|
|
4
6
|
import type { DnsProvider, DnsRecordInput } from './providers/types.js';
|
|
5
7
|
export type PointRecordType = 'A' | 'AAAA' | 'CNAME';
|
|
@@ -38,9 +40,10 @@ export interface PointDomainResult {
|
|
|
38
40
|
interface PointDomainDependencies {
|
|
39
41
|
createProvider: (provider: string, opts: {
|
|
40
42
|
account?: string;
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
43
|
+
transportErrorCode?: DoomainErrorCode;
|
|
44
|
+
}) => DoomainEffect<DnsProvider>;
|
|
45
|
+
observeDns?: (fqdn: string, target: Pick<DnsRecordInput, 'type' | 'value'>, elapsedMs: number) => DoomainEffect<DnsResolverObservation[]>;
|
|
46
|
+
resolveTarget: (input: Pick<PointDomainInput, 'account' | 'domain' | 'provider'>) => DoomainEffect<ResolvedDnsTarget>;
|
|
44
47
|
}
|
|
45
48
|
export declare function createPointRecord(input: {
|
|
46
49
|
provider: string;
|
|
@@ -49,5 +52,5 @@ export declare function createPointRecord(input: {
|
|
|
49
52
|
target: string;
|
|
50
53
|
ttl?: number;
|
|
51
54
|
}): DnsRecordInput;
|
|
52
|
-
export declare function pointDomain(input: PointDomainInput, dependencies?: PointDomainDependencies):
|
|
55
|
+
export declare function pointDomain(input: PointDomainInput, dependencies?: PointDomainDependencies): DoomainEffect<PointDomainResult>;
|
|
53
56
|
export {};
|