domainkit 0.13.0 → 0.14.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 +4 -1
- package/dist/DomainKit.mjs +3 -2
- package/dist/DomainKit.mjs.map +1 -1
- package/dist/Server.mjs +7 -1
- package/dist/Server.mjs.map +1 -1
- package/dist/Transport.mjs +13 -6
- package/dist/Transport.mjs.map +1 -1
- package/dist/Verify.mjs +61 -8
- package/dist/Verify.mjs.map +1 -1
- package/dist/package.mjs +1 -1
- package/dist/types/DomainKit.d.ts +3 -2
- package/dist/types/Server.d.ts +429 -0
- package/dist/types/Transport.d.ts +2 -0
- package/dist/types/Verify.d.ts +62 -0
- package/package.json +1 -1
package/dist/Verify.mjs
CHANGED
|
@@ -17,6 +17,7 @@ import { Context, DateTime, Duration, Effect, Layer, Option, Schema } from "effe
|
|
|
17
17
|
var Verify_exports = /* @__PURE__ */ __exportAll({
|
|
18
18
|
Evidence: () => Evidence,
|
|
19
19
|
HostEvidence: () => HostEvidence,
|
|
20
|
+
Observer: () => Observer,
|
|
20
21
|
Policy: () => Policy,
|
|
21
22
|
ProviderEvidence: () => ProviderEvidence,
|
|
22
23
|
PublicDnsEvidence: () => PublicDnsEvidence,
|
|
@@ -27,7 +28,9 @@ var Verify_exports = /* @__PURE__ */ __exportAll({
|
|
|
27
28
|
layer: () => layer,
|
|
28
29
|
make: () => make,
|
|
29
30
|
observe: () => observe,
|
|
30
|
-
|
|
31
|
+
requirementKey: () => requirementKey,
|
|
32
|
+
statusAgainst: () => statusAgainst,
|
|
33
|
+
summary: () => summary
|
|
31
34
|
});
|
|
32
35
|
/**
|
|
33
36
|
* Every piece of evidence carries `values`, what the observer returned for the requirement's name
|
|
@@ -69,11 +72,49 @@ const Evidence = Schema.Union([
|
|
|
69
72
|
HostEvidence
|
|
70
73
|
]);
|
|
71
74
|
var Service = class extends Context.Service()("@domainkit/Verify") {};
|
|
75
|
+
/**
|
|
76
|
+
* A requirement's identity: the record's type, name, and data, and nothing else. It is what a host
|
|
77
|
+
* pairs its own rows against readiness by, instead of trusting the order of the array.
|
|
78
|
+
* `@domainkit/react`'s `Records.identity` is this function.
|
|
79
|
+
*/
|
|
80
|
+
const requirementKey = (record) => [
|
|
81
|
+
record._tag,
|
|
82
|
+
record.name,
|
|
83
|
+
data(record)
|
|
84
|
+
].join(":");
|
|
85
|
+
/**
|
|
86
|
+
* How a readiness stands, as counts. Pure and total: a host renders "3 of 4 found" from the stored
|
|
87
|
+
* fact without deciding what an absent readiness means, because `observed` says so.
|
|
88
|
+
*/
|
|
89
|
+
const summary = (readiness) => {
|
|
90
|
+
const statuses = readiness?.requirements.map(({ status }) => status) ?? [];
|
|
91
|
+
const count = (status) => statuses.filter((candidate) => candidate === status).length;
|
|
92
|
+
return {
|
|
93
|
+
observed: readiness !== null,
|
|
94
|
+
total: statuses.length,
|
|
95
|
+
satisfied: count("satisfied"),
|
|
96
|
+
missing: count("missing"),
|
|
97
|
+
mismatch: count("mismatch"),
|
|
98
|
+
unknown: count("unknown")
|
|
99
|
+
};
|
|
100
|
+
};
|
|
72
101
|
const defaults = {
|
|
73
102
|
backoff: (pendingForMs) => pendingForMs < 6e4 ? 15e3 : pendingForMs < 6e5 ? 6e4 : pendingForMs < 36e5 ? 3e5 : 18e5,
|
|
74
103
|
quorum: "any"
|
|
75
104
|
};
|
|
76
105
|
var Policy = class extends Context.Reference("@domainkit/Verify/Policy", { defaultValue: () => defaults }) {};
|
|
106
|
+
/**
|
|
107
|
+
* The host seam for "DomainKit wrote readiness". The default does nothing; provide one with
|
|
108
|
+
* `Effect.provideService(Verify.Observer, ...)` or `Layer.succeed(Verify.Observer, ...)` over
|
|
109
|
+
* `DomainKit.layer`, the same way `Policy` is overridden.
|
|
110
|
+
*
|
|
111
|
+
* It runs after `storage.readiness.put` returns, outside any transaction, because `Storage`
|
|
112
|
+
* exposes none. A failure or defect in the observer is logged and swallowed: the observation is
|
|
113
|
+
* already durable, and losing it because a projection failed would make the stored fact depend on
|
|
114
|
+
* the host's side effects. A host that needs the projection to be reliable enqueues durable work
|
|
115
|
+
* here rather than doing the work inline.
|
|
116
|
+
*/
|
|
117
|
+
var Observer = class extends Context.Reference("@domainkit/Verify/Observer", { defaultValue: () => ({ readinessChanged: () => Effect.void }) }) {};
|
|
77
118
|
const StoredEvidence = Schema.Array(Evidence);
|
|
78
119
|
const StoredHost = Schema.Array(HostEvidence);
|
|
79
120
|
/** A requirement is satisfied by an exact match; an exclusive one is contradicted by any same-set record. */
|
|
@@ -112,7 +153,7 @@ const overallOf = (requirements, host) => {
|
|
|
112
153
|
return requirements.length + host.length > 0 && requirements.every(({ status }) => status === "satisfied") && host.every(({ status }) => status === "ok") ? "ready" : "pending";
|
|
113
154
|
};
|
|
114
155
|
/** The identity of a requirement set: type, name, data, and policy, independent of labels and order. */
|
|
115
|
-
const
|
|
156
|
+
const requirementSetKey = (requirements) => requirements.map(({ record }) => `${record._tag} ${record.name} ${data(record)} ${record.policy}`).sort().join("\n");
|
|
116
157
|
const make = Effect.gen(function* () {
|
|
117
158
|
const storage = yield* Service$3;
|
|
118
159
|
const connect = yield* Service$4;
|
|
@@ -127,6 +168,7 @@ const make = Effect.gen(function* () {
|
|
|
127
168
|
});
|
|
128
169
|
const decodeRow = (row) => Effect.gen(function* () {
|
|
129
170
|
const requirements = yield* Effect.forEach(row.requirements, (requirement) => decode$1(StoredEvidence, requirement.evidence, "evidence").pipe(Effect.map((evidence) => ({
|
|
171
|
+
key: requirementKey(requirement.record),
|
|
130
172
|
operationId: requirement.operationId,
|
|
131
173
|
record: requirement.record,
|
|
132
174
|
status: requirement.status,
|
|
@@ -147,9 +189,10 @@ const make = Effect.gen(function* () {
|
|
|
147
189
|
const store = (input) => Effect.gen(function* () {
|
|
148
190
|
const principal = yield* Service$1;
|
|
149
191
|
const policy = yield* Policy;
|
|
192
|
+
const observer = yield* Observer;
|
|
150
193
|
const now = yield* DateTime.now;
|
|
151
194
|
const overall = overallOf(input.requirements, input.host);
|
|
152
|
-
const sameRequirements = Option.isSome(input.previous) &&
|
|
195
|
+
const sameRequirements = Option.isSome(input.previous) && requirementSetKey(input.previous.value.requirements) === requirementSetKey(input.requirements);
|
|
153
196
|
const pendingSince = overall === "ready" ? null : sameRequirements && Option.isSome(input.previous) && input.previous.value.pendingSince !== null ? input.previous.value.pendingSince : now;
|
|
154
197
|
const nextCheckAt = pendingSince === null ? null : DateTime.addDuration(now, Duration.millis(policy.backoff(DateTime.toEpochMillis(now) - DateTime.toEpochMillis(pendingSince))));
|
|
155
198
|
const row = new Readiness({
|
|
@@ -169,7 +212,7 @@ const make = Effect.gen(function* () {
|
|
|
169
212
|
nextCheckAt
|
|
170
213
|
});
|
|
171
214
|
yield* storage.readiness.put(row);
|
|
172
|
-
|
|
215
|
+
const readiness = {
|
|
173
216
|
domain: row.domain,
|
|
174
217
|
attachmentId: row.attachmentId,
|
|
175
218
|
overall,
|
|
@@ -178,6 +221,12 @@ const make = Effect.gen(function* () {
|
|
|
178
221
|
checkedAt: now,
|
|
179
222
|
nextCheckAt
|
|
180
223
|
};
|
|
224
|
+
yield* observer.readinessChanged({
|
|
225
|
+
domain: row.domain,
|
|
226
|
+
readiness,
|
|
227
|
+
cause: input.cause
|
|
228
|
+
}).pipe(Effect.catchCause((cause) => Effect.logWarning(`Verify.Observer failed for ${row.domain}`, cause)));
|
|
229
|
+
return readiness;
|
|
181
230
|
});
|
|
182
231
|
const defaultRequirements = (attachment) => Effect.gen(function* () {
|
|
183
232
|
const latest = yield* storage.attempts.latest(attachment.id, "provisioning");
|
|
@@ -236,10 +285,12 @@ const make = Effect.gen(function* () {
|
|
|
236
285
|
observedAt: now
|
|
237
286
|
}));
|
|
238
287
|
const publicStatus = quorumStatus(policy.quorum, publicEvidence.map(({ status }) => status));
|
|
288
|
+
const status = combine([...providerEvidence.map((evidence) => evidence.status), publicStatus]);
|
|
239
289
|
return {
|
|
290
|
+
key: requirementKey(record),
|
|
240
291
|
operationId,
|
|
241
292
|
record,
|
|
242
|
-
status
|
|
293
|
+
status,
|
|
243
294
|
evidence: [...providerEvidence, ...publicEvidence]
|
|
244
295
|
};
|
|
245
296
|
}), { concurrency: "unbounded" });
|
|
@@ -250,7 +301,8 @@ const make = Effect.gen(function* () {
|
|
|
250
301
|
attachment,
|
|
251
302
|
requirements: observed,
|
|
252
303
|
host,
|
|
253
|
-
previous
|
|
304
|
+
previous,
|
|
305
|
+
cause: "observe"
|
|
254
306
|
});
|
|
255
307
|
});
|
|
256
308
|
const attachEvidence = (input) => Effect.gen(function* () {
|
|
@@ -267,7 +319,8 @@ const make = Effect.gen(function* () {
|
|
|
267
319
|
attachment,
|
|
268
320
|
requirements: current.requirements,
|
|
269
321
|
host: [...bySource.values()],
|
|
270
|
-
previous
|
|
322
|
+
previous,
|
|
323
|
+
cause: "evidence"
|
|
271
324
|
});
|
|
272
325
|
});
|
|
273
326
|
const latest = (domain) => Effect.gen(function* () {
|
|
@@ -287,6 +340,6 @@ const observe = accessor((service) => service.observe);
|
|
|
287
340
|
const attachEvidence = accessor((service) => service.attachEvidence);
|
|
288
341
|
const latest = accessor((service) => service.latest);
|
|
289
342
|
//#endregion
|
|
290
|
-
export { Evidence, HostEvidence, Policy, ProviderEvidence, PublicDnsEvidence, Service, Verify_exports, attachEvidence, defaults, latest, layer, make, observe, statusAgainst };
|
|
343
|
+
export { Evidence, HostEvidence, Observer, Policy, ProviderEvidence, PublicDnsEvidence, Service, Verify_exports, attachEvidence, defaults, latest, layer, make, observe, requirementKey, statusAgainst, summary };
|
|
291
344
|
|
|
292
345
|
//# sourceMappingURL=Verify.mjs.map
|
package/dist/Verify.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Verify.mjs","names":["Storage.RequirementStatus","DnsRecord.equals","DnsRecord.sameSet","DnsRecord.data","Storage.Service","Connect.Service","Resolver.Service","DomainName.decode","Errors.decode","Principal.Service","Storage.Readiness","Errors.fail","Reason.InvalidInput"],"sources":["../src/Verify.ts"],"sourcesContent":["/**\n * Observe provider and public DNS for an attachment, persist per-requirement readiness, and say\n * when to look again. The host keeps its own state machine (and any provider-side verification\n * such as SES) and feeds that in as `HostEvidence`.\n */\nimport { Context, DateTime, Duration, Effect, Layer, Option, Schema } from \"effect\";\n\nimport * as Connect from \"./Connect.ts\";\nimport * as DnsRecord from \"./DnsRecord.ts\";\nimport * as Errors from \"./internal/error.ts\";\nimport * as Reason from \"./Reason.ts\";\nimport * as DomainName from \"./DomainName.ts\";\nimport type * as Plan from \"./Plan.ts\";\nimport * as Principal from \"./Principal.ts\";\nimport * as Resolver from \"./Resolver.ts\";\nimport * as Storage from \"./Storage.ts\";\n\n/**\n * Every piece of evidence carries `values`, what the observer returned for the requirement's name\n * and type (empty when nothing was found, the lookup timed out, or the resolver failed), and\n * `detail`, `null` when the requirement is satisfied and otherwise the mismatch summary or the\n * resolver's error text, so a host can render \"found X, expected Y\".\n */\n// Rows written before these fields existed decode with the empty values.\nconst Values = Schema.Array(Schema.String).pipe(Schema.withDecodingDefaultKey(Effect.succeed([])));\nconst Detail = Schema.NullOr(Schema.String).pipe(\n Schema.withDecodingDefaultKey(Effect.succeed(null)),\n);\n\nexport class ProviderEvidence extends Schema.TaggedClass<ProviderEvidence>(\n \"@domainkit/Evidence/Provider\",\n)(\"Provider\", {\n provider: Schema.String,\n status: Storage.RequirementStatus,\n values: Values,\n detail: Detail,\n observedAt: Schema.DateTimeUtcFromString,\n}) {}\nexport class PublicDnsEvidence extends Schema.TaggedClass<PublicDnsEvidence>(\n \"@domainkit/Evidence/PublicDns\",\n)(\"PublicDns\", {\n resolver: Schema.String,\n status: Storage.RequirementStatus,\n values: Values,\n detail: Detail,\n observedAt: Schema.DateTimeUtcFromString,\n}) {}\n/** Anything the host knows that DomainKit cannot observe: SES identity status, a CDN cert, ... */\nexport class HostEvidence extends Schema.TaggedClass<HostEvidence>(\"@domainkit/Evidence/Host\")(\n \"Host\",\n {\n source: Schema.String,\n status: Schema.Literals([\"ok\", \"pending\", \"failed\"]),\n label: Schema.String,\n detail: Detail,\n observedAt: Schema.DateTimeUtcFromString,\n },\n) {}\nexport const Evidence = Schema.Union([ProviderEvidence, PublicDnsEvidence, HostEvidence]);\nexport type Evidence = typeof Evidence.Type;\n\nexport interface Requirement {\n readonly operationId: Plan.OperationId | null;\n readonly record: DnsRecord.Model;\n readonly status: Storage.RequirementStatus;\n readonly evidence: ReadonlyArray<Evidence>;\n}\n\nexport interface Readiness {\n readonly domain: string;\n /** `null` for observe-only domains that have no attachment. */\n readonly attachmentId: string | null;\n readonly overall: Storage.Overall;\n readonly requirements: ReadonlyArray<Requirement>;\n readonly host: ReadonlyArray<HostEvidence>;\n readonly checkedAt: DateTime.Utc;\n readonly nextCheckAt: DateTime.Utc | null;\n}\n\ntype Fx<A> = Effect.Effect<A, Errors.DomainKitError, Principal.Service>;\n\nexport interface Interface {\n /**\n * What to observe, in order of precedence: `requirements` when supplied (an attached domain\n * without a provisioning receipt, or a domain with no attachment whose records the customer\n * applies by hand), else the latest provisioning receipt of the domain's attachment (records it\n * applied or found in place). Neither is `InvalidInput`. Provider evidence is added when the\n * attachment's session can be built; public DNS is always observed, and readiness is stored\n * per domain either way.\n */\n readonly observe: (input: {\n readonly domain: string;\n readonly requirements?: ReadonlyArray<DnsRecord.Model>;\n }) => Fx<Readiness>;\n /** Merge host evidence into stored readiness without re-observing DNS. */\n readonly attachEvidence: (input: {\n readonly domain: string;\n readonly evidence: ReadonlyArray<HostEvidence>;\n }) => Fx<Readiness>;\n readonly latest: (domain: string) => Fx<Readiness | null>;\n}\n\nexport class Service extends Context.Service<Service, Interface>()(\"@domainkit/Verify\") {}\n\nexport interface PolicyShape {\n /** Delay before the next check, given time since the first pending observation. Default ladder: 15s, 1m, 5m, 30m. */\n readonly backoff: (pendingForMs: number) => number;\n /** Which resolvers must agree for `satisfied`: default `any`. */\n readonly quorum: \"any\" | \"all\" | { readonly minimum: number };\n}\nexport const defaults: PolicyShape = {\n backoff: (pendingForMs) =>\n pendingForMs < 60_000\n ? 15_000\n : pendingForMs < 10 * 60_000\n ? 60_000\n : pendingForMs < 60 * 60_000\n ? 5 * 60_000\n : 30 * 60_000,\n quorum: \"any\",\n};\nexport class Policy extends Context.Reference<PolicyShape>(\"@domainkit/Verify/Policy\", {\n defaultValue: () => defaults,\n}) {}\n\n// ---------------------------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------------------------\n\nconst StoredEvidence = Schema.Array(Evidence);\n\ninterface ProviderSide {\n readonly provider: string;\n readonly records: ReadonlyArray<DnsRecord.Observed>;\n}\nconst StoredHost = Schema.Array(HostEvidence);\n\n/** A requirement is satisfied by an exact match; an exclusive one is contradicted by any same-set record. */\nexport const statusAgainst = (\n record: DnsRecord.Model,\n observed: ReadonlyArray<DnsRecord.Observed>,\n): Storage.RequirementStatus => {\n if (observed.some((candidate) => DnsRecord.equals(candidate, record))) return \"satisfied\";\n if (\n record.policy === \"exclusive\" &&\n observed.some((candidate) => DnsRecord.sameSet(candidate, record))\n ) {\n return \"mismatch\";\n }\n return \"missing\";\n};\n\ninterface Observation {\n readonly status: Storage.RequirementStatus;\n readonly values: ReadonlyArray<string>;\n readonly detail: string | null;\n}\n\nconst render = (observed: DnsRecord.Observed): string =>\n observed._tag === \"Opaque\" ? JSON.stringify(observed.raw) : DnsRecord.data(observed);\n\n/** Status plus what the observer holds for the requirement's name and type. */\nconst observation = (\n record: DnsRecord.Model,\n observed: ReadonlyArray<DnsRecord.Observed>,\n): Observation => {\n const status = statusAgainst(record, observed);\n const values = observed.filter((candidate) => DnsRecord.sameSet(candidate, record)).map(render);\n const detail =\n status === \"satisfied\"\n ? null\n : values.length === 0\n ? `no ${record._tag} record at ${record.name}`\n : `expected ${DnsRecord.data(record)}; found ${values.join(\", \")}`;\n return { status, values, detail };\n};\n\nconst combine = (statuses: ReadonlyArray<Storage.RequirementStatus>): Storage.RequirementStatus => {\n const known = statuses.filter((status) => status !== \"unknown\");\n if (known.length === 0) return \"unknown\";\n if (known.includes(\"mismatch\")) return \"mismatch\";\n return known.every((status) => status === \"satisfied\") ? \"satisfied\" : \"missing\";\n};\n\nconst quorumStatus = (\n quorum: PolicyShape[\"quorum\"],\n statuses: ReadonlyArray<Storage.RequirementStatus>,\n): Storage.RequirementStatus => {\n const known = statuses.filter((status) => status !== \"unknown\");\n const satisfied = known.filter((status) => status === \"satisfied\").length;\n const needed =\n quorum === \"any\" ? 1 : quorum === \"all\" ? Math.max(1, known.length) : quorum.minimum;\n if (known.includes(\"mismatch\")) return \"mismatch\";\n if (known.length > 0 && satisfied >= needed) return \"satisfied\";\n return known.length === 0 ? \"unknown\" : \"missing\";\n};\n\nconst overallOf = (\n requirements: ReadonlyArray<{ readonly status: Storage.RequirementStatus }>,\n host: ReadonlyArray<HostEvidence>,\n): Storage.Overall => {\n if (\n requirements.some(({ status }) => status === \"mismatch\") ||\n host.some(({ status }) => status === \"failed\")\n ) {\n return \"failed\";\n }\n const ready =\n requirements.length + host.length > 0 &&\n requirements.every(({ status }) => status === \"satisfied\") &&\n host.every(({ status }) => status === \"ok\");\n return ready ? \"ready\" : \"pending\";\n};\n\n/** The identity of a requirement set: type, name, data, and policy, independent of labels and order. */\nconst requirementKey = (requirements: ReadonlyArray<{ readonly record: DnsRecord.Model }>) =>\n requirements\n .map(({ record }) => `${record._tag} ${record.name} ${DnsRecord.data(record)} ${record.policy}`)\n .sort()\n .join(\"\\n\");\n\nexport const make: Effect.Effect<\n Interface,\n never,\n Storage.Service | Connect.Service | Resolver.Service\n> = Effect.gen(function* () {\n const storage = yield* Storage.Service;\n const connect = yield* Connect.Service;\n const resolver = yield* Resolver.Service;\n\n const attachmentFor = (\n input: string,\n ): Fx<{\n readonly domain: DomainName.Model;\n readonly attachment: Storage.Attachment | null;\n }> =>\n Effect.gen(function* () {\n const domain = yield* DomainName.decode(input);\n const attachment = yield* storage.attachments.byDomain(domain);\n return { domain, attachment: Option.getOrNull(attachment) };\n });\n\n const decodeRow = (row: Storage.Readiness): Effect.Effect<Readiness, Errors.DomainKitError> =>\n Effect.gen(function* () {\n const requirements = yield* Effect.forEach(row.requirements, (requirement) =>\n Errors.decode(StoredEvidence, requirement.evidence, \"evidence\").pipe(\n Effect.map((evidence): Requirement => ({\n operationId: requirement.operationId,\n record: requirement.record,\n status: requirement.status,\n evidence,\n })),\n ),\n );\n const host = yield* Errors.decode(StoredHost, row.host, \"host\");\n return {\n domain: row.domain,\n attachmentId: row.attachmentId,\n overall: row.overall,\n requirements,\n host,\n checkedAt: row.checkedAt,\n nextCheckAt: row.nextCheckAt,\n };\n });\n\n /** Persist readiness, carrying the pending streak forward for the backoff ladder. */\n const store = (input: {\n readonly domain: string;\n readonly attachment: Storage.Attachment | null;\n readonly requirements: ReadonlyArray<Requirement>;\n readonly host: ReadonlyArray<HostEvidence>;\n readonly previous: Option.Option<Storage.Readiness>;\n }): Fx<Readiness> =>\n Effect.gen(function* () {\n const principal = yield* Principal.Service;\n const policy = yield* Policy;\n const now = yield* DateTime.now;\n const overall = overallOf(input.requirements, input.host);\n const sameRequirements =\n Option.isSome(input.previous) &&\n requirementKey(input.previous.value.requirements) === requirementKey(input.requirements);\n const pendingSince =\n overall === \"ready\"\n ? null\n : sameRequirements &&\n Option.isSome(input.previous) &&\n input.previous.value.pendingSince !== null\n ? input.previous.value.pendingSince\n : now;\n const nextCheckAt =\n pendingSince === null\n ? null\n : DateTime.addDuration(\n now,\n Duration.millis(\n policy.backoff(DateTime.toEpochMillis(now) - DateTime.toEpochMillis(pendingSince)),\n ),\n );\n const row = new Storage.Readiness({\n domain: input.domain,\n attachmentId: input.attachment?.id ?? null,\n ownerId: principal.ownerId,\n overall,\n requirements: input.requirements.map((requirement) => ({\n operationId: requirement.operationId,\n record: requirement.record,\n status: requirement.status,\n evidence: Schema.encodeSync(StoredEvidence)(requirement.evidence),\n })),\n host: Schema.encodeSync(StoredHost)(input.host),\n pendingSince,\n checkedAt: now,\n nextCheckAt,\n });\n yield* storage.readiness.put(row);\n return {\n domain: row.domain,\n attachmentId: row.attachmentId,\n overall,\n requirements: input.requirements,\n host: input.host,\n checkedAt: now,\n nextCheckAt,\n };\n });\n\n const defaultRequirements = (\n attachment: Storage.Attachment,\n ): Fx<\n ReadonlyArray<{\n readonly operationId: Plan.OperationId | null;\n readonly record: DnsRecord.Model;\n }>\n > =>\n Effect.gen(function* () {\n const latest = yield* storage.attempts.latest(attachment.id, \"provisioning\");\n if (Option.isNone(latest) || latest.value.receipt === null) {\n return yield* Errors.fail(\n new Reason.InvalidInput({\n message: `${attachment.domain} has no provisioning receipt; pass requirements to observe`,\n field: \"requirements\",\n }),\n );\n }\n const receipt = latest.value.receipt;\n const inPlace = new Set(\n receipt.outcomes.flatMap((outcome) =>\n outcome._tag === \"Applied\" || (outcome._tag === \"Skipped\" && outcome.reason === \"noop\")\n ? [outcome.operationId]\n : [],\n ),\n );\n return latest.value.plan.operations\n .filter((operation) => inPlace.has(operation.id))\n .map((operation) => ({ operationId: operation.id, record: operation.record }));\n });\n\n const observe: Interface[\"observe\"] = (input) =>\n Effect.gen(function* () {\n const policy = yield* Policy;\n const { domain, attachment } = yield* attachmentFor(input.domain);\n if (input.requirements === undefined && attachment === null) {\n return yield* Errors.fail(\n new Reason.InvalidInput({\n message: `${domain} is not attached; pass requirements to observe public DNS`,\n field: \"requirements\",\n }),\n );\n }\n const requirements =\n input.requirements === undefined && attachment !== null\n ? yield* defaultRequirements(attachment)\n : (input.requirements ?? []).map((record) => ({ operationId: null, record }));\n if (requirements.length === 0) {\n return yield* Errors.fail(\n new Reason.InvalidInput({\n message: \"Nothing to observe: pass at least one requirement\",\n field: \"requirements\",\n }),\n );\n }\n const now = yield* DateTime.now;\n // Provider readback is evidence when the attachment's session can be built; when it\n // cannot (no attachment, revoked credential, provider outage) public DNS stands alone.\n const providerSide = yield* attachment === null\n ? Effect.succeed(Option.none<ProviderSide>())\n : Effect.gen(function* () {\n const connection = yield* storage.connections.get(attachment.connectionId);\n const authorization = yield* storage.authorizations.get(connection.authorizationId);\n const { session, target } = yield* connect.session(attachment.id);\n const observed = yield* session.dns(target).list(target.zone);\n return Option.some<ProviderSide>({\n provider: authorization.provider,\n records: observed.map(({ record }) => record),\n });\n }).pipe(Effect.catch(() => Effect.succeed(Option.none<ProviderSide>())));\n const observed = yield* Effect.forEach(\n requirements,\n ({ operationId, record }) =>\n Effect.gen(function* () {\n const providerEvidence = Option.isSome(providerSide)\n ? [\n new ProviderEvidence({\n provider: providerSide.value.provider,\n ...observation(record, providerSide.value.records),\n observedAt: now,\n }),\n ]\n : [];\n const outcomes = yield* resolver.resolve(record.name, record._tag);\n const publicEvidence = outcomes.map((outcome) =>\n outcome._tag === \"Answered\"\n ? new PublicDnsEvidence({\n resolver: outcome.answer.resolver,\n ...observation(record, outcome.answer.records),\n observedAt: now,\n })\n : new PublicDnsEvidence({\n resolver: outcome.resolver,\n status: \"unknown\",\n values: [],\n detail:\n outcome._tag === \"TimedOut\"\n ? `${outcome.resolver} timed out`\n : outcome.message,\n observedAt: now,\n }),\n );\n const publicStatus = quorumStatus(\n policy.quorum,\n publicEvidence.map(({ status }) => status),\n );\n const status = combine([\n ...providerEvidence.map((evidence) => evidence.status),\n publicStatus,\n ]);\n return {\n operationId,\n record,\n status,\n evidence: [...providerEvidence, ...publicEvidence],\n } satisfies Requirement;\n }),\n { concurrency: \"unbounded\" },\n );\n const previous = yield* storage.readiness.get(domain);\n const host = Option.isSome(previous)\n ? yield* Errors.decode(StoredHost, previous.value.host, \"host\")\n : [];\n return yield* store({ domain, attachment, requirements: observed, host, previous });\n });\n\n const attachEvidence: Interface[\"attachEvidence\"] = (input) =>\n Effect.gen(function* () {\n const { domain, attachment } = yield* attachmentFor(input.domain);\n const previous = yield* storage.readiness.get(domain);\n const current = Option.isSome(previous)\n ? yield* decodeRow(previous.value)\n : {\n requirements: [] as ReadonlyArray<Requirement>,\n host: [] as ReadonlyArray<HostEvidence>,\n };\n const bySource = new Map(current.host.map((evidence) => [evidence.source, evidence]));\n for (const evidence of input.evidence) bySource.set(evidence.source, evidence);\n return yield* store({\n domain,\n attachment,\n requirements: current.requirements,\n host: [...bySource.values()],\n previous,\n });\n });\n\n const latest: Interface[\"latest\"] = (domain) =>\n Effect.gen(function* () {\n const { domain: name } = yield* attachmentFor(domain);\n const stored = yield* storage.readiness.get(name);\n return Option.isSome(stored) ? yield* decodeRow(stored.value) : null;\n });\n\n return { observe, attachEvidence, latest };\n});\n\nexport const layer: Layer.Layer<\n Service,\n never,\n Storage.Service | Connect.Service | Resolver.Service\n> = Layer.effect(Service)(make);\n\nconst accessor =\n <Args extends ReadonlyArray<unknown>, A>(\n pick: (service: Interface) => (...args: Args) => Fx<A>,\n ): ((...args: Args) => Effect.Effect<A, Errors.DomainKitError, Principal.Service | Service>) =>\n (...args) =>\n Effect.flatMap(Service, (service) => pick(service)(...args));\n\nexport const observe = accessor((service) => service.observe);\nexport const attachEvidence = accessor((service) => service.attachEvidence);\nexport const latest = accessor((service) => service.latest);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Verify.mjs","names":["Storage.RequirementStatus","DnsRecord.data","DnsRecord.equals","DnsRecord.sameSet","Storage.Service","Connect.Service","Resolver.Service","DomainName.decode","Errors.decode","Principal.Service","Storage.Readiness","Errors.fail","Reason.InvalidInput"],"sources":["../src/Verify.ts"],"sourcesContent":["/**\n * Observe provider and public DNS for an attachment, persist per-requirement readiness, and say\n * when to look again. The host keeps its own state machine (and any provider-side verification\n * such as SES) and feeds that in as `HostEvidence`.\n */\nimport { Context, DateTime, Duration, Effect, Layer, Option, Schema } from \"effect\";\n\nimport * as Connect from \"./Connect.ts\";\nimport * as DnsRecord from \"./DnsRecord.ts\";\nimport * as Errors from \"./internal/error.ts\";\nimport * as Reason from \"./Reason.ts\";\nimport * as DomainName from \"./DomainName.ts\";\nimport type * as Plan from \"./Plan.ts\";\nimport * as Principal from \"./Principal.ts\";\nimport * as Resolver from \"./Resolver.ts\";\nimport * as Storage from \"./Storage.ts\";\n\n/**\n * Every piece of evidence carries `values`, what the observer returned for the requirement's name\n * and type (empty when nothing was found, the lookup timed out, or the resolver failed), and\n * `detail`, `null` when the requirement is satisfied and otherwise the mismatch summary or the\n * resolver's error text, so a host can render \"found X, expected Y\".\n */\n// Rows written before these fields existed decode with the empty values.\nconst Values = Schema.Array(Schema.String).pipe(Schema.withDecodingDefaultKey(Effect.succeed([])));\nconst Detail = Schema.NullOr(Schema.String).pipe(\n Schema.withDecodingDefaultKey(Effect.succeed(null)),\n);\n\nexport class ProviderEvidence extends Schema.TaggedClass<ProviderEvidence>(\n \"@domainkit/Evidence/Provider\",\n)(\"Provider\", {\n provider: Schema.String,\n status: Storage.RequirementStatus,\n values: Values,\n detail: Detail,\n observedAt: Schema.DateTimeUtcFromString,\n}) {}\nexport class PublicDnsEvidence extends Schema.TaggedClass<PublicDnsEvidence>(\n \"@domainkit/Evidence/PublicDns\",\n)(\"PublicDns\", {\n resolver: Schema.String,\n status: Storage.RequirementStatus,\n values: Values,\n detail: Detail,\n observedAt: Schema.DateTimeUtcFromString,\n}) {}\n/** Anything the host knows that DomainKit cannot observe: SES identity status, a CDN cert, ... */\nexport class HostEvidence extends Schema.TaggedClass<HostEvidence>(\"@domainkit/Evidence/Host\")(\n \"Host\",\n {\n source: Schema.String,\n status: Schema.Literals([\"ok\", \"pending\", \"failed\"]),\n label: Schema.String,\n detail: Detail,\n observedAt: Schema.DateTimeUtcFromString,\n },\n) {}\nexport const Evidence = Schema.Union([ProviderEvidence, PublicDnsEvidence, HostEvidence]);\nexport type Evidence = typeof Evidence.Type;\n\nexport interface Requirement {\n /** `requirementKey(record)`: what a host pairs its own row against, by content. */\n readonly key: string;\n readonly operationId: Plan.OperationId | null;\n readonly record: DnsRecord.Model;\n readonly status: Storage.RequirementStatus;\n readonly evidence: ReadonlyArray<Evidence>;\n}\n\nexport interface Readiness {\n readonly domain: string;\n /** `null` for observe-only domains that have no attachment. */\n readonly attachmentId: string | null;\n readonly overall: Storage.Overall;\n readonly requirements: ReadonlyArray<Requirement>;\n readonly host: ReadonlyArray<HostEvidence>;\n readonly checkedAt: DateTime.Utc;\n readonly nextCheckAt: DateTime.Utc | null;\n}\n\ntype Fx<A> = Effect.Effect<A, Errors.DomainKitError, Principal.Service>;\n\nexport interface Interface {\n /**\n * What to observe, in order of precedence: `requirements` when supplied (an attached domain\n * without a provisioning receipt, or a domain with no attachment whose records the customer\n * applies by hand), else the latest provisioning receipt of the domain's attachment (records it\n * applied or found in place). Neither is `InvalidInput`. Provider evidence is added when the\n * attachment's session can be built; public DNS is always observed, and readiness is stored\n * per domain either way.\n */\n readonly observe: (input: {\n readonly domain: string;\n readonly requirements?: ReadonlyArray<DnsRecord.Model>;\n }) => Fx<Readiness>;\n /** Merge host evidence into stored readiness without re-observing DNS. */\n readonly attachEvidence: (input: {\n readonly domain: string;\n readonly evidence: ReadonlyArray<HostEvidence>;\n }) => Fx<Readiness>;\n readonly latest: (domain: string) => Fx<Readiness | null>;\n}\n\nexport class Service extends Context.Service<Service, Interface>()(\"@domainkit/Verify\") {}\n\n/**\n * A requirement's identity: the record's type, name, and data, and nothing else. It is what a host\n * pairs its own rows against readiness by, instead of trusting the order of the array.\n * `@domainkit/react`'s `Records.identity` is this function.\n */\nexport const requirementKey = (record: DnsRecord.Model): string =>\n [record._tag, record.name, DnsRecord.data(record)].join(\":\");\n\n/** Counts across a readiness's requirements. `observed` is false when there is no readiness yet. */\nexport interface Summary {\n readonly observed: boolean;\n readonly total: number;\n readonly satisfied: number;\n readonly missing: number;\n readonly mismatch: number;\n readonly unknown: number;\n}\n\n/** The least a value has to carry to be summarised: core `Readiness` and the wire shape both do. */\nexport interface Summarisable {\n readonly requirements: ReadonlyArray<{ readonly status: Storage.RequirementStatus }>;\n}\n\n/**\n * How a readiness stands, as counts. Pure and total: a host renders \"3 of 4 found\" from the stored\n * fact without deciding what an absent readiness means, because `observed` says so.\n */\nexport const summary = (readiness: Summarisable | null): Summary => {\n const statuses = readiness?.requirements.map(({ status }) => status) ?? [];\n const count = (status: Storage.RequirementStatus): number =>\n statuses.filter((candidate) => candidate === status).length;\n return {\n observed: readiness !== null,\n total: statuses.length,\n satisfied: count(\"satisfied\"),\n missing: count(\"missing\"),\n mismatch: count(\"mismatch\"),\n unknown: count(\"unknown\"),\n };\n};\n\nexport interface PolicyShape {\n /** Delay before the next check, given time since the first pending observation. Default ladder: 15s, 1m, 5m, 30m. */\n readonly backoff: (pendingForMs: number) => number;\n /** Which resolvers must agree for `satisfied`: default `any`. */\n readonly quorum: \"any\" | \"all\" | { readonly minimum: number };\n}\nexport const defaults: PolicyShape = {\n backoff: (pendingForMs) =>\n pendingForMs < 60_000\n ? 15_000\n : pendingForMs < 10 * 60_000\n ? 60_000\n : pendingForMs < 60 * 60_000\n ? 5 * 60_000\n : 30 * 60_000,\n quorum: \"any\",\n};\nexport class Policy extends Context.Reference<PolicyShape>(\"@domainkit/Verify/Policy\", {\n defaultValue: () => defaults,\n}) {}\n\n/** Why readiness was written: a DNS observation, or host evidence merged into the stored row. */\nexport type Cause = \"observe\" | \"evidence\";\n\nexport interface ReadinessChanged {\n readonly domain: string;\n readonly readiness: Readiness;\n readonly cause: Cause;\n}\n\nexport interface ObserverShape {\n /**\n * Called once per stored readiness, after the write. A host that projects readiness onto its own\n * rows, wakes a durable job at `nextCheckAt`, or notifies a customer hangs it here instead of\n * mirroring the fact at every call site.\n *\n * Events for one domain are not ordered against each other: two observations that overlap both\n * store, last write wins in `Storage`, and their callbacks can finish in either order. A\n * projection compares `readiness.checkedAt`, which is the moment the row was written, and keeps\n * the later one; `latest` remains the authority.\n */\n readonly readinessChanged: (event: ReadinessChanged) => Effect.Effect<void, unknown>;\n}\n\n/**\n * The host seam for \"DomainKit wrote readiness\". The default does nothing; provide one with\n * `Effect.provideService(Verify.Observer, ...)` or `Layer.succeed(Verify.Observer, ...)` over\n * `DomainKit.layer`, the same way `Policy` is overridden.\n *\n * It runs after `storage.readiness.put` returns, outside any transaction, because `Storage`\n * exposes none. A failure or defect in the observer is logged and swallowed: the observation is\n * already durable, and losing it because a projection failed would make the stored fact depend on\n * the host's side effects. A host that needs the projection to be reliable enqueues durable work\n * here rather than doing the work inline.\n */\nexport class Observer extends Context.Reference<ObserverShape>(\"@domainkit/Verify/Observer\", {\n defaultValue: (): ObserverShape => ({ readinessChanged: () => Effect.void }),\n}) {}\n\n// ---------------------------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------------------------\n\nconst StoredEvidence = Schema.Array(Evidence);\n\ninterface ProviderSide {\n readonly provider: string;\n readonly records: ReadonlyArray<DnsRecord.Observed>;\n}\nconst StoredHost = Schema.Array(HostEvidence);\n\n/** A requirement is satisfied by an exact match; an exclusive one is contradicted by any same-set record. */\nexport const statusAgainst = (\n record: DnsRecord.Model,\n observed: ReadonlyArray<DnsRecord.Observed>,\n): Storage.RequirementStatus => {\n if (observed.some((candidate) => DnsRecord.equals(candidate, record))) return \"satisfied\";\n if (\n record.policy === \"exclusive\" &&\n observed.some((candidate) => DnsRecord.sameSet(candidate, record))\n ) {\n return \"mismatch\";\n }\n return \"missing\";\n};\n\ninterface Observation {\n readonly status: Storage.RequirementStatus;\n readonly values: ReadonlyArray<string>;\n readonly detail: string | null;\n}\n\nconst render = (observed: DnsRecord.Observed): string =>\n observed._tag === \"Opaque\" ? JSON.stringify(observed.raw) : DnsRecord.data(observed);\n\n/** Status plus what the observer holds for the requirement's name and type. */\nconst observation = (\n record: DnsRecord.Model,\n observed: ReadonlyArray<DnsRecord.Observed>,\n): Observation => {\n const status = statusAgainst(record, observed);\n const values = observed.filter((candidate) => DnsRecord.sameSet(candidate, record)).map(render);\n const detail =\n status === \"satisfied\"\n ? null\n : values.length === 0\n ? `no ${record._tag} record at ${record.name}`\n : `expected ${DnsRecord.data(record)}; found ${values.join(\", \")}`;\n return { status, values, detail };\n};\n\nconst combine = (statuses: ReadonlyArray<Storage.RequirementStatus>): Storage.RequirementStatus => {\n const known = statuses.filter((status) => status !== \"unknown\");\n if (known.length === 0) return \"unknown\";\n if (known.includes(\"mismatch\")) return \"mismatch\";\n return known.every((status) => status === \"satisfied\") ? \"satisfied\" : \"missing\";\n};\n\nconst quorumStatus = (\n quorum: PolicyShape[\"quorum\"],\n statuses: ReadonlyArray<Storage.RequirementStatus>,\n): Storage.RequirementStatus => {\n const known = statuses.filter((status) => status !== \"unknown\");\n const satisfied = known.filter((status) => status === \"satisfied\").length;\n const needed =\n quorum === \"any\" ? 1 : quorum === \"all\" ? Math.max(1, known.length) : quorum.minimum;\n if (known.includes(\"mismatch\")) return \"mismatch\";\n if (known.length > 0 && satisfied >= needed) return \"satisfied\";\n return known.length === 0 ? \"unknown\" : \"missing\";\n};\n\nconst overallOf = (\n requirements: ReadonlyArray<{ readonly status: Storage.RequirementStatus }>,\n host: ReadonlyArray<HostEvidence>,\n): Storage.Overall => {\n if (\n requirements.some(({ status }) => status === \"mismatch\") ||\n host.some(({ status }) => status === \"failed\")\n ) {\n return \"failed\";\n }\n const ready =\n requirements.length + host.length > 0 &&\n requirements.every(({ status }) => status === \"satisfied\") &&\n host.every(({ status }) => status === \"ok\");\n return ready ? \"ready\" : \"pending\";\n};\n\n/** The identity of a requirement set: type, name, data, and policy, independent of labels and order. */\nconst requirementSetKey = (requirements: ReadonlyArray<{ readonly record: DnsRecord.Model }>) =>\n requirements\n .map(({ record }) => `${record._tag} ${record.name} ${DnsRecord.data(record)} ${record.policy}`)\n .sort()\n .join(\"\\n\");\n\nexport const make: Effect.Effect<\n Interface,\n never,\n Storage.Service | Connect.Service | Resolver.Service\n> = Effect.gen(function* () {\n const storage = yield* Storage.Service;\n const connect = yield* Connect.Service;\n const resolver = yield* Resolver.Service;\n\n const attachmentFor = (\n input: string,\n ): Fx<{\n readonly domain: DomainName.Model;\n readonly attachment: Storage.Attachment | null;\n }> =>\n Effect.gen(function* () {\n const domain = yield* DomainName.decode(input);\n const attachment = yield* storage.attachments.byDomain(domain);\n return { domain, attachment: Option.getOrNull(attachment) };\n });\n\n const decodeRow = (row: Storage.Readiness): Effect.Effect<Readiness, Errors.DomainKitError> =>\n Effect.gen(function* () {\n const requirements = yield* Effect.forEach(row.requirements, (requirement) =>\n Errors.decode(StoredEvidence, requirement.evidence, \"evidence\").pipe(\n Effect.map((evidence): Requirement => ({\n key: requirementKey(requirement.record),\n operationId: requirement.operationId,\n record: requirement.record,\n status: requirement.status,\n evidence,\n })),\n ),\n );\n const host = yield* Errors.decode(StoredHost, row.host, \"host\");\n return {\n domain: row.domain,\n attachmentId: row.attachmentId,\n overall: row.overall,\n requirements,\n host,\n checkedAt: row.checkedAt,\n nextCheckAt: row.nextCheckAt,\n };\n });\n\n /** Persist readiness, carrying the pending streak forward for the backoff ladder. */\n const store = (input: {\n readonly domain: string;\n readonly attachment: Storage.Attachment | null;\n readonly requirements: ReadonlyArray<Requirement>;\n readonly host: ReadonlyArray<HostEvidence>;\n readonly previous: Option.Option<Storage.Readiness>;\n readonly cause: Cause;\n }): Fx<Readiness> =>\n Effect.gen(function* () {\n const principal = yield* Principal.Service;\n const policy = yield* Policy;\n const observer = yield* Observer;\n const now = yield* DateTime.now;\n const overall = overallOf(input.requirements, input.host);\n const sameRequirements =\n Option.isSome(input.previous) &&\n requirementSetKey(input.previous.value.requirements) ===\n requirementSetKey(input.requirements);\n const pendingSince =\n overall === \"ready\"\n ? null\n : sameRequirements &&\n Option.isSome(input.previous) &&\n input.previous.value.pendingSince !== null\n ? input.previous.value.pendingSince\n : now;\n const nextCheckAt =\n pendingSince === null\n ? null\n : DateTime.addDuration(\n now,\n Duration.millis(\n policy.backoff(DateTime.toEpochMillis(now) - DateTime.toEpochMillis(pendingSince)),\n ),\n );\n const row = new Storage.Readiness({\n domain: input.domain,\n attachmentId: input.attachment?.id ?? null,\n ownerId: principal.ownerId,\n overall,\n requirements: input.requirements.map((requirement) => ({\n operationId: requirement.operationId,\n record: requirement.record,\n status: requirement.status,\n evidence: Schema.encodeSync(StoredEvidence)(requirement.evidence),\n })),\n host: Schema.encodeSync(StoredHost)(input.host),\n pendingSince,\n checkedAt: now,\n nextCheckAt,\n });\n yield* storage.readiness.put(row);\n const readiness: Readiness = {\n domain: row.domain,\n attachmentId: row.attachmentId,\n overall,\n requirements: input.requirements,\n host: input.host,\n checkedAt: now,\n nextCheckAt,\n };\n yield* observer\n .readinessChanged({ domain: row.domain, readiness, cause: input.cause })\n .pipe(\n Effect.catchCause((cause) =>\n Effect.logWarning(`Verify.Observer failed for ${row.domain}`, cause),\n ),\n );\n return readiness;\n });\n\n const defaultRequirements = (\n attachment: Storage.Attachment,\n ): Fx<\n ReadonlyArray<{\n readonly operationId: Plan.OperationId | null;\n readonly record: DnsRecord.Model;\n }>\n > =>\n Effect.gen(function* () {\n const latest = yield* storage.attempts.latest(attachment.id, \"provisioning\");\n if (Option.isNone(latest) || latest.value.receipt === null) {\n return yield* Errors.fail(\n new Reason.InvalidInput({\n message: `${attachment.domain} has no provisioning receipt; pass requirements to observe`,\n field: \"requirements\",\n }),\n );\n }\n const receipt = latest.value.receipt;\n const inPlace = new Set(\n receipt.outcomes.flatMap((outcome) =>\n outcome._tag === \"Applied\" || (outcome._tag === \"Skipped\" && outcome.reason === \"noop\")\n ? [outcome.operationId]\n : [],\n ),\n );\n return latest.value.plan.operations\n .filter((operation) => inPlace.has(operation.id))\n .map((operation) => ({ operationId: operation.id, record: operation.record }));\n });\n\n const observe: Interface[\"observe\"] = (input) =>\n Effect.gen(function* () {\n const policy = yield* Policy;\n const { domain, attachment } = yield* attachmentFor(input.domain);\n if (input.requirements === undefined && attachment === null) {\n return yield* Errors.fail(\n new Reason.InvalidInput({\n message: `${domain} is not attached; pass requirements to observe public DNS`,\n field: \"requirements\",\n }),\n );\n }\n const requirements =\n input.requirements === undefined && attachment !== null\n ? yield* defaultRequirements(attachment)\n : (input.requirements ?? []).map((record) => ({ operationId: null, record }));\n if (requirements.length === 0) {\n return yield* Errors.fail(\n new Reason.InvalidInput({\n message: \"Nothing to observe: pass at least one requirement\",\n field: \"requirements\",\n }),\n );\n }\n const now = yield* DateTime.now;\n // Provider readback is evidence when the attachment's session can be built; when it\n // cannot (no attachment, revoked credential, provider outage) public DNS stands alone.\n const providerSide = yield* attachment === null\n ? Effect.succeed(Option.none<ProviderSide>())\n : Effect.gen(function* () {\n const connection = yield* storage.connections.get(attachment.connectionId);\n const authorization = yield* storage.authorizations.get(connection.authorizationId);\n const { session, target } = yield* connect.session(attachment.id);\n const observed = yield* session.dns(target).list(target.zone);\n return Option.some<ProviderSide>({\n provider: authorization.provider,\n records: observed.map(({ record }) => record),\n });\n }).pipe(Effect.catch(() => Effect.succeed(Option.none<ProviderSide>())));\n const observed = yield* Effect.forEach(\n requirements,\n ({ operationId, record }) =>\n Effect.gen(function* () {\n const providerEvidence = Option.isSome(providerSide)\n ? [\n new ProviderEvidence({\n provider: providerSide.value.provider,\n ...observation(record, providerSide.value.records),\n observedAt: now,\n }),\n ]\n : [];\n const outcomes = yield* resolver.resolve(record.name, record._tag);\n const publicEvidence = outcomes.map((outcome) =>\n outcome._tag === \"Answered\"\n ? new PublicDnsEvidence({\n resolver: outcome.answer.resolver,\n ...observation(record, outcome.answer.records),\n observedAt: now,\n })\n : new PublicDnsEvidence({\n resolver: outcome.resolver,\n status: \"unknown\",\n values: [],\n detail:\n outcome._tag === \"TimedOut\"\n ? `${outcome.resolver} timed out`\n : outcome.message,\n observedAt: now,\n }),\n );\n const publicStatus = quorumStatus(\n policy.quorum,\n publicEvidence.map(({ status }) => status),\n );\n const status = combine([\n ...providerEvidence.map((evidence) => evidence.status),\n publicStatus,\n ]);\n return {\n key: requirementKey(record),\n operationId,\n record,\n status,\n evidence: [...providerEvidence, ...publicEvidence],\n } satisfies Requirement;\n }),\n { concurrency: \"unbounded\" },\n );\n const previous = yield* storage.readiness.get(domain);\n const host = Option.isSome(previous)\n ? yield* Errors.decode(StoredHost, previous.value.host, \"host\")\n : [];\n return yield* store({\n domain,\n attachment,\n requirements: observed,\n host,\n previous,\n cause: \"observe\",\n });\n });\n\n const attachEvidence: Interface[\"attachEvidence\"] = (input) =>\n Effect.gen(function* () {\n const { domain, attachment } = yield* attachmentFor(input.domain);\n const previous = yield* storage.readiness.get(domain);\n const current = Option.isSome(previous)\n ? yield* decodeRow(previous.value)\n : {\n requirements: [] as ReadonlyArray<Requirement>,\n host: [] as ReadonlyArray<HostEvidence>,\n };\n const bySource = new Map(current.host.map((evidence) => [evidence.source, evidence]));\n for (const evidence of input.evidence) bySource.set(evidence.source, evidence);\n return yield* store({\n domain,\n attachment,\n requirements: current.requirements,\n host: [...bySource.values()],\n previous,\n cause: \"evidence\",\n });\n });\n\n const latest: Interface[\"latest\"] = (domain) =>\n Effect.gen(function* () {\n const { domain: name } = yield* attachmentFor(domain);\n const stored = yield* storage.readiness.get(name);\n return Option.isSome(stored) ? yield* decodeRow(stored.value) : null;\n });\n\n return { observe, attachEvidence, latest };\n});\n\nexport const layer: Layer.Layer<\n Service,\n never,\n Storage.Service | Connect.Service | Resolver.Service\n> = Layer.effect(Service)(make);\n\nconst accessor =\n <Args extends ReadonlyArray<unknown>, A>(\n pick: (service: Interface) => (...args: Args) => Fx<A>,\n ): ((...args: Args) => Effect.Effect<A, Errors.DomainKitError, Principal.Service | Service>) =>\n (...args) =>\n Effect.flatMap(Service, (service) => pick(service)(...args));\n\nexport const observe = accessor((service) => service.observe);\nexport const attachEvidence = accessor((service) => service.attachEvidence);\nexport const latest = accessor((service) => service.latest);\n"],"mappings":"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"}
|
package/dist/package.mjs
CHANGED
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
* provides `Storage` and `Custody` beneath it, the same way `Persistence.layer` sits over a
|
|
4
4
|
* backing store in Effect.
|
|
5
5
|
*
|
|
6
|
-
* Defaults: `Resolver.layer` (Cloudflare + Google DoH) and the `Policy`
|
|
7
|
-
*
|
|
6
|
+
* Defaults: `Resolver.layer` (Cloudflare + Google DoH) and the reference-shaped seams, `Verify.Policy`
|
|
7
|
+
* and `Verify.Observer`. Override either with `Effect.provideService` or `Layer.succeed` over this
|
|
8
|
+
* layer; pass `resolver` for a different pool.
|
|
8
9
|
*/
|
|
9
10
|
import { Layer } from "effect";
|
|
10
11
|
import * as Cleanup from "./Cleanup.ts";
|