nukebase 2.5.0 → 2.5.1

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.
@@ -908,8 +908,9 @@ async function domainCommand() {
908
908
  if (args.has('wait')) {
909
909
  const ip = await regionIp();
910
910
  const needTxt = (await currentDomain()) !== sub; // already ours: proven when connected
911
- printDnsRecords(ip, needTxt);
912
- await waitForDns(sub, ip, needTxt);
911
+ const kind = domainKind(sub);
912
+ printDnsRecords(ip, needTxt, kind);
913
+ await waitForDns(sub, ip, needTxt, kind);
913
914
  }
914
915
  await connectDomain(sub, email);
915
916
  if (args.has('email-domain')) await emailDomainCommand(null);
@@ -939,8 +940,9 @@ async function domainCommand() {
939
940
  if (!/^[a-z0-9.-]+\.[a-z]{2,}$/.test(domain)) { console.log(` "${domain}" does not look like a domain.`); domain = ''; }
940
941
  }
941
942
 
942
- printDnsRecords(ip, true);
943
- await waitForDns(domain, ip, true);
943
+ const kind = domainKind(domain);
944
+ printDnsRecords(ip, true, kind);
945
+ await waitForDns(domain, ip, true, kind);
944
946
 
945
947
  // Step 3 — the server does the real verification and issues the cert.
946
948
  await connectDomain(domain, email);
@@ -1083,7 +1085,7 @@ async function connectDomain(domain, email) {
1083
1085
  });
1084
1086
  console.log(`\n✓ https://${r.domain} is live.`);
1085
1087
  if (r.wwwNote) console.log(` ${r.wwwNote}`);
1086
- else if (r.verified) console.log(' www redirects to the apex if its A record also points here.');
1088
+ else if (r.verified) console.log(' www redirects to the main domain.');
1087
1089
  }
1088
1090
 
1089
1091
  // This region's public IP, from /region-info. null when it cannot be read;
