nlcurl 0.5.0 → 0.7.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.
Files changed (178) hide show
  1. package/README.md +41 -8
  2. package/dist/cache/store.d.ts +89 -0
  3. package/dist/cache/store.d.ts.map +1 -0
  4. package/dist/cache/store.js +402 -0
  5. package/dist/cache/store.js.map +1 -0
  6. package/dist/cache/types.d.ts +101 -0
  7. package/dist/cache/types.d.ts.map +1 -0
  8. package/dist/cache/types.js +2 -0
  9. package/dist/cache/types.js.map +1 -0
  10. package/dist/cookies/jar.d.ts +20 -0
  11. package/dist/cookies/jar.d.ts.map +1 -1
  12. package/dist/cookies/jar.js +64 -10
  13. package/dist/cookies/jar.js.map +1 -1
  14. package/dist/cookies/parser.d.ts +2 -10
  15. package/dist/cookies/parser.d.ts.map +1 -1
  16. package/dist/cookies/parser.js +49 -2
  17. package/dist/cookies/parser.js.map +1 -1
  18. package/dist/cookies/psl-data.d.ts +12 -0
  19. package/dist/cookies/psl-data.d.ts.map +1 -0
  20. package/dist/cookies/psl-data.js +10166 -0
  21. package/dist/cookies/psl-data.js.map +1 -0
  22. package/dist/cookies/public-suffix.d.ts +37 -0
  23. package/dist/cookies/public-suffix.d.ts.map +1 -0
  24. package/dist/cookies/public-suffix.js +140 -0
  25. package/dist/cookies/public-suffix.js.map +1 -0
  26. package/dist/core/client.js +4 -0
  27. package/dist/core/client.js.map +1 -1
  28. package/dist/core/request.d.ts +73 -3
  29. package/dist/core/request.d.ts.map +1 -1
  30. package/dist/core/response.d.ts +22 -0
  31. package/dist/core/response.d.ts.map +1 -1
  32. package/dist/core/response.js +32 -0
  33. package/dist/core/response.js.map +1 -1
  34. package/dist/core/session.d.ts +23 -0
  35. package/dist/core/session.d.ts.map +1 -1
  36. package/dist/core/session.js +108 -4
  37. package/dist/core/session.js.map +1 -1
  38. package/dist/core/validation.d.ts +11 -0
  39. package/dist/core/validation.d.ts.map +1 -1
  40. package/dist/core/validation.js +22 -1
  41. package/dist/core/validation.js.map +1 -1
  42. package/dist/dns/codec.d.ts +37 -0
  43. package/dist/dns/codec.d.ts.map +1 -0
  44. package/dist/dns/codec.js +254 -0
  45. package/dist/dns/codec.js.map +1 -0
  46. package/dist/dns/doh-resolver.d.ts +52 -0
  47. package/dist/dns/doh-resolver.d.ts.map +1 -0
  48. package/dist/dns/doh-resolver.js +192 -0
  49. package/dist/dns/doh-resolver.js.map +1 -0
  50. package/dist/dns/https-rr.d.ts +51 -0
  51. package/dist/dns/https-rr.d.ts.map +1 -0
  52. package/dist/dns/https-rr.js +127 -0
  53. package/dist/dns/https-rr.js.map +1 -0
  54. package/dist/dns/types.d.ts +110 -0
  55. package/dist/dns/types.d.ts.map +1 -0
  56. package/dist/dns/types.js +34 -0
  57. package/dist/dns/types.js.map +1 -0
  58. package/dist/hsts/store.d.ts +41 -0
  59. package/dist/hsts/store.d.ts.map +1 -0
  60. package/dist/hsts/store.js +171 -0
  61. package/dist/hsts/store.js.map +1 -0
  62. package/dist/hsts/types.d.ts +28 -0
  63. package/dist/hsts/types.d.ts.map +1 -0
  64. package/dist/hsts/types.js +2 -0
  65. package/dist/hsts/types.js.map +1 -0
  66. package/dist/http/alt-svc.d.ts +85 -0
  67. package/dist/http/alt-svc.d.ts.map +1 -0
  68. package/dist/http/alt-svc.js +220 -0
  69. package/dist/http/alt-svc.js.map +1 -0
  70. package/dist/http/form-data.d.ts +59 -0
  71. package/dist/http/form-data.d.ts.map +1 -0
  72. package/dist/http/form-data.js +90 -0
  73. package/dist/http/form-data.js.map +1 -0
  74. package/dist/http/h1/client.d.ts.map +1 -1
  75. package/dist/http/h1/client.js +21 -3
  76. package/dist/http/h1/client.js.map +1 -1
  77. package/dist/http/h1/encoder.d.ts +17 -0
  78. package/dist/http/h1/encoder.d.ts.map +1 -1
  79. package/dist/http/h1/encoder.js +53 -5
  80. package/dist/http/h1/encoder.js.map +1 -1
  81. package/dist/http/h1/parser.d.ts.map +1 -1
  82. package/dist/http/h1/parser.js +7 -1
  83. package/dist/http/h1/parser.js.map +1 -1
  84. package/dist/http/h2/client.d.ts.map +1 -1
  85. package/dist/http/h2/client.js +58 -8
  86. package/dist/http/h2/client.js.map +1 -1
  87. package/dist/http/h2/hpack.d.ts +7 -0
  88. package/dist/http/h2/hpack.d.ts.map +1 -1
  89. package/dist/http/h2/hpack.js +13 -0
  90. package/dist/http/h2/hpack.js.map +1 -1
  91. package/dist/http/h3/detection.d.ts +17 -0
  92. package/dist/http/h3/detection.d.ts.map +1 -0
  93. package/dist/http/h3/detection.js +59 -0
  94. package/dist/http/h3/detection.js.map +1 -0
  95. package/dist/http/negotiator.d.ts +26 -1
  96. package/dist/http/negotiator.d.ts.map +1 -1
  97. package/dist/http/negotiator.js +93 -18
  98. package/dist/http/negotiator.js.map +1 -1
  99. package/dist/http/pool.d.ts +2 -2
  100. package/dist/http/pool.d.ts.map +1 -1
  101. package/dist/http/pool.js.map +1 -1
  102. package/dist/index.d.ts +19 -1
  103. package/dist/index.d.ts.map +1 -1
  104. package/dist/index.js +14 -0
  105. package/dist/index.js.map +1 -1
  106. package/dist/middleware/rate-limiter.d.ts.map +1 -1
  107. package/dist/middleware/rate-limiter.js +4 -0
  108. package/dist/middleware/rate-limiter.js.map +1 -1
  109. package/dist/middleware/retry.js +2 -2
  110. package/dist/middleware/retry.js.map +1 -1
  111. package/dist/proxy/env-proxy.d.ts +21 -0
  112. package/dist/proxy/env-proxy.d.ts.map +1 -0
  113. package/dist/proxy/env-proxy.js +74 -0
  114. package/dist/proxy/env-proxy.js.map +1 -0
  115. package/dist/proxy/http-proxy.d.ts +2 -0
  116. package/dist/proxy/http-proxy.d.ts.map +1 -1
  117. package/dist/proxy/http-proxy.js +29 -6
  118. package/dist/proxy/http-proxy.js.map +1 -1
  119. package/dist/proxy/socks.js +10 -1
  120. package/dist/proxy/socks.js.map +1 -1
  121. package/dist/sse/parser.d.ts +70 -0
  122. package/dist/sse/parser.d.ts.map +1 -0
  123. package/dist/sse/parser.js +153 -0
  124. package/dist/sse/parser.js.map +1 -0
  125. package/dist/tls/ech.d.ts +147 -0
  126. package/dist/tls/ech.d.ts.map +1 -0
  127. package/dist/tls/ech.js +401 -0
  128. package/dist/tls/ech.js.map +1 -0
  129. package/dist/tls/node-engine.d.ts +9 -1
  130. package/dist/tls/node-engine.d.ts.map +1 -1
  131. package/dist/tls/node-engine.js +54 -1
  132. package/dist/tls/node-engine.js.map +1 -1
  133. package/dist/tls/pin-verification.d.ts +9 -0
  134. package/dist/tls/pin-verification.d.ts.map +1 -0
  135. package/dist/tls/pin-verification.js +34 -0
  136. package/dist/tls/pin-verification.js.map +1 -0
  137. package/dist/tls/session-cache.d.ts +70 -0
  138. package/dist/tls/session-cache.d.ts.map +1 -0
  139. package/dist/tls/session-cache.js +80 -0
  140. package/dist/tls/session-cache.js.map +1 -0
  141. package/dist/tls/stealth/client-hello.d.ts +21 -0
  142. package/dist/tls/stealth/client-hello.d.ts.map +1 -1
  143. package/dist/tls/stealth/client-hello.js +116 -0
  144. package/dist/tls/stealth/client-hello.js.map +1 -1
  145. package/dist/tls/stealth/engine.d.ts.map +1 -1
  146. package/dist/tls/stealth/engine.js +152 -30
  147. package/dist/tls/stealth/engine.js.map +1 -1
  148. package/dist/tls/stealth/handshake.d.ts +2 -1
  149. package/dist/tls/stealth/handshake.d.ts.map +1 -1
  150. package/dist/tls/stealth/handshake.js +118 -5
  151. package/dist/tls/stealth/handshake.js.map +1 -1
  152. package/dist/tls/stealth/tls12-handshake.d.ts +14 -0
  153. package/dist/tls/stealth/tls12-handshake.d.ts.map +1 -0
  154. package/dist/tls/stealth/tls12-handshake.js +462 -0
  155. package/dist/tls/stealth/tls12-handshake.js.map +1 -0
  156. package/dist/tls/types.d.ts +40 -0
  157. package/dist/tls/types.d.ts.map +1 -1
  158. package/dist/utils/encoding.d.ts +8 -6
  159. package/dist/utils/encoding.d.ts.map +1 -1
  160. package/dist/utils/encoding.js +92 -24
  161. package/dist/utils/encoding.js.map +1 -1
  162. package/dist/utils/happy-eyeballs.d.ts +3 -0
  163. package/dist/utils/happy-eyeballs.d.ts.map +1 -1
  164. package/dist/utils/happy-eyeballs.js +42 -2
  165. package/dist/utils/happy-eyeballs.js.map +1 -1
  166. package/dist/ws/client.d.ts +3 -0
  167. package/dist/ws/client.d.ts.map +1 -1
  168. package/dist/ws/client.js +63 -7
  169. package/dist/ws/client.js.map +1 -1
  170. package/dist/ws/frame.d.ts +4 -2
  171. package/dist/ws/frame.d.ts.map +1 -1
  172. package/dist/ws/frame.js +18 -18
  173. package/dist/ws/frame.js.map +1 -1
  174. package/dist/ws/permessage-deflate.d.ts +58 -0
  175. package/dist/ws/permessage-deflate.d.ts.map +1 -0
  176. package/dist/ws/permessage-deflate.js +148 -0
  177. package/dist/ws/permessage-deflate.js.map +1 -0
  178. package/package.json +3 -2
