@tradetrust-tt/dnsprove 2.18.0 → 2.20.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/README.md CHANGED
@@ -111,7 +111,7 @@ Takes a DNS-TXT Record set and returns openattestation document store records if
111
111
  #### Parameters
112
112
 
113
113
  * `recordSet` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[IDNSRecord](#idnsrecord)>** Refer to tests for examples (optional, default `[]`)
114
- * `dnssec` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)**&#x20;
114
+ * `dnssec` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** Resolver AD (authenticated data) flag; applied as each record's `dnssec` field
115
115
 
116
116
  Returns **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[OpenAttestationDNSTextRecord](#openattestationdnstextrecord)>**&#x20;
117
117
 
@@ -131,7 +131,7 @@ Queries a given domain and parses the results to retrieve openattestation docume
131
131
  #### Parameters
132
132
 
133
133
  * `domain` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** e.g: "example.openattestation.com"
134
- * `customDnsResolvers` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[CustomDnsResolver](#customdnsresolver)>?**&#x20;
134
+ * `customDnsResolvers` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<[CustomDnsResolver](#customdnsresolver)>?** Optional resolver list; built-in HTTP DNS chain is used when omitted
135
135
 
136
136
  #### Examples
137
137
 
package/dist/index.js CHANGED
@@ -3,6 +3,17 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
+ var _exportNames = {
7
+ defaultDnsResolvers: true,
8
+ queryDns: true,
9
+ parseOpenAttestationRecord: true,
10
+ parseDocumentStoreResults: true,
11
+ parseDnsDidResults: true,
12
+ getDocumentStoreRecords: true,
13
+ getDnsDidRecords: true,
14
+ OpenAttestationDNSTextRecord: true,
15
+ OpenAttestationDnsDidRecord: true
16
+ };
6
17
  Object.defineProperty(exports, "OpenAttestationDNSTextRecord", {
7
18
  enumerable: true,
8
19
  get: function get() {
@@ -20,47 +31,34 @@ var _dnsTxt = require("./records/dnsTxt");
20
31
  var _dnsDid = require("./records/dnsDid");
21
32
  var _logger = require("./util/logger");
22
33
  var _error = require("./common/error");
34
+ var _dnsResolvers = require("./util/dns-resolvers");
35
+ Object.keys(_dnsResolvers).forEach(function (key) {
36
+ if (key === "default" || key === "__esModule") return;
37
+ if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
38
+ if (key in exports && exports[key] === _dnsResolvers[key]) return;
39
+ Object.defineProperty(exports, key, {
40
+ enumerable: true,
41
+ get: function get() {
42
+ return _dnsResolvers[key];
43
+ }
44
+ });
45
+ });
23
46
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
24
47
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
25
48
  function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
26
49
  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : String(i); }
27
50
  function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
51
+ function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
52
+ function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
28
53
  function _toArray(arr) { return _arrayWithHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableRest(); }
29
54
  function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
30
55
  function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
31
56
  function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
32
57
  function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
33
58
  function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
34
- function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
35
- function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
36
59
  const _getLogger = (0, _logger.getLogger)("index"),
37
60
  trace = _getLogger.trace;
38
- const defaultDnsResolvers = exports.defaultDnsResolvers = [( /*#__PURE__*/function () {
39
- var _ref = _asyncToGenerator(function* (domain) {
40
- const data = yield fetch(`https://dns.google/resolve?name=${domain}&type=TXT`, {
41
- method: "GET"
42
- });
43
- return data.json();
44
- });
45
- return function (_x) {
46
- return _ref.apply(this, arguments);
47
- };
48
- }()), ( /*#__PURE__*/function () {
49
- var _ref2 = _asyncToGenerator(function* (domain) {
50
- const data = yield fetch(`https://cloudflare-dns.com/dns-query?name=${domain}&type=TXT`, {
51
- method: "GET",
52
- headers: {
53
- accept: "application/dns-json",
54
- contentType: "application/json",
55
- connection: "keep-alive"
56
- }
57
- });
58
- return data.json();
59
- });
60
- return function (_x2) {
61
- return _ref2.apply(this, arguments);
62
- };
63
- }())];
61
+ const defaultDnsResolvers = exports.defaultDnsResolvers = [_dnsResolvers.googleDnsResolver, _dnsResolvers.cloudflareDnsResolver, _dnsResolvers.aliDnsResolver];
64
62
 
65
63
  /**
66
64
  * Returns true for strings that are openattestation records
@@ -104,7 +102,7 @@ const formatDnsDidRecord = ({
104
102
  };
105
103
  };
106
104
  const queryDns = exports.queryDns = /*#__PURE__*/function () {
107
- var _ref3 = _asyncToGenerator(function* (domain, customDnsResolvers) {
105
+ var _ref = _asyncToGenerator(function* (domain, customDnsResolvers) {
108
106
  let data;
109
107
  let i = 0;
110
108
  if (domain.includes("://")) {
@@ -124,8 +122,8 @@ const queryDns = exports.queryDns = /*#__PURE__*/function () {
124
122
  }
125
123
  return data;
126
124
  });
127
- return function queryDns(_x3, _x4) {
128
- return _ref3.apply(this, arguments);
125
+ return function queryDns(_x, _x2) {
126
+ return _ref.apply(this, arguments);
129
127
  };
130
128
  }();
131
129
 
@@ -137,8 +135,10 @@ const parseOpenAttestationRecord = record => {
137
135
  trace(`Parsing record: ${record}`);
138
136
  const keyValuePairs = record.trim().split(" "); // tokenize into key=value elements
139
137
  const recordObject = {};
140
- // @ts-ignore: we already checked for this token
141
- recordObject.type = keyValuePairs.shift();
138
+ const typeToken = keyValuePairs.shift();
139
+ if (typeToken !== undefined) {
140
+ recordObject.type = typeToken;
141
+ }
142
142
  keyValuePairs.reduce(addKeyValuePairToObject, recordObject);
143
143
  return recordObject;
144
144
  };
@@ -175,6 +175,7 @@ const parseOpenAttestationRecords = (recordSet = []) => {
175
175
  /**
176
176
  * Takes a DNS-TXT Record set and returns openattestation document store records if any
177
177
  * @param recordSet Refer to tests for examples
178
+ * @param dnssec Resolver AD (authenticated data) flag; applied as each record's `dnssec` field
178
179
  */
179
180
  const parseDocumentStoreResults = (recordSet = [], dnssec) => {
180
181
  return parseOpenAttestationRecords(recordSet).reduce((prev, curr) => {
@@ -191,6 +192,7 @@ const parseDnsDidResults = (recordSet = [], dnssec) => {
191
192
  /**
192
193
  * Queries a given domain and parses the results to retrieve openattestation document store records if any
193
194
  * @param domain e.g: "example.openattestation.com"
195
+ * @param customDnsResolvers Optional resolver list; built-in HTTP DNS chain is used when omitted
194
196
  * @example
195
197
  * > getDocumentStoreRecords("example.openattestation.com")
196
198
  * > [ { type: 'openatts',
@@ -201,28 +203,28 @@ const parseDnsDidResults = (recordSet = [], dnssec) => {
201
203
  */
202
204
  exports.parseDnsDidResults = parseDnsDidResults;
203
205
  const getDocumentStoreRecords = exports.getDocumentStoreRecords = /*#__PURE__*/function () {
204
- var _ref4 = _asyncToGenerator(function* (domain, customDnsResolvers) {
206
+ var _ref2 = _asyncToGenerator(function* (domain, customDnsResolvers) {
205
207
  trace(`Received request to resolve ${domain}`);
206
- const dnsResolvers = customDnsResolvers || defaultDnsResolvers;
208
+ const dnsResolvers = customDnsResolvers !== null && customDnsResolvers !== void 0 ? customDnsResolvers : defaultDnsResolvers;
207
209
  const results = yield queryDns(domain, dnsResolvers);
208
210
  const answers = results.Answer || [];
209
211
  trace(`Lookup results: ${JSON.stringify(answers)}`);
210
212
  return parseDocumentStoreResults(answers, results.AD);
211
213
  });
212
- return function getDocumentStoreRecords(_x5, _x6) {
213
- return _ref4.apply(this, arguments);
214
+ return function getDocumentStoreRecords(_x3, _x4) {
215
+ return _ref2.apply(this, arguments);
214
216
  };
215
217
  }();
216
218
  const getDnsDidRecords = exports.getDnsDidRecords = /*#__PURE__*/function () {
217
- var _ref5 = _asyncToGenerator(function* (domain, customDnsResolvers) {
219
+ var _ref3 = _asyncToGenerator(function* (domain, customDnsResolvers) {
218
220
  trace(`Received request to resolve ${domain}`);
219
- const dnsResolvers = customDnsResolvers || defaultDnsResolvers;
221
+ const dnsResolvers = customDnsResolvers !== null && customDnsResolvers !== void 0 ? customDnsResolvers : defaultDnsResolvers;
220
222
  const results = yield queryDns(domain, dnsResolvers);
221
223
  const answers = results.Answer || [];
222
224
  trace(`Lookup results: ${JSON.stringify(answers)}`);
223
225
  return parseDnsDidResults(answers, results.AD);
224
226
  });
225
- return function getDnsDidRecords(_x7, _x8) {
226
- return _ref5.apply(this, arguments);
227
+ return function getDnsDidRecords(_x5, _x6) {
228
+ return _ref3.apply(this, arguments);
227
229
  };
228
230
  }();
@@ -3,31 +3,14 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.RecordTypesT = exports.OpenAttestationDNSTextRecordT = exports.EthereumNetworks = exports.EthereumNetworkIdT = exports.EthereumAddressT = exports.BlockchainNetworkT = void 0;
6
+ exports.RecordTypesT = exports.OpenAttestationDNSTextRecordT = exports.EthereumNetworkIdT = exports.EthereumAddressT = exports.BlockchainNetworkT = void 0;
7
7
  var _runtypes = require("runtypes");
8
8
  const RecordTypesT = exports.RecordTypesT = (0, _runtypes.Literal)("openatts");
9
9
  const BlockchainNetworkT = exports.BlockchainNetworkT = (0, _runtypes.Literal)("ethereum");
10
10
  const EthereumAddressT = exports.EthereumAddressT = _runtypes.String.withConstraint(maybeAddress => {
11
11
  return /0x[a-fA-F0-9]{40}/.test(maybeAddress) || `${maybeAddress} is not a valid ethereum address`;
12
12
  });
13
- let EthereumNetworks = exports.EthereumNetworks = void 0;
14
- (function (EthereumNetworks) {
15
- EthereumNetworks["homestead"] = "1";
16
- EthereumNetworks["ropsten"] = "3";
17
- EthereumNetworks["rinkeby"] = "4";
18
- EthereumNetworks["goerli"] = "5";
19
- EthereumNetworks["sepolia"] = "11155111";
20
- EthereumNetworks["polygon"] = "137";
21
- EthereumNetworks["polygonAmoy"] = "80002";
22
- EthereumNetworks["local"] = "1337";
23
- EthereumNetworks["xdc"] = "50";
24
- EthereumNetworks["xdcapothem"] = "51";
25
- EthereumNetworks["stabilityTestnet"] = "20180427";
26
- EthereumNetworks["stability"] = "101010";
27
- EthereumNetworks["astronTestnet"] = "21002";
28
- EthereumNetworks["astron"] = "1338";
29
- })(EthereumNetworks || (exports.EthereumNetworks = EthereumNetworks = {}));
30
- const EthereumNetworkIdT = exports.EthereumNetworkIdT = (0, _runtypes.Union)((0, _runtypes.Literal)(EthereumNetworks.homestead), (0, _runtypes.Literal)(EthereumNetworks.ropsten), (0, _runtypes.Literal)(EthereumNetworks.rinkeby), (0, _runtypes.Literal)(EthereumNetworks.goerli), (0, _runtypes.Literal)(EthereumNetworks.sepolia), (0, _runtypes.Literal)(EthereumNetworks.polygon), (0, _runtypes.Literal)(EthereumNetworks.polygonAmoy), (0, _runtypes.Literal)(EthereumNetworks.xdc), (0, _runtypes.Literal)(EthereumNetworks.xdcapothem), (0, _runtypes.Literal)(EthereumNetworks.stabilityTestnet), (0, _runtypes.Literal)(EthereumNetworks.stability), (0, _runtypes.Literal)(EthereumNetworks.local), (0, _runtypes.Literal)(EthereumNetworks.astronTestnet), (0, _runtypes.Literal)(EthereumNetworks.astron));
13
+ const EthereumNetworkIdT = exports.EthereumNetworkIdT = _runtypes.String.withConstraint(maybeNetId => /^\d+$/.test(maybeNetId) || `${maybeNetId} is not a valid numeric network id`);
31
14
  const OpenAttestationDNSTextRecordT = exports.OpenAttestationDNSTextRecordT = (0, _runtypes.Record)({
32
15
  type: RecordTypesT,
33
16
  net: BlockchainNetworkT,
@@ -24,12 +24,14 @@ export declare const parseOpenAttestationRecord: (record: string) => GenericObje
24
24
  /**
25
25
  * Takes a DNS-TXT Record set and returns openattestation document store records if any
26
26
  * @param recordSet Refer to tests for examples
27
+ * @param dnssec Resolver AD (authenticated data) flag; applied as each record's `dnssec` field
27
28
  */
28
29
  export declare const parseDocumentStoreResults: (recordSet: IDNSRecord[] | undefined, dnssec: boolean) => OpenAttestationDNSTextRecord[];
29
30
  export declare const parseDnsDidResults: (recordSet: IDNSRecord[] | undefined, dnssec: boolean) => OpenAttestationDnsDidRecord[];
30
31
  /**
31
32
  * Queries a given domain and parses the results to retrieve openattestation document store records if any
32
33
  * @param domain e.g: "example.openattestation.com"
34
+ * @param customDnsResolvers Optional resolver list; built-in HTTP DNS chain is used when omitted
33
35
  * @example
34
36
  * > getDocumentStoreRecords("example.openattestation.com")
35
37
  * > [ { type: 'openatts',
@@ -41,3 +43,4 @@ export declare const parseDnsDidResults: (recordSet: IDNSRecord[] | undefined, d
41
43
  export declare const getDocumentStoreRecords: (domain: string, customDnsResolvers?: CustomDnsResolver[]) => Promise<OpenAttestationDNSTextRecord[]>;
42
44
  export declare const getDnsDidRecords: (domain: string, customDnsResolvers?: CustomDnsResolver[]) => Promise<OpenAttestationDnsDidRecord[]>;
43
45
  export { OpenAttestationDNSTextRecord, OpenAttestationDnsDidRecord };
46
+ export * from "./util/dns-resolvers";
@@ -1,28 +1,12 @@
1
- import { Static, Boolean, String, Literal, Record, Union, Partial } from "runtypes";
1
+ import { Static, Boolean, String, Literal, Record, Partial } from "runtypes";
2
2
  export declare const RecordTypesT: Literal<"openatts">;
3
3
  export declare const BlockchainNetworkT: Literal<"ethereum">;
4
4
  export declare const EthereumAddressT: import("runtypes").Constraint<String, string, unknown>;
5
- export declare enum EthereumNetworks {
6
- homestead = "1",
7
- ropsten = "3",
8
- rinkeby = "4",
9
- goerli = "5",
10
- sepolia = "11155111",
11
- polygon = "137",
12
- polygonAmoy = "80002",
13
- local = "1337",
14
- xdc = "50",
15
- xdcapothem = "51",
16
- stabilityTestnet = "20180427",
17
- stability = "101010",
18
- astronTestnet = "21002",
19
- astron = "1338"
20
- }
21
- export declare const EthereumNetworkIdT: Union<[Literal<EthereumNetworks.homestead>, Literal<EthereumNetworks.ropsten>, Literal<EthereumNetworks.rinkeby>, Literal<EthereumNetworks.goerli>, Literal<EthereumNetworks.sepolia>, Literal<EthereumNetworks.polygon>, Literal<EthereumNetworks.polygonAmoy>, Literal<EthereumNetworks.xdc>, Literal<EthereumNetworks.xdcapothem>, Literal<EthereumNetworks.stabilityTestnet>, Literal<EthereumNetworks.stability>, Literal<EthereumNetworks.local>, Literal<EthereumNetworks.astronTestnet>, Literal<EthereumNetworks.astron>]>;
5
+ export declare const EthereumNetworkIdT: import("runtypes").Constraint<String, string, unknown>;
22
6
  export declare const OpenAttestationDNSTextRecordT: import("runtypes").Intersect<[Record<{
23
7
  type: Literal<"openatts">;
24
8
  net: Literal<"ethereum">;
25
- netId: Union<[Literal<EthereumNetworks.homestead>, Literal<EthereumNetworks.ropsten>, Literal<EthereumNetworks.rinkeby>, Literal<EthereumNetworks.goerli>, Literal<EthereumNetworks.sepolia>, Literal<EthereumNetworks.polygon>, Literal<EthereumNetworks.polygonAmoy>, Literal<EthereumNetworks.xdc>, Literal<EthereumNetworks.xdcapothem>, Literal<EthereumNetworks.stabilityTestnet>, Literal<EthereumNetworks.stability>, Literal<EthereumNetworks.local>, Literal<EthereumNetworks.astronTestnet>, Literal<EthereumNetworks.astron>]>;
9
+ netId: import("runtypes").Constraint<String, string, unknown>;
26
10
  addr: import("runtypes").Constraint<String, string, unknown>;
27
11
  }, false>, Partial<{
28
12
  dnssec: Boolean;
@@ -0,0 +1,2 @@
1
+ import type { CustomDnsResolver } from "../..";
2
+ export declare const aliDnsResolver: CustomDnsResolver;
@@ -0,0 +1,2 @@
1
+ import type { CustomDnsResolver } from "../..";
2
+ export declare const cloudflareDnsResolver: CustomDnsResolver;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ import type { CustomDnsResolver } from "../..";
2
+ export declare const googleDnsResolver: CustomDnsResolver;
@@ -0,0 +1,3 @@
1
+ export * from "./google-dns-resolver";
2
+ export * from "./cloudflare-dns-resolver";
3
+ export * from "./ali-dns-resolver";
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.aliDnsResolver = void 0;
7
+ function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
8
+ function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
9
+ /** Ali DNS JSON API uses numeric RRTYPE; 16 = TXT */
10
+ const ALI_DNS_TXT_QUERY_TYPE = "16";
11
+ const aliDnsResolver = exports.aliDnsResolver = /*#__PURE__*/function () {
12
+ var _ref = _asyncToGenerator(function* (domain) {
13
+ const url = new URL("https://dns.alidns.com/resolve");
14
+ if (!domain) {
15
+ throw new Error("Domain is required");
16
+ }
17
+ url.searchParams.set("name", domain);
18
+ url.searchParams.set("type", ALI_DNS_TXT_QUERY_TYPE);
19
+ const res = yield fetch(url);
20
+ if (!res.ok) {
21
+ throw new Error(`Ali DNS request failed: HTTP ${res.status}`);
22
+ }
23
+ let data;
24
+ try {
25
+ data = yield res.json();
26
+ } catch (_unused) {
27
+ throw new Error("Failed to parse DNS response JSON");
28
+ }
29
+ return data;
30
+ });
31
+ return function aliDnsResolver(_x) {
32
+ return _ref.apply(this, arguments);
33
+ };
34
+ }();
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.cloudflareDnsResolver = void 0;
7
+ function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
8
+ function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
9
+ const cloudflareDnsResolver = exports.cloudflareDnsResolver = /*#__PURE__*/function () {
10
+ var _ref = _asyncToGenerator(function* (domain) {
11
+ const url = new URL("https://cloudflare-dns.com/dns-query");
12
+ if (!domain) {
13
+ throw new Error("Domain is required");
14
+ }
15
+ url.searchParams.set("name", domain);
16
+ url.searchParams.set("type", "TXT");
17
+ const res = yield fetch(url, {
18
+ headers: {
19
+ Accept: "application/dns-json"
20
+ }
21
+ });
22
+ if (!res.ok) {
23
+ throw new Error(`Cloudflare DNS request failed: HTTP ${res.status}`);
24
+ }
25
+ let data;
26
+ try {
27
+ data = yield res.json();
28
+ } catch (_unused) {
29
+ throw new Error("Failed to parse DNS response JSON");
30
+ }
31
+ return data;
32
+ });
33
+ return function cloudflareDnsResolver(_x) {
34
+ return _ref.apply(this, arguments);
35
+ };
36
+ }();
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.googleDnsResolver = void 0;
7
+ function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
8
+ function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
9
+ const googleDnsResolver = exports.googleDnsResolver = /*#__PURE__*/function () {
10
+ var _ref = _asyncToGenerator(function* (domain) {
11
+ const url = new URL("https://dns.google/resolve");
12
+ if (!domain) {
13
+ throw new Error("Domain is required");
14
+ }
15
+ url.searchParams.set("name", domain);
16
+ url.searchParams.set("type", "TXT");
17
+ const res = yield fetch(url);
18
+ if (!res.ok) {
19
+ throw new Error(`Google DNS request failed: HTTP ${res.status}`);
20
+ }
21
+ let data;
22
+ try {
23
+ data = yield res.json();
24
+ } catch (_unused) {
25
+ throw new Error("Failed to parse DNS response JSON");
26
+ }
27
+ return data;
28
+ });
29
+ return function googleDnsResolver(_x) {
30
+ return _ref.apply(this, arguments);
31
+ };
32
+ }();
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ var _googleDnsResolver = require("./google-dns-resolver");
7
+ Object.keys(_googleDnsResolver).forEach(function (key) {
8
+ if (key === "default" || key === "__esModule") return;
9
+ if (key in exports && exports[key] === _googleDnsResolver[key]) return;
10
+ Object.defineProperty(exports, key, {
11
+ enumerable: true,
12
+ get: function get() {
13
+ return _googleDnsResolver[key];
14
+ }
15
+ });
16
+ });
17
+ var _cloudflareDnsResolver = require("./cloudflare-dns-resolver");
18
+ Object.keys(_cloudflareDnsResolver).forEach(function (key) {
19
+ if (key === "default" || key === "__esModule") return;
20
+ if (key in exports && exports[key] === _cloudflareDnsResolver[key]) return;
21
+ Object.defineProperty(exports, key, {
22
+ enumerable: true,
23
+ get: function get() {
24
+ return _cloudflareDnsResolver[key];
25
+ }
26
+ });
27
+ });
28
+ var _aliDnsResolver = require("./ali-dns-resolver");
29
+ Object.keys(_aliDnsResolver).forEach(function (key) {
30
+ if (key === "default" || key === "__esModule") return;
31
+ if (key in exports && exports[key] === _aliDnsResolver[key]) return;
32
+ Object.defineProperty(exports, key, {
33
+ enumerable: true,
34
+ get: function get() {
35
+ return _aliDnsResolver[key];
36
+ }
37
+ });
38
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tradetrust-tt/dnsprove",
3
- "version": "2.18.0",
3
+ "version": "2.20.0",
4
4
  "description": "Helper utility for retrieving OpenAttestations document store address records from DNS",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
package/src/index.test.ts CHANGED
@@ -1,6 +1,14 @@
1
1
  import { setupServer, SetupServerApi } from "msw/node";
2
2
  import { http, HttpResponse } from "msw";
3
- import { CustomDnsResolver, getDocumentStoreRecords, queryDns, parseDocumentStoreResults, getDnsDidRecords } from ".";
3
+ import {
4
+ aliDnsResolver,
5
+ cloudflareDnsResolver,
6
+ getDocumentStoreRecords,
7
+ getDnsDidRecords,
8
+ googleDnsResolver,
9
+ parseDocumentStoreResults,
10
+ queryDns,
11
+ } from ".";
4
12
  import { DnsproveStatusCode } from "./common/error";
5
13
 
6
14
  describe("getCertStoreRecords", () => {
@@ -8,11 +16,11 @@ describe("getCertStoreRecords", () => {
8
16
  type: "openatts",
9
17
  net: "ethereum",
10
18
  netId: "3",
11
- dnssec: true,
19
+ dnssec: false,
12
20
  addr: "0x2f60375e8144e16Adf1979936301D8341D58C36C",
13
21
  };
14
22
  test("it should work", async () => {
15
- const records = await getDocumentStoreRecords("donotuse.openattestation.com");
23
+ const records = await getDocumentStoreRecords("donotuse.trustvc.io");
16
24
  expect(records).toStrictEqual([sampleDnsTextRecordWithDnssec]);
17
25
  });
18
26
 
@@ -27,14 +35,14 @@ describe("getCertStoreRecords", () => {
27
35
 
28
36
  describe("getDnsDidRecords", () => {
29
37
  test("it should work", async () => {
30
- const records = await getDnsDidRecords("donotuse.openattestation.com");
38
+ const records = await getDnsDidRecords("donotuse.trustvc.io");
31
39
  expect(records).toStrictEqual([
32
40
  {
33
41
  type: "openatts",
34
42
  algorithm: "dns-did",
35
43
  publicKey: "did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89#controller",
36
44
  version: "1.0",
37
- dnssec: true,
45
+ dnssec: false,
38
46
  },
39
47
  ]);
40
48
  });
@@ -165,6 +173,76 @@ describe("parseDocumentStoreResults", () => {
165
173
  ];
166
174
  expect(parseDocumentStoreResults(sampleRecord, true)).toStrictEqual([]);
167
175
  });
176
+
177
+ test("it should accept any numeric netId (e.g. Mantle mainnet 5000)", () => {
178
+ const addr = "0x2f60375e8144e16Adf1979936301D8341D58C36C";
179
+ const sampleRecord = [
180
+ {
181
+ name: "example.example.com.",
182
+ type: 16,
183
+ TTL: 110,
184
+ data: `"openatts net=ethereum netId=5000 addr=${addr}"`,
185
+ dnssec: false,
186
+ },
187
+ ];
188
+ expect(parseDocumentStoreResults(sampleRecord, false)).toStrictEqual([
189
+ {
190
+ type: "openatts",
191
+ net: "ethereum",
192
+ netId: "5000",
193
+ addr,
194
+ dnssec: false,
195
+ },
196
+ ]);
197
+ });
198
+
199
+ test("it should accept an arbitrary previously-unknown numeric netId", () => {
200
+ const addr = "0x2f60375e8144e16Adf1979936301D8341D58C36C";
201
+ const sampleRecord = [
202
+ {
203
+ name: "example.example.com.",
204
+ type: 16,
205
+ TTL: 110,
206
+ data: `"openatts net=ethereum netId=99999 addr=${addr}"`,
207
+ dnssec: false,
208
+ },
209
+ ];
210
+ expect(parseDocumentStoreResults(sampleRecord, false)).toStrictEqual([
211
+ {
212
+ type: "openatts",
213
+ net: "ethereum",
214
+ netId: "99999",
215
+ addr,
216
+ dnssec: false,
217
+ },
218
+ ]);
219
+ });
220
+
221
+ test("it should reject a non-numeric netId", () => {
222
+ const sampleRecord = [
223
+ {
224
+ name: "example.example.com.",
225
+ type: 16,
226
+ TTL: 110,
227
+ data: '"openatts net=ethereum netId=abc addr=0x2f60375e8144e16Adf1979936301D8341D58C36C"',
228
+ dnssec: false,
229
+ },
230
+ ];
231
+ expect(parseDocumentStoreResults(sampleRecord, false)).toStrictEqual([]);
232
+ });
233
+
234
+ test("it should reject an alphanumeric netId", () => {
235
+ const sampleRecord = [
236
+ {
237
+ name: "example.example.com.",
238
+ type: 16,
239
+ TTL: 110,
240
+ data: '"openatts net=ethereum netId=1abc addr=0x2f60375e8144e16Adf1979936301D8341D58C36C"',
241
+ dnssec: false,
242
+ },
243
+ ];
244
+ expect(parseDocumentStoreResults(sampleRecord, false)).toStrictEqual([]);
245
+ });
168
246
  });
169
247
 
170
248
  describe("queryDns", () => {
@@ -177,29 +255,29 @@ describe("queryDns", () => {
177
255
  RA: true,
178
256
  AD: true,
179
257
  CD: false,
180
- Question: [{ name: "donotuse.openattestation.com.", type: 16 }],
258
+ Question: [{ name: "donotuse.trustvc.io.", type: 16 }],
181
259
  Answer: [
182
260
  {
183
- name: "donotuse.openattestation.com.",
261
+ name: "donotuse.trustvc.io.",
184
262
  type: 16,
185
263
  TTL: 300,
186
264
  data: "openatts a=dns-did; p=did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89#controller; v=1.0;",
187
265
  },
188
266
  {
189
- name: "donotuse.openattestation.com.",
267
+ name: "donotuse.trustvc.io.",
190
268
  type: 16,
191
269
  TTL: 300,
192
270
  data:
193
271
  "openatts DO NOT ADD ANY RECORDS BEYOND THIS AS THIS DOMAIN IS USED FOR DNSPROVE NPM LIBRARY INTEGRATION TESTS",
194
272
  },
195
273
  {
196
- name: "donotuse.openattestation.com.",
274
+ name: "donotuse.trustvc.io.",
197
275
  type: 16,
198
276
  TTL: 300,
199
277
  data: "openatts fooooooobarrrrrrrrr this entry exists to ensure validation works",
200
278
  },
201
279
  {
202
- name: "donotuse.openattestation.com.",
280
+ name: "donotuse.trustvc.io.",
203
281
  type: 16,
204
282
  TTL: 300,
205
283
  data: "openatts net=ethereum netId=3 addr=0x2f60375e8144e16Adf1979936301D8341D58C36C",
@@ -208,22 +286,7 @@ describe("queryDns", () => {
208
286
  Comment: "Response from 205.251.199.177.",
209
287
  };
210
288
 
211
- const testDnsResolvers: CustomDnsResolver[] = [
212
- async (domain) => {
213
- const data = await fetch(`https://dns.google/resolve?name=${domain}&type=TXT`, {
214
- method: "GET",
215
- });
216
-
217
- return data.json();
218
- },
219
- async (domain) => {
220
- const data = await fetch(`https://cloudflare-dns.com/dns-query?name=${domain}&type=TXT`, {
221
- method: "GET",
222
- headers: { accept: "application/dns-json", contentType: "application/json", connection: "keep-alive" },
223
- });
224
- return data.json();
225
- },
226
- ];
289
+ const testDnsResolvers = [googleDnsResolver, cloudflareDnsResolver, aliDnsResolver];
227
290
 
228
291
  afterEach(() => {
229
292
  server.close();
@@ -239,7 +302,7 @@ describe("queryDns", () => {
239
302
  server = setupServer(...handlers);
240
303
  server.listen();
241
304
 
242
- const records = await queryDns("https://donotuse.openattestation.com", testDnsResolvers);
305
+ const records = await queryDns("https://donotuse.trustvc.io", testDnsResolvers);
243
306
  const sortedAnswer = records?.Answer.sort((a, b) => a.data.localeCompare(b.data));
244
307
  expect(sortedAnswer).toMatchObject(sampleResponse.Answer);
245
308
  });
@@ -256,7 +319,28 @@ describe("queryDns", () => {
256
319
  server = setupServer(...handlers);
257
320
  server.listen();
258
321
 
259
- const records = await queryDns("https://donotuse.openattestation.com", testDnsResolvers);
322
+ const records = await queryDns("https://donotuse.trustvc.io", testDnsResolvers);
323
+
324
+ const sortedAnswer = records?.Answer.sort((a, b) => a.data.localeCompare(b.data));
325
+ expect(sortedAnswer).toMatchObject(sampleResponse.Answer);
326
+ });
327
+
328
+ test("Should fallback to third dns when first and second dns is down", async () => {
329
+ const handlers = [
330
+ http.get("https://dns.google/resolve", (_) => {
331
+ return new HttpResponse(null, { status: 500 });
332
+ }),
333
+ http.get("https://cloudflare-dns.com/dns-query", (_) => {
334
+ return new HttpResponse(null, { status: 500 });
335
+ }),
336
+ http.get("https://dns.alidns.com/resolve", (_) => {
337
+ return HttpResponse.json(sampleResponse);
338
+ }),
339
+ ];
340
+ server = setupServer(...handlers);
341
+ server.listen();
342
+
343
+ const records = await queryDns("https://donotuse.trustvc.io", testDnsResolvers);
260
344
 
261
345
  const sortedAnswer = records?.Answer.sort((a, b) => a.data.localeCompare(b.data));
262
346
  expect(sortedAnswer).toMatchObject(sampleResponse.Answer);
@@ -270,14 +354,16 @@ describe("queryDns", () => {
270
354
  http.get("https://cloudflare-dns.com/dns-query", (_) => {
271
355
  return new HttpResponse(null, { status: 500 });
272
356
  }),
357
+ http.get("https://dns.alidns.com/resolve", (_) => {
358
+ return new HttpResponse(null, { status: 500 });
359
+ }),
273
360
  ];
274
361
  server = setupServer(...handlers);
275
362
  server.listen();
276
- try {
277
- await queryDns("https://donotuse.openattestation.com", testDnsResolvers);
278
- } catch (e: any) {
279
- expect(e.code).toStrictEqual(DnsproveStatusCode.IDNS_QUERY_ERROR_GENERAL);
280
- }
363
+
364
+ await expect(queryDns("https://donotuse.trustvc.io", testDnsResolvers)).rejects.toMatchObject({
365
+ code: DnsproveStatusCode.IDNS_QUERY_ERROR_GENERAL,
366
+ });
281
367
  });
282
368
  });
283
369
 
@@ -321,6 +407,13 @@ describe("getDocumentStoreRecords for Astron", () => {
321
407
  addr: "0x18bc0127Ae33389cD96593a1a612774fD14c0737",
322
408
  dnssec: false,
323
409
  },
410
+ {
411
+ type: "openatts",
412
+ net: "ethereum",
413
+ netId: "1338",
414
+ addr: "0x94FD21A026E29E0686583b8be71Cb28a8ca1A8d4",
415
+ dnssec: false,
416
+ },
324
417
  {
325
418
  type: "openatts",
326
419
  net: "ethereum",
package/src/index.ts CHANGED
@@ -2,6 +2,7 @@ import { OpenAttestationDNSTextRecord, OpenAttestationDNSTextRecordT } from "./r
2
2
  import { OpenAttestationDnsDidRecord, OpenAttestationDnsDidRecordT } from "./records/dnsDid";
3
3
  import { getLogger } from "./util/logger";
4
4
  import { CodedError, DnsproveStatusCode } from "./common/error";
5
+ import { aliDnsResolver, cloudflareDnsResolver, googleDnsResolver } from "./util/dns-resolvers";
5
6
 
6
7
  const { trace } = getLogger("index");
7
8
 
@@ -23,22 +24,7 @@ interface GenericObject {
23
24
 
24
25
  export type CustomDnsResolver = (domain: string) => Promise<IDNSQueryResponse>;
25
26
 
26
- export const defaultDnsResolvers: CustomDnsResolver[] = [
27
- async (domain) => {
28
- const data = await fetch(`https://dns.google/resolve?name=${domain}&type=TXT`, {
29
- method: "GET",
30
- });
31
-
32
- return data.json();
33
- },
34
- async (domain) => {
35
- const data = await fetch(`https://cloudflare-dns.com/dns-query?name=${domain}&type=TXT`, {
36
- method: "GET",
37
- headers: { accept: "application/dns-json", contentType: "application/json", connection: "keep-alive" },
38
- });
39
- return data.json();
40
- },
41
- ];
27
+ export const defaultDnsResolvers: CustomDnsResolver[] = [googleDnsResolver, cloudflareDnsResolver, aliDnsResolver];
42
28
 
43
29
  /**
44
30
  * Returns true for strings that are openattestation records
@@ -115,8 +101,10 @@ export const parseOpenAttestationRecord = (record: string): GenericObject => {
115
101
  trace(`Parsing record: ${record}`);
116
102
  const keyValuePairs = record.trim().split(" "); // tokenize into key=value elements
117
103
  const recordObject = {} as GenericObject;
118
- // @ts-ignore: we already checked for this token
119
- recordObject.type = keyValuePairs.shift();
104
+ const typeToken = keyValuePairs.shift();
105
+ if (typeToken !== undefined) {
106
+ recordObject.type = typeToken;
107
+ }
120
108
  keyValuePairs.reduce<GenericObject>(addKeyValuePairToObject, recordObject);
121
109
  return recordObject;
122
110
  };
@@ -153,6 +141,7 @@ const parseOpenAttestationRecords = (recordSet: IDNSRecord[] = []): GenericObjec
153
141
  /**
154
142
  * Takes a DNS-TXT Record set and returns openattestation document store records if any
155
143
  * @param recordSet Refer to tests for examples
144
+ * @param dnssec Resolver AD (authenticated data) flag; applied as each record's `dnssec` field
156
145
  */
157
146
  export const parseDocumentStoreResults = (
158
147
  recordSet: IDNSRecord[] = [],
@@ -177,6 +166,7 @@ export const parseDnsDidResults = (recordSet: IDNSRecord[] = [], dnssec: boolean
177
166
  /**
178
167
  * Queries a given domain and parses the results to retrieve openattestation document store records if any
179
168
  * @param domain e.g: "example.openattestation.com"
169
+ * @param customDnsResolvers Optional resolver list; built-in HTTP DNS chain is used when omitted
180
170
  * @example
181
171
  * > getDocumentStoreRecords("example.openattestation.com")
182
172
  * > [ { type: 'openatts',
@@ -191,7 +181,7 @@ export const getDocumentStoreRecords = async (
191
181
  ): Promise<OpenAttestationDNSTextRecord[]> => {
192
182
  trace(`Received request to resolve ${domain}`);
193
183
 
194
- const dnsResolvers = customDnsResolvers || defaultDnsResolvers;
184
+ const dnsResolvers = customDnsResolvers ?? defaultDnsResolvers;
195
185
 
196
186
  const results = await queryDns(domain, dnsResolvers);
197
187
  const answers = results.Answer || [];
@@ -207,7 +197,7 @@ export const getDnsDidRecords = async (
207
197
  ): Promise<OpenAttestationDnsDidRecord[]> => {
208
198
  trace(`Received request to resolve ${domain}`);
209
199
 
210
- const dnsResolvers = customDnsResolvers || defaultDnsResolvers;
200
+ const dnsResolvers = customDnsResolvers ?? defaultDnsResolvers;
211
201
 
212
202
  const results = await queryDns(domain, dnsResolvers);
213
203
  const answers = results.Answer || [];
@@ -218,3 +208,4 @@ export const getDnsDidRecords = async (
218
208
  };
219
209
 
220
210
  export { OpenAttestationDNSTextRecord, OpenAttestationDnsDidRecord };
211
+ export * from "./util/dns-resolvers";
@@ -1,4 +1,4 @@
1
- import { Static, Boolean, String, Literal, Record, Union, Partial } from "runtypes";
1
+ import { Static, Boolean, String, Literal, Record, Partial } from "runtypes";
2
2
 
3
3
  export const RecordTypesT = Literal("openatts");
4
4
 
@@ -8,38 +8,8 @@ export const EthereumAddressT = String.withConstraint((maybeAddress: string) =>
8
8
  return /0x[a-fA-F0-9]{40}/.test(maybeAddress) || `${maybeAddress} is not a valid ethereum address`;
9
9
  });
10
10
 
11
- export enum EthereumNetworks {
12
- homestead = "1",
13
- ropsten = "3",
14
- rinkeby = "4",
15
- goerli = "5",
16
- sepolia = "11155111",
17
- polygon = "137",
18
- polygonAmoy = "80002",
19
- local = "1337",
20
- xdc = "50",
21
- xdcapothem = "51",
22
- stabilityTestnet = "20180427",
23
- stability = "101010",
24
- astronTestnet = "21002",
25
- astron = "1338",
26
- }
27
-
28
- export const EthereumNetworkIdT = Union(
29
- Literal(EthereumNetworks.homestead),
30
- Literal(EthereumNetworks.ropsten),
31
- Literal(EthereumNetworks.rinkeby),
32
- Literal(EthereumNetworks.goerli),
33
- Literal(EthereumNetworks.sepolia),
34
- Literal(EthereumNetworks.polygon),
35
- Literal(EthereumNetworks.polygonAmoy),
36
- Literal(EthereumNetworks.xdc),
37
- Literal(EthereumNetworks.xdcapothem),
38
- Literal(EthereumNetworks.stabilityTestnet),
39
- Literal(EthereumNetworks.stability),
40
- Literal(EthereumNetworks.local),
41
- Literal(EthereumNetworks.astronTestnet),
42
- Literal(EthereumNetworks.astron)
11
+ export const EthereumNetworkIdT = String.withConstraint(
12
+ (maybeNetId: string) => /^\d+$/.test(maybeNetId) || `${maybeNetId} is not a valid numeric network id`
43
13
  );
44
14
 
45
15
  export const OpenAttestationDNSTextRecordT = Record({
@@ -0,0 +1,29 @@
1
+ import type { CustomDnsResolver, IDNSQueryResponse } from "../..";
2
+
3
+ /** Ali DNS JSON API uses numeric RRTYPE; 16 = TXT */
4
+ const ALI_DNS_TXT_QUERY_TYPE = "16";
5
+ export const aliDnsResolver: CustomDnsResolver = async (domain) => {
6
+ const url = new URL("https://dns.alidns.com/resolve");
7
+
8
+ if (!domain) {
9
+ throw new Error("Domain is required");
10
+ }
11
+
12
+ url.searchParams.set("name", domain);
13
+ url.searchParams.set("type", ALI_DNS_TXT_QUERY_TYPE);
14
+
15
+ const res = await fetch(url);
16
+
17
+ if (!res.ok) {
18
+ throw new Error(`Ali DNS request failed: HTTP ${res.status}`);
19
+ }
20
+
21
+ let data;
22
+ try {
23
+ data = await res.json();
24
+ } catch {
25
+ throw new Error("Failed to parse DNS response JSON");
26
+ }
27
+
28
+ return data as IDNSQueryResponse;
29
+ };
@@ -0,0 +1,29 @@
1
+ import type { CustomDnsResolver, IDNSQueryResponse } from "../..";
2
+
3
+ export const cloudflareDnsResolver: CustomDnsResolver = async (domain) => {
4
+ const url = new URL("https://cloudflare-dns.com/dns-query");
5
+
6
+ if (!domain) {
7
+ throw new Error("Domain is required");
8
+ }
9
+
10
+ url.searchParams.set("name", domain);
11
+ url.searchParams.set("type", "TXT");
12
+
13
+ const res = await fetch(url, {
14
+ headers: { Accept: "application/dns-json" },
15
+ });
16
+
17
+ if (!res.ok) {
18
+ throw new Error(`Cloudflare DNS request failed: HTTP ${res.status}`);
19
+ }
20
+
21
+ let data;
22
+ try {
23
+ data = await res.json();
24
+ } catch {
25
+ throw new Error("Failed to parse DNS response JSON");
26
+ }
27
+
28
+ return data as IDNSQueryResponse;
29
+ };
@@ -0,0 +1,120 @@
1
+ import { setupServer, SetupServerApi } from "msw/node";
2
+ import { http, HttpResponse } from "msw";
3
+ import { aliDnsResolver } from "./ali-dns-resolver";
4
+ import { cloudflareDnsResolver } from "./cloudflare-dns-resolver";
5
+ import { googleDnsResolver } from "./google-dns-resolver";
6
+
7
+ const emptyDnsJson = {
8
+ Status: 0,
9
+ TC: false,
10
+ RD: true,
11
+ RA: true,
12
+ AD: false,
13
+ CD: false,
14
+ Answer: [] as [],
15
+ };
16
+
17
+ describe("googleDnsResolver", () => {
18
+ let server: SetupServerApi | undefined;
19
+
20
+ afterEach(() => {
21
+ server?.close();
22
+ });
23
+
24
+ test("requests Google DNS JSON with name, TXT type, and encoded query", async () => {
25
+ server = setupServer(
26
+ http.get("https://dns.google/resolve", ({ request }) => {
27
+ const url = new URL(request.url);
28
+ expect(url.searchParams.get("name")).toBe("my domain.test");
29
+ expect(url.searchParams.get("type")).toBe("TXT");
30
+ return HttpResponse.json(emptyDnsJson);
31
+ })
32
+ );
33
+ server.listen();
34
+
35
+ const out = await googleDnsResolver("my domain.test");
36
+ expect(out).toMatchObject({ Status: 0, Answer: [] });
37
+ });
38
+
39
+ test("throws when Google DNS returns non-2xx", async () => {
40
+ server = setupServer(http.get("https://dns.google/resolve", () => new HttpResponse(null, { status: 503 })));
41
+ server.listen();
42
+
43
+ await expect(googleDnsResolver("my.domain.test")).rejects.toThrow(/HTTP 503/);
44
+ });
45
+
46
+ test("throws when domain is empty", async () => {
47
+ await expect(googleDnsResolver("")).rejects.toThrow("Domain is required");
48
+ });
49
+ });
50
+
51
+ describe("cloudflareDnsResolver", () => {
52
+ let server: SetupServerApi | undefined;
53
+
54
+ afterEach(() => {
55
+ server?.close();
56
+ });
57
+
58
+ test("requests Cloudflare DNS JSON with name, TXT type, Accept header, and encoded query", async () => {
59
+ server = setupServer(
60
+ http.get("https://cloudflare-dns.com/dns-query", ({ request }) => {
61
+ const url = new URL(request.url);
62
+ expect(url.searchParams.get("name")).toBe("cf example.test");
63
+ expect(url.searchParams.get("type")).toBe("TXT");
64
+ expect(request.headers.get("accept")).toBe("application/dns-json");
65
+ return HttpResponse.json(emptyDnsJson);
66
+ })
67
+ );
68
+ server.listen();
69
+
70
+ const out = await cloudflareDnsResolver("cf example.test");
71
+ expect(out).toMatchObject({ Status: 0, Answer: [] });
72
+ });
73
+
74
+ test("throws when Cloudflare DNS returns non-2xx", async () => {
75
+ server = setupServer(
76
+ http.get("https://cloudflare-dns.com/dns-query", () => new HttpResponse(null, { status: 502 }))
77
+ );
78
+ server.listen();
79
+
80
+ await expect(cloudflareDnsResolver("cf.example.test")).rejects.toThrow(/HTTP 502/);
81
+ });
82
+
83
+ test("throws when domain is empty", async () => {
84
+ await expect(cloudflareDnsResolver("")).rejects.toThrow("Domain is required");
85
+ });
86
+ });
87
+
88
+ describe("aliDnsResolver", () => {
89
+ let server: SetupServerApi | undefined;
90
+
91
+ afterEach(() => {
92
+ server?.close();
93
+ });
94
+
95
+ test("requests Ali DNS JSON with name, type 16 (TXT), and encoded query", async () => {
96
+ server = setupServer(
97
+ http.get("https://dns.alidns.com/resolve", ({ request }) => {
98
+ const url = new URL(request.url);
99
+ expect(url.searchParams.get("name")).toBe("ali example.test");
100
+ expect(url.searchParams.get("type")).toBe("16");
101
+ return HttpResponse.json(emptyDnsJson);
102
+ })
103
+ );
104
+ server.listen();
105
+
106
+ const out = await aliDnsResolver("ali example.test");
107
+ expect(out).toMatchObject({ Status: 0, Answer: [] });
108
+ });
109
+
110
+ test("throws when Ali DNS returns non-2xx", async () => {
111
+ server = setupServer(http.get("https://dns.alidns.com/resolve", () => new HttpResponse(null, { status: 503 })));
112
+ server.listen();
113
+
114
+ await expect(aliDnsResolver("ali.example.test")).rejects.toThrow(/HTTP 503/);
115
+ });
116
+
117
+ test("throws when domain is empty", async () => {
118
+ await expect(aliDnsResolver("")).rejects.toThrow("Domain is required");
119
+ });
120
+ });
@@ -0,0 +1,27 @@
1
+ import type { CustomDnsResolver, IDNSQueryResponse } from "../..";
2
+
3
+ export const googleDnsResolver: CustomDnsResolver = async (domain) => {
4
+ const url = new URL("https://dns.google/resolve");
5
+
6
+ if (!domain) {
7
+ throw new Error("Domain is required");
8
+ }
9
+
10
+ url.searchParams.set("name", domain);
11
+ url.searchParams.set("type", "TXT");
12
+
13
+ const res = await fetch(url);
14
+
15
+ if (!res.ok) {
16
+ throw new Error(`Google DNS request failed: HTTP ${res.status}`);
17
+ }
18
+
19
+ let data;
20
+ try {
21
+ data = await res.json();
22
+ } catch {
23
+ throw new Error("Failed to parse DNS response JSON");
24
+ }
25
+
26
+ return data as IDNSQueryResponse;
27
+ };
@@ -0,0 +1,3 @@
1
+ export * from "./google-dns-resolver";
2
+ export * from "./cloudflare-dns-resolver";
3
+ export * from "./ali-dns-resolver";