@@ -1099,31 +1101,47 @@ async function regionIp() {
1099
1101
  // The ownership record the server checks on a domain's first connection.
1100
1102
  const domainTxtValue = () => `nukebase-uid=${uid}`;
1101
1103
 
1102
- function printDnsRecords(ip, withTxt) {
1104
+ // A domain of its own (example.com) or a subdomain (app.example.com), and
1105
+ // what its record is called (@, or "app"). By the dots: two labels is a
1106
+ // domain, three or more a subdomain, except under a two-part ending
1107
+ // (example.co.uk is a domain). Only for the instructions; the server decides
1108
+ // at connect. Same rule as the dashboard's domainKind.
1109
+ const TWO_PART_ENDING = /^(co|com|net|org|gov|edu|ac|ltd|plc|sch)\.[a-z]{2}$/;
1110
+ function domainKind(domain) {
1111
+ const labels = domain.split('.');
1112
+ const base = labels.length >= 3 && TWO_PART_ENDING.test(labels.slice(-2).join('.')) ? 3 : 2;
1113
+ if (labels.length <= base) return { subdomain: false, zone: domain, name: '@' };
1114
+ return { subdomain: true, zone: labels.slice(-base).join('.'), name: labels.slice(0, -base).join('.') };
1115
+ }
1116
+
1117
+ function printDnsRecords(ip, withTxt, kind) {
1118
+ const sub = kind && kind.subdomain;
1119
+ const pad = (s) => s.padEnd(10);
1103
1120
  console.log('\n Step 1 — point it here. At your registrar (GoDaddy, Namecheap,');
1104
- console.log(' Cloudflare, ...) add these DNS records:\n');
1121
+ console.log(` Cloudflare, ...) add these DNS records${sub ? ` to ${kind.zone}` : ''}:\n`);
1105
1122
  console.log(' Type Name Value');
1106
- console.log(` A @ ${ip || "(this region's IP — see the dashboard)"}`);
1107
- console.log(` A www ${ip || '(same)'}`);
1123
+ console.log(` A ${pad(sub ? kind.name : '@')} ${ip || "(this region's IP — see the dashboard)"}`);
1124
+ if (!sub) console.log(` A www ${ip || '(same)'}`);
1108
1125
  if (withTxt) {
1109
1126
  console.log(` TXT nukebase ${domainTxtValue()}`);
1110
1127
  console.log('\n The TXT record proves the domain is yours, so no other account can');
1111
1128
  console.log(' connect it. One on your main domain covers its subdomains too.');
1112
1129
  }
1130
+ if (sub) console.log(`\n ${kind.name}.${kind.zone} is a subdomain, so no www record is needed.`);
1113
1131
  console.log('\n If you use Cloudflare, set the records to "DNS only" (grey cloud)');
1114
1132
  console.log(' so the certificate can be issued.\n');
1115
1133
  }
1116
1134
 
1117
1135
  // Step 2 — wait for DNS. Poll quietly; Enter forces a check; Ctrl+C leaves
1118
1136
  // the records in place and the command can be re-run later. Resolves once
1119
- // the apex points here; says so if www does not yet.
1137
+ // the apex and www point here (only the apex for a subdomain).
1120
1138
  //
1121
1139
  // Ask public resolvers directly, not the machine's. Two reasons: the OS
1122
1140
  // resolver caches, so a record changed a minute ago can look unchanged here
1123
1141
  // long after the server sees it — and on some Windows setups Node's own
1124
1142
  // resolver cannot reach the system DNS server at all (ECONNREFUSED on every
1125
1143
  // name). getaddrinfo is the fallback for a machine that blocks 1.1.1.1.
1126
- async function waitForDns(domain, ip, needTxt) {
1144
+ async function waitForDns(domain, ip, needTxt, kind) {
1127
1145
  console.log(' Step 2 — waiting for DNS. This can take a few minutes.');
1128
1146
  console.log(' Press Enter to check now, Ctrl+C to stop and come back later.\n');
1129
1147
  const dns = require('dns').promises;
@@ -1144,6 +1162,12 @@ async function waitForDns(domain, ip, needTxt) {
1144
1162
  return false;
1145
1163
  };
1146
1164
  let apexOk = false, wwwOk = false, txtOk = !needTxt;
1165
+ // The server connects a domain of its own with www or not at all (since
1166
+ // 2026-11); only a subdomain (no NS records of its own) goes without it.
1167
+ // By the dots, not "resolveNs says ENODATA": a subdomain whose record is
1168
+ // not added yet is NXDOMAIN, which read as a domain of its own and waited
1169
+ // for www.
1170
+ const subdomain = (kind || domainKind(domain)).subdomain;
1147
1171
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
1148
1172
  let poke = null;
1149
1173
  rl.on('line', () => { if (poke) poke(); });
@@ -1155,18 +1179,15 @@ async function waitForDns(domain, ip, needTxt) {
1155
1179
  wwwOk = ip ? www.includes(ip) : www.length > 0;
1156
1180
  txtOk = txt;
1157
1181
  const mark = ok => ok ? '✓' : '…';
1158
- process.stdout.write(`\r ${mark(apexOk)} ${domain} ${apex.length ? '→ ' + apex.join(', ') : '(no record yet)'} ${mark(wwwOk)} www ${www.length ? '→ ' + www.join(', ') : '(no record yet)'}${needTxt ? ` ${mark(txtOk)} TXT ${txtOk ? 'found' : '(not yet)'}` : ''} `);
1159
- if (apexOk && txtOk) break;
1182
+ const wwwPart = subdomain ? '' : ` ${mark(wwwOk)} www ${www.length ? '→ ' + www.join(', ') : '(no record yet)'}`;
1183
+ process.stdout.write(`\r ${mark(apexOk)} ${domain} ${apex.length ? '→ ' + apex.join(', ') : '(no record yet)'}${wwwPart}${needTxt ? ` ${mark(txtOk)} TXT ${txtOk ? 'found' : '(not yet)'}` : ''} `);
1184
+ if (apexOk && txtOk && (wwwOk || subdomain)) break;
1160
1185
  await wait(15000);
1161
1186
  }
1162
1187
  } finally {
1163
1188
  rl.close();
1164
1189
  }
1165
1190
  console.log('\n');
1166
- if (!wwwOk) {
1167
- console.log(' www is not pointing here yet — connecting the apex only. Re-run');
1168
- console.log(' `nukebase domain ' + domain + '` later to add the www redirect.\n');
1169
- }
1170
1191
  }
1171
1192
 
1172
1193
  // nukebase logs [--follow] the recent journal for the unit; --follow keeps
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nukebase",
3
- "version": "2.5.0",
3
+ "version": "2.5.1",
4
4
  "description": "NukeBase CLI: create, deploy and manage NukeBase projects",
5
5
  "bin": {
6
6
  "nukebase": "bin/nukebase.js"