diggy 1.0.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) Roman Ožana <roman@ozana.cz> (https://ozana.cz/)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,2 @@
1
+ import type { AnyDNSRecord, BuildInDNSResolver, DNSResolver } from "./types";
2
+ export declare function getDnsRecords(host: string, type?: string, resolver?: string | BuildInDNSResolver | DNSResolver): Promise<AnyDNSRecord[]>;
@@ -0,0 +1,10 @@
1
+ import { getResolver } from "./get-resolver";
2
+ import { resolveAllRecords } from "./utils/resolve-all-records";
3
+ import { toDnsType } from "./utils/to-dns-type";
4
+ export function getDnsRecords(host, type, resolver) {
5
+ const dnsResolver = getResolver(resolver);
6
+ const dnsType = toDnsType(type);
7
+ return dnsType
8
+ ? dnsResolver(host, dnsType)
9
+ : resolveAllRecords(host, dnsResolver);
10
+ }
@@ -0,0 +1,2 @@
1
+ import type { BuildInDNSResolver, DNSResolver } from "./types";
2
+ export declare function getResolver(resolver?: string | BuildInDNSResolver | DNSResolver): DNSResolver;
@@ -0,0 +1,18 @@
1
+ import { digResolver } from "./resolvers/dig-resolver";
2
+ import { dohResolver } from "./resolvers/doh-resolver";
3
+ import { nodeResolver } from "./resolvers/node-resolver";
4
+ const resolvers = {
5
+ nodejs: nodeResolver(),
6
+ dig: digResolver(),
7
+ google: dohResolver("https://dns.google/resolve"),
8
+ cloudflare: dohResolver("https://cloudflare-dns.com/dns-query"),
9
+ };
10
+ export function getResolver(resolver) {
11
+ if (typeof resolver === "function") {
12
+ return resolver;
13
+ }
14
+ if (typeof resolver === "string" && resolver in resolvers) {
15
+ return resolvers[resolver];
16
+ }
17
+ return resolvers.google;
18
+ }
@@ -0,0 +1,3 @@
1
+ export * from "./get-dns-records";
2
+ export * from "./get-resolver";
3
+ export * from "./types";
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export * from "./get-dns-records";
2
+ export * from "./get-resolver";
3
+ export * from "./types";
@@ -0,0 +1,2 @@
1
+ import type { DNSResolver } from "../types";
2
+ export declare function digResolver(server?: string): DNSResolver;
@@ -0,0 +1,32 @@
1
+ import { toDnsRecord } from "../utils/to-dns-record";
2
+ export function digResolver(server) {
3
+ return async (host, type) => {
4
+ const args = [];
5
+ if (server) {
6
+ if (!server.startsWith("@")) {
7
+ server = `@${server}`;
8
+ }
9
+ args.push(server);
10
+ }
11
+ args.push(host, type);
12
+ const { spawnSync } = await import("node:child_process");
13
+ // Use spawnSync to run the dig command synchronously and capture the output
14
+ const dig = spawnSync("dig", [...args, "+noall", "+answer", "+cdflag"]);
15
+ const results = dig.stdout.toString();
16
+ const records = [];
17
+ // split lines & ignore comments or empty lines
18
+ results
19
+ .split("\n")
20
+ .filter((line) => line.length && !line.startsWith(";"))
21
+ .forEach((line) => {
22
+ const parts = line.replace(/\t+/g, " ").split(" ");
23
+ records.push(toDnsRecord({
24
+ name: parts[0] ?? "",
25
+ ttl: Number(parts[1]),
26
+ type: parts[3],
27
+ data: parts.slice(4).join(" "),
28
+ }));
29
+ });
30
+ return records;
31
+ };
32
+ }
@@ -0,0 +1,2 @@
1
+ import type { DNSResolver } from "../types";
2
+ export declare function dohResolver(url: string): DNSResolver;
@@ -0,0 +1,42 @@
1
+ import { toDnsRecord } from "../utils/to-dns-record";
2
+ const DNSTypeNumbers = new Map([
3
+ [1, "A"],
4
+ [2, "NS"],
5
+ [5, "CNAME"],
6
+ [6, "SOA"],
7
+ [12, "PTR"],
8
+ [15, "MX"],
9
+ [16, "TXT"],
10
+ [24, "SIG"],
11
+ [25, "KEY"],
12
+ [28, "AAAA"],
13
+ [33, "SRV"],
14
+ [35, "NAPTR"],
15
+ [43, "DS"],
16
+ [48, "DNSKEY"],
17
+ [257, "CAA"],
18
+ ]);
19
+ export function dohResolver(url) {
20
+ const dnsUrl = new URL(url);
21
+ return async (host, type) => {
22
+ dnsUrl.searchParams.set("name", host);
23
+ dnsUrl.searchParams.set("type", type);
24
+ const re = await fetch(dnsUrl, {
25
+ headers: { accept: "application/dns-json" },
26
+ });
27
+ if (!re.ok) {
28
+ throw new Error(`Error fetching DNS records for ${host}: ${re.status} ${re.statusText}`);
29
+ }
30
+ const json = (await re.json());
31
+ if (!Array.isArray(json.Answer))
32
+ return []; // No records found
33
+ return json.Answer.map((record) => {
34
+ return toDnsRecord({
35
+ name: record.name,
36
+ type: DNSTypeNumbers.get(record.type) ?? "UNKNOWN",
37
+ ttl: record.TTL,
38
+ data: record.data,
39
+ });
40
+ });
41
+ };
42
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ export {};
2
+ // TODO: write a DNS Over TLS resolver :)
@@ -0,0 +1,2 @@
1
+ import { type DNSResolver } from "../types";
2
+ export declare function nodeResolver(servers?: string[]): DNSResolver;
@@ -0,0 +1,45 @@
1
+ import * as _dns from "node:dns/promises";
2
+ import { DNSRecordType } from "../types";
3
+ export function nodeResolver(servers = []) {
4
+ const dns = new _dns.Resolver();
5
+ if (servers.length > 0) {
6
+ dns.setServers(servers);
7
+ }
8
+ return async (host, type) => {
9
+ try {
10
+ // Handle special cases for A and AAAA records to include TTL
11
+ if (type === DNSRecordType.A || type === DNSRecordType.AAAA) {
12
+ const records = type === DNSRecordType.A
13
+ ? await dns.resolve4(host, { ttl: true })
14
+ : await dns.resolve6(host, { ttl: true });
15
+ return records.map(({ address, ttl }) => ({
16
+ name: host,
17
+ type: type,
18
+ ttl: ttl,
19
+ data: address,
20
+ }));
21
+ }
22
+ // Handle SOA (is not an array)
23
+ if (type === DNSRecordType.SOA) {
24
+ const record = await dns.resolveSoa(host);
25
+ return [
26
+ {
27
+ name: host,
28
+ type: type,
29
+ data: record,
30
+ },
31
+ ];
32
+ }
33
+ // Handle CAA, MX, NAPTR, NS, PTR, SRV, TXT (all return arrays)
34
+ const records = (await dns.resolve(host, type));
35
+ return records.map((record) => ({
36
+ name: host,
37
+ type: type,
38
+ data: Array.isArray(record) ? record.join(" ") : record,
39
+ }));
40
+ }
41
+ catch {
42
+ return [];
43
+ }
44
+ };
45
+ }
@@ -0,0 +1,54 @@
1
+ export type DNSResolver = (host: string, type: DNSRecordType) => Promise<AnyDNSRecord[]>;
2
+ export type BuildInDNSResolver = "nodejs" | "dig" | "google" | "cloudflare";
3
+ export declare enum DNSRecordType {
4
+ A = "A",
5
+ AAAA = "AAAA",
6
+ CAA = "CAA",
7
+ CNAME = "CNAME",
8
+ NAPTR = "NAPTR",
9
+ MX = "MX",
10
+ NS = "NS",
11
+ PTR = "PTR",
12
+ SOA = "SOA",
13
+ SRV = "SRV",
14
+ TXT = "TXT"
15
+ }
16
+ export type MxRecordData = {
17
+ exchange: string;
18
+ priority: number;
19
+ };
20
+ export type SoaRecordData = {
21
+ nsname: string;
22
+ hostmaster: string;
23
+ serial: number;
24
+ refresh: number;
25
+ retry: number;
26
+ expire: number;
27
+ minttl: number;
28
+ };
29
+ export type CaaRecordData = {
30
+ flags: number;
31
+ tag: string;
32
+ value: string;
33
+ };
34
+ export type NaptrRecordData = {
35
+ order: number;
36
+ preference: number;
37
+ flags: string;
38
+ service: string;
39
+ regexp: string;
40
+ replacement: string;
41
+ };
42
+ export type SrvRecordData = {
43
+ priority: number;
44
+ weight: number;
45
+ port: number;
46
+ name: string;
47
+ };
48
+ export type DNSRecord<T = string | string[]> = {
49
+ name: string;
50
+ type: DNSRecordType;
51
+ ttl?: number;
52
+ data: T;
53
+ };
54
+ export type AnyDNSRecord = DNSRecord | DNSRecord<MxRecordData> | DNSRecord<SoaRecordData> | DNSRecord<CaaRecordData> | DNSRecord<NaptrRecordData> | DNSRecord<SrvRecordData>;
package/dist/types.js ADDED
@@ -0,0 +1,14 @@
1
+ export var DNSRecordType;
2
+ (function (DNSRecordType) {
3
+ DNSRecordType["A"] = "A";
4
+ DNSRecordType["AAAA"] = "AAAA";
5
+ DNSRecordType["CAA"] = "CAA";
6
+ DNSRecordType["CNAME"] = "CNAME";
7
+ DNSRecordType["NAPTR"] = "NAPTR";
8
+ DNSRecordType["MX"] = "MX";
9
+ DNSRecordType["NS"] = "NS";
10
+ DNSRecordType["PTR"] = "PTR";
11
+ DNSRecordType["SOA"] = "SOA";
12
+ DNSRecordType["SRV"] = "SRV";
13
+ DNSRecordType["TXT"] = "TXT";
14
+ })(DNSRecordType || (DNSRecordType = {}));
@@ -0,0 +1,2 @@
1
+ import { type AnyDNSRecord, type DNSResolver } from "../types";
2
+ export declare function resolveAllRecords(host: string, resolver: DNSResolver): Promise<AnyDNSRecord[]>;
@@ -0,0 +1,8 @@
1
+ import { DNSRecordType } from "../types";
2
+ export async function resolveAllRecords(host, resolver) {
3
+ const allTypes = Object.values(DNSRecordType);
4
+ const records = await Promise.allSettled(allTypes.map((t) => resolver(host, t)));
5
+ return records
6
+ .filter((record) => record.status === "fulfilled")
7
+ .flatMap((record) => record.value);
8
+ }
@@ -0,0 +1,9 @@
1
+ import { type AnyDNSRecord } from "../types";
2
+ type InputData = string | string[];
3
+ export declare function toDnsRecord({ name, type, ttl, data, }: {
4
+ name: string;
5
+ type: string;
6
+ ttl?: number;
7
+ data: InputData;
8
+ }): AnyDNSRecord;
9
+ export {};
@@ -0,0 +1,53 @@
1
+ import { DNSRecordType, } from "../types";
2
+ function convertDataByType(type, data) {
3
+ switch (type) {
4
+ case DNSRecordType.SOA: {
5
+ const parts = String(data).split(" ");
6
+ if (parts.length < 7) {
7
+ throw new Error("Invalid SOA record format");
8
+ }
9
+ return {
10
+ nsname: (parts[0] ?? "").replace(/\.$/, ""),
11
+ hostmaster: (parts[1] ?? "").replace(/\.$/, ""),
12
+ serial: parseInt(parts[2] ?? "", 10),
13
+ refresh: parseInt(parts[3] ?? "", 10),
14
+ retry: parseInt(parts[4] ?? "", 10),
15
+ expire: parseInt(parts[5] ?? "", 10),
16
+ minttl: parseInt(parts[6] ?? "", 10),
17
+ };
18
+ }
19
+ case DNSRecordType.MX: {
20
+ const mx = String(data).split(" ");
21
+ if (mx.length < 2) {
22
+ throw new Error("Invalid MX record format");
23
+ }
24
+ return {
25
+ priority: parseInt(mx[0] ?? "", 10),
26
+ exchange: mx.slice(1).join(" ").replace(/\.$/, ""),
27
+ };
28
+ }
29
+ case DNSRecordType.TXT: {
30
+ const txt = String(data) ?? "";
31
+ // Cloudflare or dig returns TXT records as `"rec1" "rec2" "rec3 "` need to be parsed
32
+ if (txt.startsWith('"') && txt.endsWith('"')) {
33
+ const matches = txt.match(/"([^"]*)"/g);
34
+ return matches
35
+ ? matches.map((part) => part.replace(/"/g, ""))
36
+ : [];
37
+ }
38
+ }
39
+ }
40
+ return Array.isArray(data) ? data.join(" ") : data;
41
+ }
42
+ export function toDnsRecord({ name, type, ttl, data, }) {
43
+ return {
44
+ // remove trailing dot from name
45
+ name: name.replace(/\.$/, ""),
46
+ // ensure type is a valid DNSRecordType
47
+ type: type,
48
+ // default ttl to 0 if not provided
49
+ ttl: ttl ?? 0,
50
+ // convert data based on type
51
+ data: convertDataByType(type, data),
52
+ };
53
+ }
@@ -0,0 +1,2 @@
1
+ import { DNSRecordType } from "../types";
2
+ export declare function toDnsType(type?: string): DNSRecordType | undefined;
@@ -0,0 +1,9 @@
1
+ import { DNSRecordType } from "../types";
2
+ export function toDnsType(type) {
3
+ if (!type)
4
+ return undefined;
5
+ const typeUpper = type.toUpperCase();
6
+ return Object.values(DNSRecordType).includes(typeUpper)
7
+ ? typeUpper
8
+ : undefined;
9
+ }
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "diggy",
3
+ "version": "1.0.0",
4
+ "description": "Multi-backend DNS resolver for Node.js/Browser — supports dig, DNS over HTTPS, and native Node.js DNS.",
5
+ "repository": "git@github.com:OzzyCzech/diggy.git",
6
+ "author": "Roman Ožana <roman@ozana.cz>",
7
+ "keywords": [
8
+ "dns",
9
+ "dig",
10
+ "dns-over-https",
11
+ "DoH",
12
+ "nodejs",
13
+ "browser",
14
+ "dns-resolver",
15
+ "resolver",
16
+ "lookup",
17
+ "dns-client",
18
+ "dns-server",
19
+ "dns-tools",
20
+ "google-dns",
21
+ "cloudflare-dns",
22
+ "dns-over-tls",
23
+ "js-dns"
24
+ ],
25
+ "license": "MIT",
26
+ "main": "dist/index.js",
27
+ "types": "dist/index.d.ts",
28
+ "type": "module",
29
+ "exports": {
30
+ "types": "./dist/index.d.ts",
31
+ "default": "./dist/index.js"
32
+ },
33
+ "files": [
34
+ "dist"
35
+ ],
36
+ "scripts": {
37
+ "build": "rm -rf dist && tsc",
38
+ "release": "np",
39
+ "prepare": "npm run build",
40
+ "test": "tsc --noEmit && vitest",
41
+ "format": "biome check --write .",
42
+ "format:check": "biome check ."
43
+ },
44
+ "devDependencies": {
45
+ "@biomejs/biome": "^2.1.4",
46
+ "@types/node": "^24.2.1",
47
+ "np": "^10.2.0",
48
+ "tsx": "^4.20.4",
49
+ "typescript": "^5.9.2",
50
+ "vitest": "^3.2.4"
51
+ }
52
+ }
package/readme.md ADDED
@@ -0,0 +1,238 @@
1
+ # 👾 Diggy
2
+
3
+ 👾 **Diggy** is a flexible, multi-backend JavaScript **DNS resolver** for fetching **DNS records**
4
+ with support for various resolution methods including DNS over HTTPS, native `dig` commands, and Node.js built-in DNS
5
+ functionality.
6
+
7
+ ## Features
8
+
9
+ - **✨ Multiple DNS backends** - Choose from Google DoH, Cloudflare DoH, Node.js DNS, or native dig command or even
10
+ create your own custom resolver!
11
+ - **🔒 DNS over HTTPS (DoH)** - Secure DNS queries over encrypted connections
12
+ - **⚡ Native dig Support** - Leverage system [DNS tools](https://linux.die.net/man/1/dig) directly from Node.js
13
+ - **🛠️ Node.js Integration** - Use built-in Node.js [DNS functionality](https://nodejs.org/api/dns.html)
14
+ - **📋 Complete Record Support** - Fetch A, AAAA, SOA, CNAME, TXT, MX, SRV, CAA, NAPTR, and more
15
+ - **🚀 Zero Dependencies** - Lightweight with literally no external dependencies
16
+ - **🎯 TypeScript ready** - Full type definitions included
17
+
18
+ ## 📦 Installation
19
+
20
+ ```bash
21
+ npm install diggy
22
+ ```
23
+
24
+ ```bash
25
+ yarn add diggy
26
+ ```
27
+
28
+ ```bash
29
+ pnpm add diggy
30
+ ```
31
+
32
+ ```bash
33
+ bun add diggy
34
+ ```
35
+
36
+ ## 🚀 Quick Start
37
+
38
+ ### Basic Usage
39
+
40
+ ```javascript
41
+ import { getDnsRecords } from 'diggy';
42
+
43
+ // Fetch all DNS records for a domain
44
+ const allRecords = await getDnsRecords('example.com');
45
+ console.log(allRecords);
46
+
47
+ // Fetch specific record types
48
+ const aRecords = await getDnsRecords('example.com', 'A');
49
+ const txtRecords = await getDnsRecords('example.com', 'TXT');
50
+ const mxRecords = await getDnsRecords('example.com', 'MX');
51
+ ```
52
+
53
+ ### TypeScript Support
54
+
55
+ ```typescript
56
+ import { type AnyDNSRecord, getDnsRecords } from 'diggy';
57
+
58
+ const records: AnyDNSRecord[] = await getDnsRecords('example.com', 'A');
59
+ ```
60
+
61
+ ```typescript
62
+ function getDnsRecords(
63
+ host: string,
64
+ type?: string,
65
+ resolver?: string | BuildInDNSResolver | DNSResolver,
66
+ ): Promise<AnyDNSRecord[]>
67
+ ```
68
+
69
+ **Parameters:**
70
+
71
+ - `host` (string): The domain name to query
72
+ - `type` (string, optional): DNS record type (A, AAAA, MX, TXT, etc.). If omitted, returns all available records
73
+ - `resolver` (string | BuildInDNSResolver | DNSResolver, optional): DNS resolver to use
74
+
75
+ **Returns:** Promise resolving to an array of DNS records
76
+
77
+ ## 🌐 Available Resolvers
78
+
79
+ ### Built-in Resolvers
80
+
81
+ Diggy includes several pre-configured resolvers:
82
+
83
+ ```javascript
84
+ // Use Google DNS JSON Over HTTPS
85
+ const records = await getDnsRecords('example.com', 'A', "google");
86
+
87
+ // Use Cloudflare DNS JSON Over HTTPS
88
+ const records = await getDnsRecords('example.com', 'A', "cloudflare");
89
+
90
+ // Use nodejs dns module
91
+ const records = await getDnsRecords('example.com', 'A', "nodejs");
92
+
93
+ // Use dig command
94
+ const records = await getDnsRecords('example.com', 'A', "dig");
95
+ ```
96
+
97
+ | Resolver | Description | Environment |
98
+ |--------------|-----------------------------|---------------------------|
99
+ | `google` | Google DNS over HTTPS | Works in Browser |
100
+ | `cloudflare` | Cloudflare DNS over HTTPS | Works in Browser |
101
+ | `nodejs` | Node.js built-in DNS module | Node.js runtime |
102
+ | `dig` | Native dig command | `dig` installed on system |
103
+
104
+ ### Configure built-in resolvers
105
+
106
+ Create your own DNS resolver for custom endpoints:
107
+
108
+ ```javascript
109
+ import { getDnsRecords, dohResolver } from 'diggy';
110
+
111
+ const customDohResolver = dohResolver("https://custom.dns.provider/resolve");
112
+ const records = await getDnsRecords('example.com', 'TXT', customDohResolver);
113
+ ```
114
+
115
+ > 💡 **Tip:** Find more public [DoH endpoints here](https://github.com/curl/curl/wiki/DNS-over-HTTPS)
116
+
117
+ Just like with `dohResolver`, you can also use `digResolver` or `nodeResolver` and specify a custom DNS server:
118
+
119
+ ```javascript
120
+ import { getDnsRecords, digResolver, nodeResolver } from 'diggy';
121
+
122
+ // Native nodejs dns resolver witg specific DNS server
123
+ const customNodejsResolver = nodeResolver(['8.8.8.8']);
124
+ const records = await getDnsRecords('example.com', 'A', customNodejsResolver);
125
+
126
+ // Native dig command with specific DNS server
127
+ const customDigResolver = digResolver('1.1.1.1');
128
+ const records = await getDnsRecords('example.com', 'A', customDigResolver);
129
+ ```
130
+
131
+ > 💡 **Tip:** Find more public [DNS servers here](https://public-dns.info/)
132
+
133
+ You can also **create your own custom resolver** by implementing the `DNSResolver` interface:
134
+
135
+ ```typescript
136
+ export type DNSResolver = (
137
+ host: string,
138
+ type: DNSRecordType,
139
+ ) => Promise<AnyDNSRecord[]>;
140
+ ```
141
+
142
+ ## 📝 Supported Record Types
143
+
144
+ | Type | Description | Example Use Case |
145
+ |-------|------------------------|-------------------------|
146
+ | A | IPv4 address | Website hosting |
147
+ | AAAA | IPv6 address | IPv6 connectivity |
148
+ | CNAME | Canonical name | Domain aliases |
149
+ | MX | Mail exchange | Email routing |
150
+ | TXT | Text records | SPF, DKIM, verification |
151
+ | SOA | Start of authority | Zone information |
152
+ | SRV | Service records | Service discovery |
153
+ | CAA | Certificate authority | SSL/TLS security |
154
+ | NAPTR | Name authority pointer | ENUM, SIP routing |
155
+
156
+ ## 📜 Response Format
157
+
158
+ DNS records are returned as an array of objects with the following structure:
159
+
160
+ ```typescript
161
+ import { CaaRecordData, MxRecordData, SoaRecordData, SrvRecordData, NaptrRecordData } from "./types";
162
+
163
+ interface AnyDNSRecord {
164
+ name: string; // Domain name
165
+ type: string; // Record type (A, AAAA, MX, etc.)
166
+ ttl: number; // Time-to-live in seconds
167
+
168
+ // Record data (format varies by type)
169
+ data:
170
+ | string
171
+ | string[]
172
+ | MxRecordData
173
+ | SoaRecordData
174
+ | CaaRecordData
175
+ | NaptrRecordData
176
+ | SrvRecordData;
177
+ }
178
+ ```
179
+
180
+ ### Example Response
181
+
182
+ ```json
183
+ [
184
+ {
185
+ "name": "example.com",
186
+ "type": "SOA",
187
+ "ttl": 3600,
188
+ "data": {
189
+ "nsname": "ns1.example.com.",
190
+ "hostmaster": "hostmaster.example.com.",
191
+ "serial": 2025051204,
192
+ "refresh": 10800,
193
+ "retry": 3600,
194
+ "expire": 604800,
195
+ "minttl": 3600
196
+ }
197
+ },
198
+ {
199
+ "name": "example.cz",
200
+ "type": "A",
201
+ "ttl": 1800,
202
+ "data": "66.33.66.33"
203
+ },
204
+ {
205
+ "name": "example.cz",
206
+ "type": "MX",
207
+ "ttl": 60,
208
+ "data": {
209
+ "priority": 10,
210
+ "exchange": "mail.example.com"
211
+ }
212
+ }
213
+ ]
214
+ ```
215
+
216
+ ## 🔧 Requirements
217
+
218
+ - **Node.js**: Version 14 or higher
219
+ - **dig command**: Required only when using the 'dig' resolver
220
+ - **Internet connection**: Required for DoH resolvers (google, cloudflare)
221
+
222
+ ## 📄 License
223
+
224
+ [MIT License](LICENSE) - see the [LICENSE](LICENSE) file for details.
225
+
226
+ ## 🤝 Contributing
227
+
228
+ Contributions are welcome! Please feel free to submit a Pull Request.
229
+
230
+ 1. Fork the repository
231
+ 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
232
+ 3. Commit your changes (`git commit -m 'Add some amazing feature'`)
233
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
234
+ 5. Open a Pull Request
235
+
236
+ ---
237
+
238
+ Made with ❤️ by the [Roman Ožana](https://ozana.cz)