domainkit 0.2.1 → 0.3.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 +31 -26
- package/dist/index.d.mts +639 -187
- package/dist/index.mjs +301 -300
- package/dist/index.mjs.map +1 -1
- package/dist/promise.d.mts +464 -0
- package/dist/promise.mjs +636 -0
- package/dist/promise.mjs.map +1 -0
- package/dist/{resolver-CxE-VDw4.mjs → resolver-CvK7JoJM.mjs} +401 -54
- package/dist/resolver-CvK7JoJM.mjs.map +1 -0
- package/dist/{resolver-BbVzTbCX.d.mts → resolver-uqe1h5Vz.d.mts} +1513 -25
- package/dist/testing.d.mts +4 -5
- package/dist/testing.mjs +17 -3
- package/dist/testing.mjs.map +1 -1
- package/dist/verify-B00c81-n.mjs +2420 -0
- package/dist/verify-B00c81-n.mjs.map +1 -0
- package/dist/{verify-7_EbKbFM.d.mts → verify-CkgVrwyl.d.mts} +318 -21
- package/package.json +6 -30
- package/dist/auth-6-KRy1gN.mjs +0 -671
- package/dist/auth-6-KRy1gN.mjs.map +0 -1
- package/dist/auth-B4jq3yk3.mjs +0 -738
- package/dist/auth-B4jq3yk3.mjs.map +0 -1
- package/dist/authorization-CQSKKMrW.mjs +0 -255
- package/dist/authorization-CQSKKMrW.mjs.map +0 -1
- package/dist/canonical-json-CpvqGMcr.mjs +0 -57
- package/dist/canonical-json-CpvqGMcr.mjs.map +0 -1
- package/dist/cloudflare-Bg_p-M2c.mjs +0 -69
- package/dist/cloudflare-Bg_p-M2c.mjs.map +0 -1
- package/dist/cloudflare-FDl5q7Dm.d.mts +0 -32
- package/dist/cloudflare.d.mts +0 -3
- package/dist/cloudflare.mjs +0 -2
- package/dist/connection-BXoSrhZs.mjs +0 -58
- package/dist/connection-BXoSrhZs.mjs.map +0 -1
- package/dist/connection-CEqadXSm.d.mts +0 -182
- package/dist/connection-X3rZhrBZ.mjs +0 -186
- package/dist/connection-X3rZhrBZ.mjs.map +0 -1
- package/dist/connection-api-DftK7eHo.d.mts +0 -7
- package/dist/dns-record-BLeqo56C.mjs +0 -289
- package/dist/dns-record-BLeqo56C.mjs.map +0 -1
- package/dist/effect-cloudflare.d.mts +0 -2
- package/dist/effect-cloudflare.mjs +0 -2
- package/dist/effect-vercel.d.mts +0 -2
- package/dist/effect-vercel.mjs +0 -2
- package/dist/effect.d.mts +0 -36
- package/dist/effect.mjs +0 -50
- package/dist/effect.mjs.map +0 -1
- package/dist/index-B1opsgls.d.mts +0 -159
- package/dist/index-Ewc__bQu.d.mts +0 -145
- package/dist/resolver-CxE-VDw4.mjs.map +0 -1
- package/dist/vercel-BIFpZRPu.mjs +0 -60
- package/dist/vercel-BIFpZRPu.mjs.map +0 -1
- package/dist/vercel-DgJaUX9D.d.mts +0 -31
- package/dist/vercel.d.mts +0 -3
- package/dist/vercel.mjs +0 -2
- package/dist/verify-Q5RYpnuw.mjs +0 -642
- package/dist/verify-Q5RYpnuw.mjs.map +0 -1
- package/dist/zone-discovery-BA5I24vu.d.mts +0 -1390
- package/dist/zones-DKHTpX9C.mjs +0 -19
- package/dist/zones-DKHTpX9C.mjs.map +0 -1
package/dist/verify-Q5RYpnuw.mjs
DELETED
|
@@ -1,642 +0,0 @@
|
|
|
1
|
-
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
|
|
2
|
-
import { E as decode$1, O as parse$1, T as Schema$2, _ as equals, b as Service$1, k as Error$1, m as data } from "./dns-record-BLeqo56C.mjs";
|
|
3
|
-
import { t as candidates } from "./zones-DKHTpX9C.mjs";
|
|
4
|
-
import { a as authorizationError, i as assertGrant, t as AuthorizationError } from "./connection-BXoSrhZs.mjs";
|
|
5
|
-
import { i as toAsync$3, n as Service$2, t as Error$2, x as authorize$1 } from "./resolver-CxE-VDw4.mjs";
|
|
6
|
-
import { Context, Data, Effect, Layer, Option, Schema, SchemaIssue, SchemaTransformation } from "effect";
|
|
7
|
-
import * as DnsPacket from "@leichtgewicht/dns-packet";
|
|
8
|
-
//#region package.json
|
|
9
|
-
var version = "0.2.1";
|
|
10
|
-
//#endregion
|
|
11
|
-
//#region src/auth/diagnostic.ts
|
|
12
|
-
var diagnostic_exports = /* @__PURE__ */ __exportAll({
|
|
13
|
-
Category: () => Category,
|
|
14
|
-
Fields: () => Fields,
|
|
15
|
-
RetryAdvice: () => RetryAdvice,
|
|
16
|
-
from: () => from
|
|
17
|
-
});
|
|
18
|
-
const Category = Schema.Literals([
|
|
19
|
-
"authorization",
|
|
20
|
-
"configuration",
|
|
21
|
-
"provider",
|
|
22
|
-
"storage",
|
|
23
|
-
"validation"
|
|
24
|
-
]);
|
|
25
|
-
const RetryAdvice = Schema.Literals([
|
|
26
|
-
"never",
|
|
27
|
-
"after-user-action",
|
|
28
|
-
"safe",
|
|
29
|
-
"unknown"
|
|
30
|
-
]);
|
|
31
|
-
const Fields = {
|
|
32
|
-
category: Category,
|
|
33
|
-
message: Schema.String,
|
|
34
|
-
operation: Schema.String,
|
|
35
|
-
retry: RetryAdvice
|
|
36
|
-
};
|
|
37
|
-
function from(error) {
|
|
38
|
-
return {
|
|
39
|
-
category: error.category,
|
|
40
|
-
message: error.message,
|
|
41
|
-
operation: error.operation,
|
|
42
|
-
retry: error.retry,
|
|
43
|
-
tag: error._tag
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
//#endregion
|
|
47
|
-
//#region src/auth/manifest.ts
|
|
48
|
-
var manifest_exports = /* @__PURE__ */ __exportAll({
|
|
49
|
-
Method: () => Method,
|
|
50
|
-
Schema: () => Schema$1,
|
|
51
|
-
decode: () => decode
|
|
52
|
-
});
|
|
53
|
-
const Method = Schema.TaggedUnion({
|
|
54
|
-
integration: {
|
|
55
|
-
capabilities: Schema.Array(Schema.Literals(["dns:read", "dns:write"])),
|
|
56
|
-
installUrl: Schema.String,
|
|
57
|
-
tokenEndpoint: Schema.String
|
|
58
|
-
},
|
|
59
|
-
oauth2: {
|
|
60
|
-
authorizationServer: Schema.Struct({
|
|
61
|
-
authorization_endpoint: Schema.String,
|
|
62
|
-
issuer: Schema.String,
|
|
63
|
-
revocation_endpoint: Schema.optionalKey(Schema.String),
|
|
64
|
-
token_endpoint: Schema.String
|
|
65
|
-
}),
|
|
66
|
-
capabilities: Schema.Array(Schema.Literals(["dns:read", "dns:write"])),
|
|
67
|
-
clientAuth: Schema.Literals([
|
|
68
|
-
"none",
|
|
69
|
-
"client_secret_basic",
|
|
70
|
-
"client_secret_post"
|
|
71
|
-
]),
|
|
72
|
-
scopes: Schema.Array(Schema.String)
|
|
73
|
-
},
|
|
74
|
-
token: {
|
|
75
|
-
capabilities: Schema.Array(Schema.Literals(["dns:read", "dns:write"])),
|
|
76
|
-
instructionsUrl: Schema.String
|
|
77
|
-
}
|
|
78
|
-
});
|
|
79
|
-
const Schema$1 = Schema.Struct({
|
|
80
|
-
methods: Schema.Array(Method),
|
|
81
|
-
providerId: Schema.String
|
|
82
|
-
});
|
|
83
|
-
const decode = Effect.fn("ProviderAuth.decode")((input) => Schema.decodeUnknownEffect(Schema$1)(input).pipe(Effect.mapError((cause) => new Error$1({ message: cause.message }))));
|
|
84
|
-
//#endregion
|
|
85
|
-
//#region src/discovery/selection.ts
|
|
86
|
-
var selection_exports = /* @__PURE__ */ __exportAll({
|
|
87
|
-
Evidence: () => Evidence,
|
|
88
|
-
Selection: () => Selection,
|
|
89
|
-
select: () => select
|
|
90
|
-
});
|
|
91
|
-
const Evidence = Schema.Struct({
|
|
92
|
-
accountId: Schema.String,
|
|
93
|
-
connectionId: Schema.String,
|
|
94
|
-
decisiveNameserverMatch: Schema.Boolean,
|
|
95
|
-
matchedNameservers: Schema.Array(Schema$2),
|
|
96
|
-
providerId: Schema.String,
|
|
97
|
-
zone: Schema$2
|
|
98
|
-
});
|
|
99
|
-
const Selection = Schema.TaggedUnion({
|
|
100
|
-
selected: {
|
|
101
|
-
candidate: Evidence,
|
|
102
|
-
evidence: Schema.Array(Evidence),
|
|
103
|
-
reason: Schema.Literals(["explicit", "unique-nameserver-match"])
|
|
104
|
-
},
|
|
105
|
-
manual: {
|
|
106
|
-
evidence: Schema.Array(Evidence),
|
|
107
|
-
reason: Schema.Literals(["ambiguous", "unsupported"])
|
|
108
|
-
}
|
|
109
|
-
});
|
|
110
|
-
function select(input) {
|
|
111
|
-
const domain = parse$1(input.domain);
|
|
112
|
-
const zoneCandidates = new Set(candidates(domain));
|
|
113
|
-
const authoritative = nameserverSet(input.authoritativeNameservers);
|
|
114
|
-
const eligible = input.connectedZones.filter((connected) => {
|
|
115
|
-
if (!zoneCandidates.has(parse$1(connected.zone))) return false;
|
|
116
|
-
try {
|
|
117
|
-
assertGrant(connected.connection, connected.authorization, {
|
|
118
|
-
capability: "dns:read",
|
|
119
|
-
domain,
|
|
120
|
-
...input.now === void 0 ? {} : { now: input.now },
|
|
121
|
-
providerId: connected.providerId
|
|
122
|
-
});
|
|
123
|
-
return true;
|
|
124
|
-
} catch {
|
|
125
|
-
return false;
|
|
126
|
-
}
|
|
127
|
-
});
|
|
128
|
-
const evidence = eligible.map((connected) => evidenceFor(connected, authoritative));
|
|
129
|
-
if (input.explicit !== void 0) {
|
|
130
|
-
const explicitZone = parse$1(input.explicit.zone);
|
|
131
|
-
const index = eligible.findIndex((connected) => connected.providerId === input.explicit?.providerId && connected.authorization.providerAccountId === input.explicit.accountId && parse$1(connected.zone) === explicitZone);
|
|
132
|
-
if (index < 0) throw authorizationError("Explicit provider zone is not connected or granted", "ProviderDiscovery.select");
|
|
133
|
-
const candidate = evidence[index];
|
|
134
|
-
if (candidate === void 0) throw new Error("Eligible provider evidence is missing");
|
|
135
|
-
return {
|
|
136
|
-
_tag: "selected",
|
|
137
|
-
candidate,
|
|
138
|
-
evidence,
|
|
139
|
-
reason: "explicit"
|
|
140
|
-
};
|
|
141
|
-
}
|
|
142
|
-
const decisive = evidence.filter(({ decisiveNameserverMatch }) => decisiveNameserverMatch);
|
|
143
|
-
const candidate = decisive[0];
|
|
144
|
-
return decisive.length === 1 && candidate !== void 0 ? {
|
|
145
|
-
_tag: "selected",
|
|
146
|
-
candidate,
|
|
147
|
-
evidence,
|
|
148
|
-
reason: "unique-nameserver-match"
|
|
149
|
-
} : {
|
|
150
|
-
_tag: "manual",
|
|
151
|
-
evidence,
|
|
152
|
-
reason: decisive.length > 1 ? "ambiguous" : "unsupported"
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
function evidenceFor(connected, authoritative) {
|
|
156
|
-
const configured = new Set(nameserverSet(connected.nameservers));
|
|
157
|
-
const matchedNameservers = authoritative.filter((nameserver) => configured.has(nameserver));
|
|
158
|
-
return {
|
|
159
|
-
accountId: connected.authorization.providerAccountId,
|
|
160
|
-
connectionId: connected.connection.id,
|
|
161
|
-
decisiveNameserverMatch: authoritative.length > 0 && matchedNameservers.length === authoritative.length,
|
|
162
|
-
matchedNameservers,
|
|
163
|
-
providerId: connected.providerId,
|
|
164
|
-
zone: parse$1(connected.zone)
|
|
165
|
-
};
|
|
166
|
-
}
|
|
167
|
-
function nameserverSet(nameservers) {
|
|
168
|
-
return [...new Set(nameservers.map(parse$1))].sort();
|
|
169
|
-
}
|
|
170
|
-
//#endregion
|
|
171
|
-
//#region src/plan/connection-authorization.ts
|
|
172
|
-
var connection_authorization_exports = /* @__PURE__ */ __exportAll({ authorize: () => authorize });
|
|
173
|
-
const authorize = Effect.fn("ConnectionAuthorization.authorize")(function* (input) {
|
|
174
|
-
const domain = yield* Effect.try({
|
|
175
|
-
try: () => assertGrant(input.connection, input.authorization, {
|
|
176
|
-
capability: "dns:write",
|
|
177
|
-
domain: input.domain,
|
|
178
|
-
providerId: input.plan.providerId
|
|
179
|
-
}),
|
|
180
|
-
catch: (cause) => cause instanceof AuthorizationError || cause instanceof Error$1 ? cause : authorizationError("Connection grant is invalid", "ConnectionAuthorization.authorize")
|
|
181
|
-
});
|
|
182
|
-
const authorization = yield* authorize$1(input.plan, input.operationIds, input.allowPartial === void 0 ? {} : { allowPartial: input.allowPartial });
|
|
183
|
-
const selected = new Set(authorization.operationIds);
|
|
184
|
-
if (input.plan.operations.filter((operation) => operation._tag === "create" && selected.has(operation.id) && operation.requirement.name !== domain && !operation.requirement.name.endsWith(`.${domain}`)).length > 0) return yield* authorizationError(`Plan operations are outside the granted domain ${domain}`, "ConnectionAuthorization.authorize");
|
|
185
|
-
return authorization;
|
|
186
|
-
});
|
|
187
|
-
//#endregion
|
|
188
|
-
//#region src/verification/dns-data.ts
|
|
189
|
-
var dns_data_exports = /* @__PURE__ */ __exportAll({ parse: () => parse });
|
|
190
|
-
const CanonicalAddress = Schema.String.pipe(Schema.decodeTo(Schema.String, SchemaTransformation.transform({
|
|
191
|
-
decode: (value) => value.trim().toLowerCase(),
|
|
192
|
-
encode: (value) => value
|
|
193
|
-
})));
|
|
194
|
-
const CanonicalDomain = Schema.String.pipe(Schema.decodeTo(Schema.String, SchemaTransformation.transformOrFail({
|
|
195
|
-
decode: (value, options) => Effect.try({
|
|
196
|
-
try: () => parse$1(value).toString(),
|
|
197
|
-
catch: (cause) => new SchemaIssue.InvalidValue({ message: cause instanceof Error ? cause.message : String(cause) }, value, options)
|
|
198
|
-
}),
|
|
199
|
-
encode: (value) => Effect.succeed(value)
|
|
200
|
-
})));
|
|
201
|
-
const CanonicalMx = tokenizedDomain(2, 1);
|
|
202
|
-
const CanonicalSrv = tokenizedDomain(4, 3);
|
|
203
|
-
const CanonicalTxt = Schema.String.pipe(Schema.decodeTo(Schema.String, SchemaTransformation.transform({
|
|
204
|
-
decode: (value) => {
|
|
205
|
-
const trimmed = value.trim();
|
|
206
|
-
const chunks = [...trimmed.matchAll(/"((?:\\.|[^"])*)"/g)];
|
|
207
|
-
return chunks.length === 0 ? trimmed : chunks.map((match) => (match[1] ?? "").replace(/\\"/g, "\"").replace(/\\\\/g, "\\")).join("");
|
|
208
|
-
},
|
|
209
|
-
encode: (value) => value
|
|
210
|
-
})));
|
|
211
|
-
const CanonicalCaa = Schema.String.pipe(Schema.decodeTo(Schema.String, SchemaTransformation.transform({
|
|
212
|
-
decode: (value) => value.trim().replace(/\s+"([\s\S]*)"$/, " $1"),
|
|
213
|
-
encode: (value) => value
|
|
214
|
-
})));
|
|
215
|
-
function parse(type, input) {
|
|
216
|
-
const schema = (() => {
|
|
217
|
-
switch (type) {
|
|
218
|
-
case "A":
|
|
219
|
-
case "AAAA": return CanonicalAddress;
|
|
220
|
-
case "CNAME":
|
|
221
|
-
case "NS": return CanonicalDomain;
|
|
222
|
-
case "MX": return CanonicalMx;
|
|
223
|
-
case "SRV": return CanonicalSrv;
|
|
224
|
-
case "TXT": return CanonicalTxt;
|
|
225
|
-
case "CAA": return CanonicalCaa;
|
|
226
|
-
}
|
|
227
|
-
})();
|
|
228
|
-
try {
|
|
229
|
-
return Schema.decodeUnknownSync(schema)(input);
|
|
230
|
-
} catch (cause) {
|
|
231
|
-
throw new Error$1({ message: cause instanceof Error ? cause.message : String(cause) });
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
function tokenizedDomain(length, domainIndex) {
|
|
235
|
-
return Schema.String.pipe(Schema.decodeTo(Schema.String, SchemaTransformation.transformOrFail({
|
|
236
|
-
decode: (value, options) => Effect.try({
|
|
237
|
-
try: () => {
|
|
238
|
-
const parts = value.trim().split(/\s+/);
|
|
239
|
-
if (parts.length !== length) throw new Error("DNS record data has an invalid field count");
|
|
240
|
-
const domain = parts[domainIndex];
|
|
241
|
-
if (domain === void 0) throw new Error("DNS record target is missing");
|
|
242
|
-
parts[domainIndex] = parse$1(domain);
|
|
243
|
-
return parts.join(" ");
|
|
244
|
-
},
|
|
245
|
-
catch: (cause) => new SchemaIssue.InvalidValue({ message: cause instanceof Error ? cause.message : String(cause) }, value, options)
|
|
246
|
-
}),
|
|
247
|
-
encode: (value) => Effect.succeed(value)
|
|
248
|
-
})));
|
|
249
|
-
}
|
|
250
|
-
//#endregion
|
|
251
|
-
//#region src/verification/doh.ts
|
|
252
|
-
var doh_exports = /* @__PURE__ */ __exportAll({
|
|
253
|
-
layer: () => layer$2,
|
|
254
|
-
make: () => make$3,
|
|
255
|
-
toAsync: () => toAsync$2
|
|
256
|
-
});
|
|
257
|
-
function make$3(options) {
|
|
258
|
-
const fetch = options.fetch ?? globalThis.fetch;
|
|
259
|
-
const timeoutMs = options.timeoutMs ?? 5e3;
|
|
260
|
-
return { resolve: Effect.fn("DnsOverHttps.resolve")((query) => Effect.tryPromise({
|
|
261
|
-
try: (effectSignal) => request({
|
|
262
|
-
effectSignal,
|
|
263
|
-
fetch,
|
|
264
|
-
query,
|
|
265
|
-
timeoutMs,
|
|
266
|
-
endpoint: options.endpoint
|
|
267
|
-
}),
|
|
268
|
-
catch: (cause) => cause instanceof Error$2 ? cause : new Error$2({
|
|
269
|
-
cause,
|
|
270
|
-
message: cause instanceof Error ? cause.message : "DNS resolution failed",
|
|
271
|
-
reason: "transport"
|
|
272
|
-
})
|
|
273
|
-
}).pipe(Effect.flatMap((message) => decodeResolution(query, message)))) };
|
|
274
|
-
}
|
|
275
|
-
const layer$2 = (options) => Layer.succeed(Service$2, make$3(options));
|
|
276
|
-
const toAsync$2 = (options) => toAsync$3(make$3(options));
|
|
277
|
-
function decodeResolution(query, message) {
|
|
278
|
-
return Effect.try({
|
|
279
|
-
try: () => DnsPacket.decode(message),
|
|
280
|
-
catch: (cause) => new Error$2({
|
|
281
|
-
cause,
|
|
282
|
-
message: "DoH returned an invalid DNS message",
|
|
283
|
-
reason: "response"
|
|
284
|
-
})
|
|
285
|
-
}).pipe(Effect.flatMap((packet) => validatePacket(query, packet)), Effect.flatMap(decodePacket));
|
|
286
|
-
}
|
|
287
|
-
function decodePacket(packet) {
|
|
288
|
-
if (packet.rcode === "NXDOMAIN") return Effect.succeed({ _tag: "nodata" });
|
|
289
|
-
if (packet.rcode !== "NOERROR") return Effect.fail(new Error$2({
|
|
290
|
-
message: `DNS resolver returned ${packet.rcode ?? "an unknown response code"}`,
|
|
291
|
-
reason: "response"
|
|
292
|
-
}));
|
|
293
|
-
if (packet.answers === void 0 || packet.answers.length === 0) return Effect.succeed({ _tag: "nodata" });
|
|
294
|
-
return Effect.forEach(packet.answers, decodeAnswer, { concurrency: "unbounded" }).pipe(Effect.map((answers) => {
|
|
295
|
-
const supportedAnswers = answers.filter((answer) => answer !== null);
|
|
296
|
-
return supportedAnswers.length === 0 ? { _tag: "nodata" } : {
|
|
297
|
-
_tag: "answer",
|
|
298
|
-
answers: supportedAnswers
|
|
299
|
-
};
|
|
300
|
-
}));
|
|
301
|
-
}
|
|
302
|
-
function validatePacket(query, packet) {
|
|
303
|
-
return Effect.try({
|
|
304
|
-
try: () => {
|
|
305
|
-
if (packet.type !== "response") throw new Error("DoH returned a DNS query");
|
|
306
|
-
if (packet.id !== 0) throw new Error("DoH response used an unexpected message ID");
|
|
307
|
-
if (packet.flag_tc) throw new Error("DoH returned a truncated DNS response");
|
|
308
|
-
const questions = packet.questions ?? [];
|
|
309
|
-
if (questions.length !== 1) throw new Error("DoH returned an unexpected question count");
|
|
310
|
-
const question = questions[0];
|
|
311
|
-
if (question === void 0) throw new Error("DoH response omitted the DNS question");
|
|
312
|
-
if (question.class !== "IN" || question.type !== query.type || parse$1(question.name) !== query.name) throw new Error("DoH response did not match the DNS question");
|
|
313
|
-
return packet;
|
|
314
|
-
},
|
|
315
|
-
catch: (cause) => new Error$2({
|
|
316
|
-
cause,
|
|
317
|
-
message: cause instanceof Error ? cause.message : "DoH returned an invalid DNS response",
|
|
318
|
-
reason: "response"
|
|
319
|
-
})
|
|
320
|
-
});
|
|
321
|
-
}
|
|
322
|
-
function decodeAnswer(answer) {
|
|
323
|
-
return Effect.gen(function* () {
|
|
324
|
-
if (answer.class !== "IN") return null;
|
|
325
|
-
const decoded = yield* Effect.try({
|
|
326
|
-
try: () => answerData(answer),
|
|
327
|
-
catch: (cause) => new Error$2({
|
|
328
|
-
cause,
|
|
329
|
-
message: "DoH returned invalid DNS record data",
|
|
330
|
-
reason: "response"
|
|
331
|
-
})
|
|
332
|
-
});
|
|
333
|
-
if (decoded === void 0) return null;
|
|
334
|
-
const name = yield* decode$1(answer.name);
|
|
335
|
-
const data = yield* Effect.try({
|
|
336
|
-
try: () => parse(decoded.type, decoded.data),
|
|
337
|
-
catch: (cause) => new Error$2({
|
|
338
|
-
cause,
|
|
339
|
-
message: "DoH returned invalid DNS record data",
|
|
340
|
-
reason: "response"
|
|
341
|
-
})
|
|
342
|
-
});
|
|
343
|
-
if (answer.ttl === void 0) return yield* new Error$2({
|
|
344
|
-
message: "DoH response omitted the DNS record TTL",
|
|
345
|
-
reason: "response"
|
|
346
|
-
});
|
|
347
|
-
return {
|
|
348
|
-
data,
|
|
349
|
-
name,
|
|
350
|
-
ttl: answer.ttl,
|
|
351
|
-
type: decoded.type
|
|
352
|
-
};
|
|
353
|
-
}).pipe(Effect.mapError((cause) => cause instanceof Error$2 ? cause : new Error$2({
|
|
354
|
-
cause,
|
|
355
|
-
message: cause.message,
|
|
356
|
-
reason: "response"
|
|
357
|
-
})));
|
|
358
|
-
}
|
|
359
|
-
function answerData(answer) {
|
|
360
|
-
switch (answer.type) {
|
|
361
|
-
case "A":
|
|
362
|
-
case "AAAA":
|
|
363
|
-
case "CNAME":
|
|
364
|
-
case "NS": return {
|
|
365
|
-
data: answer.data,
|
|
366
|
-
type: answer.type
|
|
367
|
-
};
|
|
368
|
-
case "CAA": return {
|
|
369
|
-
data: `${answer.data.flags ?? 0} ${answer.data.tag} ${answer.data.value}`,
|
|
370
|
-
type: answer.type
|
|
371
|
-
};
|
|
372
|
-
case "MX": return {
|
|
373
|
-
data: `${requiredNumber(answer.data.preference, "MX preference")} ${answer.data.exchange}`,
|
|
374
|
-
type: answer.type
|
|
375
|
-
};
|
|
376
|
-
case "SRV": return {
|
|
377
|
-
data: [
|
|
378
|
-
requiredNumber(answer.data.priority, "SRV priority"),
|
|
379
|
-
requiredNumber(answer.data.weight, "SRV weight"),
|
|
380
|
-
requiredNumber(answer.data.port, "SRV port"),
|
|
381
|
-
answer.data.target
|
|
382
|
-
].join(" "),
|
|
383
|
-
type: answer.type
|
|
384
|
-
};
|
|
385
|
-
case "TXT": return {
|
|
386
|
-
data: decodeTxt(answer.data),
|
|
387
|
-
type: answer.type
|
|
388
|
-
};
|
|
389
|
-
default: return;
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
function decodeTxt(data) {
|
|
393
|
-
const encoded = (Array.isArray(data) ? data : [data]).map((chunk) => typeof chunk === "string" ? new TextEncoder().encode(chunk) : chunk);
|
|
394
|
-
const length = encoded.reduce((total, chunk) => total + chunk.length, 0);
|
|
395
|
-
const joined = new Uint8Array(length);
|
|
396
|
-
let offset = 0;
|
|
397
|
-
for (const chunk of encoded) {
|
|
398
|
-
joined.set(chunk, offset);
|
|
399
|
-
offset += chunk.length;
|
|
400
|
-
}
|
|
401
|
-
return new TextDecoder("utf-8", { fatal: true }).decode(joined);
|
|
402
|
-
}
|
|
403
|
-
function requiredNumber(value, field) {
|
|
404
|
-
if (value === void 0) throw new Error(`DoH response omitted ${field}`);
|
|
405
|
-
return value;
|
|
406
|
-
}
|
|
407
|
-
async function request(input) {
|
|
408
|
-
const controller = new AbortController();
|
|
409
|
-
let timedOut = false;
|
|
410
|
-
const abortFromEffect = () => controller.abort(input.effectSignal.reason);
|
|
411
|
-
const abortFromHost = () => controller.abort(input.query.signal?.reason);
|
|
412
|
-
input.effectSignal.addEventListener("abort", abortFromEffect, { once: true });
|
|
413
|
-
input.query.signal?.addEventListener("abort", abortFromHost, { once: true });
|
|
414
|
-
const timer = setTimeout(() => {
|
|
415
|
-
timedOut = true;
|
|
416
|
-
controller.abort(/* @__PURE__ */ new Error("DNS resolution timed out"));
|
|
417
|
-
}, input.timeoutMs);
|
|
418
|
-
try {
|
|
419
|
-
const query = Uint8Array.from(DnsPacket.encode({
|
|
420
|
-
flags: DnsPacket.RECURSION_DESIRED,
|
|
421
|
-
id: 0,
|
|
422
|
-
questions: [{
|
|
423
|
-
name: input.query.name,
|
|
424
|
-
type: input.query.type
|
|
425
|
-
}],
|
|
426
|
-
type: "query"
|
|
427
|
-
}));
|
|
428
|
-
const response = await input.fetch(new URL(input.endpoint), {
|
|
429
|
-
body: query,
|
|
430
|
-
headers: {
|
|
431
|
-
accept: "application/dns-message",
|
|
432
|
-
"content-type": "application/dns-message"
|
|
433
|
-
},
|
|
434
|
-
method: "POST",
|
|
435
|
-
signal: controller.signal
|
|
436
|
-
});
|
|
437
|
-
if (!response.ok) throw new Error$2({
|
|
438
|
-
message: `DoH returned HTTP ${response.status}`,
|
|
439
|
-
reason: "transport"
|
|
440
|
-
});
|
|
441
|
-
const contentType = response.headers.get("content-type")?.split(";", 1)[0]?.trim().toLowerCase();
|
|
442
|
-
if (contentType !== "application/dns-message") throw new Error$2({
|
|
443
|
-
message: `DoH returned unsupported content type ${contentType ?? "<missing>"}`,
|
|
444
|
-
reason: "response"
|
|
445
|
-
});
|
|
446
|
-
return new Uint8Array(await response.arrayBuffer());
|
|
447
|
-
} catch (cause) {
|
|
448
|
-
if (timedOut) throw new Error$2({
|
|
449
|
-
message: "DNS query timed out",
|
|
450
|
-
reason: "timeout"
|
|
451
|
-
});
|
|
452
|
-
throw cause;
|
|
453
|
-
} finally {
|
|
454
|
-
clearTimeout(timer);
|
|
455
|
-
input.effectSignal.removeEventListener("abort", abortFromEffect);
|
|
456
|
-
input.query.signal?.removeEventListener("abort", abortFromHost);
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
//#endregion
|
|
460
|
-
//#region src/verification/cloudflare-doh.ts
|
|
461
|
-
var cloudflare_doh_exports = /* @__PURE__ */ __exportAll({
|
|
462
|
-
layer: () => layer$1,
|
|
463
|
-
make: () => make$2,
|
|
464
|
-
toAsync: () => toAsync$1
|
|
465
|
-
});
|
|
466
|
-
const endpoint$1 = "https://cloudflare-dns.com/dns-query";
|
|
467
|
-
function make$2(options = {}) {
|
|
468
|
-
return make$3({
|
|
469
|
-
...options,
|
|
470
|
-
endpoint: options.endpoint ?? endpoint$1
|
|
471
|
-
});
|
|
472
|
-
}
|
|
473
|
-
const layer$1 = (options = {}) => Layer.succeed(Service$2, make$2(options));
|
|
474
|
-
const toAsync$1 = (options = {}) => toAsync$3(make$2(options));
|
|
475
|
-
//#endregion
|
|
476
|
-
//#region src/verification/google-doh.ts
|
|
477
|
-
var google_doh_exports = /* @__PURE__ */ __exportAll({
|
|
478
|
-
layer: () => layer,
|
|
479
|
-
make: () => make$1,
|
|
480
|
-
toAsync: () => toAsync
|
|
481
|
-
});
|
|
482
|
-
const endpoint = "https://dns.google/dns-query";
|
|
483
|
-
function make$1(options = {}) {
|
|
484
|
-
return make$3({
|
|
485
|
-
...options,
|
|
486
|
-
endpoint: options.endpoint ?? endpoint
|
|
487
|
-
});
|
|
488
|
-
}
|
|
489
|
-
const layer = (options = {}) => Layer.succeed(Service$2, make$1(options));
|
|
490
|
-
const toAsync = (options = {}) => toAsync$3(make$1(options));
|
|
491
|
-
//#endregion
|
|
492
|
-
//#region src/verification/resolver-pool.ts
|
|
493
|
-
var resolver_pool_exports = /* @__PURE__ */ __exportAll({
|
|
494
|
-
Observation: () => Observation,
|
|
495
|
-
Policy: () => Policy,
|
|
496
|
-
Service: () => Service,
|
|
497
|
-
defaultLayer: () => defaultLayer,
|
|
498
|
-
defaultMake: () => defaultMake,
|
|
499
|
-
make: () => make
|
|
500
|
-
});
|
|
501
|
-
const Observation = Data.taggedEnum();
|
|
502
|
-
const PolicyVariants = Data.taggedEnum();
|
|
503
|
-
const QuorumMinimum = Schema.Int.check(Schema.isGreaterThanOrEqualTo(1));
|
|
504
|
-
const PolicySchema = Schema.TaggedUnion({
|
|
505
|
-
AllMatch: {},
|
|
506
|
-
AnyMatch: {},
|
|
507
|
-
Quorum: { minimum: QuorumMinimum }
|
|
508
|
-
});
|
|
509
|
-
const Policy = {
|
|
510
|
-
AllMatch: PolicyVariants.AllMatch,
|
|
511
|
-
AnyMatch: PolicyVariants.AnyMatch,
|
|
512
|
-
Quorum: ({ minimum }) => PolicyVariants.Quorum({ minimum: Schema.decodeUnknownSync(QuorumMinimum)(minimum) }),
|
|
513
|
-
Schema: PolicySchema
|
|
514
|
-
};
|
|
515
|
-
var Service = class extends Context.Service()("@domainkit/DnsResolverPool") {};
|
|
516
|
-
function make(entries) {
|
|
517
|
-
return Service.of({ observe: Effect.fn("DnsResolverPool.observe")((query) => Effect.forEach(entries, (entry) => observe$1(entry, query), { concurrency: "unbounded" })) });
|
|
518
|
-
}
|
|
519
|
-
/** Cloudflare and Google RFC wire-format DoH resolvers. */
|
|
520
|
-
function defaultMake(options = {}) {
|
|
521
|
-
const timeoutMs = options.timeoutMs ?? 5e3;
|
|
522
|
-
return make([{
|
|
523
|
-
id: "cloudflare",
|
|
524
|
-
resolver: make$2(options),
|
|
525
|
-
timeoutMs
|
|
526
|
-
}, {
|
|
527
|
-
id: "google",
|
|
528
|
-
resolver: make$1(options),
|
|
529
|
-
timeoutMs
|
|
530
|
-
}]);
|
|
531
|
-
}
|
|
532
|
-
const defaultLayer = (options = {}) => Layer.succeed(Service, defaultMake(options));
|
|
533
|
-
function observe$1(entry, query) {
|
|
534
|
-
const timeoutMs = entry.timeoutMs ?? 5e3;
|
|
535
|
-
return entry.resolver.resolve(query).pipe(Effect.timeout(timeoutMs), Effect.match({
|
|
536
|
-
onFailure: (failure) => {
|
|
537
|
-
if (failure._tag === "TimeoutError" || failure._tag === "ResolverError" && failure.reason === "timeout") return Observation.TimedOut({
|
|
538
|
-
resolverId: entry.id,
|
|
539
|
-
timeoutMs
|
|
540
|
-
});
|
|
541
|
-
return Observation.Failed({
|
|
542
|
-
message: failure.message,
|
|
543
|
-
reason: failure.reason === "response" ? "response" : "transport",
|
|
544
|
-
resolverId: entry.id
|
|
545
|
-
});
|
|
546
|
-
},
|
|
547
|
-
onSuccess: (resolution) => resolution._tag === "nodata" ? Observation.NoData({ resolverId: entry.id }) : Observation.Answer({
|
|
548
|
-
answers: resolution.answers,
|
|
549
|
-
resolverId: entry.id
|
|
550
|
-
})
|
|
551
|
-
}));
|
|
552
|
-
}
|
|
553
|
-
//#endregion
|
|
554
|
-
//#region src/verification/verify.ts
|
|
555
|
-
var verify_exports = /* @__PURE__ */ __exportAll({
|
|
556
|
-
Provider: () => Provider,
|
|
557
|
-
ProviderObservation: () => ProviderObservation,
|
|
558
|
-
PublicDns: () => PublicDns,
|
|
559
|
-
PublicDnsObservation: () => PublicDnsObservation,
|
|
560
|
-
Result: () => Result,
|
|
561
|
-
observe: () => observe
|
|
562
|
-
});
|
|
563
|
-
const PublicDns = Data.taggedEnum();
|
|
564
|
-
const Provider = Data.taggedEnum();
|
|
565
|
-
const ProviderObservation = Data.taggedEnum();
|
|
566
|
-
const PublicDnsObservation = Data.taggedEnum();
|
|
567
|
-
const Result = Data.taggedEnum();
|
|
568
|
-
function observe(input) {
|
|
569
|
-
return observeEffect(input);
|
|
570
|
-
}
|
|
571
|
-
const observeEffect = Effect.fn("Verification.observe")(function* (input) {
|
|
572
|
-
const publicDns = input.publicDns ?? PublicDns.Enabled({});
|
|
573
|
-
const provider = input.provider ?? Provider.Disabled();
|
|
574
|
-
const publicEffect = publicDns._tag === "Disabled" ? Effect.succeed(null) : observePublicDns(input.record, publicDns.policy ?? Policy.AnyMatch());
|
|
575
|
-
const providerEffect = provider._tag === "Disabled" ? Effect.succeed(null) : Effect.flatMap(Service$1, (service) => observeProvider(service, provider.zone, input.record).pipe(Effect.catch((failure) => Effect.succeed(ProviderObservation.Unavailable({ message: failure.message })))));
|
|
576
|
-
const [providerObservation, publicDnsObservation] = yield* Effect.all([providerEffect, publicEffect], { concurrency: "unbounded" });
|
|
577
|
-
return aggregate({
|
|
578
|
-
provider: providerObservation,
|
|
579
|
-
publicDns: publicDnsObservation
|
|
580
|
-
});
|
|
581
|
-
});
|
|
582
|
-
function observeProvider(provider, zone, requirement) {
|
|
583
|
-
return provider.listRecords(zone).pipe(Effect.map((records) => {
|
|
584
|
-
if (records.some((existing) => existing._tag !== "Opaque" && equals(existing, requirement))) return ProviderObservation.Matched();
|
|
585
|
-
const sameSet = records.filter((existing) => existing._tag !== "Opaque" && existing.name === requirement.name && existing._tag === requirement._tag);
|
|
586
|
-
return sameSet.length === 0 ? ProviderObservation.Pending() : ProviderObservation.Mismatch({ records: sameSet });
|
|
587
|
-
}));
|
|
588
|
-
}
|
|
589
|
-
function observePublicDns(requirement, policy) {
|
|
590
|
-
return Effect.gen(function* () {
|
|
591
|
-
const validatedPolicy = yield* Schema.decodeUnknownEffect(Policy.Schema)(policy).pipe(Effect.mapError((cause) => new Error$1({ message: cause.message })));
|
|
592
|
-
const configuredPool = yield* Effect.serviceOption(Service);
|
|
593
|
-
const evidence = yield* Option.getOrElse(configuredPool, () => defaultMake()).observe({
|
|
594
|
-
name: requirement.name,
|
|
595
|
-
type: requirement._tag
|
|
596
|
-
});
|
|
597
|
-
if (validatedPolicy._tag === "Quorum" && validatedPolicy.minimum > evidence.length) return yield* new Error$1({ message: `Quorum minimum ${validatedPolicy.minimum} exceeds the ${evidence.length} configured resolvers` });
|
|
598
|
-
return evaluatePublicDns(requirement, validatedPolicy, evidence);
|
|
599
|
-
});
|
|
600
|
-
}
|
|
601
|
-
function evaluatePublicDns(requirement, policy, evidence) {
|
|
602
|
-
const matchedResolverIds = evidence.flatMap((observation) => observation._tag === "Answer" && answersMatch(requirement, observation.answers) ? [observation.resolverId] : []);
|
|
603
|
-
const required = policy._tag === "AnyMatch" ? 1 : policy._tag === "AllMatch" ? evidence.length : policy.minimum;
|
|
604
|
-
if (required > 0 && matchedResolverIds.length >= required) return PublicDnsObservation.Verified({
|
|
605
|
-
evidence,
|
|
606
|
-
matchedResolverIds
|
|
607
|
-
});
|
|
608
|
-
const unavailable = evidence.filter((observation) => observation._tag === "TimedOut" || observation._tag === "Failed").length;
|
|
609
|
-
if (unavailable > 0 && matchedResolverIds.length + unavailable >= required) return PublicDnsObservation.Unavailable({ evidence });
|
|
610
|
-
if (evidence.some((observation) => observation._tag === "Answer" && !answersMatch(requirement, observation.answers))) return PublicDnsObservation.Mismatch({ evidence });
|
|
611
|
-
if (evidence.some((observation) => observation._tag === "NoData")) return PublicDnsObservation.Pending({ evidence });
|
|
612
|
-
return PublicDnsObservation.Unavailable({ evidence });
|
|
613
|
-
}
|
|
614
|
-
function answersMatch(requirement, answers) {
|
|
615
|
-
const expected = parse(requirement._tag, recordValue(requirement));
|
|
616
|
-
return answers.some((answer) => answer.name === requirement.name && answer.type === requirement._tag && parse(requirement._tag, answer.data) === expected);
|
|
617
|
-
}
|
|
618
|
-
function aggregate(evidence) {
|
|
619
|
-
const requested = [evidence.provider, evidence.publicDns].filter((observation) => observation !== null);
|
|
620
|
-
if (requested.length === 0) return Result.NotObserved(evidence);
|
|
621
|
-
if (requested.every((observation) => observation._tag === "Matched" || observation._tag === "Verified")) return Result.Verified(evidence);
|
|
622
|
-
if (requested.some((observation) => observation._tag === "Unavailable")) return Result.Unavailable(evidence);
|
|
623
|
-
if (requested.some((observation) => observation._tag === "Mismatch")) return Result.Mismatch(evidence);
|
|
624
|
-
return Result.Pending(evidence);
|
|
625
|
-
}
|
|
626
|
-
function recordValue(requirement) {
|
|
627
|
-
const data$1 = data(requirement);
|
|
628
|
-
switch (requirement._tag) {
|
|
629
|
-
case "A":
|
|
630
|
-
case "AAAA": return String(data$1.address);
|
|
631
|
-
case "CNAME":
|
|
632
|
-
case "NS": return String(data$1.target);
|
|
633
|
-
case "TXT": return String(data$1.value);
|
|
634
|
-
case "MX": return `${data$1.priority} ${data$1.exchange}`;
|
|
635
|
-
case "CAA": return `${data$1.flags} ${data$1.tag} ${data$1.value}`;
|
|
636
|
-
case "SRV": return `${data$1.priority} ${data$1.weight} ${data$1.port} ${data$1.target}`;
|
|
637
|
-
}
|
|
638
|
-
}
|
|
639
|
-
//#endregion
|
|
640
|
-
export { selection_exports as C, manifest_exports as D, decode as E, diagnostic_exports as O, connection_authorization_exports as S, Schema$1 as T, toAsync$1 as _, Result as a, dns_data_exports as b, Observation as c, defaultMake as d, make as f, cloudflare_doh_exports as g, toAsync as h, PublicDnsObservation as i, version as k, Policy as l, google_doh_exports as m, ProviderObservation as n, observe as o, resolver_pool_exports as p, PublicDns as r, verify_exports as s, Provider as t, Service as u, doh_exports as v, Method as w, authorize as x, toAsync$2 as y };
|
|
641
|
-
|
|
642
|
-
//# sourceMappingURL=verify-Q5RYpnuw.mjs.map
|