@@ -0,0 +1,254 @@
1
+ /**
2
+ * Minimal DNS wire-format encoder/decoder for DNS-over-HTTPS (RFC 8484)
3
+ * and HTTPS/SVCB record parsing (RFC 9460).
4
+ *
5
+ * Supports building A/AAAA/HTTPS queries and parsing response packets
6
+ * including SvcParam extraction. Uses only Node.js built-in `Buffer`.
7
+ */
8
+ import { RCLASS, SvcParamKey } from "./types.js";
9
+ /**
10
+ * Builds a DNS query packet in wire format (RFC 1035 §4).
11
+ *
12
+ * @param name The domain name to query.
13
+ * @param type DNS record type (A = 1, AAAA = 28, HTTPS = 65).
14
+ * @param id 16-bit query ID.
15
+ * @returns Raw DNS query packet.
16
+ */
17
+ export function buildDNSQuery(name, type, id = 0) {
18
+ const labels = encodeName(name);
19
+ const buf = Buffer.alloc(12 + labels.length + 4);
20
+ buf.writeUInt16BE(id & 0xffff, 0);
21
+ buf.writeUInt16BE(0x0100, 2);
22
+ buf.writeUInt16BE(1, 4);
23
+ buf.writeUInt16BE(0, 6);
24
+ buf.writeUInt16BE(0, 8);
25
+ buf.writeUInt16BE(0, 10);
26
+ labels.copy(buf, 12);
27
+ buf.writeUInt16BE(type, 12 + labels.length);
28
+ buf.writeUInt16BE(RCLASS.IN, 12 + labels.length + 2);
29
+ return buf;
30
+ }
31
+ /**
32
+ * Encodes a domain name into DNS wire format labels.
33
+ */
34
+ function encodeName(name) {
35
+ const parts = name.replace(/\.$/, "").split(".");
36
+ const buffers = [];
37
+ for (const part of parts) {
38
+ const label = Buffer.from(part, "ascii");
39
+ if (label.length > 63)
40
+ throw new Error(`DNS label too long: "${part}"`);
41
+ buffers.push(Buffer.from([label.length]), label);
42
+ }
43
+ buffers.push(Buffer.from([0]));
44
+ return Buffer.concat(buffers);
45
+ }
46
+ /**
47
+ * Parses a DNS response packet and extracts answer records.
48
+ *
49
+ * @param packet Raw DNS response bytes.
50
+ * @returns Array of parsed DNS records from the answer section.
51
+ */
52
+ export function parseDNSResponse(packet) {
53
+ if (packet.length < 12)
54
+ throw new Error("DNS packet too short");
55
+ const flags = packet.readUInt16BE(2);
56
+ const rcode = flags & 0x000f;
57
+ if (rcode !== 0) {
58
+ throw new Error(`DNS query failed with RCODE ${rcode}`);
59
+ }
60
+ const qdcount = packet.readUInt16BE(4);
61
+ const ancount = packet.readUInt16BE(6);
62
+ let offset = 12;
63
+ for (let i = 0; i < qdcount; i++) {
64
+ const result = skipName(packet, offset);
65
+ offset = result + 4;
66
+ }
67
+ const records = [];
68
+ for (let i = 0; i < ancount; i++) {
69
+ const { name, newOffset } = decodeName(packet, offset);
70
+ offset = newOffset;
71
+ if (offset + 10 > packet.length)
72
+ break;
73
+ const type = packet.readUInt16BE(offset);
74
+ offset += 2;
75
+ packet.readUInt16BE(offset);
76
+ offset += 2;
77
+ const ttl = packet.readUInt32BE(offset);
78
+ offset += 4;
79
+ const rdlength = packet.readUInt16BE(offset);
80
+ offset += 2;
81
+ if (offset + rdlength > packet.length)
82
+ break;
83
+ const data = packet.subarray(offset, offset + rdlength);
84
+ offset += rdlength;
85
+ records.push({ name, type, ttl, data });
86
+ }
87
+ return records;
88
+ }
89
+ /**
90
+ * Decodes a DNS compressed name from a packet.
91
+ */
92
+ function decodeName(packet, offset) {
93
+ const labels = [];
94
+ let jumped = false;
95
+ let returnOffset = offset;
96
+ let depth = 0;
97
+ while (offset < packet.length) {
98
+ if (depth++ > 128)
99
+ throw new Error("DNS name compression loop detected");
100
+ const len = packet[offset];
101
+ if (len === 0) {
102
+ if (!jumped)
103
+ returnOffset = offset + 1;
104
+ break;
105
+ }
106
+ if ((len & 0xc0) === 0xc0) {
107
+ if (!jumped)
108
+ returnOffset = offset + 2;
109
+ offset = ((len & 0x3f) << 8) | packet[offset + 1];
110
+ jumped = true;
111
+ continue;
112
+ }
113
+ offset++;
114
+ labels.push(packet.subarray(offset, offset + len).toString("ascii"));
115
+ offset += len;
116
+ }
117
+ if (!jumped && labels.length > 0)
118
+ returnOffset = offset + 1;
119
+ return { name: labels.join("."), newOffset: returnOffset };
120
+ }
121
+ /**
122
+ * Skips past a DNS name in a packet (handles compression pointers).
123
+ */
124
+ function skipName(packet, offset) {
125
+ let depth = 0;
126
+ while (offset < packet.length) {
127
+ if (depth++ > 128)
128
+ throw new Error("DNS name compression loop detected");
129
+ const len = packet[offset];
130
+ if (len === 0)
131
+ return offset + 1;
132
+ if ((len & 0xc0) === 0xc0)
133
+ return offset + 2;
134
+ offset += 1 + len;
135
+ }
136
+ return offset;
137
+ }
138
+ /**
139
+ * Parses an A record (IPv4 address) from rdata.
140
+ */
141
+ export function parseARecord(data) {
142
+ if (data.length !== 4)
143
+ throw new Error("Invalid A record length");
144
+ return `${data[0]}.${data[1]}.${data[2]}.${data[3]}`;
145
+ }
146
+ /**
147
+ * Parses an AAAA record (IPv6 address) from rdata.
148
+ */
149
+ export function parseAAAARecord(data) {
150
+ if (data.length !== 16)
151
+ throw new Error("Invalid AAAA record length");
152
+ const groups = [];
153
+ for (let i = 0; i < 16; i += 2) {
154
+ groups.push(data.readUInt16BE(i).toString(16));
155
+ }
156
+ return groups.join(":");
157
+ }
158
+ /**
159
+ * Parses an HTTPS/SVCB record from rdata (RFC 9460 §2.2).
160
+ */
161
+ export function parseSVCBRecord(data) {
162
+ if (data.length < 3)
163
+ throw new Error("SVCB record too short");
164
+ const priority = data.readUInt16BE(0);
165
+ let offset = 2;
166
+ const labels = [];
167
+ while (offset < data.length) {
168
+ const len = data[offset];
169
+ if (len === 0) {
170
+ offset++;
171
+ break;
172
+ }
173
+ offset++;
174
+ if (offset + len > data.length)
175
+ break;
176
+ labels.push(data.subarray(offset, offset + len).toString("ascii"));
177
+ offset += len;
178
+ }
179
+ const target = labels.join(".");
180
+ const record = { priority, target };
181
+ while (offset + 4 <= data.length) {
182
+ const key = data.readUInt16BE(offset);
183
+ offset += 2;
184
+ const valLen = data.readUInt16BE(offset);
185
+ offset += 2;
186
+ if (offset + valLen > data.length)
187
+ break;
188
+ const value = data.subarray(offset, offset + valLen);
189
+ offset += valLen;
190
+ switch (key) {
191
+ case SvcParamKey.ALPN:
192
+ record.alpn = parseAlpnParam(value);
193
+ break;
194
+ case SvcParamKey.NO_DEFAULT_ALPN:
195
+ record.noDefaultAlpn = true;
196
+ break;
197
+ case SvcParamKey.PORT:
198
+ if (value.length >= 2)
199
+ record.port = value.readUInt16BE(0);
200
+ break;
201
+ case SvcParamKey.IPV4HINT:
202
+ record.ipv4Hints = parseIPv4Hints(value);
203
+ break;
204
+ case SvcParamKey.IPV6HINT:
205
+ record.ipv6Hints = parseIPv6Hints(value);
206
+ break;
207
+ case SvcParamKey.ECH:
208
+ record.echConfigList = Buffer.from(value);
209
+ break;
210
+ }
211
+ }
212
+ return record;
213
+ }
214
+ /**
215
+ * Parses the ALPN SvcParam — length-prefixed protocol identifier list.
216
+ */
217
+ function parseAlpnParam(data) {
218
+ const protocols = [];
219
+ let offset = 0;
220
+ while (offset < data.length) {
221
+ const len = data[offset];
222
+ offset++;
223
+ if (offset + len > data.length)
224
+ break;
225
+ protocols.push(data.subarray(offset, offset + len).toString("ascii"));
226
+ offset += len;
227
+ }
228
+ return protocols;
229
+ }
230
+ /**
231
+ * Parses IPv4 address hints from SvcParam value.
232
+ */
233
+ function parseIPv4Hints(data) {
234
+ const addresses = [];
235
+ for (let i = 0; i + 4 <= data.length; i += 4) {
236
+ addresses.push(`${data[i]}.${data[i + 1]}.${data[i + 2]}.${data[i + 3]}`);
237
+ }
238
+ return addresses;
239
+ }
240
+ /**
241
+ * Parses IPv6 address hints from SvcParam value.
242
+ */
243
+ function parseIPv6Hints(data) {
244
+ const addresses = [];
245
+ for (let i = 0; i + 16 <= data.length; i += 16) {
246
+ const groups = [];
247
+ for (let j = 0; j < 16; j += 2) {
248
+ groups.push(data.readUInt16BE(i + j).toString(16));
249
+ }
250
+ addresses.push(groups.join(":"));
251
+ }
252
+ return addresses;
253
+ }
254
+ //# sourceMappingURL=codec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codec.js","sourceRoot":"","sources":["../../src/dns/codec.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ import type { DoHConfig, DNSRecord } from "./types.js";
2
+ /**
3
+ * DNS-over-HTTPS resolver for encrypted DNS resolution.
4
+ *
5
+ * Usage:
6
+ * ```typescript
7
+ * const resolver = new DoHResolver({ server: "https://1.1.1.1/dns-query" });
8
+ * const records = await resolver.query("example.com", "A");
9
+ * ```
10
+ */
11
+ export declare class DoHResolver {
12
+ private readonly serverUrl;
13
+ private readonly method;
14
+ private readonly timeout;
15
+ private readonly bootstrap;
16
+ private queryId;
17
+ constructor(config: DoHConfig);
18
+ /**
19
+ * Sends a DNS query over HTTPS and returns the parsed answer records.
20
+ *
21
+ * @param name Domain name to resolve.
22
+ * @param type Record type: "A", "AAAA", "HTTPS", or "SVCB".
23
+ * @param signal Optional AbortSignal for cancellation.
24
+ * @returns Array of raw DNS records from the answer section.
25
+ */
26
+ query(name: string, type: "A" | "AAAA" | "HTTPS" | "SVCB", signal?: AbortSignal): Promise<DNSRecord[]>;
27
+ /**
28
+ * Sends the DNS wire-format query to the DoH server.
29
+ */
30
+ private sendQuery;
31
+ /**
32
+ * GET method: base64url-encode the DNS query in the `dns` query parameter.
33
+ */
34
+ private sendGET;
35
+ /**
36
+ * POST method: send DNS wire-format directly in the request body.
37
+ */
38
+ private sendPOST;
39
+ /**
40
+ * Make the HTTPS request with timeout and abort support.
41
+ */
42
+ private httpRequest;
43
+ /**
44
+ * Resolves the DoH server hostname to an IP address (bootstrap).
45
+ * Uses the system DNS resolver for this bootstrap step to avoid
46
+ * a circular dependency (we can't use DoH to resolve the DoH server).
47
+ *
48
+ * If the server URL already uses an IP address, returns it directly.
49
+ */
50
+ private resolveServerHost;
51
+ }
52
+ //# sourceMappingURL=doh-resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doh-resolver.d.ts","sourceRoot":"","sources":["../../src/dns/doh-resolver.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AASvD;;;;;;;;GAQG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAM;IAChC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiB;IACxC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAU;IACpC,OAAO,CAAC,OAAO,CAAK;gBAER,MAAM,EAAE,SAAS;IAO7B;;;;;;;OAOG;IACG,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAS5G;;OAEG;YACW,SAAS;IASvB;;OAEG;IACH,OAAO,CAAC,OAAO;IAqBf;;OAEG;IACH,OAAO,CAAC,QAAQ;IAmBhB;;OAEG;IACH,OAAO,CAAC,WAAW;IA+EnB;;;;;;OAMG;YACW,iBAAiB;CAmBhC"}
@@ -0,0 +1,192 @@
1
+ /**
2
+ * DNS-over-HTTPS (DoH) resolver implementing RFC 8484.
3
+ *
4
+ * Sends DNS queries over HTTPS to a configured DoH server, providing
5
+ * encrypted DNS resolution for privacy. Supports both GET (wire-format
6
+ * in query parameter) and POST (wire-format in body) methods.
7
+ *
8
+ * @see https://datatracker.ietf.org/doc/html/rfc8484
9
+ */
10
+ import * as https from "node:https";
11
+ import * as http from "node:http";
12
+ import { lookup } from "node:dns/promises";
13
+ import { buildDNSQuery, parseDNSResponse } from "./codec.js";
14
+ import { RTYPE } from "./types.js";
15
+ const DOH_CONTENT_TYPE = "application/dns-message";
16
+ const DEFAULT_TIMEOUT = 5000;
17
+ /** Cache of bootstrapped DoH server IP → avoid infinite recursion. */
18
+ const bootstrapCache = new Map();
19
+ /**
20
+ * DNS-over-HTTPS resolver for encrypted DNS resolution.
21
+ *
22
+ * Usage:
23
+ * ```typescript
24
+ * const resolver = new DoHResolver({ server: "https://1.1.1.1/dns-query" });
25
+ * const records = await resolver.query("example.com", "A");
26
+ * ```
27
+ */
28
+ export class DoHResolver {
29
+ serverUrl;
30
+ method;
31
+ timeout;
32
+ bootstrap;
33
+ queryId = 0;
34
+ constructor(config) {
35
+ this.serverUrl = new URL(config.server);
36
+ this.method = config.method ?? "POST";
37
+ this.timeout = config.timeout ?? DEFAULT_TIMEOUT;
38
+ this.bootstrap = config.bootstrap ?? true;
39
+ }
40
+ /**
41
+ * Sends a DNS query over HTTPS and returns the parsed answer records.
42
+ *
43
+ * @param name Domain name to resolve.
44
+ * @param type Record type: "A", "AAAA", "HTTPS", or "SVCB".
45
+ * @param signal Optional AbortSignal for cancellation.
46
+ * @returns Array of raw DNS records from the answer section.
47
+ */
48
+ async query(name, type, signal) {
49
+ const rtype = RTYPE[type];
50
+ const id = this.queryId++ & 0xffff;
51
+ const queryPacket = buildDNSQuery(name, rtype, id);
52
+ const responseData = await this.sendQuery(queryPacket, signal);
53
+ return parseDNSResponse(responseData);
54
+ }
55
+ /**
56
+ * Sends the DNS wire-format query to the DoH server.
57
+ */
58
+ async sendQuery(packet, signal) {
59
+ const host = await this.resolveServerHost();
60
+ if (this.method === "GET") {
61
+ return this.sendGET(packet, host, signal);
62
+ }
63
+ return this.sendPOST(packet, host, signal);
64
+ }
65
+ /**
66
+ * GET method: base64url-encode the DNS query in the `dns` query parameter.
67
+ */
68
+ sendGET(packet, host, signal) {
69
+ const encoded = packet.toString("base64url");
70
+ const url = new URL(this.serverUrl.toString());
71
+ url.searchParams.set("dns", encoded);
72
+ return this.httpRequest({
73
+ method: "GET",
74
+ hostname: host,
75
+ port: parseInt(this.serverUrl.port) || 443,
76
+ path: `${url.pathname}${url.search}`,
77
+ headers: {
78
+ accept: DOH_CONTENT_TYPE,
79
+ host: this.serverUrl.hostname,
80
+ },
81
+ }, undefined, signal);
82
+ }
83
+ /**
84
+ * POST method: send DNS wire-format directly in the request body.
85
+ */
86
+ sendPOST(packet, host, signal) {
87
+ return this.httpRequest({
88
+ method: "POST",
89
+ hostname: host,
90
+ port: parseInt(this.serverUrl.port) || 443,
91
+ path: this.serverUrl.pathname,
92
+ headers: {
93
+ "content-type": DOH_CONTENT_TYPE,
94
+ accept: DOH_CONTENT_TYPE,
95
+ "content-length": packet.length.toString(),
96
+ host: this.serverUrl.hostname,
97
+ },
98
+ }, packet, signal);
99
+ }
100
+ /**
101
+ * Make the HTTPS request with timeout and abort support.
102
+ */
103
+ httpRequest(options, body, signal) {
104
+ return new Promise((resolve, reject) => {
105
+ if (signal?.aborted) {
106
+ reject(new Error("DoH query aborted"));
107
+ return;
108
+ }
109
+ const isHTTPS = this.serverUrl.protocol === "https:";
110
+ const requestFn = isHTTPS ? https.request : http.request;
111
+ const tlsOpts = isHTTPS
112
+ ? {
113
+ ...options,
114
+ rejectUnauthorized: true,
115
+ servername: this.serverUrl.hostname,
116
+ }
117
+ : options;
118
+ const req = requestFn(tlsOpts, (res) => {
119
+ if (res.statusCode !== 200) {
120
+ req.destroy();
121
+ reject(new Error(`DoH server returned HTTP ${res.statusCode}`));
122
+ return;
123
+ }
124
+ const contentType = res.headers["content-type"];
125
+ if (contentType && !contentType.includes(DOH_CONTENT_TYPE)) {
126
+ req.destroy();
127
+ reject(new Error(`DoH server returned unexpected content-type: ${contentType}`));
128
+ return;
129
+ }
130
+ const chunks = [];
131
+ let totalSize = 0;
132
+ const MAX_DNS_RESPONSE = 65535;
133
+ res.on("data", (chunk) => {
134
+ totalSize += chunk.length;
135
+ if (totalSize > MAX_DNS_RESPONSE) {
136
+ req.destroy();
137
+ reject(new Error("DoH response exceeds maximum DNS message size"));
138
+ return;
139
+ }
140
+ chunks.push(chunk);
141
+ });
142
+ res.on("end", () => {
143
+ resolve(Buffer.concat(chunks));
144
+ });
145
+ res.on("error", reject);
146
+ });
147
+ req.on("error", reject);
148
+ const timer = setTimeout(() => {
149
+ req.destroy();
150
+ reject(new Error("DoH query timed out"));
151
+ }, this.timeout);
152
+ req.once("close", () => clearTimeout(timer));
153
+ if (signal) {
154
+ const onAbort = () => {
155
+ clearTimeout(timer);
156
+ req.destroy();
157
+ reject(new Error("DoH query aborted"));
158
+ };
159
+ signal.addEventListener("abort", onAbort, { once: true });
160
+ req.once("close", () => signal.removeEventListener("abort", onAbort));
161
+ }
162
+ if (body) {
163
+ req.write(body);
164
+ }
165
+ req.end();
166
+ });
167
+ }
168
+ /**
169
+ * Resolves the DoH server hostname to an IP address (bootstrap).
170
+ * Uses the system DNS resolver for this bootstrap step to avoid
171
+ * a circular dependency (we can't use DoH to resolve the DoH server).
172
+ *
173
+ * If the server URL already uses an IP address, returns it directly.
174
+ */
175
+ async resolveServerHost() {
176
+ const host = this.serverUrl.hostname;
177
+ if (/^\d+\.\d+\.\d+\.\d+$/.test(host) || host.includes(":")) {
178
+ return host;
179
+ }
180
+ const cached = bootstrapCache.get(host);
181
+ if (cached)
182
+ return cached;
183
+ if (!this.bootstrap) {
184
+ return host;
185
+ }
186
+ const result = await lookup(host, { family: 0 });
187
+ const address = Array.isArray(result) ? result[0].address : result.address;
188
+ bootstrapCache.set(host, address);
189
+ return address;
190
+ }
191
+ }
192
+ //# sourceMappingURL=doh-resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doh-resolver.js","sourceRoot":"","sources":["../../src/dns/doh-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,KAAK,MAAM,YAAY,CAAC;AACpC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE7D,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC,MAAM,gBAAgB,GAAG,yBAAyB,CAAC;AACnD,MAAM,eAAe,GAAG,IAAI,CAAC;AAE7B,sEAAsE;AACtE,MAAM,cAAc,GAAG,IAAI,GAAG,EAAkB,CAAC;AAEjD;;;;;;;;GAQG;AACH,MAAM,OAAO,WAAW;IACL,SAAS,CAAM;IACf,MAAM,CAAiB;IACvB,OAAO,CAAS;IAChB,SAAS,CAAU;IAC5B,OAAO,GAAG,CAAC,CAAC;IAEpB,YAAY,MAAiB;QAC3B,IAAI,CAAC,SAAS,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACxC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC;QACtC,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO,IAAI,eAAe,CAAC;QACjD,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC,SAAS,IAAI,IAAI,CAAC;IAC5C,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,KAAK,CAAC,IAAY,EAAE,IAAqC,EAAE,MAAoB;QACnF,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC;QAC1B,MAAM,EAAE,GAAG,IAAI,CAAC,OAAO,EAAE,GAAG,MAAM,CAAC;QACnC,MAAM,WAAW,GAAG,aAAa,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;QAEnD,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;QAC/D,OAAO,gBAAgB,CAAC,YAAY,CAAC,CAAC;IACxC,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,SAAS,CAAC,MAAc,EAAE,MAAoB;QAC1D,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAE5C,IAAI,IAAI,CAAC,MAAM,KAAK,KAAK,EAAE,CAAC;YAC1B,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;QAC5C,CAAC;QACD,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;IAC7C,CAAC;IAED;;OAEG;IACK,OAAO,CAAC,MAAc,EAAE,IAAY,EAAE,MAAoB;QAChE,MAAM,OAAO,GAAG,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;QAC7C,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,CAAC,CAAC;QAC/C,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QAErC,OAAO,IAAI,CAAC,WAAW,CACrB;YACE,MAAM,EAAE,KAAK;YACb,QAAQ,EAAE,IAAI;YACd,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,GAAG;YAC1C,IAAI,EAAE,GAAG,GAAG,CAAC,QAAQ,GAAG,GAAG,CAAC,MAAM,EAAE;YACpC,OAAO,EAAE;gBACP,MAAM,EAAE,gBAAgB;gBACxB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ;aAC9B;SACF,EACD,SAAS,EACT,MAAM,CACP,CAAC;IACJ,CAAC;IAED;;OAEG;IACK,QAAQ,CAAC,MAAc,EAAE,IAAY,EAAE,MAAoB;QACjE,OAAO,IAAI,CAAC,WAAW,CACrB;YACE,MAAM,EAAE,MAAM;YACd,QAAQ,EAAE,IAAI;YACd,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,GAAG;YAC1C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ;YAC7B,OAAO,EAAE;gBACP,cAAc,EAAE,gBAAgB;gBAChC,MAAM,EAAE,gBAAgB;gBACxB,gBAAgB,EAAE,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE;gBAC1C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ;aAC9B;SACF,EACD,MAAM,EACN,MAAM,CACP,CAAC;IACJ,CAAC;IAED;;OAEG;IACK,WAAW,CAAC,OAA6B,EAAE,IAAwB,EAAE,MAAoB;QAC/F,OAAO,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAC7C,IAAI,MAAM,EAAE,OAAO,EAAE,CAAC;gBACpB,MAAM,CAAC,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC,CAAC;gBACvC,OAAO;YACT,CAAC;YAED,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,KAAK,QAAQ,CAAC;YACrD,MAAM,SAAS,GAAG,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC;YAEzD,MAAM,OAAO,GAAyB,OAAO;gBAC3C,CAAC,CAAC;oBACE,GAAG,OAAO;oBACV,kBAAkB,EAAE,IAAI;oBACxB,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ;iBACpC;gBACH,CAAC,CAAC,OAAO,CAAC;YAEZ,MAAM,GAAG,GAAG,SAAS,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;gBACrC,IAAI,GAAG,CAAC,UAAU,KAAK,GAAG,EAAE,CAAC;oBAC3B,GAAG,CAAC,OAAO,EAAE,CAAC;oBACd,MAAM,CAAC,IAAI,KAAK,CAAC,4BAA4B,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;oBAChE,OAAO;gBACT,CAAC;gBAED,MAAM,WAAW,GAAG,GAAG,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;gBAChD,IAAI,WAAW,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,CAAC;oBAC3D,GAAG,CAAC,OAAO,EAAE,CAAC;oBACd,MAAM,CAAC,IAAI,KAAK,CAAC,gDAAgD,WAAW,EAAE,CAAC,CAAC,CAAC;oBACjF,OAAO;gBACT,CAAC;gBAED,MAAM,MAAM,GAAa,EAAE,CAAC;gBAC5B,IAAI,SAAS,GAAG,CAAC,CAAC;gBAClB,MAAM,gBAAgB,GAAG,KAAK,CAAC;gBAE/B,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;oBAC/B,SAAS,IAAI,KAAK,CAAC,MAAM,CAAC;oBAC1B,IAAI,SAAS,GAAG,gBAAgB,EAAE,CAAC;wBACjC,GAAG,CAAC,OAAO,EAAE,CAAC;wBACd,MAAM,CAAC,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC,CAAC;wBACnE,OAAO;oBACT,CAAC;oBACD,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBACrB,CAAC,CAAC,CAAC;gBAEH,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;oBACjB,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;gBACjC,CAAC,CAAC,CAAC;gBAEH,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YAC1B,CAAC,CAAC,CAAC;YAEH,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YAExB,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;gBAC5B,GAAG,CAAC,OAAO,EAAE,CAAC;gBACd,MAAM,CAAC,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC,CAAC;YAC3C,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;YAEjB,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC;YAE7C,IAAI,MAAM,EAAE,CAAC;gBACX,MAAM,OAAO,GAAG,GAAG,EAAE;oBACnB,YAAY,CAAC,KAAK,CAAC,CAAC;oBACpB,GAAG,CAAC,OAAO,EAAE,CAAC;oBACd,MAAM,CAAC,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC,CAAC;gBACzC,CAAC,CAAC;gBACF,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;gBAC1D,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;YACxE,CAAC;YAED,IAAI,IAAI,EAAE,CAAC;gBACT,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAClB,CAAC;YACD,GAAG,CAAC,GAAG,EAAE,CAAC;QACZ,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;;;;OAMG;IACK,KAAK,CAAC,iBAAiB;QAC7B,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC;QAErC,IAAI,sBAAsB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YAC5D,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,MAAM,GAAG,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACxC,IAAI,MAAM;YAAE,OAAO,MAAM,CAAC;QAE1B,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YACpB,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;QACjD,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAE,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC;QAC5E,cAAc,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAClC,OAAO,OAAO,CAAC;IACjB,CAAC;CACF"}
@@ -0,0 +1,51 @@
1
+ import { type SVCBRecord, type DoHConfig, type ResolvedAddress } from "./types.js";
2
+ /**
3
+ * Result of an HTTPS-RR lookup, combining SVCB records with address hints.
4
+ */
5
+ export interface HTTPSRRResult {
6
+ /** Parsed SVCB/HTTPS records, sorted by priority (lowest first). */
7
+ svcb: SVCBRecord[];
8
+ /** ECH config list from the highest-priority record that has one. */
9
+ echConfigList?: Buffer;
10
+ /** ALPN protocols from the highest-priority ServiceMode record. */
11
+ alpn?: string[];
12
+ /** IP address hints extracted from SVCB records + A/AAAA. */
13
+ addresses: ResolvedAddress[];
14
+ /** Port override from the SVCB record, if any. */
15
+ port?: number;
16
+ }
17
+ /**
18
+ * Resolver for HTTPS/SVCB DNS Resource Records (RFC 9460).
19
+ *
20
+ * Supports two modes:
21
+ * 1. **System DNS** — Uses `node:dns` (resolveAny or raw query).
22
+ * 2. **DoH** — Uses a DoHResolver for encrypted HTTPS-RR queries.
23
+ */
24
+ export declare class HTTPSRRResolver {
25
+ private readonly dohResolver?;
26
+ private readonly systemResolver;
27
+ constructor(dohConfig?: DoHConfig);
28
+ /**
29
+ * Queries HTTPS records for a hostname and parses the results.
30
+ *
31
+ * @param hostname The domain to look up HTTPS records for.
32
+ * @param signal Optional AbortSignal for cancellation.
33
+ * @returns Parsed HTTPS-RR result with SVCB params, or null if no records found.
34
+ */
35
+ resolve(hostname: string, signal?: AbortSignal): Promise<HTTPSRRResult | null>;
36
+ /**
37
+ * Resolves HTTPS records via DoH.
38
+ */
39
+ private resolveViaDoH;
40
+ /**
41
+ * Resolves HTTPS records via the system DNS resolver.
42
+ * Uses raw DNS query + decode since node:dns doesn't natively support type 65.
43
+ */
44
+ private resolveViaSystem;
45
+ /**
46
+ * Parses a native Node.js HTTPS DNS record into our SVCBRecord format.
47
+ * Node.js 22+ may return HTTPS records with a specific structure.
48
+ */
49
+ private parseNativeHTTPSRecord;
50
+ }
51
+ //# sourceMappingURL=https-rr.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"https-rr.d.ts","sourceRoot":"","sources":["../../src/dns/https-rr.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAS,KAAK,UAAU,EAAE,KAAK,SAAS,EAAE,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AAE1F;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,oEAAoE;IACpE,IAAI,EAAE,UAAU,EAAE,CAAC;IACnB,qEAAqE;IACrE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,mEAAmE;IACnE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,6DAA6D;IAC7D,SAAS,EAAE,eAAe,EAAE,CAAC;IAC7B,kDAAkD;IAClD,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;;GAMG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAc;IAC3C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAW;gBAE9B,SAAS,CAAC,EAAE,SAAS;IAOjC;;;;;;OAMG;IACG,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IA+CpF;;OAEG;YACW,aAAa;IAK3B;;;OAGG;YACW,gBAAgB;IAU9B;;;OAGG;IACH,OAAO,CAAC,sBAAsB;CAc/B"}