doomain 0.1.6 → 0.1.7
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/link.js +1 -0
- package/dist/lib/command-schema.d.ts +3 -0
- package/dist/lib/command-schema.js +5 -1
- package/dist/lib/config.d.ts +1 -0
- package/dist/lib/flags.js +1 -1
- package/dist/lib/link-domain.d.ts +2 -0
- package/dist/lib/link-domain.js +87 -5
- package/oclif.manifest.json +39 -38
- package/package.json +1 -1
package/dist/commands/link.js
CHANGED
|
@@ -6,6 +6,7 @@ import { createOutput, outputError } from '../lib/output.js';
|
|
|
6
6
|
export default class Link extends Command {
|
|
7
7
|
static description = 'Link a Vercel project to a domain and create DNS records.';
|
|
8
8
|
static examples = [
|
|
9
|
+
'<%= config.bin %> <%= command.id %> app.example.com --json',
|
|
9
10
|
'<%= config.bin %> <%= command.id %> app.example.com --project my-app --json',
|
|
10
11
|
'<%= config.bin %> <%= command.id %> --domain app.example.com --project my-app --json',
|
|
11
12
|
'<%= config.bin %> <%= command.id %> --domain example.com --subdomain app --project my-app',
|
|
@@ -3,18 +3,22 @@ export const commandSchemas = [
|
|
|
3
3
|
name: 'link',
|
|
4
4
|
description: 'Link a Vercel project to a domain and create DNS records.',
|
|
5
5
|
examples: [
|
|
6
|
+
'doomain link app.example.com --json',
|
|
6
7
|
'doomain link app.example.com --project my-app --json',
|
|
7
8
|
'doomain link --domain app.example.com --project my-app --json',
|
|
8
9
|
'doomain link --domain example.com --subdomain app --project my-app --json',
|
|
9
10
|
'doomain link --provider spaceship --domain example.com --apex --project my-app --dry-run --json',
|
|
10
11
|
],
|
|
12
|
+
agentHint: 'For agent use, run `doomain link <domain> --json`. The project is inferred from --project, DOOMAIN_PROJECT, config defaults, .vercel/project.json, or nearest package.json name.',
|
|
13
|
+
mutates: true,
|
|
14
|
+
safeForAgents: true,
|
|
11
15
|
flags: [
|
|
12
16
|
{ name: 'json', type: 'boolean', description: 'Output a single JSON object and never prompt.' },
|
|
13
17
|
{ name: 'provider', type: 'string', description: 'DNS provider id. Inferred from the target domain when omitted.' },
|
|
14
18
|
{ name: 'domain', type: 'string', description: 'Target domain or base zone, for example app.example.com or example.com.' },
|
|
15
19
|
{ name: 'subdomain', type: 'string', description: 'Subdomain to add.' },
|
|
16
20
|
{ name: 'apex', type: 'boolean', description: 'Use the root/apex domain.' },
|
|
17
|
-
{ name: 'project', type: 'string', description: 'Vercel project id/name.' },
|
|
21
|
+
{ name: 'project', type: 'string', description: 'Vercel project id/name. Optional when project inference succeeds.' },
|
|
18
22
|
{ name: 'dry-run', type: 'boolean', description: 'Preview changes without writing.' },
|
|
19
23
|
{ name: 'force', type: 'boolean', description: 'Overwrite conflicting DNS records.' },
|
|
20
24
|
{ name: 'wait', type: 'boolean', description: 'Wait for DNS and Vercel verification.', default: true },
|
package/dist/lib/config.d.ts
CHANGED
package/dist/lib/flags.js
CHANGED
|
@@ -6,5 +6,5 @@ export const subdomainFlag = Flags.string({ description: 'Subdomain to add, for
|
|
|
6
6
|
export const apexFlag = Flags.boolean({ description: 'Use the root/apex domain instead of a subdomain.' });
|
|
7
7
|
export const projectFlag = Flags.string({
|
|
8
8
|
char: 'p',
|
|
9
|
-
description: 'Vercel project id/name.
|
|
9
|
+
description: 'Vercel project id/name. Inferred from env, config, .vercel/project.json, or package.json when omitted.',
|
|
10
10
|
});
|
|
@@ -11,6 +11,7 @@ export interface LinkDomainInput {
|
|
|
11
11
|
timeoutSeconds?: number;
|
|
12
12
|
progress?: LinkDomainProgressCallback;
|
|
13
13
|
}
|
|
14
|
+
export type LinkDomainProjectSource = 'config' | 'env' | 'flag' | 'packageJson' | 'vercelProjectFile';
|
|
14
15
|
export type LinkDomainProgressStage = 'dns:apply' | 'dns:plan' | 'dns:resolve-zone' | 'dns:wait' | 'vercel:add-domain' | 'vercel:get-domain' | 'vercel:get-target' | 'vercel:verify';
|
|
15
16
|
export interface LinkDomainProgress {
|
|
16
17
|
message: string;
|
|
@@ -21,6 +22,7 @@ export interface LinkDomainPlan {
|
|
|
21
22
|
provider: string;
|
|
22
23
|
providerInferred: boolean;
|
|
23
24
|
project: string;
|
|
25
|
+
projectSource: LinkDomainProjectSource;
|
|
24
26
|
recordName: string;
|
|
25
27
|
zoneDomain: string;
|
|
26
28
|
domain: string;
|
package/dist/lib/link-domain.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { resolve4, resolveCname, resolveTxt } from 'node:dns/promises';
|
|
2
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
3
|
+
import { dirname, join, parse } from 'node:path';
|
|
2
4
|
import { loadConfig } from './config.js';
|
|
3
5
|
import { DoomainError } from './errors.js';
|
|
4
6
|
import { detectLocalVercelProject } from './local-vercel.js';
|
|
@@ -19,13 +21,92 @@ async function resolveConfiguredDomain(domain) {
|
|
|
19
21
|
throw new DoomainError('MISSING_ARGUMENT', 'Domain is required. Use --domain or set a default domain.');
|
|
20
22
|
return resolved;
|
|
21
23
|
}
|
|
22
|
-
function
|
|
24
|
+
function findPackageProjectName(start = process.cwd()) {
|
|
25
|
+
let current = start;
|
|
26
|
+
const root = parse(start).root;
|
|
27
|
+
while (true) {
|
|
28
|
+
const packagePath = join(current, 'package.json');
|
|
29
|
+
if (existsSync(packagePath)) {
|
|
30
|
+
try {
|
|
31
|
+
const data = JSON.parse(readFileSync(packagePath, 'utf8'));
|
|
32
|
+
if (typeof data.name === 'string' && data.name.trim())
|
|
33
|
+
return data.name.trim();
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (current === root)
|
|
40
|
+
return undefined;
|
|
41
|
+
current = dirname(current);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function projectSuggestionScore(projectName, project) {
|
|
45
|
+
const target = projectName.toLowerCase();
|
|
46
|
+
const name = project.name.toLowerCase();
|
|
47
|
+
const id = project.id.toLowerCase();
|
|
48
|
+
if (name === target || id === target)
|
|
49
|
+
return 0;
|
|
50
|
+
if (name.startsWith(target))
|
|
51
|
+
return 1;
|
|
52
|
+
if (name.includes(target))
|
|
53
|
+
return 2;
|
|
54
|
+
if (target.includes(name))
|
|
55
|
+
return 3;
|
|
56
|
+
const targetParts = target.split(/[^a-z0-9]+/).filter(Boolean);
|
|
57
|
+
const matchingParts = targetParts.filter((part) => name.includes(part)).length;
|
|
58
|
+
return matchingParts > 0 ? 4 + (targetParts.length - matchingParts) : 99;
|
|
59
|
+
}
|
|
60
|
+
function projectSuggestions(projectName, projects) {
|
|
61
|
+
const seen = new Set();
|
|
62
|
+
return projects
|
|
63
|
+
.filter((project) => {
|
|
64
|
+
if (seen.has(project.id))
|
|
65
|
+
return false;
|
|
66
|
+
seen.add(project.id);
|
|
67
|
+
return true;
|
|
68
|
+
})
|
|
69
|
+
.map((project) => ({ project, score: projectSuggestionScore(projectName, project) }))
|
|
70
|
+
.filter(({ score }) => score < 99)
|
|
71
|
+
.sort((a, b) => a.score - b.score || a.project.name.localeCompare(b.project.name))
|
|
72
|
+
.slice(0, 5)
|
|
73
|
+
.map(({ project }) => ({ id: project.id, name: project.name }));
|
|
74
|
+
}
|
|
75
|
+
async function resolvePackageProject(projectName) {
|
|
76
|
+
const vercel = createVercelClient(await resolveVercelConfig());
|
|
77
|
+
const projects = await vercel.listProjects(projectName);
|
|
78
|
+
const match = projects.find((project) => project.name === projectName || project.id === projectName);
|
|
79
|
+
if (match)
|
|
80
|
+
return match.name;
|
|
81
|
+
const allProjects = await vercel.listProjects().catch(() => []);
|
|
82
|
+
const suggestions = projectSuggestions(projectName, [...projects, ...allProjects]);
|
|
83
|
+
throw new DoomainError('VERCEL_PROJECT_NOT_LINKED', `No Vercel project named ${projectName} was found. Pass --project to choose a project.`, {
|
|
84
|
+
project: projectName,
|
|
85
|
+
projectSource: 'packageJson',
|
|
86
|
+
suggestions,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
async function resolveProject(project) {
|
|
23
90
|
if (project)
|
|
24
|
-
return { project, localProjectDetected: false };
|
|
91
|
+
return { project, projectSource: 'flag', localProjectDetected: false };
|
|
92
|
+
const config = await loadConfig();
|
|
93
|
+
const envProject = process.env.DOOMAIN_PROJECT;
|
|
94
|
+
if (envProject)
|
|
95
|
+
return { project: envProject, projectSource: 'env', localProjectDetected: false };
|
|
96
|
+
if (config.defaults?.project)
|
|
97
|
+
return { project: config.defaults.project, projectSource: 'config', localProjectDetected: false };
|
|
25
98
|
const localProject = detectLocalVercelProject();
|
|
26
99
|
if (localProject)
|
|
27
|
-
return { project: localProject.projectId, localProjectDetected: true };
|
|
28
|
-
|
|
100
|
+
return { project: localProject.projectId, projectSource: 'vercelProjectFile', localProjectDetected: true };
|
|
101
|
+
const packageProject = findPackageProjectName();
|
|
102
|
+
if (packageProject) {
|
|
103
|
+
return {
|
|
104
|
+
project: await resolvePackageProject(packageProject),
|
|
105
|
+
projectSource: 'packageJson',
|
|
106
|
+
localProjectDetected: false,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
throw new DoomainError('VERCEL_PROJECT_NOT_LINKED', 'No Vercel project could be inferred. Run inside a project with package.json/.vercel/project.json or pass --project.');
|
|
29
110
|
}
|
|
30
111
|
async function resolveZone(provider, zoneDomain) {
|
|
31
112
|
const zone = await provider.getZone(zoneDomain);
|
|
@@ -312,7 +393,7 @@ function reportProgress(input, stage, message) {
|
|
|
312
393
|
input.progress?.({ message, stage });
|
|
313
394
|
}
|
|
314
395
|
export async function createLinkPlan(input) {
|
|
315
|
-
const project = resolveProject(input.project);
|
|
396
|
+
const project = await resolveProject(input.project);
|
|
316
397
|
const resolved = await resolveProviderTarget(input);
|
|
317
398
|
const { provider, providerInferred, target } = resolved;
|
|
318
399
|
const record = planBaseRecord({ isApex: target.isApex, provider, recordName: target.recordName });
|
|
@@ -320,6 +401,7 @@ export async function createLinkPlan(input) {
|
|
|
320
401
|
provider,
|
|
321
402
|
providerInferred,
|
|
322
403
|
project: project.project,
|
|
404
|
+
projectSource: project.projectSource,
|
|
323
405
|
recordName: target.recordName,
|
|
324
406
|
zoneDomain: target.zoneDomain,
|
|
325
407
|
domain: target.fullDomain,
|
package/oclif.manifest.json
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
},
|
|
12
12
|
"description": "Link a Vercel project to a domain and create DNS records.",
|
|
13
13
|
"examples": [
|
|
14
|
+
"<%= config.bin %> <%= command.id %> app.example.com --json",
|
|
14
15
|
"<%= config.bin %> <%= command.id %> app.example.com --project my-app --json",
|
|
15
16
|
"<%= config.bin %> <%= command.id %> --domain app.example.com --project my-app --json",
|
|
16
17
|
"<%= config.bin %> <%= command.id %> --domain example.com --subdomain app --project my-app",
|
|
@@ -50,7 +51,7 @@
|
|
|
50
51
|
},
|
|
51
52
|
"project": {
|
|
52
53
|
"char": "p",
|
|
53
|
-
"description": "Vercel project id/name.
|
|
54
|
+
"description": "Vercel project id/name. Inferred from env, config, .vercel/project.json, or package.json when omitted.",
|
|
54
55
|
"name": "project",
|
|
55
56
|
"hasDynamicHelp": false,
|
|
56
57
|
"multiple": false,
|
|
@@ -159,7 +160,7 @@
|
|
|
159
160
|
},
|
|
160
161
|
"project": {
|
|
161
162
|
"char": "p",
|
|
162
|
-
"description": "Vercel project id/name.
|
|
163
|
+
"description": "Vercel project id/name. Inferred from env, config, .vercel/project.json, or package.json when omitted.",
|
|
163
164
|
"name": "project",
|
|
164
165
|
"hasDynamicHelp": false,
|
|
165
166
|
"multiple": false,
|
|
@@ -300,6 +301,41 @@
|
|
|
300
301
|
"list.js"
|
|
301
302
|
]
|
|
302
303
|
},
|
|
304
|
+
"projects:list": {
|
|
305
|
+
"aliases": [],
|
|
306
|
+
"args": {},
|
|
307
|
+
"description": "List Vercel projects.",
|
|
308
|
+
"flags": {
|
|
309
|
+
"json": {
|
|
310
|
+
"description": "Output a single JSON object and never prompt.",
|
|
311
|
+
"name": "json",
|
|
312
|
+
"allowNo": false,
|
|
313
|
+
"type": "boolean"
|
|
314
|
+
},
|
|
315
|
+
"search": {
|
|
316
|
+
"description": "Filter projects by search term.",
|
|
317
|
+
"name": "search",
|
|
318
|
+
"hasDynamicHelp": false,
|
|
319
|
+
"multiple": false,
|
|
320
|
+
"type": "option"
|
|
321
|
+
}
|
|
322
|
+
},
|
|
323
|
+
"hasDynamicHelp": false,
|
|
324
|
+
"hiddenAliases": [],
|
|
325
|
+
"id": "projects:list",
|
|
326
|
+
"pluginAlias": "doomain",
|
|
327
|
+
"pluginName": "doomain",
|
|
328
|
+
"pluginType": "core",
|
|
329
|
+
"strict": true,
|
|
330
|
+
"enableJsonFlag": false,
|
|
331
|
+
"isESM": true,
|
|
332
|
+
"relativePath": [
|
|
333
|
+
"dist",
|
|
334
|
+
"commands",
|
|
335
|
+
"projects",
|
|
336
|
+
"list.js"
|
|
337
|
+
]
|
|
338
|
+
},
|
|
303
339
|
"providers:add": {
|
|
304
340
|
"aliases": [],
|
|
305
341
|
"args": {
|
|
@@ -560,41 +596,6 @@
|
|
|
560
596
|
"verify.js"
|
|
561
597
|
]
|
|
562
598
|
},
|
|
563
|
-
"projects:list": {
|
|
564
|
-
"aliases": [],
|
|
565
|
-
"args": {},
|
|
566
|
-
"description": "List Vercel projects.",
|
|
567
|
-
"flags": {
|
|
568
|
-
"json": {
|
|
569
|
-
"description": "Output a single JSON object and never prompt.",
|
|
570
|
-
"name": "json",
|
|
571
|
-
"allowNo": false,
|
|
572
|
-
"type": "boolean"
|
|
573
|
-
},
|
|
574
|
-
"search": {
|
|
575
|
-
"description": "Filter projects by search term.",
|
|
576
|
-
"name": "search",
|
|
577
|
-
"hasDynamicHelp": false,
|
|
578
|
-
"multiple": false,
|
|
579
|
-
"type": "option"
|
|
580
|
-
}
|
|
581
|
-
},
|
|
582
|
-
"hasDynamicHelp": false,
|
|
583
|
-
"hiddenAliases": [],
|
|
584
|
-
"id": "projects:list",
|
|
585
|
-
"pluginAlias": "doomain",
|
|
586
|
-
"pluginName": "doomain",
|
|
587
|
-
"pluginType": "core",
|
|
588
|
-
"strict": true,
|
|
589
|
-
"enableJsonFlag": false,
|
|
590
|
-
"isESM": true,
|
|
591
|
-
"relativePath": [
|
|
592
|
-
"dist",
|
|
593
|
-
"commands",
|
|
594
|
-
"projects",
|
|
595
|
-
"list.js"
|
|
596
|
-
]
|
|
597
|
-
},
|
|
598
599
|
"auth:logout:vercel": {
|
|
599
600
|
"aliases": [],
|
|
600
601
|
"args": {},
|
|
@@ -629,5 +630,5 @@
|
|
|
629
630
|
]
|
|
630
631
|
}
|
|
631
632
|
},
|
|
632
|
-
"version": "0.1.
|
|
633
|
+
"version": "0.1.7"
|
|
633
634
|
}
|