doomain 0.1.19 → 0.1.20
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/dist/commands/auth/clerk.js +1 -1
- package/dist/commands/auth/vercel.js +10 -4
- package/dist/commands/clerk/domains/add.js +9 -3
- package/dist/commands/dns/point.js +2 -2
- package/dist/commands/domains/list.js +9 -3
- package/dist/commands/link.js +9 -3
- package/dist/commands/providers/connect.js +15 -4
- package/dist/commands/providers/disconnect.js +16 -4
- package/dist/commands/providers/list.js +1 -1
- package/dist/commands/wizard.js +50 -14
- package/dist/index.d.ts +1 -1
- package/dist/lib/clerk.js +5 -1
- package/dist/lib/command-schema.js +122 -35
- package/dist/lib/domain-provider.js +10 -3
- package/dist/lib/flags.js +9 -3
- package/dist/lib/link-domain.js +11 -4
- package/dist/lib/providers/cloudflare/index.js +13 -5
- package/dist/lib/providers/core/config.js +2 -1
- package/dist/lib/providers/core/planner.js +5 -1
- package/dist/lib/providers/hostinger/index.js +7 -1
- package/dist/lib/providers/namecheap/index.js +11 -2
- package/dist/lib/providers/registry.js +6 -1
- package/dist/lib/providers/status.js +3 -1
- package/dist/lib/validate.js +9 -2
- package/dist/lib/vercel.js +5 -2
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Command, Flags } from '@oclif/core';
|
|
2
1
|
import * as p from '@clack/prompts';
|
|
2
|
+
import { Command, Flags } from '@oclif/core';
|
|
3
3
|
import { createClerkPlatformClient } from '../../lib/clerk.js';
|
|
4
4
|
import { getConfigPath, maskSecret, updateConfig } from '../../lib/config.js';
|
|
5
5
|
import { jsonFlag } from '../../lib/flags.js';
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { Command, Flags } from '@oclif/core';
|
|
2
1
|
import * as p from '@clack/prompts';
|
|
2
|
+
import { Command, Flags } from '@oclif/core';
|
|
3
3
|
import { getConfigPath, maskSecret, updateConfig } from '../../lib/config.js';
|
|
4
4
|
import { jsonFlag } from '../../lib/flags.js';
|
|
5
5
|
import { createOutput, outputError } from '../../lib/output.js';
|
|
6
|
-
import { listGlobalVercelTokens } from '../../lib/vercel-auth.js';
|
|
7
6
|
import { createVercelClient } from '../../lib/vercel.js';
|
|
7
|
+
import { listGlobalVercelTokens } from '../../lib/vercel-auth.js';
|
|
8
8
|
const PERSONAL_ACCOUNT = '__personal__';
|
|
9
9
|
const NEW_TOKEN = '__new_token__';
|
|
10
10
|
function assertValue(value, message) {
|
|
@@ -24,7 +24,11 @@ async function promptVercelToken(globalTokens) {
|
|
|
24
24
|
const selected = await p.select({
|
|
25
25
|
message: 'Vercel token',
|
|
26
26
|
options: [
|
|
27
|
-
...globalTokens.map((token, index) => ({
|
|
27
|
+
...globalTokens.map((token, index) => ({
|
|
28
|
+
label: globalTokenLabel(token),
|
|
29
|
+
value: String(index),
|
|
30
|
+
hint: maskSecret(token.token),
|
|
31
|
+
})),
|
|
28
32
|
{ label: 'Enter a new token', value: NEW_TOKEN },
|
|
29
33
|
],
|
|
30
34
|
});
|
|
@@ -46,7 +50,9 @@ export default class AuthVercel extends Command {
|
|
|
46
50
|
static description = 'Save Vercel credentials locally.';
|
|
47
51
|
static flags = {
|
|
48
52
|
json: jsonFlag,
|
|
49
|
-
'team-id': Flags.string({
|
|
53
|
+
'team-id': Flags.string({
|
|
54
|
+
description: 'Vercel team id. Interactive mode can fetch and select this from your token.',
|
|
55
|
+
}),
|
|
50
56
|
token: Flags.string({ description: 'Vercel API token.' }),
|
|
51
57
|
};
|
|
52
58
|
async run() {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Args, Command, Flags } from '@oclif/core';
|
|
2
1
|
import * as p from '@clack/prompts';
|
|
2
|
+
import { Args, Command, Flags } from '@oclif/core';
|
|
3
3
|
import { addClerkProductionDomain } from '../../../lib/clerk-domain.js';
|
|
4
4
|
import { accountFlag, jsonFlag, providerFlag } from '../../../lib/flags.js';
|
|
5
5
|
import { createOutput, outputError } from '../../../lib/output.js';
|
|
@@ -33,12 +33,18 @@ export default class ClerkDomainsAdd extends Command {
|
|
|
33
33
|
static flags = {
|
|
34
34
|
account: accountFlag,
|
|
35
35
|
app: Flags.string({ description: 'Clerk application id. Defaults to CLERK_APPLICATION_ID or saved Clerk config.' }),
|
|
36
|
-
'dry-run': Flags.boolean({
|
|
36
|
+
'dry-run': Flags.boolean({
|
|
37
|
+
description: 'Check the Clerk application and DNS zone without creating the production instance.',
|
|
38
|
+
}),
|
|
37
39
|
force: Flags.boolean({ description: 'Overwrite DNS records that conflict with Clerk requirements.' }),
|
|
38
40
|
json: jsonFlag,
|
|
39
41
|
provider: providerFlag,
|
|
40
42
|
timeout: Flags.integer({ default: 300, description: 'Wait timeout in seconds.' }),
|
|
41
|
-
wait: Flags.boolean({
|
|
43
|
+
wait: Flags.boolean({
|
|
44
|
+
allowNo: true,
|
|
45
|
+
default: true,
|
|
46
|
+
description: 'Wait for Clerk DNS, SSL, and email DNS verification.',
|
|
47
|
+
}),
|
|
42
48
|
};
|
|
43
49
|
async run() {
|
|
44
50
|
const { args, flags } = await this.parse(ClerkDomainsAdd);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as p from '@clack/prompts';
|
|
2
2
|
import { Args, Command, Flags } from '@oclif/core';
|
|
3
|
-
import { jsonFlag, providerFlag
|
|
4
|
-
import {
|
|
3
|
+
import { accountFlag, jsonFlag, providerFlag } from '../../lib/flags.js';
|
|
4
|
+
import { createOutput, outputError } from '../../lib/output.js';
|
|
5
5
|
import { pointDomain } from '../../lib/point-domain.js';
|
|
6
6
|
function preview(result) {
|
|
7
7
|
return [
|
|
@@ -33,7 +33,9 @@ export default class DomainsList extends Command {
|
|
|
33
33
|
const accounts = account
|
|
34
34
|
? [{ account, isDefaultAccount: isDefaultProviderAccount(account), providerId: definition.id }]
|
|
35
35
|
: listConfiguredProviderAccounts(config, definition);
|
|
36
|
-
const selectedAccounts = accounts.length > 0
|
|
36
|
+
const selectedAccounts = accounts.length > 0
|
|
37
|
+
? accounts
|
|
38
|
+
: [{ account: DEFAULT_PROVIDER_ACCOUNT, isDefaultAccount: true, providerId: definition.id }];
|
|
37
39
|
const results = [];
|
|
38
40
|
for (const selectedAccount of selectedAccounts) {
|
|
39
41
|
const provider = await createProvider(definition.id, { account: selectedAccount.account });
|
|
@@ -47,14 +49,18 @@ export default class DomainsList extends Command {
|
|
|
47
49
|
records,
|
|
48
50
|
zone,
|
|
49
51
|
});
|
|
50
|
-
const accountLabel = selectedAccount.isDefaultAccount
|
|
52
|
+
const accountLabel = selectedAccount.isDefaultAccount
|
|
53
|
+
? provider.id
|
|
54
|
+
: `${provider.id}/${selectedAccount.account}`;
|
|
51
55
|
out.info(`${zone.name} (${records.length} records) via ${accountLabel}`);
|
|
52
56
|
for (const record of records)
|
|
53
57
|
out.info(` ${record.type} ${record.name} -> ${record.value}`);
|
|
54
58
|
}
|
|
55
59
|
}
|
|
56
60
|
out.result({
|
|
57
|
-
...(selectedAccounts.length === 1
|
|
61
|
+
...(selectedAccounts.length === 1
|
|
62
|
+
? { account: selectedAccounts[0].account, isDefaultAccount: selectedAccounts[0].isDefaultAccount }
|
|
63
|
+
: {}),
|
|
58
64
|
provider: definition.id,
|
|
59
65
|
zones: results,
|
|
60
66
|
});
|
package/dist/commands/link.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Args, Command, Flags } from '@oclif/core';
|
|
2
1
|
import * as p from '@clack/prompts';
|
|
2
|
+
import { Args, Command, Flags } from '@oclif/core';
|
|
3
3
|
import { accountFlag, apexFlag, domainFlag, jsonFlag, projectFlag, providerFlag, subdomainFlag } from '../lib/flags.js';
|
|
4
4
|
import { linkDomain } from '../lib/link-domain.js';
|
|
5
5
|
import { createOutput, outputError } from '../lib/output.js';
|
|
@@ -56,13 +56,19 @@ export default class Link extends Command {
|
|
|
56
56
|
apex: apexFlag,
|
|
57
57
|
domain: domainFlag,
|
|
58
58
|
'dry-run': Flags.boolean({ description: 'Preview changes without writing to Vercel or DNS.' }),
|
|
59
|
-
force: Flags.boolean({
|
|
59
|
+
force: Flags.boolean({
|
|
60
|
+
description: 'Move existing Vercel project domains and overwrite conflicting DNS records.',
|
|
61
|
+
}),
|
|
60
62
|
json: jsonFlag,
|
|
61
63
|
project: projectFlag,
|
|
62
64
|
provider: providerFlag,
|
|
63
65
|
subdomain: subdomainFlag,
|
|
64
66
|
timeout: Flags.integer({ default: 300, description: 'Wait timeout in seconds.' }),
|
|
65
|
-
wait: Flags.boolean({
|
|
67
|
+
wait: Flags.boolean({
|
|
68
|
+
allowNo: true,
|
|
69
|
+
default: true,
|
|
70
|
+
description: 'Wait for DNS propagation and Vercel verification.',
|
|
71
|
+
}),
|
|
66
72
|
};
|
|
67
73
|
async run() {
|
|
68
74
|
const { args, flags } = await this.parse(Link);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Args, Command, Flags } from '@oclif/core';
|
|
2
1
|
import * as p from '@clack/prompts';
|
|
2
|
+
import { Args, Command, Flags } from '@oclif/core';
|
|
3
3
|
import { getConfigPath, loadConfig, maskSecret, updateConfig } from '../../lib/config.js';
|
|
4
4
|
import { accountFlag, jsonFlag } from '../../lib/flags.js';
|
|
5
5
|
import { createOutput, outputError } from '../../lib/output.js';
|
|
@@ -68,7 +68,11 @@ function validateProviderAccount(value) {
|
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
70
|
async function promptProviderAccount() {
|
|
71
|
-
const value = await p.text({
|
|
71
|
+
const value = await p.text({
|
|
72
|
+
message: 'Profile name',
|
|
73
|
+
placeholder: DEFAULT_PROVIDER_ACCOUNT,
|
|
74
|
+
validate: validateProviderAccount,
|
|
75
|
+
});
|
|
72
76
|
if (p.isCancel(value)) {
|
|
73
77
|
p.cancel('Cancelled');
|
|
74
78
|
return null;
|
|
@@ -136,7 +140,11 @@ export default class ProvidersConnect extends Command {
|
|
|
136
140
|
const definition = args.provider ? getProviderDefinition(args.provider) : await promptProvider();
|
|
137
141
|
if (!definition)
|
|
138
142
|
return;
|
|
139
|
-
const account = flags.account
|
|
143
|
+
const account = flags.account
|
|
144
|
+
? normalizeProviderAccount(flags.account)
|
|
145
|
+
: out.json
|
|
146
|
+
? DEFAULT_PROVIDER_ACCOUNT
|
|
147
|
+
: await promptProviderAccount();
|
|
140
148
|
if (!account)
|
|
141
149
|
return;
|
|
142
150
|
const isDefaultAccount = isDefaultProviderAccount(account);
|
|
@@ -152,7 +160,10 @@ export default class ProvidersConnect extends Command {
|
|
|
152
160
|
if (!out.json)
|
|
153
161
|
showSetupGuide(definition, detectedPublicIp);
|
|
154
162
|
for (const credential of definition.credentials) {
|
|
155
|
-
const value = process.env[credential.env] ||
|
|
163
|
+
const value = process.env[credential.env] ||
|
|
164
|
+
passedCredentials[credential.key] ||
|
|
165
|
+
legacyFlagValue(flags, credential) ||
|
|
166
|
+
undefined;
|
|
156
167
|
if (value) {
|
|
157
168
|
credentials[credential.key] = value;
|
|
158
169
|
continue;
|
|
@@ -38,7 +38,9 @@ export default class ProvidersDisconnect extends Command {
|
|
|
38
38
|
else if (provider) {
|
|
39
39
|
const nextProvider = { ...provider };
|
|
40
40
|
if (isDefaultProviderAccount(account)) {
|
|
41
|
-
removed =
|
|
41
|
+
removed =
|
|
42
|
+
nextProvider.credentials !== undefined ||
|
|
43
|
+
(definition.id === 'spaceship' && ('apiKey' in nextProvider || 'apiSecret' in nextProvider));
|
|
42
44
|
delete nextProvider.credentials;
|
|
43
45
|
if (definition.id === 'spaceship') {
|
|
44
46
|
delete nextProvider.apiKey;
|
|
@@ -52,7 +54,9 @@ export default class ProvidersDisconnect extends Command {
|
|
|
52
54
|
delete accounts[account];
|
|
53
55
|
nextProvider.accounts = Object.keys(accounts).length > 0 ? accounts : undefined;
|
|
54
56
|
}
|
|
55
|
-
if (nextProvider.credentials ||
|
|
57
|
+
if (nextProvider.credentials ||
|
|
58
|
+
nextProvider.settings ||
|
|
59
|
+
(nextProvider.accounts && Object.keys(nextProvider.accounts).length > 0)) {
|
|
56
60
|
providers[definition.id] = nextProvider;
|
|
57
61
|
}
|
|
58
62
|
else {
|
|
@@ -69,10 +73,18 @@ export default class ProvidersDisconnect extends Command {
|
|
|
69
73
|
};
|
|
70
74
|
});
|
|
71
75
|
const overrides = envOverrides(definition);
|
|
72
|
-
out.result({
|
|
76
|
+
out.result({
|
|
77
|
+
account,
|
|
78
|
+
configPath: getConfigPath(),
|
|
79
|
+
environmentOverrides: overrides,
|
|
80
|
+
provider: definition.id,
|
|
81
|
+
removed,
|
|
82
|
+
});
|
|
73
83
|
if (overrides.length > 0)
|
|
74
84
|
out.warn(`${definition.displayName} environment credentials are still set: ${overrides.join(', ')}.`);
|
|
75
|
-
out.success(removed
|
|
85
|
+
out.success(removed
|
|
86
|
+
? `${definition.displayName} credentials removed from ${getConfigPath()}.`
|
|
87
|
+
: `${definition.displayName} was not connected.`);
|
|
76
88
|
}
|
|
77
89
|
catch (error) {
|
|
78
90
|
outputError(out.json, error, 'PROVIDER_NOT_FOUND');
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Command } from '@oclif/core';
|
|
2
2
|
import { jsonFlag } from '../../lib/flags.js';
|
|
3
|
-
import { listProviderDefinitions } from '../../lib/providers/registry.js';
|
|
4
3
|
import { createOutput } from '../../lib/output.js';
|
|
4
|
+
import { listProviderDefinitions } from '../../lib/providers/registry.js';
|
|
5
5
|
export default class ProvidersList extends Command {
|
|
6
6
|
static description = 'List supported DNS providers.';
|
|
7
7
|
static flags = {
|
package/dist/commands/wizard.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Command } from '@oclif/core';
|
|
2
1
|
import * as p from '@clack/prompts';
|
|
2
|
+
import { Command } from '@oclif/core';
|
|
3
3
|
import { loadConfig, maskSecret, updateConfig } from '../lib/config.js';
|
|
4
4
|
import { DoomainError } from '../lib/errors.js';
|
|
5
5
|
import { jsonFlag } from '../lib/flags.js';
|
|
@@ -8,8 +8,8 @@ import { detectLocalVercelProject } from '../lib/local-vercel.js';
|
|
|
8
8
|
import { createOutput, outputError } from '../lib/output.js';
|
|
9
9
|
import { DEFAULT_PROVIDER_ACCOUNT, isDefaultProviderAccount, listConfiguredProviderAccounts, normalizeProviderAccount, providerAccountHasCredentials, withProviderAccountCredentials, } from '../lib/providers/core/config.js';
|
|
10
10
|
import { createProvider, getProviderDefinition, listProviderDefinitions } from '../lib/providers/registry.js';
|
|
11
|
-
import { listGlobalVercelTokens } from '../lib/vercel-auth.js';
|
|
12
11
|
import { createVercelClient } from '../lib/vercel.js';
|
|
12
|
+
import { listGlobalVercelTokens } from '../lib/vercel-auth.js';
|
|
13
13
|
const PERSONAL_ACCOUNT = '__personal__';
|
|
14
14
|
const NEW_TOKEN = '__new_token__';
|
|
15
15
|
function cancelIfNeeded(value) {
|
|
@@ -22,7 +22,11 @@ function cancelIfNeeded(value) {
|
|
|
22
22
|
async function promptRequired(message, opts = {}) {
|
|
23
23
|
const value = opts.password
|
|
24
24
|
? await p.password({ message })
|
|
25
|
-
: await p.text({
|
|
25
|
+
: await p.text({
|
|
26
|
+
message,
|
|
27
|
+
placeholder: opts.placeholder,
|
|
28
|
+
validate: (input) => (input?.trim() ? undefined : 'Required'),
|
|
29
|
+
});
|
|
26
30
|
const resolved = cancelIfNeeded(value);
|
|
27
31
|
return typeof resolved === 'string' ? resolved.trim() : null;
|
|
28
32
|
}
|
|
@@ -36,7 +40,11 @@ function validateProviderAccount(value) {
|
|
|
36
40
|
}
|
|
37
41
|
}
|
|
38
42
|
async function promptProviderAccount() {
|
|
39
|
-
const value = await p.text({
|
|
43
|
+
const value = await p.text({
|
|
44
|
+
message: 'Profile name',
|
|
45
|
+
placeholder: DEFAULT_PROVIDER_ACCOUNT,
|
|
46
|
+
validate: validateProviderAccount,
|
|
47
|
+
});
|
|
40
48
|
const resolved = cancelIfNeeded(value);
|
|
41
49
|
return typeof resolved === 'string' ? normalizeProviderAccount(resolved) : null;
|
|
42
50
|
}
|
|
@@ -145,7 +153,11 @@ async function promptVercelToken(globalTokens) {
|
|
|
145
153
|
const selected = await p.select({
|
|
146
154
|
message: 'Vercel token',
|
|
147
155
|
options: [
|
|
148
|
-
...globalTokens.map((token, index) => ({
|
|
156
|
+
...globalTokens.map((token, index) => ({
|
|
157
|
+
label: globalTokenLabel(token),
|
|
158
|
+
value: String(index),
|
|
159
|
+
hint: maskSecret(token.token),
|
|
160
|
+
})),
|
|
149
161
|
{ label: 'Enter a new token', value: NEW_TOKEN },
|
|
150
162
|
],
|
|
151
163
|
});
|
|
@@ -256,7 +268,9 @@ export default class Wizard extends Command {
|
|
|
256
268
|
}
|
|
257
269
|
}
|
|
258
270
|
const selectedTeam = teams.find((team) => team.id === vercelTeamId);
|
|
259
|
-
const teamDisplay = vercelTeamId
|
|
271
|
+
const teamDisplay = vercelTeamId
|
|
272
|
+
? teamLabel(selectedTeam ?? { id: vercelTeamId, name: null, role: null, slug: vercelTeamId })
|
|
273
|
+
: 'Personal account';
|
|
260
274
|
p.log.success(`Vercel account ready: ${teamDisplay}`);
|
|
261
275
|
const projectSpinner = p.spinner();
|
|
262
276
|
activeSpinner = projectSpinner;
|
|
@@ -268,7 +282,9 @@ export default class Wizard extends Command {
|
|
|
268
282
|
throw new DoomainError('PROJECT_NOT_FOUND', `No Vercel projects found in ${teamDisplay}.`);
|
|
269
283
|
}
|
|
270
284
|
const localProjectMatchesTeam = localProject && (vercelTeamId ? localProject.orgId === vercelTeamId : !localProject.orgId);
|
|
271
|
-
const initialProject = localProjectMatchesTeam && projects.some((item) => item.id === localProject.projectId)
|
|
285
|
+
const initialProject = localProjectMatchesTeam && projects.some((item) => item.id === localProject.projectId)
|
|
286
|
+
? localProject.projectId
|
|
287
|
+
: undefined;
|
|
272
288
|
const selected = await p.autocomplete({
|
|
273
289
|
message: 'Select Vercel project',
|
|
274
290
|
placeholder: 'Type to filter projects...',
|
|
@@ -303,7 +319,11 @@ export default class Wizard extends Command {
|
|
|
303
319
|
if (!overwrite)
|
|
304
320
|
return;
|
|
305
321
|
}
|
|
306
|
-
const providerAccount = {
|
|
322
|
+
const providerAccount = {
|
|
323
|
+
account,
|
|
324
|
+
isDefaultAccount: isDefaultProviderAccount(account),
|
|
325
|
+
providerId: selectedDefinition.id,
|
|
326
|
+
};
|
|
307
327
|
const credentials = await promptProviderCredentials(selectedDefinition);
|
|
308
328
|
if (!credentials)
|
|
309
329
|
return;
|
|
@@ -334,7 +354,9 @@ export default class Wizard extends Command {
|
|
|
334
354
|
domainOptions.push(...(await listProviderDomainOptions(definition, account)));
|
|
335
355
|
}
|
|
336
356
|
catch (error) {
|
|
337
|
-
const label = account.isDefaultAccount
|
|
357
|
+
const label = account.isDefaultAccount
|
|
358
|
+
? definition.displayName
|
|
359
|
+
: `${definition.displayName}/${account.account}`;
|
|
338
360
|
providerFailures.push(`${label}: ${error instanceof Error ? error.message : String(error)}`);
|
|
339
361
|
}
|
|
340
362
|
}
|
|
@@ -354,14 +376,18 @@ export default class Wizard extends Command {
|
|
|
354
376
|
p.log.success(`DNS ready: ${providerNames} (${domainOptions.length} domains)`);
|
|
355
377
|
let selectedDomain = domainOptions[0];
|
|
356
378
|
if (domainOptions.length > 1) {
|
|
357
|
-
const initialValue = domainOptions.find((option) => option.providerId === defaultProvider && option.domain === defaultDomain)
|
|
358
|
-
domainOptions.find((option) => option.domain === defaultDomain)?.id;
|
|
379
|
+
const initialValue = domainOptions.find((option) => option.providerId === defaultProvider && option.domain === defaultDomain)
|
|
380
|
+
?.id ?? domainOptions.find((option) => option.domain === defaultDomain)?.id;
|
|
359
381
|
const selectedDomainId = await p.autocomplete({
|
|
360
382
|
message: 'Select domain',
|
|
361
383
|
placeholder: 'Type to filter domains...',
|
|
362
384
|
maxItems: 10,
|
|
363
385
|
initialValue,
|
|
364
|
-
options: domainOptions.map((option) => ({
|
|
386
|
+
options: domainOptions.map((option) => ({
|
|
387
|
+
label: option.domain,
|
|
388
|
+
value: option.id,
|
|
389
|
+
hint: providerAccountLabel(option),
|
|
390
|
+
})),
|
|
365
391
|
});
|
|
366
392
|
const selectedId = cancelIfNeeded(selectedDomainId);
|
|
367
393
|
if (selectedId === null)
|
|
@@ -392,8 +418,18 @@ export default class Wizard extends Command {
|
|
|
392
418
|
return;
|
|
393
419
|
}
|
|
394
420
|
const fullDomain = apex ? domain : `${subdomain}.${domain}`;
|
|
395
|
-
const preview = await createLinkPlan({
|
|
396
|
-
|
|
421
|
+
const preview = await createLinkPlan({
|
|
422
|
+
account: selectedDomain.account,
|
|
423
|
+
provider: selectedDomain.providerId,
|
|
424
|
+
domain,
|
|
425
|
+
subdomain,
|
|
426
|
+
apex,
|
|
427
|
+
project,
|
|
428
|
+
});
|
|
429
|
+
p.note([
|
|
430
|
+
`Vercel: add ${preview.domain} to ${projectDisplay}`,
|
|
431
|
+
...preview.records.map((record) => recordPreview(record, providerAccountLabel(selectedDomain))),
|
|
432
|
+
].join('\n'), 'Preview');
|
|
397
433
|
const confirmed = await p.confirm({
|
|
398
434
|
message: `Link ${fullDomain} via ${selectedDomain.providerName} to Vercel project ${projectDisplay}?`,
|
|
399
435
|
initialValue: true,
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { run } from '@oclif/core';
|
|
2
|
-
export {
|
|
2
|
+
export { type DomainProviderResult, type FindDomainProviderInput, findDomainProvider, type ProviderSearchWarning, } from './lib/domain-provider.js';
|
package/dist/lib/clerk.js
CHANGED
|
@@ -17,7 +17,11 @@ export async function resolveClerkPlatformConfig(appId) {
|
|
|
17
17
|
return { appId: resolvedAppId, platformApiKey };
|
|
18
18
|
}
|
|
19
19
|
function apiErrorMessage(status, body) {
|
|
20
|
-
return body?.errors?.[0]?.long_message ??
|
|
20
|
+
return (body?.errors?.[0]?.long_message ??
|
|
21
|
+
body?.errors?.[0]?.message ??
|
|
22
|
+
body?.error?.message ??
|
|
23
|
+
body?.message ??
|
|
24
|
+
`Clerk API error (${status}).`);
|
|
21
25
|
}
|
|
22
26
|
function apiErrorCode(body) {
|
|
23
27
|
return body?.errors?.[0]?.code ?? body?.error?.code ?? body?.code;
|
|
@@ -24,12 +24,21 @@ export const commandSchemas = [
|
|
|
24
24
|
{ name: 'domain', type: 'string', description: 'Fully qualified apex or subdomain to point.', required: true },
|
|
25
25
|
{ name: 'target', type: 'string', description: 'IPv4, IPv6, or hostname target.', required: true },
|
|
26
26
|
{ name: 'type', type: 'string', description: 'A, AAAA, or CNAME. Inferred from the target when omitted.' },
|
|
27
|
-
{
|
|
27
|
+
{
|
|
28
|
+
name: 'provider',
|
|
29
|
+
type: 'string',
|
|
30
|
+
description: 'DNS provider id. Inferred from the target domain when omitted.',
|
|
31
|
+
},
|
|
28
32
|
{ name: 'account', type: 'string', description: 'DNS provider profile/account alias.' },
|
|
29
33
|
{ name: 'ttl', type: 'integer', description: 'DNS record TTL in seconds.', default: 300 },
|
|
30
34
|
{ name: 'dry-run', type: 'boolean', description: 'Preview without writing.' },
|
|
31
35
|
{ name: 'force', type: 'boolean', description: 'Overwrite conflicting DNS records.' },
|
|
32
|
-
{
|
|
36
|
+
{
|
|
37
|
+
name: 'wait',
|
|
38
|
+
type: 'boolean',
|
|
39
|
+
description: 'Wait for public DNS propagation. Use --no-wait to skip.',
|
|
40
|
+
default: true,
|
|
41
|
+
},
|
|
33
42
|
{ name: 'timeout', type: 'integer', description: 'DNS propagation wait timeout in seconds.', default: 300 },
|
|
34
43
|
],
|
|
35
44
|
},
|
|
@@ -59,8 +68,16 @@ export const commandSchemas = [
|
|
|
59
68
|
safeForAgents: true,
|
|
60
69
|
flags: [
|
|
61
70
|
{ name: 'json', type: 'boolean', description: 'Output a single JSON object and never prompt.' },
|
|
62
|
-
{
|
|
63
|
-
|
|
71
|
+
{
|
|
72
|
+
name: 'provider',
|
|
73
|
+
type: 'string',
|
|
74
|
+
description: 'DNS provider id. Inferred from the target domain when omitted.',
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
name: 'account',
|
|
78
|
+
type: 'string',
|
|
79
|
+
description: 'DNS provider profile/account alias. Defaults to the provider default account.',
|
|
80
|
+
},
|
|
64
81
|
{
|
|
65
82
|
name: 'domain',
|
|
66
83
|
type: 'string',
|
|
@@ -68,14 +85,27 @@ export const commandSchemas = [
|
|
|
68
85
|
},
|
|
69
86
|
{ name: 'subdomain', type: 'string', description: 'Subdomain to add.' },
|
|
70
87
|
{ name: 'apex', type: 'boolean', description: 'Use the root/apex domain.' },
|
|
71
|
-
{
|
|
72
|
-
|
|
88
|
+
{
|
|
89
|
+
name: 'project',
|
|
90
|
+
type: 'string',
|
|
91
|
+
description: 'Vercel project id/name. Optional when project inference succeeds.',
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
name: 'dry-run',
|
|
95
|
+
type: 'boolean',
|
|
96
|
+
description: 'Preview changes without writing. Intended for human previews; agents should not use this unless explicitly asked.',
|
|
97
|
+
},
|
|
73
98
|
{
|
|
74
99
|
name: 'force',
|
|
75
100
|
type: 'boolean',
|
|
76
101
|
description: 'Move existing Vercel project domains and overwrite conflicting DNS records. Interactive DNS override confirmation does not move Vercel aliases; pass --force for that.',
|
|
77
102
|
},
|
|
78
|
-
{
|
|
103
|
+
{
|
|
104
|
+
name: 'wait',
|
|
105
|
+
type: 'boolean',
|
|
106
|
+
description: 'Wait for DNS and Vercel verification. Use --no-wait to skip waiting.',
|
|
107
|
+
default: true,
|
|
108
|
+
},
|
|
79
109
|
{ name: 'timeout', type: 'integer', description: 'Wait timeout in seconds.', default: 300 },
|
|
80
110
|
],
|
|
81
111
|
},
|
|
@@ -97,12 +127,33 @@ export const commandSchemas = [
|
|
|
97
127
|
safeForAgents: true,
|
|
98
128
|
flags: [
|
|
99
129
|
{ name: 'json', type: 'boolean', description: 'Output a single JSON object and never prompt.' },
|
|
100
|
-
{
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
130
|
+
{
|
|
131
|
+
name: 'app',
|
|
132
|
+
type: 'string',
|
|
133
|
+
description: 'Clerk application id. Defaults to CLERK_APPLICATION_ID or saved Clerk config.',
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
name: 'provider',
|
|
137
|
+
type: 'string',
|
|
138
|
+
description: 'DNS provider id. Inferred from the target domain when omitted.',
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
name: 'account',
|
|
142
|
+
type: 'string',
|
|
143
|
+
description: 'DNS provider profile/account alias. Defaults to the provider default account.',
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
name: 'dry-run',
|
|
147
|
+
type: 'boolean',
|
|
148
|
+
description: 'Check application eligibility and DNS zone without creating production.',
|
|
149
|
+
},
|
|
104
150
|
{ name: 'force', type: 'boolean', description: 'Overwrite DNS records that conflict with Clerk requirements.' },
|
|
105
|
-
{
|
|
151
|
+
{
|
|
152
|
+
name: 'wait',
|
|
153
|
+
type: 'boolean',
|
|
154
|
+
description: 'Wait for Clerk DNS, SSL, and email DNS verification. Use --no-wait to skip waiting.',
|
|
155
|
+
default: true,
|
|
156
|
+
},
|
|
106
157
|
{ name: 'timeout', type: 'integer', description: 'Wait timeout in seconds.', default: 300 },
|
|
107
158
|
],
|
|
108
159
|
},
|
|
@@ -111,18 +162,14 @@ export const commandSchemas = [
|
|
|
111
162
|
description: 'Print machine-readable command schemas for agents.',
|
|
112
163
|
examples: ['doomain schema --json', 'doomain schema link --json'],
|
|
113
164
|
safeForAgents: true,
|
|
114
|
-
flags: [
|
|
115
|
-
{ name: 'json', type: 'boolean', description: 'Output a single JSON object and never prompt.' },
|
|
116
|
-
],
|
|
165
|
+
flags: [{ name: 'json', type: 'boolean', description: 'Output a single JSON object and never prompt.' }],
|
|
117
166
|
},
|
|
118
167
|
{
|
|
119
168
|
name: 'providers list',
|
|
120
169
|
description: 'List supported DNS providers.',
|
|
121
170
|
examples: ['doomain providers list --json'],
|
|
122
171
|
safeForAgents: true,
|
|
123
|
-
flags: [
|
|
124
|
-
{ name: 'json', type: 'boolean', description: 'Output a single JSON object and never prompt.' },
|
|
125
|
-
],
|
|
172
|
+
flags: [{ name: 'json', type: 'boolean', description: 'Output a single JSON object and never prompt.' }],
|
|
126
173
|
},
|
|
127
174
|
{
|
|
128
175
|
name: 'providers connect',
|
|
@@ -137,7 +184,11 @@ export const commandSchemas = [
|
|
|
137
184
|
],
|
|
138
185
|
flags: [
|
|
139
186
|
{ name: 'json', type: 'boolean', description: 'Output a single JSON object and never prompt.' },
|
|
140
|
-
{
|
|
187
|
+
{
|
|
188
|
+
name: 'account',
|
|
189
|
+
type: 'string',
|
|
190
|
+
description: 'DNS provider profile/account alias. Defaults to the provider default account.',
|
|
191
|
+
},
|
|
141
192
|
{ name: 'credential', type: 'string', description: 'Provider credential as key=value. Can be repeated.' },
|
|
142
193
|
{ name: 'api-key', type: 'string', description: 'Spaceship API key.' },
|
|
143
194
|
{ name: 'api-secret', type: 'string', description: 'Spaceship API secret.' },
|
|
@@ -147,10 +198,18 @@ export const commandSchemas = [
|
|
|
147
198
|
{
|
|
148
199
|
name: 'providers add',
|
|
149
200
|
description: 'Alias for providers connect.',
|
|
150
|
-
examples: [
|
|
201
|
+
examples: [
|
|
202
|
+
'doomain providers add',
|
|
203
|
+
'doomain providers add namecheap',
|
|
204
|
+
'doomain providers add spaceship --account work',
|
|
205
|
+
],
|
|
151
206
|
flags: [
|
|
152
207
|
{ name: 'json', type: 'boolean', description: 'Output a single JSON object and never prompt.' },
|
|
153
|
-
{
|
|
208
|
+
{
|
|
209
|
+
name: 'account',
|
|
210
|
+
type: 'string',
|
|
211
|
+
description: 'DNS provider profile/account alias. Defaults to the provider default account.',
|
|
212
|
+
},
|
|
154
213
|
{ name: 'credential', type: 'string', description: 'Provider credential as key=value. Can be repeated.' },
|
|
155
214
|
{ name: 'no-verify', type: 'boolean', description: 'Save credentials without verifying them first.' },
|
|
156
215
|
],
|
|
@@ -175,7 +234,11 @@ export const commandSchemas = [
|
|
|
175
234
|
],
|
|
176
235
|
flags: [
|
|
177
236
|
{ name: 'json', type: 'boolean', description: 'Output a single JSON object and never prompt.' },
|
|
178
|
-
{
|
|
237
|
+
{
|
|
238
|
+
name: 'account',
|
|
239
|
+
type: 'string',
|
|
240
|
+
description: 'DNS provider profile/account alias. Omit to remove all accounts for the provider.',
|
|
241
|
+
},
|
|
179
242
|
],
|
|
180
243
|
},
|
|
181
244
|
{
|
|
@@ -189,7 +252,11 @@ export const commandSchemas = [
|
|
|
189
252
|
],
|
|
190
253
|
flags: [
|
|
191
254
|
{ name: 'json', type: 'boolean', description: 'Output a single JSON object and never prompt.' },
|
|
192
|
-
{
|
|
255
|
+
{
|
|
256
|
+
name: 'account',
|
|
257
|
+
type: 'string',
|
|
258
|
+
description: 'DNS provider profile/account alias. Defaults to the provider default account.',
|
|
259
|
+
},
|
|
193
260
|
],
|
|
194
261
|
},
|
|
195
262
|
{
|
|
@@ -206,9 +273,7 @@ export const commandSchemas = [
|
|
|
206
273
|
name: 'auth logout clerk',
|
|
207
274
|
description: 'Remove saved Clerk credentials locally.',
|
|
208
275
|
examples: ['doomain auth logout clerk --json'],
|
|
209
|
-
flags: [
|
|
210
|
-
{ name: 'json', type: 'boolean', description: 'Output a single JSON object and never prompt.' },
|
|
211
|
-
],
|
|
276
|
+
flags: [{ name: 'json', type: 'boolean', description: 'Output a single JSON object and never prompt.' }],
|
|
212
277
|
},
|
|
213
278
|
{
|
|
214
279
|
name: 'auth vercel',
|
|
@@ -217,16 +282,18 @@ export const commandSchemas = [
|
|
|
217
282
|
flags: [
|
|
218
283
|
{ name: 'json', type: 'boolean', description: 'Output a single JSON object and never prompt.' },
|
|
219
284
|
{ name: 'token', type: 'string', description: 'Vercel API token.' },
|
|
220
|
-
{
|
|
285
|
+
{
|
|
286
|
+
name: 'team-id',
|
|
287
|
+
type: 'string',
|
|
288
|
+
description: 'Optional Vercel team id. Interactive mode can fetch and select it.',
|
|
289
|
+
},
|
|
221
290
|
],
|
|
222
291
|
},
|
|
223
292
|
{
|
|
224
293
|
name: 'auth logout vercel',
|
|
225
294
|
description: 'Remove saved Vercel credentials locally.',
|
|
226
295
|
examples: ['doomain auth logout vercel --json'],
|
|
227
|
-
flags: [
|
|
228
|
-
{ name: 'json', type: 'boolean', description: 'Output a single JSON object and never prompt.' },
|
|
229
|
-
],
|
|
296
|
+
flags: [{ name: 'json', type: 'boolean', description: 'Output a single JSON object and never prompt.' }],
|
|
230
297
|
},
|
|
231
298
|
{
|
|
232
299
|
name: 'domains find',
|
|
@@ -252,8 +319,16 @@ export const commandSchemas = [
|
|
|
252
319
|
safeForAgents: true,
|
|
253
320
|
flags: [
|
|
254
321
|
{ name: 'json', type: 'boolean', description: 'Output a single JSON object and never prompt.' },
|
|
255
|
-
{
|
|
256
|
-
|
|
322
|
+
{
|
|
323
|
+
name: 'provider',
|
|
324
|
+
type: 'string',
|
|
325
|
+
description: 'DNS provider id. Defaults to DOOMAIN_PROVIDER, configured default provider, then spaceship.',
|
|
326
|
+
},
|
|
327
|
+
{
|
|
328
|
+
name: 'account',
|
|
329
|
+
type: 'string',
|
|
330
|
+
description: 'DNS provider profile/account alias. Omit to list all configured accounts for the provider.',
|
|
331
|
+
},
|
|
257
332
|
{ name: 'domain', type: 'string', description: 'Limit output to one DNS zone.' },
|
|
258
333
|
],
|
|
259
334
|
},
|
|
@@ -270,15 +345,27 @@ export const commandSchemas = [
|
|
|
270
345
|
{
|
|
271
346
|
name: 'verify',
|
|
272
347
|
description: 'Ask Vercel to verify a project domain.',
|
|
273
|
-
examples: [
|
|
348
|
+
examples: [
|
|
349
|
+
'doomain verify --domain app.example.com --project my-app --json',
|
|
350
|
+
'doomain verify --domain example.com --apex --project my-app --json',
|
|
351
|
+
],
|
|
274
352
|
mutates: true,
|
|
275
353
|
safeForAgents: true,
|
|
276
354
|
flags: [
|
|
277
355
|
{ name: 'json', type: 'boolean', description: 'Output a single JSON object and never prompt.' },
|
|
278
|
-
{
|
|
356
|
+
{
|
|
357
|
+
name: 'domain',
|
|
358
|
+
type: 'string',
|
|
359
|
+
description: 'Target domain or base zone, for example app.example.com or example.com.',
|
|
360
|
+
required: true,
|
|
361
|
+
},
|
|
279
362
|
{ name: 'subdomain', type: 'string', description: 'Subdomain to verify.' },
|
|
280
363
|
{ name: 'apex', type: 'boolean', description: 'Use the root/apex domain.' },
|
|
281
|
-
{
|
|
364
|
+
{
|
|
365
|
+
name: 'project',
|
|
366
|
+
type: 'string',
|
|
367
|
+
description: 'Vercel project id/name. Optional when local .vercel/project.json is available.',
|
|
368
|
+
},
|
|
282
369
|
],
|
|
283
370
|
},
|
|
284
371
|
];
|
|
@@ -122,7 +122,9 @@ async function loadConfiguredProviderZones(providerId, accountInput, toleratePro
|
|
|
122
122
|
const account = accountInput ? normalizeProviderAccount(accountInput) : undefined;
|
|
123
123
|
if (providerId) {
|
|
124
124
|
const definition = getProviderDefinition(providerId);
|
|
125
|
-
const accounts = account
|
|
125
|
+
const accounts = account
|
|
126
|
+
? [explicitAccountRef(definition.id, account)]
|
|
127
|
+
: listConfiguredProviderAccounts(config, definition);
|
|
126
128
|
const selectedAccounts = accounts.length > 0 ? accounts : [defaultAccountRef(definition.id)];
|
|
127
129
|
const tolerateAccountErrors = tolerateProviderAccountErrors && !account && selectedAccounts.length > 1;
|
|
128
130
|
const results = await Promise.all(selectedAccounts.map((ref) => tolerateAccountErrors ? loadProviderZonesSafely(definition, ref) : loadProviderZones(definition, ref)));
|
|
@@ -174,12 +176,17 @@ export async function resolveProviderTarget(input, options = {}) {
|
|
|
174
176
|
domain: requested.fullDomain,
|
|
175
177
|
recovery: 'Retry with --provider <id> --account <alias> only if another configured provider account owns this zone. Otherwise connect the DNS provider account that owns this domain.',
|
|
176
178
|
searchedZones: zones.searched,
|
|
177
|
-
suggestedCommands: [
|
|
179
|
+
suggestedCommands: [
|
|
180
|
+
`doomain link ${requested.fullDomain} --provider <id> --account <alias> --json`,
|
|
181
|
+
'doomain providers connect',
|
|
182
|
+
],
|
|
178
183
|
});
|
|
179
184
|
}
|
|
180
185
|
const bestLength = matches[0].zone.name.length;
|
|
181
186
|
const bestMatches = matches.filter((candidate) => candidate.zone.name.length === bestLength);
|
|
182
|
-
const uniqueBestMatches = bestMatches.filter((candidate, index, candidates) => candidates.findIndex((item) => item.provider === candidate.provider &&
|
|
187
|
+
const uniqueBestMatches = bestMatches.filter((candidate, index, candidates) => candidates.findIndex((item) => item.provider === candidate.provider &&
|
|
188
|
+
item.account === candidate.account &&
|
|
189
|
+
item.zone.name === candidate.zone.name) === index);
|
|
183
190
|
if (uniqueBestMatches.length > 1) {
|
|
184
191
|
throw new DoomainError('PROVIDER_ZONE_AMBIGUOUS', `Multiple DNS provider accounts have a matching DNS zone for ${requested.fullDomain}. Pass --provider and --account to choose one.`, { candidates: candidateDetails(uniqueBestMatches), domain: requested.fullDomain });
|
|
185
192
|
}
|
package/dist/lib/flags.js
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import { Flags } from '@oclif/core';
|
|
2
2
|
export const jsonFlag = Flags.boolean({ description: 'Output a single JSON object and never prompt.' });
|
|
3
|
-
export const providerFlag = Flags.string({
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
export const providerFlag = Flags.string({
|
|
4
|
+
description: 'DNS provider id. Inferred from the target domain when omitted.',
|
|
5
|
+
});
|
|
6
|
+
export const accountFlag = Flags.string({
|
|
7
|
+
description: 'DNS provider profile/account alias. Defaults to the provider default account.',
|
|
8
|
+
});
|
|
9
|
+
export const domainFlag = Flags.string({
|
|
10
|
+
description: 'Target domain or base zone, for example app.example.com or example.com.',
|
|
11
|
+
});
|
|
6
12
|
export const subdomainFlag = Flags.string({ description: 'Subdomain to add, for example app for app.example.com.' });
|
|
7
13
|
export const apexFlag = Flags.boolean({ description: 'Use the root/apex domain instead of a subdomain.' });
|
|
8
14
|
export const projectFlag = Flags.string({
|
package/dist/lib/link-domain.js
CHANGED
|
@@ -6,7 +6,7 @@ import { resolveProviderTarget } from './domain-provider.js';
|
|
|
6
6
|
import { DoomainError } from './errors.js';
|
|
7
7
|
import { detectLocalVercelProject } from './local-vercel.js';
|
|
8
8
|
import { createProvider } from './providers/registry.js';
|
|
9
|
-
import { createVercelClient, resolveVercelConfig, VERCEL_APEX_A_RECORD, VERCEL_CNAME_RECORD } from './vercel.js';
|
|
9
|
+
import { createVercelClient, resolveVercelConfig, VERCEL_APEX_A_RECORD, VERCEL_CNAME_RECORD, } from './vercel.js';
|
|
10
10
|
function cleanDnsValue(value) {
|
|
11
11
|
return value.toLowerCase().replace(/\.$/, '');
|
|
12
12
|
}
|
|
@@ -146,7 +146,7 @@ function collectVerificationRecords(raw, seen = new Set()) {
|
|
|
146
146
|
const object = raw;
|
|
147
147
|
const verification = object.verification;
|
|
148
148
|
const records = Array.isArray(verification) ? verification : [];
|
|
149
|
-
const nested = Object.entries(object).flatMap(([key, value]) =>
|
|
149
|
+
const nested = Object.entries(object).flatMap(([key, value]) => key === 'verification' ? [] : collectVerificationRecords(value, seen));
|
|
150
150
|
return [...records, ...nested];
|
|
151
151
|
}
|
|
152
152
|
function uniqueRecords(records) {
|
|
@@ -359,7 +359,12 @@ export async function linkDomain(input) {
|
|
|
359
359
|
const zone = await resolveZone(provider, plan.zoneDomain);
|
|
360
360
|
reportProgress(input, 'vercel:get-target', 'Reading Vercel DNS target');
|
|
361
361
|
const cname = plan.isApex ? undefined : await vercel.getRecommendedCname(plan.domain);
|
|
362
|
-
const baseRecord = planBaseRecord({
|
|
362
|
+
const baseRecord = planBaseRecord({
|
|
363
|
+
isApex: plan.isApex,
|
|
364
|
+
provider: plan.provider,
|
|
365
|
+
recordName: plan.recordName,
|
|
366
|
+
cname,
|
|
367
|
+
});
|
|
363
368
|
const forceDns = await resolveDnsForce(input, { baseRecord, plan, provider, zone });
|
|
364
369
|
reportProgress(input, 'vercel:add-domain', 'Adding domain to Vercel');
|
|
365
370
|
const addResult = await vercel.addDomainToProject(plan.project, plan.domain, { force: input.force });
|
|
@@ -376,7 +381,9 @@ export async function linkDomain(input) {
|
|
|
376
381
|
const dnsResult = await provider.applyChanges(zone, dnsPlan, { force: forceDns });
|
|
377
382
|
const shouldWait = input.wait ?? true;
|
|
378
383
|
if (shouldWait) {
|
|
379
|
-
reportProgress(input, 'dns:wait', verificationDnsRecords.length > 0
|
|
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');
|
|
380
387
|
}
|
|
381
388
|
const waitResult = shouldWait
|
|
382
389
|
? await waitForVercelDomainReady({
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { normalizeDomain } from '../../validate.js';
|
|
2
|
+
import { ProviderError } from '../core/errors.js';
|
|
2
3
|
import { createProviderHttpClient } from '../core/http.js';
|
|
3
4
|
import { assertNoConflicts, planDnsChanges } from '../core/planner.js';
|
|
4
|
-
import { ProviderError } from '../core/errors.js';
|
|
5
5
|
const CLOUDFLARE_API_URL = 'https://api.cloudflare.com/client/v4';
|
|
6
6
|
const capabilities = {
|
|
7
7
|
defaultTtl: 3600,
|
|
@@ -60,7 +60,9 @@ function toCloudflareRecord(record, zone) {
|
|
|
60
60
|
ttl: record.ttl ?? capabilities.defaultTtl,
|
|
61
61
|
type: record.type,
|
|
62
62
|
...(record.priority === undefined ? {} : { priority: record.priority }),
|
|
63
|
-
...(record.proxied === undefined || !['A', 'AAAA', 'CNAME'].includes(record.type)
|
|
63
|
+
...(record.proxied === undefined || !['A', 'AAAA', 'CNAME'].includes(record.type)
|
|
64
|
+
? {}
|
|
65
|
+
: { proxied: record.proxied }),
|
|
64
66
|
};
|
|
65
67
|
}
|
|
66
68
|
function toZone(zone) {
|
|
@@ -102,7 +104,7 @@ export class CloudflareProvider {
|
|
|
102
104
|
const zones = [];
|
|
103
105
|
for (let page = 1; page <= 100; page += 1) {
|
|
104
106
|
const response = await this.request('/zones', {
|
|
105
|
-
query: { 'account.id': this.accountId, direction: 'asc', order: 'name', page,
|
|
107
|
+
query: { 'account.id': this.accountId, direction: 'asc', order: 'name', page, per_page: 50 },
|
|
106
108
|
});
|
|
107
109
|
for (const zone of response.result ?? []) {
|
|
108
110
|
const dnsZone = toZone(zone);
|
|
@@ -124,7 +126,7 @@ export class CloudflareProvider {
|
|
|
124
126
|
const records = [];
|
|
125
127
|
for (let page = 1; page <= 100; page += 1) {
|
|
126
128
|
const response = await this.request(`/zones/${zone.id}/dns_records`, {
|
|
127
|
-
query: { page,
|
|
129
|
+
query: { page, per_page: 100 },
|
|
128
130
|
});
|
|
129
131
|
for (const record of response.result ?? []) {
|
|
130
132
|
const dnsRecord = toDnsRecord(record, zone);
|
|
@@ -138,7 +140,13 @@ export class CloudflareProvider {
|
|
|
138
140
|
return records;
|
|
139
141
|
}
|
|
140
142
|
async planChanges(zone, desired, opts = {}) {
|
|
141
|
-
return planDnsChanges({
|
|
143
|
+
return planDnsChanges({
|
|
144
|
+
desired,
|
|
145
|
+
existing: await this.listRecords(zone),
|
|
146
|
+
force: opts.force,
|
|
147
|
+
providerId: this.id,
|
|
148
|
+
zone,
|
|
149
|
+
});
|
|
142
150
|
}
|
|
143
151
|
async applyChanges(zone, plan) {
|
|
144
152
|
assertNoConflicts(this.id, plan);
|
|
@@ -50,7 +50,8 @@ export function providerAccountHasCredentials(config, providerId, accountInput)
|
|
|
50
50
|
return false;
|
|
51
51
|
if (account !== DEFAULT_PROVIDER_ACCOUNT)
|
|
52
52
|
return hasCredentials(current.accounts?.[account]?.credentials);
|
|
53
|
-
return hasCredentials(current.credentials) ||
|
|
53
|
+
return (hasCredentials(current.credentials) ||
|
|
54
|
+
Boolean(legacyCredential(config, providerId, 'apiKey') || legacyCredential(config, providerId, 'apiSecret')));
|
|
54
55
|
}
|
|
55
56
|
export function withProviderAccountCredentials(current, accountInput, credentials) {
|
|
56
57
|
const account = normalizeProviderAccount(accountInput);
|
|
@@ -50,7 +50,11 @@ export function planDnsChanges(input) {
|
|
|
50
50
|
continue;
|
|
51
51
|
}
|
|
52
52
|
const [replace, ...remainingSameTyped] = sameTyped;
|
|
53
|
-
changes.push(...(replace ? [{ action: 'update', existing: replace, record }] : []), ...remainingSameTyped.map((existing) => ({
|
|
53
|
+
changes.push(...(replace ? [{ action: 'update', existing: replace, record }] : []), ...remainingSameTyped.map((existing) => ({
|
|
54
|
+
action: 'delete',
|
|
55
|
+
existing,
|
|
56
|
+
reason: 'same_type_record_exists',
|
|
57
|
+
})), ...cnameConflicts.map((existing) => ({ action: 'delete', existing, reason: 'cname_slot_conflict' })), ...(replace ? [] : [{ action: 'create', record }]));
|
|
54
58
|
}
|
|
55
59
|
return { changes, conflicts, desired: input.desired, existing: input.existing, zone: input.zone };
|
|
56
60
|
}
|
|
@@ -129,7 +129,13 @@ export class HostingerProvider {
|
|
|
129
129
|
return records.flatMap((record) => toDnsRecords(record, zone));
|
|
130
130
|
}
|
|
131
131
|
async planChanges(zone, desired, opts = {}) {
|
|
132
|
-
const plan = planDnsChanges({
|
|
132
|
+
const plan = planDnsChanges({
|
|
133
|
+
desired,
|
|
134
|
+
existing: await this.listRecords(zone),
|
|
135
|
+
force: opts.force,
|
|
136
|
+
providerId: this.id,
|
|
137
|
+
zone,
|
|
138
|
+
});
|
|
133
139
|
return collapseRecordSetWrites(plan);
|
|
134
140
|
}
|
|
135
141
|
async applyChanges(zone, plan) {
|
|
@@ -53,7 +53,10 @@ function providerCodeFromNamecheapError(message) {
|
|
|
53
53
|
const lower = message.toLowerCase();
|
|
54
54
|
if (lower.includes('clientip') || lower.includes('client ip') || lower.includes('whitelist'))
|
|
55
55
|
return 'PROVIDER_PERMISSION_DENIED';
|
|
56
|
-
if (lower.includes('api key') ||
|
|
56
|
+
if (lower.includes('api key') ||
|
|
57
|
+
lower.includes('apiuser') ||
|
|
58
|
+
lower.includes('username') ||
|
|
59
|
+
lower.includes('authentication')) {
|
|
57
60
|
return 'PROVIDER_AUTH_FAILED';
|
|
58
61
|
}
|
|
59
62
|
if (lower.includes('rate'))
|
|
@@ -176,7 +179,13 @@ export class NamecheapProvider {
|
|
|
176
179
|
});
|
|
177
180
|
}
|
|
178
181
|
async planChanges(zone, desired, opts = {}) {
|
|
179
|
-
return planDnsChanges({
|
|
182
|
+
return planDnsChanges({
|
|
183
|
+
desired,
|
|
184
|
+
existing: await this.listRecords(zone),
|
|
185
|
+
force: opts.force,
|
|
186
|
+
providerId: this.id,
|
|
187
|
+
zone,
|
|
188
|
+
});
|
|
180
189
|
}
|
|
181
190
|
async applyChanges(zone, plan) {
|
|
182
191
|
assertNoConflicts(this.id, plan);
|
|
@@ -5,7 +5,12 @@ import { createProviderContext } from './core/config.js';
|
|
|
5
5
|
import { hostingerProviderDefinition } from './hostinger/index.js';
|
|
6
6
|
import { namecheapProviderDefinition } from './namecheap/index.js';
|
|
7
7
|
import { spaceshipProviderDefinition } from './spaceship/index.js';
|
|
8
|
-
const definitions = [
|
|
8
|
+
const definitions = [
|
|
9
|
+
spaceshipProviderDefinition,
|
|
10
|
+
namecheapProviderDefinition,
|
|
11
|
+
cloudflareProviderDefinition,
|
|
12
|
+
hostingerProviderDefinition,
|
|
13
|
+
];
|
|
9
14
|
export function listProviderDefinitions() {
|
|
10
15
|
return definitions;
|
|
11
16
|
}
|
|
@@ -9,7 +9,9 @@ export async function listProviderStatuses(opts = {}) {
|
|
|
9
9
|
const statuses = [];
|
|
10
10
|
for (const definition of listProviderDefinitions()) {
|
|
11
11
|
const accounts = listConfiguredProviderAccounts(config, definition);
|
|
12
|
-
const refs = accounts.length > 0
|
|
12
|
+
const refs = accounts.length > 0
|
|
13
|
+
? accounts
|
|
14
|
+
: [{ account: DEFAULT_PROVIDER_ACCOUNT, isDefaultAccount: true, providerId: definition.id }];
|
|
13
15
|
for (const ref of refs) {
|
|
14
16
|
const account = normalizeProviderAccount(ref.account);
|
|
15
17
|
const configured = accounts.some((item) => item.account === account);
|
package/dist/lib/validate.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { DoomainError } from './errors.js';
|
|
2
2
|
const DOMAIN_LABEL = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
|
|
3
3
|
export function normalizeDomain(input) {
|
|
4
|
-
const value = input
|
|
4
|
+
const value = input
|
|
5
|
+
.trim()
|
|
6
|
+
.toLowerCase()
|
|
7
|
+
.replace(/^https?:\/\//, '')
|
|
8
|
+
.replace(/\/$/, '');
|
|
5
9
|
const domain = value.split('/')[0];
|
|
6
10
|
if (!domain || domain.length > 253) {
|
|
7
11
|
throw new DoomainError('INVALID_INPUT', 'Domain is required.');
|
|
@@ -13,7 +17,10 @@ export function normalizeDomain(input) {
|
|
|
13
17
|
return domain;
|
|
14
18
|
}
|
|
15
19
|
export function normalizeSubdomain(input) {
|
|
16
|
-
const subdomain = input
|
|
20
|
+
const subdomain = input
|
|
21
|
+
.trim()
|
|
22
|
+
.toLowerCase()
|
|
23
|
+
.replace(/^\.+|\.+$/g, '');
|
|
17
24
|
if (!subdomain || subdomain === '@') {
|
|
18
25
|
throw new DoomainError('INVALID_INPUT', 'Subdomain is required unless --apex is used.');
|
|
19
26
|
}
|
package/dist/lib/vercel.js
CHANGED
|
@@ -47,7 +47,8 @@ function findProjectDomainTarget(raw, domain) {
|
|
|
47
47
|
const targets = Array.isArray(raw) ? raw : [raw];
|
|
48
48
|
return targets.find((target) => target &&
|
|
49
49
|
typeof target === 'object' &&
|
|
50
|
-
(isSameDomain(target.domain, domain) ||
|
|
50
|
+
(isSameDomain(target.domain, domain) ||
|
|
51
|
+
isSameDomain(target.name, domain)));
|
|
51
52
|
}
|
|
52
53
|
export function createVercelClient(config) {
|
|
53
54
|
async function request(path, init = {}, opts = {}) {
|
|
@@ -177,7 +178,9 @@ export function createVercelClient(config) {
|
|
|
177
178
|
return result.domains ?? [];
|
|
178
179
|
},
|
|
179
180
|
async removeDomainFromProject(project, domain) {
|
|
180
|
-
await request(`/v9/projects/${encodeURIComponent(project)}/domains/${encodeURIComponent(domain)}`, {
|
|
181
|
+
await request(`/v9/projects/${encodeURIComponent(project)}/domains/${encodeURIComponent(domain)}`, {
|
|
182
|
+
method: 'DELETE',
|
|
183
|
+
});
|
|
181
184
|
},
|
|
182
185
|
async verifyProjectDomain(project, domain) {
|
|
183
186
|
return request(`/v9/projects/${encodeURIComponent(project)}/domains/${encodeURIComponent(domain)}/verify`, { method: 'POST' });
|
package/oclif.manifest.json
CHANGED