@hasna/domains 0.0.17 → 0.0.19

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.
@@ -1,3 +1,4 @@
1
1
  import type { Command } from "commander";
2
+ export declare function assertInteractiveTty(): void;
2
3
  export declare function registerInteractiveCommand(program: Command): void;
3
4
  //# sourceMappingURL=interactive.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"interactive.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/interactive.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKzC,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAgBjE"}
1
+ {"version":3,"file":"interactive.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/interactive.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKzC,wBAAgB,oBAAoB,IAAI,IAAI,CAQ3C;AAED,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CASjE"}
@@ -1 +1 @@
1
- {"version":3,"file":"route53.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/route53.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAuBzC,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAogB9D"}
1
+ {"version":3,"file":"route53.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/route53.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAyBzC,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAmhB9D"}
package/dist/cli/index.js CHANGED
@@ -38079,9 +38079,47 @@ function registerServeCommand(program2) {
38079
38079
 
38080
38080
  // src/cli/commands/route53.ts
38081
38081
  init_route53();
38082
+ import { readFileSync as readFileSync6 } from "fs";
38083
+
38084
+ // src/lib/zone-setup.ts
38085
+ function nameserversMatch(a3, b3) {
38086
+ const norm = (ns) => new Set(ns.map((n3) => n3.trim().toLowerCase().replace(/\.$/, "")));
38087
+ const sa = norm(a3);
38088
+ const sb = norm(b3);
38089
+ if (sa.size !== sb.size)
38090
+ return false;
38091
+ for (const n3 of sa)
38092
+ if (!sb.has(n3))
38093
+ return false;
38094
+ return true;
38095
+ }
38096
+ async function setupDomainZone(domain, deps) {
38097
+ const log = deps.log ?? (() => {});
38098
+ const existing = await deps.findExistingZone(domain);
38099
+ let zone;
38100
+ let created;
38101
+ if (existing) {
38102
+ zone = existing;
38103
+ created = false;
38104
+ log("zone_reused", { domain, zoneId: zone.id });
38105
+ } else {
38106
+ zone = await deps.createZone(domain);
38107
+ created = true;
38108
+ log("zone_created", { domain, zoneId: zone.id });
38109
+ }
38110
+ let nsUpdated = false;
38111
+ const registrarNs = await deps.getRegistrarNs(domain);
38112
+ if (!nameserversMatch(registrarNs, zone.name_servers)) {
38113
+ await deps.setRegistrarNs(domain, zone.name_servers);
38114
+ nsUpdated = true;
38115
+ log("ns_aligned", { domain, zoneId: zone.id, nameservers: zone.name_servers });
38116
+ }
38117
+ return { zoneId: zone.id, nameServers: zone.name_servers, created, nsUpdated };
38118
+ }
38119
+
38120
+ // src/cli/commands/route53.ts
38082
38121
  init_domains();
38083
38122
  init_config();
38084
- import { readFileSync as readFileSync6 } from "fs";
38085
38123
  function registerRoute53Commands(program2) {
38086
38124
  const r53 = program2.command("r53").description("AWS Route 53 \u2014 domain purchase, hosted zones & DNS");
38087
38125
  r53.command("check <domains...>").description("Check if one or more domains are available for purchase").action(async (domains) => {
@@ -38429,46 +38467,55 @@ DNS Records for ${domain}:`);
38429
38467
  }
38430
38468
  const reg = await registerDomain(domain, contact, parseInt(opts.years));
38431
38469
  console.log(` \u2713 Submitted (operation: ${reg.operationId})`);
38432
- if (opts.wait) {
38433
- console.log(` Waiting for registration to complete...`);
38434
- let status = "IN_PROGRESS";
38435
- while (status === "IN_PROGRESS" || status === "SUBMITTED") {
38436
- await new Promise((r3) => setTimeout(r3, 1e4));
38437
- const s3 = await getRegistrationStatus(reg.operationId);
38438
- status = s3.status;
38439
- process.stdout.write(` Status: ${status}\r`);
38440
- }
38441
- console.log();
38442
- if (status !== "SUCCESSFUL") {
38443
- console.error(`\u2717 Registration ${status}`);
38444
- process.exit(1);
38445
- }
38446
- console.log(` \u2713 Registration complete`);
38470
+ console.log(` Waiting for registration to complete...`);
38471
+ let status = "IN_PROGRESS";
38472
+ while (status === "IN_PROGRESS" || status === "SUBMITTED") {
38473
+ await new Promise((r3) => setTimeout(r3, 1e4));
38474
+ const s3 = await getRegistrationStatus(reg.operationId);
38475
+ status = s3.status;
38476
+ process.stdout.write(` Status: ${status}\r`);
38447
38477
  }
38448
- console.log(`[3/4] Creating hosted zone...`);
38449
- const zone = await createHostedZone(domain, `Managed by @hasna/domains`);
38450
- console.log(` \u2713 Zone created (${zone.id})`);
38478
+ console.log();
38479
+ if (status !== "SUCCESSFUL") {
38480
+ console.error(`\u2717 Registration ${status}`);
38481
+ process.exit(1);
38482
+ }
38483
+ console.log(` \u2713 Registration complete`);
38484
+ console.log(`[3/4] Configuring hosted zone...`);
38485
+ const setup = await setupDomainZone(domain, {
38486
+ findExistingZone: async (d3) => {
38487
+ const z2 = await findHostedZoneByDomain(d3);
38488
+ if (!z2)
38489
+ return null;
38490
+ const full = await getHostedZone(z2.id);
38491
+ return { id: full.id, name_servers: full.name_servers };
38492
+ },
38493
+ createZone: async (d3) => {
38494
+ const z2 = await createHostedZone(d3, `Managed by @hasna/domains`);
38495
+ return { id: z2.id, name_servers: z2.name_servers ?? [] };
38496
+ },
38497
+ getRegistrarNs: async (d3) => (await getDomainDetail(d3)).nameservers,
38498
+ setRegistrarNs: async (d3, ns) => {
38499
+ await updateNameservers(d3, ns);
38500
+ }
38501
+ });
38502
+ console.log(` \u2713 Zone ${setup.created ? "created" : "reused"} (${setup.zoneId})` + (setup.nsUpdated ? ` \u2014 registry NS repointed to this zone` : ``));
38451
38503
  console.log(`[4/4] Adding to local database...`);
38452
38504
  createDomain({
38453
38505
  name: domain,
38454
38506
  registrar: "AWS Route 53",
38455
38507
  status: "active",
38456
38508
  auto_renew: true,
38457
- nameservers: zone.name_servers
38509
+ nameservers: setup.nameServers
38458
38510
  });
38459
38511
  console.log(` \u2713 Added to portfolio`);
38460
38512
  console.log(`
38461
38513
  \u2713 Full setup complete for ${domain}`);
38462
- if (!opts.wait) {
38463
- console.log(` Registration may take a few minutes.`);
38464
- console.log(` \u26A0 Hosted zone created before registration confirmed.`);
38465
- console.log(` If registration fails, clean up: domains r53 zone-delete ${zone.id} --force`);
38466
- }
38467
38514
  console.log(` Check: domains r53 status ${reg.operationId}`);
38468
- if (zone.name_servers && zone.name_servers.length > 0) {
38515
+ if (setup.nameServers && setup.nameServers.length > 0) {
38469
38516
  console.log(`
38470
38517
  Name servers:`);
38471
- for (const ns of zone.name_servers) {
38518
+ for (const ns of setup.nameServers) {
38472
38519
  console.log(` ${ns}`);
38473
38520
  }
38474
38521
  }
@@ -39975,8 +40022,13 @@ function formatDate(iso) {
39975
40022
  function daysUntil(iso) {
39976
40023
  if (!iso)
39977
40024
  return "\u2014";
39978
- const ms = new Date(iso).getTime() - Date.now();
40025
+ const parsed = new Date(iso).getTime();
40026
+ if (!Number.isFinite(parsed))
40027
+ return "\u2014";
40028
+ const ms = parsed - Date.now();
39979
40029
  const days = Math.ceil(ms / (1000 * 60 * 60 * 24));
40030
+ if (!Number.isFinite(days))
40031
+ return "\u2014";
39980
40032
  if (days < 0)
39981
40033
  return "expired";
39982
40034
  if (days === 0)
@@ -40010,6 +40062,20 @@ function filterLabel(filter) {
40010
40062
  return "Premium";
40011
40063
  }
40012
40064
  }
40065
+ function resolveInitialFilter(initialStatus) {
40066
+ if (initialStatus === "active")
40067
+ return "active";
40068
+ if (initialStatus === "premium")
40069
+ return "premium";
40070
+ if (initialStatus === "expiring")
40071
+ return "expiring";
40072
+ return "all";
40073
+ }
40074
+ function clampSelectedIndex(index, length) {
40075
+ if (length <= 0)
40076
+ return 0;
40077
+ return Math.max(0, Math.min(index, length - 1));
40078
+ }
40013
40079
 
40014
40080
  // src/cli/tui/Header.tsx
40015
40081
  import { jsxDEV } from "react/jsx-dev-runtime";
@@ -40054,18 +40120,24 @@ function Header({ count, filter, view }) {
40054
40120
  import { Box as Box2, Text as Text2 } from "ink";
40055
40121
  import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
40056
40122
  var VISIBLE_ROWS = 16;
40057
- function DomainTable({ domains, selectedIndex, compact = false }) {
40123
+ function DomainTable({
40124
+ domains,
40125
+ selectedIndex,
40126
+ compact = false,
40127
+ emptyMessage = "No domains match this filter. Press [f] to change filter or [r] to refresh."
40128
+ }) {
40058
40129
  if (domains.length === 0) {
40059
40130
  return /* @__PURE__ */ jsxDEV2(Box2, {
40060
40131
  flexDirection: "column",
40061
40132
  paddingLeft: compact ? 0 : 1,
40062
40133
  children: /* @__PURE__ */ jsxDEV2(Text2, {
40063
40134
  dimColor: true,
40064
- children: "No domains match this filter. Press [f] to change filter or [r] to refresh."
40135
+ children: emptyMessage
40065
40136
  }, undefined, false, undefined, this)
40066
40137
  }, undefined, false, undefined, this);
40067
40138
  }
40068
- const start = Math.max(0, Math.min(selectedIndex - Math.floor(VISIBLE_ROWS / 2), Math.max(0, domains.length - VISIBLE_ROWS)));
40139
+ const safeSelectedIndex = clampSelectedIndex(selectedIndex, domains.length);
40140
+ const start = Math.max(0, Math.min(safeSelectedIndex - Math.floor(VISIBLE_ROWS / 2), Math.max(0, domains.length - VISIBLE_ROWS)));
40069
40141
  const visible = domains.slice(start, start + VISIBLE_ROWS);
40070
40142
  return /* @__PURE__ */ jsxDEV2(Box2, {
40071
40143
  flexDirection: "column",
@@ -40099,7 +40171,7 @@ function DomainTable({ domains, selectedIndex, compact = false }) {
40099
40171
  }, undefined, true, undefined, this),
40100
40172
  visible.map((domain, offset) => {
40101
40173
  const index = start + offset;
40102
- const selected = index === selectedIndex;
40174
+ const selected = index === safeSelectedIndex;
40103
40175
  const statusColor = STATUS_COLORS[domain.status] ?? "white";
40104
40176
  return /* @__PURE__ */ jsxDEV2(Box2, {
40105
40177
  children: [
@@ -40362,6 +40434,7 @@ function SearchView({
40362
40434
  onSelect(results[selectedIndex]);
40363
40435
  }
40364
40436
  });
40437
+ const emptyMessage = query.trim().length < 2 ? "Type at least 2 characters to search." : "No matching domains. Refine your query or press [esc] to go back.";
40365
40438
  return /* @__PURE__ */ jsxDEV4(Box4, {
40366
40439
  flexDirection: "column",
40367
40440
  children: [
@@ -40391,13 +40464,14 @@ function SearchView({
40391
40464
  }, undefined, true, undefined, this),
40392
40465
  /* @__PURE__ */ jsxDEV4(Text4, {
40393
40466
  dimColor: true,
40394
- children: "[enter] open \xB7 [esc] back"
40467
+ children: "[enter] open \xB7 [esc] back \xB7 [q] quit \xB7 \u2191\u2193 navigate results"
40395
40468
  }, undefined, false, undefined, this),
40396
40469
  /* @__PURE__ */ jsxDEV4(Box4, {
40397
40470
  marginTop: 1,
40398
40471
  children: /* @__PURE__ */ jsxDEV4(DomainTable, {
40399
40472
  domains: results,
40400
- selectedIndex
40473
+ selectedIndex,
40474
+ emptyMessage
40401
40475
  }, undefined, false, undefined, this)
40402
40476
  }, undefined, false, undefined, this)
40403
40477
  ]
@@ -40421,7 +40495,7 @@ function loadDomainsForFilter(activeFilter) {
40421
40495
  function App({ initialStatus }) {
40422
40496
  const { exit } = useApp();
40423
40497
  const [view, setView] = useState2("list");
40424
- const initialFilter = initialStatus === "active" ? "active" : initialStatus === "premium" ? "premium" : "all";
40498
+ const initialFilter = resolveInitialFilter(initialStatus);
40425
40499
  const [filter, setFilter] = useState2(initialFilter);
40426
40500
  const [selectedIndex, setSelectedIndex] = useState2(0);
40427
40501
  const [details, setDetails] = useState2(null);
@@ -40431,13 +40505,13 @@ function App({ initialStatus }) {
40431
40505
  const refresh = useCallback(() => {
40432
40506
  const next = loadDomainsForFilter(filter);
40433
40507
  setDomains(next);
40434
- setSelectedIndex((current) => Math.min(current, Math.max(0, next.length - 1)));
40508
+ setSelectedIndex((current) => clampSelectedIndex(current, next.length));
40435
40509
  return next;
40436
40510
  }, [filter]);
40437
40511
  useEffect(() => {
40438
40512
  refresh();
40439
40513
  }, [refresh]);
40440
- const selectedDomain = domains[selectedIndex] ?? null;
40514
+ const selectedDomain = domains[clampSelectedIndex(selectedIndex, domains.length)] ?? null;
40441
40515
  const previewDetails = useMemo(() => {
40442
40516
  if (!selectedDomain)
40443
40517
  return null;
@@ -40449,15 +40523,17 @@ function App({ initialStatus }) {
40449
40523
  return listDnsRecords(selectedDomain.id);
40450
40524
  }, [selectedDomain]);
40451
40525
  useInput2((input, key) => {
40452
- if (input === "q" && view !== "search") {
40526
+ if (input === "q") {
40453
40527
  exit();
40454
40528
  return;
40455
40529
  }
40456
40530
  if (view === "list") {
40457
40531
  if (key.upArrow || input === "k") {
40458
- setSelectedIndex((index) => Math.max(0, index - 1));
40532
+ setSelectedIndex((index) => clampSelectedIndex(index - 1, domains.length));
40459
40533
  } else if (key.downArrow || input === "j") {
40460
- setSelectedIndex((index) => Math.min(domains.length - 1, index + 1));
40534
+ if (domains.length === 0)
40535
+ return;
40536
+ setSelectedIndex((index) => clampSelectedIndex(index + 1, domains.length));
40461
40537
  } else if (key.return && selectedDomain) {
40462
40538
  const full = getDomainDetails(selectedDomain.id);
40463
40539
  if (full) {
@@ -40473,16 +40549,19 @@ function App({ initialStatus }) {
40473
40549
  const index = DOMAIN_FILTERS.indexOf(current);
40474
40550
  return DOMAIN_FILTERS[(index + 1) % DOMAIN_FILTERS.length];
40475
40551
  });
40552
+ setSelectedIndex(0);
40476
40553
  } else if (input === "r") {
40477
40554
  refresh();
40478
40555
  }
40479
40556
  return;
40480
40557
  }
40481
40558
  if (view === "search") {
40482
- if (key.upArrow || input === "k") {
40483
- setSearchIndex((index) => Math.max(0, index - 1));
40484
- } else if (key.downArrow || input === "j") {
40485
- setSearchIndex((index) => Math.min(searchResults.length - 1, index + 1));
40559
+ if (key.upArrow) {
40560
+ setSearchIndex((index) => clampSelectedIndex(index - 1, searchResults.length));
40561
+ } else if (key.downArrow) {
40562
+ if (searchResults.length === 0)
40563
+ return;
40564
+ setSearchIndex((index) => clampSelectedIndex(index + 1, searchResults.length));
40486
40565
  }
40487
40566
  return;
40488
40567
  }
@@ -40514,6 +40593,7 @@ function App({ initialStatus }) {
40514
40593
  }
40515
40594
  }, []);
40516
40595
  const detailDns = details ? listDnsRecords(details.domain.id) : [];
40596
+ const listSelectedIndex = clampSelectedIndex(selectedIndex, domains.length);
40517
40597
  return /* @__PURE__ */ jsxDEV5(Box5, {
40518
40598
  flexDirection: "column",
40519
40599
  padding: 1,
@@ -40528,7 +40608,7 @@ function App({ initialStatus }) {
40528
40608
  children: [
40529
40609
  /* @__PURE__ */ jsxDEV5(DomainTable, {
40530
40610
  domains,
40531
- selectedIndex
40611
+ selectedIndex: listSelectedIndex
40532
40612
  }, undefined, false, undefined, this),
40533
40613
  previewDetails && /* @__PURE__ */ jsxDEV5(Box5, {
40534
40614
  marginTop: 1,
@@ -40561,7 +40641,7 @@ function App({ initialStatus }) {
40561
40641
  }, undefined, false, undefined, this),
40562
40642
  view === "search" && /* @__PURE__ */ jsxDEV5(SearchView, {
40563
40643
  results: searchResults,
40564
- selectedIndex: searchIndex,
40644
+ selectedIndex: clampSelectedIndex(searchIndex, searchResults.length),
40565
40645
  onSearch: handleSearch,
40566
40646
  onSelect: handleSearchSelect,
40567
40647
  onBack: () => setView("list")
@@ -40572,13 +40652,16 @@ function App({ initialStatus }) {
40572
40652
 
40573
40653
  // src/cli/commands/interactive.tsx
40574
40654
  import { jsxDEV as jsxDEV6 } from "react/jsx-dev-runtime";
40655
+ function assertInteractiveTty() {
40656
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
40657
+ console.error(chalk.red("Interactive mode requires a TTY terminal."));
40658
+ console.error(chalk.dim("Use `domains domain list` or `domains domain get <name>` for non-interactive use."));
40659
+ process.exit(1);
40660
+ }
40661
+ }
40575
40662
  function registerInteractiveCommand(program2) {
40576
- program2.command("interactive").description("Launch interactive domain portfolio browser (Ink TUI)").option("--status <status>", "Initial filter status (e.g. active, premium)").action((opts) => {
40577
- if (!process.stdin.isTTY || !process.stdout.isTTY) {
40578
- console.error(chalk.red("Interactive mode requires a TTY terminal."));
40579
- console.error(chalk.dim("Use `domains domain list` or `domains domain get <name>` for non-interactive use."));
40580
- process.exit(1);
40581
- }
40663
+ program2.command("interactive").description("Launch interactive domain portfolio browser (Ink TUI)").option("--status <status>", "Initial filter status (e.g. active, premium, expiring)").action((opts) => {
40664
+ assertInteractiveTty();
40582
40665
  render(/* @__PURE__ */ jsxDEV6(App, {
40583
40666
  initialStatus: opts.status
40584
40667
  }, undefined, false, undefined, this));
@@ -1 +1 @@
1
- {"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../../src/cli/tui/App.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAoD,MAAM,OAAO,CAAC;AAmBzE,MAAM,WAAW,QAAQ;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAeD,wBAAgB,GAAG,CAAC,EAAE,aAAa,EAAE,EAAE,QAAQ,qBAqJ9C"}
1
+ {"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../../src/cli/tui/App.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAoD,MAAM,OAAO,CAAC;AAmBzE,MAAM,WAAW,QAAQ;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAeD,wBAAgB,GAAG,CAAC,EAAE,aAAa,EAAE,EAAE,QAAQ,qBAoJ9C"}
@@ -4,7 +4,8 @@ interface DomainTableProps {
4
4
  domains: Domain[];
5
5
  selectedIndex: number;
6
6
  compact?: boolean;
7
+ emptyMessage?: string;
7
8
  }
8
- export declare function DomainTable({ domains, selectedIndex, compact }: DomainTableProps): React.JSX.Element;
9
+ export declare function DomainTable({ domains, selectedIndex, compact, emptyMessage, }: DomainTableProps): React.JSX.Element;
9
10
  export {};
10
11
  //# sourceMappingURL=DomainTable.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"DomainTable.d.ts","sourceRoot":"","sources":["../../../src/cli/tui/DomainTable.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAWlD,UAAU,gBAAgB;IACxB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,wBAAgB,WAAW,CAAC,EAAE,OAAO,EAAE,aAAa,EAAE,OAAe,EAAE,EAAE,gBAAgB,qBA2DxF"}
1
+ {"version":3,"file":"DomainTable.d.ts","sourceRoot":"","sources":["../../../src/cli/tui/DomainTable.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAYlD,UAAU,gBAAgB;IACxB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,wBAAgB,WAAW,CAAC,EAC1B,OAAO,EACP,aAAa,EACb,OAAe,EACf,YAA4F,GAC7F,EAAE,gBAAgB,qBA6DlB"}
@@ -1 +1 @@
1
- {"version":3,"file":"SearchView.d.ts","sourceRoot":"","sources":["../../../src/cli/tui/SearchView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAGxC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAGlD,UAAU,eAAe;IACvB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB;AAED,wBAAgB,UAAU,CAAC,EACzB,OAAO,EACP,aAAa,EACb,QAAQ,EACR,QAAQ,EACR,MAAM,GACP,EAAE,eAAe,qBAgCjB"}
1
+ {"version":3,"file":"SearchView.d.ts","sourceRoot":"","sources":["../../../src/cli/tui/SearchView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAGxC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAGlD,UAAU,eAAe;IACvB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB;AAED,wBAAgB,UAAU,CAAC,EACzB,OAAO,EACP,aAAa,EACb,QAAQ,EACR,QAAQ,EACR,MAAM,GACP,EAAE,eAAe,qBAyCjB"}
@@ -14,4 +14,6 @@ export declare function stripAnsi(value: string | undefined): string;
14
14
  export type DomainFilter = "all" | "active" | "expiring" | "premium";
15
15
  export declare const DOMAIN_FILTERS: DomainFilter[];
16
16
  export declare function filterLabel(filter: DomainFilter): string;
17
+ export declare function resolveInitialFilter(initialStatus?: string): DomainFilter;
18
+ export declare function clampSelectedIndex(index: number, length: number): number;
17
19
  //# sourceMappingURL=format.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../../src/cli/tui/format.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEhE,eAAO,MAAM,UAAU;;;;;CAKb,CAAC;AAEX,wBAAgB,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAGxD;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAGjE;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAOhE;AAED,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,YAAY,EAAE,MAAM,CAatD,CAAC;AAEF,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAErD;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAE3D;AAED,MAAM,MAAM,YAAY,GAAG,KAAK,GAAG,QAAQ,GAAG,UAAU,GAAG,SAAS,CAAC;AAErE,eAAO,MAAM,cAAc,EAAE,YAAY,EAA6C,CAAC;AAEvF,wBAAgB,WAAW,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAWxD"}
1
+ {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../../src/cli/tui/format.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEhE,eAAO,MAAM,UAAU;;;;;CAKb,CAAC;AAEX,wBAAgB,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAGxD;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAGjE;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAUhE;AAED,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,YAAY,EAAE,MAAM,CAatD,CAAC;AAEF,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAErD;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAE3D;AAED,MAAM,MAAM,YAAY,GAAG,KAAK,GAAG,QAAQ,GAAG,UAAU,GAAG,SAAS,CAAC;AAErE,eAAO,MAAM,cAAc,EAAE,YAAY,EAA6C,CAAC;AAEvF,wBAAgB,WAAW,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAWxD;AAED,wBAAgB,oBAAoB,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,YAAY,CAKzE;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAGxE"}
@@ -0,0 +1,2 @@
1
+ export declare const tuiTestTempDir: string;
2
+ //# sourceMappingURL=tui-test-setup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tui-test-setup.d.ts","sourceRoot":"","sources":["../../../src/cli/tui/tui-test-setup.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,cAAc,QAAU,CAAC"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Hosted-zone setup for a freshly registered domain.
3
+ *
4
+ * Route 53 AUTO-CREATES a hosted zone when a domain is registered, and the
5
+ * registry delegation already points at it. Blindly calling CreateHostedZone
6
+ * afterwards produces a SECOND zone with a different delegation set, leaving the
7
+ * domain delegated to the (now orphaned) auto zone — DNS records added to the
8
+ * new zone never resolve. This module reuses the existing zone when present and
9
+ * only ever creates one if none exists, then reconciles the registry NS to the
10
+ * managed zone so delegation is always correct.
11
+ *
12
+ * Dependency-injected (mirrors reconcileNsDrift) so the decision logic is unit
13
+ * testable without hitting AWS.
14
+ */
15
+ /** Order/case/trailing-dot-insensitive set comparison of two NS lists. */
16
+ export declare function nameserversMatch(a: string[], b: string[]): boolean;
17
+ export interface ZoneSetupDeps {
18
+ /** Find an existing hosted zone for the domain (including its NS), or null. */
19
+ findExistingZone: (domain: string) => Promise<{
20
+ id: string;
21
+ name_servers: string[];
22
+ } | null>;
23
+ /** Create a new hosted zone for the domain and return it (with NS). */
24
+ createZone: (domain: string) => Promise<{
25
+ id: string;
26
+ name_servers: string[];
27
+ }>;
28
+ /** Current registry/registrar nameservers for the domain. */
29
+ getRegistrarNs: (domain: string) => Promise<string[]>;
30
+ /** Point the registry/registrar delegation at the given nameservers. */
31
+ setRegistrarNs: (domain: string, ns: string[]) => Promise<void>;
32
+ log?: (event: string, detail: Record<string, unknown>) => void;
33
+ }
34
+ export interface ZoneSetupResult {
35
+ zoneId: string;
36
+ nameServers: string[];
37
+ /** true if a new zone was created, false if an existing one was reused. */
38
+ created: boolean;
39
+ /** true if the registry delegation had to be repointed at the managed zone. */
40
+ nsUpdated: boolean;
41
+ }
42
+ export declare function setupDomainZone(domain: string, deps: ZoneSetupDeps): Promise<ZoneSetupResult>;
43
+ //# sourceMappingURL=zone-setup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zone-setup.d.ts","sourceRoot":"","sources":["../../src/lib/zone-setup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,0EAA0E;AAC1E,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAQlE;AAED,MAAM,WAAW,aAAa;IAC5B,+EAA+E;IAC/E,gBAAgB,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,EAAE,CAAA;KAAE,GAAG,IAAI,CAAC,CAAC;IAC7F,uEAAuE;IACvE,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;IAChF,6DAA6D;IAC7D,cAAc,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACtD,wEAAwE;IACxE,cAAc,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAChE,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;CAChE;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,2EAA2E;IAC3E,OAAO,EAAE,OAAO,CAAC;IACjB,+EAA+E;IAC/E,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,wBAAsB,eAAe,CACnC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,aAAa,GAClB,OAAO,CAAC,eAAe,CAAC,CA0B1B"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/domains",
3
- "version": "0.0.17",
3
+ "version": "0.0.19",
4
4
  "description": "Domain portfolio and DNS management for AI agents — CLI + MCP server with SQLite",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",