doomain 0.1.19 → 0.1.21
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/update.d.ts +9 -0
- package/dist/commands/update.js +28 -0
- package/dist/commands/upgrade.d.ts +1 -0
- package/dist/commands/upgrade.js +1 -0
- 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 +136 -35
- package/dist/lib/domain-provider.js +10 -3
- package/dist/lib/errors.d.ts +1 -1
- 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/self-update.d.ts +27 -0
- package/dist/lib/self-update.js +63 -0
- package/dist/lib/validate.js +9 -2
- package/dist/lib/vercel.js +5 -2
- package/oclif.manifest.json +63 -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 = {
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
import { jsonFlag } from '../lib/flags.js';
|
|
3
|
+
import { createOutput, outputError } from '../lib/output.js';
|
|
4
|
+
import { installLatestVersion } from '../lib/self-update.js';
|
|
5
|
+
export default class Update extends Command {
|
|
6
|
+
static description = 'Install the latest doomain version from npm without using the existing npm cache.';
|
|
7
|
+
static examples = ['<%= config.bin %> <%= command.id %>', '<%= config.bin %> <%= command.id %> --json'];
|
|
8
|
+
static flags = {
|
|
9
|
+
json: jsonFlag,
|
|
10
|
+
};
|
|
11
|
+
async run() {
|
|
12
|
+
const { flags } = await this.parse(Update);
|
|
13
|
+
const out = createOutput({ json: flags.json });
|
|
14
|
+
const spinner = out.spinner();
|
|
15
|
+
try {
|
|
16
|
+
spinner.start('Downloading the latest doomain version from npm');
|
|
17
|
+
const result = await installLatestVersion();
|
|
18
|
+
spinner.stop('Installed the latest doomain version');
|
|
19
|
+
out.result(result);
|
|
20
|
+
out.success('Installed the latest doomain version from npm.');
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
spinner.error('Update failed');
|
|
24
|
+
outputError(out.json, error, 'SELF_UPDATE_FAILED');
|
|
25
|
+
this.exit(1);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './update.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './update.js';
|
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,28 @@ 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
|
-
|
|
116
|
-
|
|
165
|
+
flags: [{ name: 'json', type: 'boolean', description: 'Output a single JSON object and never prompt.' }],
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
name: 'update',
|
|
169
|
+
description: 'Install the latest doomain version from npm without using the existing npm cache.',
|
|
170
|
+
examples: ['doomain update', 'doomain update --json'],
|
|
171
|
+
mutates: true,
|
|
172
|
+
flags: [{ name: 'json', type: 'boolean', description: 'Output a single JSON object and never prompt.' }],
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
name: 'upgrade',
|
|
176
|
+
description: 'Alias for update.',
|
|
177
|
+
examples: ['doomain upgrade', 'doomain upgrade --json'],
|
|
178
|
+
mutates: true,
|
|
179
|
+
flags: [{ name: 'json', type: 'boolean', description: 'Output a single JSON object and never prompt.' }],
|
|
117
180
|
},
|
|
118
181
|
{
|
|
119
182
|
name: 'providers list',
|
|
120
183
|
description: 'List supported DNS providers.',
|
|
121
184
|
examples: ['doomain providers list --json'],
|
|
122
185
|
safeForAgents: true,
|
|
123
|
-
flags: [
|
|
124
|
-
{ name: 'json', type: 'boolean', description: 'Output a single JSON object and never prompt.' },
|
|
125
|
-
],
|
|
186
|
+
flags: [{ name: 'json', type: 'boolean', description: 'Output a single JSON object and never prompt.' }],
|
|
126
187
|
},
|
|
127
188
|
{
|
|
128
189
|
name: 'providers connect',
|
|
@@ -137,7 +198,11 @@ export const commandSchemas = [
|
|
|
137
198
|
],
|
|
138
199
|
flags: [
|
|
139
200
|
{ name: 'json', type: 'boolean', description: 'Output a single JSON object and never prompt.' },
|
|
140
|
-
{
|
|
201
|
+
{
|
|
202
|
+
name: 'account',
|
|
203
|
+
type: 'string',
|
|
204
|
+
description: 'DNS provider profile/account alias. Defaults to the provider default account.',
|
|
205
|
+
},
|
|
141
206
|
{ name: 'credential', type: 'string', description: 'Provider credential as key=value. Can be repeated.' },
|
|
142
207
|
{ name: 'api-key', type: 'string', description: 'Spaceship API key.' },
|
|
143
208
|
{ name: 'api-secret', type: 'string', description: 'Spaceship API secret.' },
|
|
@@ -147,10 +212,18 @@ export const commandSchemas = [
|
|
|
147
212
|
{
|
|
148
213
|
name: 'providers add',
|
|
149
214
|
description: 'Alias for providers connect.',
|
|
150
|
-
examples: [
|
|
215
|
+
examples: [
|
|
216
|
+
'doomain providers add',
|
|
217
|
+
'doomain providers add namecheap',
|
|
218
|
+
'doomain providers add spaceship --account work',
|
|
219
|
+
],
|
|
151
220
|
flags: [
|
|
152
221
|
{ name: 'json', type: 'boolean', description: 'Output a single JSON object and never prompt.' },
|
|
153
|
-
{
|
|
222
|
+
{
|
|
223
|
+
name: 'account',
|
|
224
|
+
type: 'string',
|
|
225
|
+
description: 'DNS provider profile/account alias. Defaults to the provider default account.',
|
|
226
|
+
},
|
|
154
227
|
{ name: 'credential', type: 'string', description: 'Provider credential as key=value. Can be repeated.' },
|
|
155
228
|
{ name: 'no-verify', type: 'boolean', description: 'Save credentials without verifying them first.' },
|
|
156
229
|
],
|
|
@@ -175,7 +248,11 @@ export const commandSchemas = [
|
|
|
175
248
|
],
|
|
176
249
|
flags: [
|
|
177
250
|
{ name: 'json', type: 'boolean', description: 'Output a single JSON object and never prompt.' },
|
|
178
|
-
{
|
|
251
|
+
{
|
|
252
|
+
name: 'account',
|
|
253
|
+
type: 'string',
|
|
254
|
+
description: 'DNS provider profile/account alias. Omit to remove all accounts for the provider.',
|
|
255
|
+
},
|
|
179
256
|
],
|
|
180
257
|
},
|
|
181
258
|
{
|
|
@@ -189,7 +266,11 @@ export const commandSchemas = [
|
|
|
189
266
|
],
|
|
190
267
|
flags: [
|
|
191
268
|
{ name: 'json', type: 'boolean', description: 'Output a single JSON object and never prompt.' },
|
|
192
|
-
{
|
|
269
|
+
{
|
|
270
|
+
name: 'account',
|
|
271
|
+
type: 'string',
|
|
272
|
+
description: 'DNS provider profile/account alias. Defaults to the provider default account.',
|
|
273
|
+
},
|
|
193
274
|
],
|
|
194
275
|
},
|
|
195
276
|
{
|
|
@@ -206,9 +287,7 @@ export const commandSchemas = [
|
|
|
206
287
|
name: 'auth logout clerk',
|
|
207
288
|
description: 'Remove saved Clerk credentials locally.',
|
|
208
289
|
examples: ['doomain auth logout clerk --json'],
|
|
209
|
-
flags: [
|
|
210
|
-
{ name: 'json', type: 'boolean', description: 'Output a single JSON object and never prompt.' },
|
|
211
|
-
],
|
|
290
|
+
flags: [{ name: 'json', type: 'boolean', description: 'Output a single JSON object and never prompt.' }],
|
|
212
291
|
},
|
|
213
292
|
{
|
|
214
293
|
name: 'auth vercel',
|
|
@@ -217,16 +296,18 @@ export const commandSchemas = [
|
|
|
217
296
|
flags: [
|
|
218
297
|
{ name: 'json', type: 'boolean', description: 'Output a single JSON object and never prompt.' },
|
|
219
298
|
{ name: 'token', type: 'string', description: 'Vercel API token.' },
|
|
220
|
-
{
|
|
299
|
+
{
|
|
300
|
+
name: 'team-id',
|
|
301
|
+
type: 'string',
|
|
302
|
+
description: 'Optional Vercel team id. Interactive mode can fetch and select it.',
|
|
303
|
+
},
|
|
221
304
|
],
|
|
222
305
|
},
|
|
223
306
|
{
|
|
224
307
|
name: 'auth logout vercel',
|
|
225
308
|
description: 'Remove saved Vercel credentials locally.',
|
|
226
309
|
examples: ['doomain auth logout vercel --json'],
|
|
227
|
-
flags: [
|
|
228
|
-
{ name: 'json', type: 'boolean', description: 'Output a single JSON object and never prompt.' },
|
|
229
|
-
],
|
|
310
|
+
flags: [{ name: 'json', type: 'boolean', description: 'Output a single JSON object and never prompt.' }],
|
|
230
311
|
},
|
|
231
312
|
{
|
|
232
313
|
name: 'domains find',
|
|
@@ -252,8 +333,16 @@ export const commandSchemas = [
|
|
|
252
333
|
safeForAgents: true,
|
|
253
334
|
flags: [
|
|
254
335
|
{ name: 'json', type: 'boolean', description: 'Output a single JSON object and never prompt.' },
|
|
255
|
-
{
|
|
256
|
-
|
|
336
|
+
{
|
|
337
|
+
name: 'provider',
|
|
338
|
+
type: 'string',
|
|
339
|
+
description: 'DNS provider id. Defaults to DOOMAIN_PROVIDER, configured default provider, then spaceship.',
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
name: 'account',
|
|
343
|
+
type: 'string',
|
|
344
|
+
description: 'DNS provider profile/account alias. Omit to list all configured accounts for the provider.',
|
|
345
|
+
},
|
|
257
346
|
{ name: 'domain', type: 'string', description: 'Limit output to one DNS zone.' },
|
|
258
347
|
],
|
|
259
348
|
},
|
|
@@ -270,15 +359,27 @@ export const commandSchemas = [
|
|
|
270
359
|
{
|
|
271
360
|
name: 'verify',
|
|
272
361
|
description: 'Ask Vercel to verify a project domain.',
|
|
273
|
-
examples: [
|
|
362
|
+
examples: [
|
|
363
|
+
'doomain verify --domain app.example.com --project my-app --json',
|
|
364
|
+
'doomain verify --domain example.com --apex --project my-app --json',
|
|
365
|
+
],
|
|
274
366
|
mutates: true,
|
|
275
367
|
safeForAgents: true,
|
|
276
368
|
flags: [
|
|
277
369
|
{ name: 'json', type: 'boolean', description: 'Output a single JSON object and never prompt.' },
|
|
278
|
-
{
|
|
370
|
+
{
|
|
371
|
+
name: 'domain',
|
|
372
|
+
type: 'string',
|
|
373
|
+
description: 'Target domain or base zone, for example app.example.com or example.com.',
|
|
374
|
+
required: true,
|
|
375
|
+
},
|
|
279
376
|
{ name: 'subdomain', type: 'string', description: 'Subdomain to verify.' },
|
|
280
377
|
{ name: 'apex', type: 'boolean', description: 'Use the root/apex domain.' },
|
|
281
|
-
{
|
|
378
|
+
{
|
|
379
|
+
name: 'project',
|
|
380
|
+
type: 'string',
|
|
381
|
+
description: 'Vercel project id/name. Optional when local .vercel/project.json is available.',
|
|
382
|
+
},
|
|
282
383
|
],
|
|
283
384
|
},
|
|
284
385
|
];
|
|
@@ -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/errors.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type DoomainErrorCode = 'CONFIG_NOT_FOUND' | 'CLERK_AUTH_FAILED' | 'CLERK_PRODUCTION_EXISTS' | 'DNS_POINT_FAILED' | 'DNS_TARGET_CONFLICT' | 'DOMAIN_LINK_FAILED' | 'DOMAIN_PROVIDER_DISCOVERY_FAILED' | 'DOMAIN_ALREADY_ASSIGNED' | 'DOMAIN_VERIFY_FAILED' | 'INVALID_INPUT' | 'MISSING_ARGUMENT' | 'MISSING_CREDENTIALS' | 'PROVIDER_API_ERROR' | 'PROVIDER_AUTH_FAILED' | 'PROVIDER_PERMISSION_DENIED' | 'PROVIDER_NOT_FOUND' | 'PROVIDER_RATE_LIMITED' | 'PROVIDER_RECORD_CONFLICT' | 'PROVIDER_UNSUPPORTED_RECORD' | 'PROVIDER_ZONE_AMBIGUOUS' | 'PROVIDER_ZONE_NOT_FOUND' | 'PROJECT_NOT_FOUND' | 'VERCEL_AUTH_FAILED' | 'VERCEL_PROJECT_NOT_LINKED';
|
|
1
|
+
export type DoomainErrorCode = 'CONFIG_NOT_FOUND' | 'CLERK_AUTH_FAILED' | 'CLERK_PRODUCTION_EXISTS' | 'DNS_POINT_FAILED' | 'DNS_TARGET_CONFLICT' | 'DOMAIN_LINK_FAILED' | 'DOMAIN_PROVIDER_DISCOVERY_FAILED' | 'DOMAIN_ALREADY_ASSIGNED' | 'DOMAIN_VERIFY_FAILED' | 'INVALID_INPUT' | 'MISSING_ARGUMENT' | 'MISSING_CREDENTIALS' | 'PROVIDER_API_ERROR' | 'PROVIDER_AUTH_FAILED' | 'PROVIDER_PERMISSION_DENIED' | 'PROVIDER_NOT_FOUND' | 'PROVIDER_RATE_LIMITED' | 'PROVIDER_RECORD_CONFLICT' | 'PROVIDER_UNSUPPORTED_RECORD' | 'PROVIDER_ZONE_AMBIGUOUS' | 'PROVIDER_ZONE_NOT_FOUND' | 'PROJECT_NOT_FOUND' | 'SELF_UPDATE_FAILED' | 'VERCEL_AUTH_FAILED' | 'VERCEL_PROJECT_NOT_LINKED';
|
|
2
2
|
export declare class DoomainError extends Error {
|
|
3
3
|
readonly code: DoomainErrorCode;
|
|
4
4
|
readonly details?: unknown;
|
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);
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
interface ProcessResult {
|
|
2
|
+
exitCode: number | null;
|
|
3
|
+
stderr: string;
|
|
4
|
+
stdout: string;
|
|
5
|
+
}
|
|
6
|
+
interface ProcessOptions {
|
|
7
|
+
env: NodeJS.ProcessEnv;
|
|
8
|
+
shell: boolean;
|
|
9
|
+
}
|
|
10
|
+
type ProcessRunner = (command: string, args: string[], options: ProcessOptions) => Promise<ProcessResult>;
|
|
11
|
+
export interface SelfUpdateResult {
|
|
12
|
+
package: string;
|
|
13
|
+
packageSpec: string;
|
|
14
|
+
packageManager: 'npm';
|
|
15
|
+
}
|
|
16
|
+
export interface SelfUpdateOptions {
|
|
17
|
+
cacheRoot?: string;
|
|
18
|
+
platform?: NodeJS.Platform;
|
|
19
|
+
runner?: ProcessRunner;
|
|
20
|
+
}
|
|
21
|
+
export declare function npmInstallCommand(cacheDirectory: string, platform?: NodeJS.Platform, environment?: NodeJS.ProcessEnv): {
|
|
22
|
+
args: string[];
|
|
23
|
+
command: string;
|
|
24
|
+
options: ProcessOptions;
|
|
25
|
+
};
|
|
26
|
+
export declare function installLatestVersion(options?: SelfUpdateOptions): Promise<SelfUpdateResult>;
|
|
27
|
+
export {};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { mkdtemp, rm } from 'node:fs/promises';
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { DoomainError } from './errors.js';
|
|
6
|
+
function runProcess(command, args, options) {
|
|
7
|
+
return new Promise((resolve, reject) => {
|
|
8
|
+
const child = spawn(command, args, {
|
|
9
|
+
...options,
|
|
10
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
11
|
+
windowsHide: true,
|
|
12
|
+
});
|
|
13
|
+
let stderr = '';
|
|
14
|
+
let stdout = '';
|
|
15
|
+
child.stderr.setEncoding('utf8');
|
|
16
|
+
child.stderr.on('data', (chunk) => {
|
|
17
|
+
stderr += chunk;
|
|
18
|
+
});
|
|
19
|
+
child.stdout.setEncoding('utf8');
|
|
20
|
+
child.stdout.on('data', (chunk) => {
|
|
21
|
+
stdout += chunk;
|
|
22
|
+
});
|
|
23
|
+
child.once('error', reject);
|
|
24
|
+
child.once('close', (exitCode) => resolve({ exitCode, stderr, stdout }));
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
export function npmInstallCommand(cacheDirectory, platform = process.platform, environment = process.env) {
|
|
28
|
+
const env = Object.fromEntries(Object.entries(environment).filter(([key]) => key.toLowerCase() !== 'npm_config_cache'));
|
|
29
|
+
env.npm_config_cache = cacheDirectory;
|
|
30
|
+
return {
|
|
31
|
+
command: 'npm',
|
|
32
|
+
args: ['install', '--global', 'doomain@latest', '--prefer-online', '--offline=false'],
|
|
33
|
+
options: {
|
|
34
|
+
env,
|
|
35
|
+
shell: platform === 'win32',
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
export async function installLatestVersion(options = {}) {
|
|
40
|
+
const cacheDirectory = await mkdtemp(join(options.cacheRoot ?? tmpdir(), 'doomain-npm-cache-'));
|
|
41
|
+
try {
|
|
42
|
+
const invocation = npmInstallCommand(cacheDirectory, options.platform);
|
|
43
|
+
const result = await (options.runner ?? runProcess)(invocation.command, invocation.args, invocation.options);
|
|
44
|
+
if (result.exitCode !== 0) {
|
|
45
|
+
const reason = result.stderr.trim() || result.stdout.trim() || `npm exited with code ${result.exitCode ?? 'unknown'}`;
|
|
46
|
+
throw new DoomainError('SELF_UPDATE_FAILED', `Unable to update doomain: ${reason}`);
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
package: 'doomain',
|
|
50
|
+
packageSpec: 'doomain@latest',
|
|
51
|
+
packageManager: 'npm',
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
if (error instanceof DoomainError)
|
|
56
|
+
throw error;
|
|
57
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
58
|
+
throw new DoomainError('SELF_UPDATE_FAILED', `Unable to update doomain: ${message}`);
|
|
59
|
+
}
|
|
60
|
+
finally {
|
|
61
|
+
await rm(cacheDirectory, { force: true, recursive: true }).catch(() => undefined);
|
|
62
|
+
}
|
|
63
|
+
}
|
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
|
@@ -142,6 +142,68 @@
|
|
|
142
142
|
"schema.js"
|
|
143
143
|
]
|
|
144
144
|
},
|
|
145
|
+
"update": {
|
|
146
|
+
"aliases": [],
|
|
147
|
+
"args": {},
|
|
148
|
+
"description": "Install the latest doomain version from npm without using the existing npm cache.",
|
|
149
|
+
"examples": [
|
|
150
|
+
"<%= config.bin %> <%= command.id %>",
|
|
151
|
+
"<%= config.bin %> <%= command.id %> --json"
|
|
152
|
+
],
|
|
153
|
+
"flags": {
|
|
154
|
+
"json": {
|
|
155
|
+
"description": "Output a single JSON object and never prompt.",
|
|
156
|
+
"name": "json",
|
|
157
|
+
"allowNo": false,
|
|
158
|
+
"type": "boolean"
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
"hasDynamicHelp": false,
|
|
162
|
+
"hiddenAliases": [],
|
|
163
|
+
"id": "update",
|
|
164
|
+
"pluginAlias": "doomain",
|
|
165
|
+
"pluginName": "doomain",
|
|
166
|
+
"pluginType": "core",
|
|
167
|
+
"strict": true,
|
|
168
|
+
"enableJsonFlag": false,
|
|
169
|
+
"isESM": true,
|
|
170
|
+
"relativePath": [
|
|
171
|
+
"dist",
|
|
172
|
+
"commands",
|
|
173
|
+
"update.js"
|
|
174
|
+
]
|
|
175
|
+
},
|
|
176
|
+
"upgrade": {
|
|
177
|
+
"aliases": [],
|
|
178
|
+
"args": {},
|
|
179
|
+
"description": "Install the latest doomain version from npm without using the existing npm cache.",
|
|
180
|
+
"examples": [
|
|
181
|
+
"<%= config.bin %> <%= command.id %>",
|
|
182
|
+
"<%= config.bin %> <%= command.id %> --json"
|
|
183
|
+
],
|
|
184
|
+
"flags": {
|
|
185
|
+
"json": {
|
|
186
|
+
"description": "Output a single JSON object and never prompt.",
|
|
187
|
+
"name": "json",
|
|
188
|
+
"allowNo": false,
|
|
189
|
+
"type": "boolean"
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
"hasDynamicHelp": false,
|
|
193
|
+
"hiddenAliases": [],
|
|
194
|
+
"id": "upgrade",
|
|
195
|
+
"pluginAlias": "doomain",
|
|
196
|
+
"pluginName": "doomain",
|
|
197
|
+
"pluginType": "core",
|
|
198
|
+
"strict": true,
|
|
199
|
+
"enableJsonFlag": false,
|
|
200
|
+
"isESM": true,
|
|
201
|
+
"relativePath": [
|
|
202
|
+
"dist",
|
|
203
|
+
"commands",
|
|
204
|
+
"upgrade.js"
|
|
205
|
+
]
|
|
206
|
+
},
|
|
145
207
|
"verify": {
|
|
146
208
|
"aliases": [],
|
|
147
209
|
"args": {},
|
|
@@ -1001,5 +1063,5 @@
|
|
|
1001
1063
|
]
|
|
1002
1064
|
}
|
|
1003
1065
|
},
|
|
1004
|
-
"version": "0.1.
|
|
1066
|
+
"version": "0.1.21"
|
|
1005
1067
|
}
|