domainkit 0.7.0 → 0.9.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 +214 -39
- package/dist/Approval.mjs +34 -0
- package/dist/Approval.mjs.map +1 -0
- package/dist/Cleanup.mjs +97 -0
- package/dist/Cleanup.mjs.map +1 -0
- package/dist/Cloudflare.mjs +225 -0
- package/dist/Cloudflare.mjs.map +1 -0
- package/dist/Connect.mjs +463 -0
- package/dist/Connect.mjs.map +1 -0
- package/dist/Custody.mjs +64 -0
- package/dist/Custody.mjs.map +1 -0
- package/dist/DnsRecord.mjs +213 -0
- package/dist/DnsRecord.mjs.map +1 -0
- package/dist/DomainKit.mjs +34 -0
- package/dist/DomainKit.mjs.map +1 -0
- package/dist/DomainName.mjs +100 -0
- package/dist/DomainName.mjs.map +1 -0
- package/dist/Plan.mjs +102 -0
- package/dist/Plan.mjs.map +1 -0
- package/dist/Principal.mjs +23 -0
- package/dist/Principal.mjs.map +1 -0
- package/dist/Provider.mjs +130 -0
- package/dist/Provider.mjs.map +1 -0
- package/dist/Providers.mjs +40 -0
- package/dist/Providers.mjs.map +1 -0
- package/dist/Provision.mjs +93 -0
- package/dist/Provision.mjs.map +1 -0
- package/dist/Reason.mjs +162 -0
- package/dist/Reason.mjs.map +1 -0
- package/dist/Receipt.mjs +71 -0
- package/dist/Receipt.mjs.map +1 -0
- package/dist/Resolver.mjs +75 -0
- package/dist/Resolver.mjs.map +1 -0
- package/dist/Server.mjs +516 -0
- package/dist/Server.mjs.map +1 -0
- package/dist/Storage.mjs +236 -0
- package/dist/Storage.mjs.map +1 -0
- package/dist/Testing.mjs +226 -0
- package/dist/Testing.mjs.map +1 -0
- package/dist/Transport.mjs +187 -0
- package/dist/Transport.mjs.map +1 -0
- package/dist/Vercel.mjs +116 -0
- package/dist/Vercel.mjs.map +1 -0
- package/dist/Verify.mjs +292 -0
- package/dist/Verify.mjs.map +1 -0
- package/dist/{rolldown-runtime-D7D4PA-g.mjs → _virtual/_rolldown/runtime.mjs} +1 -1
- package/dist/entry/client.mjs +2 -0
- package/dist/entry/server.mjs +2 -0
- package/dist/entry/testing.mjs +3 -0
- package/dist/index.mjs +33 -356
- package/dist/index.mjs.map +1 -1
- package/dist/internal/aes.mjs +61 -0
- package/dist/internal/aes.mjs.map +1 -0
- package/dist/internal/attempts.mjs +284 -0
- package/dist/internal/attempts.mjs.map +1 -0
- package/dist/internal/cloudflare/client.mjs +100 -0
- package/dist/internal/cloudflare/client.mjs.map +1 -0
- package/dist/internal/cloudflare/protocol.mjs +59 -0
- package/dist/internal/cloudflare/protocol.mjs.map +1 -0
- package/dist/internal/cloudflare/records.mjs +156 -0
- package/dist/internal/cloudflare/records.mjs.map +1 -0
- package/dist/internal/config.mjs +13 -0
- package/dist/internal/config.mjs.map +1 -0
- package/dist/internal/conformance/provider.mjs +207 -0
- package/dist/internal/conformance/provider.mjs.map +1 -0
- package/dist/internal/conformance/storage.mjs +428 -0
- package/dist/internal/conformance/storage.mjs.map +1 -0
- package/dist/internal/digest.mjs +25 -0
- package/dist/internal/digest.mjs.map +1 -0
- package/dist/internal/doh.mjs +152 -0
- package/dist/internal/doh.mjs.map +1 -0
- package/dist/internal/error.mjs +69 -0
- package/dist/internal/error.mjs.map +1 -0
- package/dist/internal/http.mjs +89 -0
- package/dist/internal/http.mjs.map +1 -0
- package/dist/internal/ids.mjs +8 -0
- package/dist/internal/ids.mjs.map +1 -0
- package/dist/internal/oauth.mjs +113 -0
- package/dist/internal/oauth.mjs.map +1 -0
- package/dist/internal/planner.mjs +88 -0
- package/dist/internal/planner.mjs.map +1 -0
- package/dist/internal/provider-async.mjs +83 -0
- package/dist/internal/provider-async.mjs.map +1 -0
- package/dist/internal/storage-memory.mjs +302 -0
- package/dist/internal/storage-memory.mjs.map +1 -0
- package/dist/internal/vercel/client.mjs +122 -0
- package/dist/internal/vercel/client.mjs.map +1 -0
- package/dist/internal/vercel/protocol.mjs +80 -0
- package/dist/internal/vercel/protocol.mjs.map +1 -0
- package/dist/internal/vercel/records.mjs +151 -0
- package/dist/internal/vercel/records.mjs.map +1 -0
- package/dist/package.mjs +6 -0
- package/dist/package.mjs.map +1 -0
- package/dist/types/Approval.d.ts +26 -0
- package/dist/types/Cleanup.d.ts +41 -0
- package/dist/types/Cloudflare.d.ts +36 -0
- package/dist/types/Connect.d.ts +506 -0
- package/dist/types/Custody.d.ts +38 -0
- package/dist/types/DnsRecord.d.ts +158 -0
- package/dist/types/DomainKit.d.ts +31 -0
- package/dist/types/DomainName.d.ts +27 -0
- package/dist/types/Plan.d.ts +76 -0
- package/dist/types/Principal.d.ts +18 -0
- package/dist/types/Provider.d.ts +248 -0
- package/dist/types/Providers.d.ts +16 -0
- package/dist/types/Provision.d.ts +75 -0
- package/dist/types/Reason.d.ts +118 -0
- package/dist/types/Receipt.d.ts +55 -0
- package/dist/types/Resolver.d.ts +135 -0
- package/dist/types/Server.d.ts +6523 -0
- package/dist/types/Storage.d.ts +314 -0
- package/dist/types/Testing.d.ts +84 -0
- package/dist/types/Transport.d.ts +124 -0
- package/dist/types/Vercel.d.ts +25 -0
- package/dist/types/Verify.d.ts +109 -0
- package/dist/types/entry/client.d.ts +2 -0
- package/dist/types/entry/server.d.ts +2 -0
- package/dist/types/entry/testing.d.ts +3 -0
- package/dist/types/index.d.ts +21 -0
- package/dist/types/internal/aes.d.ts +8 -0
- package/dist/types/internal/attempts.d.ts +40 -0
- package/dist/types/internal/cloudflare/client.d.ts +23 -0
- package/dist/types/internal/cloudflare/protocol.d.ts +71 -0
- package/dist/types/internal/cloudflare/records.d.ts +8 -0
- package/dist/types/internal/config.d.ts +4 -0
- package/dist/types/internal/conformance/provider.d.ts +12 -0
- package/dist/types/internal/conformance/storage.d.ts +14 -0
- package/dist/types/internal/digest.d.ts +10 -0
- package/dist/types/internal/doh.d.ts +19 -0
- package/dist/types/internal/error.d.ts +24 -0
- package/dist/types/internal/http.d.ts +27 -0
- package/dist/types/internal/ids.d.ts +3 -0
- package/dist/types/internal/oauth.d.ts +58 -0
- package/dist/types/internal/planner.d.ts +22 -0
- package/dist/types/internal/provider-async.d.ts +78 -0
- package/dist/types/internal/storage-memory.d.ts +2 -0
- package/dist/types/internal/vercel/client.d.ts +29 -0
- package/dist/types/internal/vercel/protocol.d.ts +124 -0
- package/dist/types/internal/vercel/records.d.ts +7 -0
- package/package.json +88 -12
- package/dist/canonical-json-CQYKsZw7.mjs +0 -310
- package/dist/canonical-json-CQYKsZw7.mjs.map +0 -1
- package/dist/connect-10vHuShV.d.mts +0 -1401
- package/dist/connect-oI8hcMxc.mjs +0 -185
- package/dist/connect-oI8hcMxc.mjs.map +0 -1
- package/dist/connection-C0xsU41C.d.mts +0 -186
- package/dist/connection-api-DW7xUJdj.d.mts +0 -26
- package/dist/index.d.mts +0 -882
- package/dist/managed-dns-connections-C3A_GBaz.mjs +0 -99
- package/dist/managed-dns-connections-C3A_GBaz.mjs.map +0 -1
- package/dist/promise.d.mts +0 -378
- package/dist/promise.mjs +0 -598
- package/dist/promise.mjs.map +0 -1
- package/dist/resolver-DPJs8h0a.mjs +0 -859
- package/dist/resolver-DPJs8h0a.mjs.map +0 -1
- package/dist/resolver-DRrLm_hB.d.mts +0 -3734
- package/dist/server.d.mts +0 -128
- package/dist/server.mjs +0 -319
- package/dist/server.mjs.map +0 -1
- package/dist/testing.d.mts +0 -62
- package/dist/testing.mjs +0 -507
- package/dist/testing.mjs.map +0 -1
- package/dist/verify-C3V_GtzL.d.mts +0 -1268
- package/dist/verify-DDnK8U7C.mjs +0 -2396
- package/dist/verify-DDnK8U7C.mjs.map +0 -1
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { isWithin } from "../../DomainName.mjs";
|
|
2
|
+
import { Opaque, a, aaaa, caa, cname, mx, ns, srv, txt } from "../../DnsRecord.mjs";
|
|
3
|
+
import { Effect, Schema } from "effect";
|
|
4
|
+
//#region src/internal/cloudflare/records.ts
|
|
5
|
+
const Data$1 = Schema.Struct({
|
|
6
|
+
flags: Schema.optionalKey(Schema.Number),
|
|
7
|
+
port: Schema.optionalKey(Schema.Number),
|
|
8
|
+
priority: Schema.optionalKey(Schema.Number),
|
|
9
|
+
tag: Schema.optionalKey(Schema.String),
|
|
10
|
+
target: Schema.optionalKey(Schema.String),
|
|
11
|
+
value: Schema.optionalKey(Schema.String),
|
|
12
|
+
weight: Schema.optionalKey(Schema.Number)
|
|
13
|
+
});
|
|
14
|
+
/** Records DomainKit cannot model, or cannot parse, come back opaque so plans never touch them. */
|
|
15
|
+
const decode = (record) => Effect.map(decodeRecord(record), (observed) => ({
|
|
16
|
+
record: observed,
|
|
17
|
+
providerRecordId: record.id
|
|
18
|
+
}));
|
|
19
|
+
const decodeRecord = (record) => {
|
|
20
|
+
const opaque = new Opaque({
|
|
21
|
+
name: record.name,
|
|
22
|
+
type: record.type,
|
|
23
|
+
raw: record
|
|
24
|
+
});
|
|
25
|
+
const ttl = record.ttl === 1 ? null : record.ttl;
|
|
26
|
+
const content = record.content;
|
|
27
|
+
const attempt = () => {
|
|
28
|
+
switch (record.type) {
|
|
29
|
+
case "A": return a({
|
|
30
|
+
name: record.name,
|
|
31
|
+
address: required(content),
|
|
32
|
+
...ttlOption(ttl)
|
|
33
|
+
});
|
|
34
|
+
case "AAAA": return aaaa({
|
|
35
|
+
name: record.name,
|
|
36
|
+
address: required(content),
|
|
37
|
+
...ttlOption(ttl)
|
|
38
|
+
});
|
|
39
|
+
case "CNAME": return cname({
|
|
40
|
+
name: record.name,
|
|
41
|
+
target: required(content),
|
|
42
|
+
...ttlOption(ttl)
|
|
43
|
+
});
|
|
44
|
+
case "NS": return ns({
|
|
45
|
+
name: record.name,
|
|
46
|
+
nameserver: required(content),
|
|
47
|
+
...ttlOption(ttl)
|
|
48
|
+
});
|
|
49
|
+
case "TXT": return txt({
|
|
50
|
+
name: record.name,
|
|
51
|
+
value: unquote(required(content)),
|
|
52
|
+
...ttlOption(ttl)
|
|
53
|
+
});
|
|
54
|
+
case "MX": return mx({
|
|
55
|
+
name: record.name,
|
|
56
|
+
exchange: required(content),
|
|
57
|
+
priority: required(record.priority),
|
|
58
|
+
...ttlOption(ttl)
|
|
59
|
+
});
|
|
60
|
+
case "CAA": {
|
|
61
|
+
const data = Schema.decodeUnknownSync(Data$1)(record.data);
|
|
62
|
+
return caa({
|
|
63
|
+
name: record.name,
|
|
64
|
+
flags: required(data.flags),
|
|
65
|
+
tag: required(data.tag),
|
|
66
|
+
value: required(data.value),
|
|
67
|
+
...ttlOption(ttl)
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
case "SRV": {
|
|
71
|
+
const data = Schema.decodeUnknownSync(Data$1)(record.data);
|
|
72
|
+
return srv({
|
|
73
|
+
name: record.name,
|
|
74
|
+
target: required(data.target),
|
|
75
|
+
port: required(data.port),
|
|
76
|
+
priority: required(data.priority),
|
|
77
|
+
weight: required(data.weight),
|
|
78
|
+
...ttlOption(ttl)
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
default: return opaque;
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
return Effect.sync(() => {
|
|
85
|
+
try {
|
|
86
|
+
return attempt();
|
|
87
|
+
} catch {
|
|
88
|
+
return opaque;
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
};
|
|
92
|
+
const encode = (record) => {
|
|
93
|
+
const common = {
|
|
94
|
+
name: record.name,
|
|
95
|
+
proxied: false,
|
|
96
|
+
ttl: record.ttl ?? 1,
|
|
97
|
+
type: record._tag
|
|
98
|
+
};
|
|
99
|
+
switch (record._tag) {
|
|
100
|
+
case "A":
|
|
101
|
+
case "AAAA": return {
|
|
102
|
+
...common,
|
|
103
|
+
content: record.address
|
|
104
|
+
};
|
|
105
|
+
case "CNAME": return {
|
|
106
|
+
...common,
|
|
107
|
+
content: record.target
|
|
108
|
+
};
|
|
109
|
+
case "NS": return {
|
|
110
|
+
...common,
|
|
111
|
+
content: record.nameserver
|
|
112
|
+
};
|
|
113
|
+
case "TXT": return {
|
|
114
|
+
...common,
|
|
115
|
+
content: record.value
|
|
116
|
+
};
|
|
117
|
+
case "MX": return {
|
|
118
|
+
...common,
|
|
119
|
+
content: record.exchange,
|
|
120
|
+
priority: record.priority
|
|
121
|
+
};
|
|
122
|
+
case "CAA": return {
|
|
123
|
+
...common,
|
|
124
|
+
data: {
|
|
125
|
+
flags: record.flags,
|
|
126
|
+
tag: record.tag,
|
|
127
|
+
value: record.value
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
case "SRV": return {
|
|
131
|
+
...common,
|
|
132
|
+
data: {
|
|
133
|
+
port: record.port,
|
|
134
|
+
priority: record.priority,
|
|
135
|
+
target: record.target,
|
|
136
|
+
weight: record.weight
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
const isWithinZone = (name, zone) => isWithin(name, zone);
|
|
142
|
+
function required(value) {
|
|
143
|
+
if (value === void 0) throw new Error("Cloudflare record field is missing");
|
|
144
|
+
return value;
|
|
145
|
+
}
|
|
146
|
+
function ttlOption(ttl) {
|
|
147
|
+
return ttl === null ? {} : { ttl };
|
|
148
|
+
}
|
|
149
|
+
function unquote(value) {
|
|
150
|
+
const chunks = [...value.matchAll(/"((?:\\.|[^"])*)"/g)];
|
|
151
|
+
return chunks.length === 0 ? value : chunks.map((match) => (match[1] ?? "").replace(/\\"/g, "\"").replace(/\\\\/g, "\\")).join("");
|
|
152
|
+
}
|
|
153
|
+
//#endregion
|
|
154
|
+
export { decode, encode, isWithinZone };
|
|
155
|
+
|
|
156
|
+
//# sourceMappingURL=records.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"records.mjs","names":["Data","S","DnsRecord.Opaque","DnsRecord.a","DnsRecord.aaaa","DnsRecord.cname","DnsRecord.ns","DnsRecord.txt","DnsRecord.mx","DnsRecord.caa","DnsRecord.srv","DomainName.isWithin"],"sources":["../../../src/internal/cloudflare/records.ts"],"sourcesContent":["import { Effect, Schema as S } from \"effect\";\n\nimport * as DnsRecord from \"../../DnsRecord.ts\";\nimport * as DomainName from \"../../DomainName.ts\";\nimport type * as Provider from \"../../Provider.ts\";\nimport type * as Protocol from \"./protocol.ts\";\n\nconst Data = S.Struct({\n flags: S.optionalKey(S.Number),\n port: S.optionalKey(S.Number),\n priority: S.optionalKey(S.Number),\n tag: S.optionalKey(S.String),\n target: S.optionalKey(S.String),\n value: S.optionalKey(S.String),\n weight: S.optionalKey(S.Number),\n});\n\n/** Records DomainKit cannot model, or cannot parse, come back opaque so plans never touch them. */\nexport const decode = (record: Protocol.Record): Effect.Effect<Provider.ObservedRecord> =>\n Effect.map(decodeRecord(record), (observed) => ({\n record: observed,\n providerRecordId: record.id,\n }));\n\nconst decodeRecord = (record: Protocol.Record): Effect.Effect<DnsRecord.Observed> => {\n const opaque = new DnsRecord.Opaque({ name: record.name, type: record.type, raw: record });\n const ttl = record.ttl === 1 ? null : record.ttl;\n const content = record.content;\n const attempt = (): DnsRecord.Observed => {\n switch (record.type) {\n case \"A\":\n return DnsRecord.a({ name: record.name, address: required(content), ...ttlOption(ttl) });\n case \"AAAA\":\n return DnsRecord.aaaa({ name: record.name, address: required(content), ...ttlOption(ttl) });\n case \"CNAME\":\n return DnsRecord.cname({ name: record.name, target: required(content), ...ttlOption(ttl) });\n case \"NS\":\n return DnsRecord.ns({\n name: record.name,\n nameserver: required(content),\n ...ttlOption(ttl),\n });\n case \"TXT\":\n return DnsRecord.txt({\n name: record.name,\n value: unquote(required(content)),\n ...ttlOption(ttl),\n });\n case \"MX\":\n return DnsRecord.mx({\n name: record.name,\n exchange: required(content),\n priority: required(record.priority),\n ...ttlOption(ttl),\n });\n case \"CAA\": {\n const data = S.decodeUnknownSync(Data)(record.data);\n return DnsRecord.caa({\n name: record.name,\n flags: required(data.flags),\n tag: required(data.tag),\n value: required(data.value),\n ...ttlOption(ttl),\n });\n }\n case \"SRV\": {\n const data = S.decodeUnknownSync(Data)(record.data);\n return DnsRecord.srv({\n name: record.name,\n target: required(data.target),\n port: required(data.port),\n priority: required(data.priority),\n weight: required(data.weight),\n ...ttlOption(ttl),\n });\n }\n default:\n return opaque;\n }\n };\n return Effect.sync(() => {\n try {\n return attempt();\n } catch {\n return opaque;\n }\n });\n};\n\nexport const encode = (record: DnsRecord.Model): Record<string, unknown> => {\n const common = { name: record.name, proxied: false, ttl: record.ttl ?? 1, type: record._tag };\n switch (record._tag) {\n case \"A\":\n case \"AAAA\":\n return { ...common, content: record.address };\n case \"CNAME\":\n return { ...common, content: record.target };\n case \"NS\":\n return { ...common, content: record.nameserver };\n case \"TXT\":\n return { ...common, content: record.value };\n case \"MX\":\n return { ...common, content: record.exchange, priority: record.priority };\n case \"CAA\":\n return { ...common, data: { flags: record.flags, tag: record.tag, value: record.value } };\n case \"SRV\":\n return {\n ...common,\n data: {\n port: record.port,\n priority: record.priority,\n target: record.target,\n weight: record.weight,\n },\n };\n }\n};\n\nexport const isWithinZone = (name: string, zone: string): boolean =>\n DomainName.isWithin(name, zone);\n\nfunction required<A>(value: A | undefined): A {\n if (value === undefined) throw new Error(\"Cloudflare record field is missing\");\n return value;\n}\n\nfunction ttlOption(ttl: number | null): { readonly ttl?: number } {\n return ttl === null ? {} : { ttl };\n}\n\nfunction unquote(value: string): string {\n const chunks = [...value.matchAll(/\"((?:\\\\.|[^\"])*)\"/g)];\n return chunks.length === 0\n ? value\n : chunks.map((match) => (match[1] ?? \"\").replace(/\\\\\"/g, '\"').replace(/\\\\\\\\/g, \"\\\\\")).join(\"\");\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { InvalidInput } from "../Reason.mjs";
|
|
2
|
+
import { DomainKitError } from "./error.mjs";
|
|
3
|
+
import { Config, Effect } from "effect";
|
|
4
|
+
//#region src/internal/config.ts
|
|
5
|
+
/** Accept a literal or a `Config`; config failures surface as `InvalidInput` on `field`. */
|
|
6
|
+
const resolve = (value, field) => Config.isConfig(value) ? value.pipe(Effect.mapError((cause) => new DomainKitError({ reason: new InvalidInput({
|
|
7
|
+
message: cause.message,
|
|
8
|
+
field
|
|
9
|
+
}) }))) : Effect.succeed(value);
|
|
10
|
+
//#endregion
|
|
11
|
+
export { resolve };
|
|
12
|
+
|
|
13
|
+
//# sourceMappingURL=config.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.mjs","names":["Errors.DomainKitError","Reason.InvalidInput"],"sources":["../../src/internal/config.ts"],"sourcesContent":["import { Config, Effect } from \"effect\";\n\nimport * as Errors from \"./error.ts\";\nimport * as Reason from \"../Reason.ts\";\n\n/** Accept a literal or a `Config`; config failures surface as `InvalidInput` on `field`. */\nexport const resolve = <A>(\n value: A | Config.Config<A>,\n field: string,\n): Effect.Effect<A, Errors.DomainKitError> =>\n Config.isConfig(value)\n ? value.pipe(\n Effect.mapError(\n (cause) =>\n new Errors.DomainKitError({\n reason: new Reason.InvalidInput({ message: cause.message, field }),\n }),\n ),\n )\n : Effect.succeed(value);\n"],"mappings":";;;;;AAMA,MAAa,WACX,OACA,UAEA,OAAO,SAAS,KAAK,IACjB,MAAM,KACJ,OAAO,UACJ,UACC,IAAIA,eAAsB,EACxB,QAAQ,IAAIC,aAAoB;CAAE,SAAS,MAAM;CAAS;AAAM,CAAC,EACnE,CAAC,CACL,CACF,IACA,OAAO,QAAQ,KAAK"}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { cname as cname$1, equals, txt as txt$1 } from "../../DnsRecord.mjs";
|
|
2
|
+
import { InvalidInput, ProviderUnavailable } from "../../Reason.mjs";
|
|
3
|
+
import { DomainKitError, fail } from "../error.mjs";
|
|
4
|
+
import { Service } from "../../Custody.mjs";
|
|
5
|
+
import { fresh } from "../ids.mjs";
|
|
6
|
+
import { Service as Service$1, make } from "../../Principal.mjs";
|
|
7
|
+
import { encodeContext } from "../../Provider.mjs";
|
|
8
|
+
import { Service as Service$2 } from "../../Resolver.mjs";
|
|
9
|
+
import { applied } from "../../Receipt.mjs";
|
|
10
|
+
import { Attachment, Authorization, Credential, Service as Service$3 } from "../../Storage.mjs";
|
|
11
|
+
import { attach, session } from "../../Connect.mjs";
|
|
12
|
+
import { apply, approve, plan } from "../../Provision.mjs";
|
|
13
|
+
import { apply as apply$1, approve as approve$1, plan as plan$1 } from "../../Cleanup.mjs";
|
|
14
|
+
import { layerMemory } from "../../DomainKit.mjs";
|
|
15
|
+
import { DateTime, Effect, Layer } from "effect";
|
|
16
|
+
//#region src/internal/conformance/provider.ts
|
|
17
|
+
const principal = make({
|
|
18
|
+
ownerId: "conformance",
|
|
19
|
+
actorId: "conformance"
|
|
20
|
+
});
|
|
21
|
+
const failure = (name, message) => new DomainKitError({ reason: new InvalidInput({
|
|
22
|
+
message: `provider conformance ${name}: ${message}`,
|
|
23
|
+
field: "conformance"
|
|
24
|
+
}) });
|
|
25
|
+
const expect = (name, condition, message) => condition ? Effect.void : Effect.fail(failure(name, message));
|
|
26
|
+
/** Attach `zone` to a connection built straight from `credential`, bypassing `authenticate`. */
|
|
27
|
+
const attachZone = (definition, credential, zone) => Effect.gen(function* () {
|
|
28
|
+
const storage = yield* Service$3;
|
|
29
|
+
const custody = yield* Service;
|
|
30
|
+
const now = yield* DateTime.now;
|
|
31
|
+
const authorization = new Authorization({
|
|
32
|
+
id: yield* fresh("auth"),
|
|
33
|
+
ownerId: principal.ownerId,
|
|
34
|
+
provider: definition.id,
|
|
35
|
+
method: "token",
|
|
36
|
+
capabilities: ["dns:read", "dns:write"],
|
|
37
|
+
context: yield* encodeContext(definition, credential.context),
|
|
38
|
+
revocation: "active",
|
|
39
|
+
createdBy: principal.actorId,
|
|
40
|
+
createdAt: now
|
|
41
|
+
});
|
|
42
|
+
yield* storage.authorizations.upsert({
|
|
43
|
+
authorization,
|
|
44
|
+
credential: new Credential({
|
|
45
|
+
ciphertext: yield* custody.seal(credential.secret),
|
|
46
|
+
expiresAt: null,
|
|
47
|
+
rotatedAt: now
|
|
48
|
+
})
|
|
49
|
+
});
|
|
50
|
+
const connection = yield* storage.connections.create(authorization.id);
|
|
51
|
+
const attached = yield* attach({
|
|
52
|
+
connectionId: connection.id,
|
|
53
|
+
domain: zone
|
|
54
|
+
});
|
|
55
|
+
if (!(attached instanceof Attachment)) return yield* Effect.fail(failure("create-readback-cleanup", `zone ${zone} needs a selection; pass an exact zone`));
|
|
56
|
+
return attached;
|
|
57
|
+
});
|
|
58
|
+
const cname = (zone, label) => cname$1({
|
|
59
|
+
name: `${label}.${zone}`,
|
|
60
|
+
target: "target.example.net",
|
|
61
|
+
ttl: 300
|
|
62
|
+
});
|
|
63
|
+
const txt = (zone, label, value) => txt$1({
|
|
64
|
+
name: `${label}.${zone}`,
|
|
65
|
+
value,
|
|
66
|
+
ttl: 300
|
|
67
|
+
});
|
|
68
|
+
const recordIds = (receipt) => applied(receipt).map(({ providerRecordId }) => providerRecordId);
|
|
69
|
+
/**
|
|
70
|
+
* Runs create/readback/cleanup, exact-noop, conflict, stale-plan, and partial-apply against a
|
|
71
|
+
* real provider definition, through the same Provision and Cleanup services hosts use.
|
|
72
|
+
*/
|
|
73
|
+
const provider = (definition, credential, zone, options = {}) => {
|
|
74
|
+
const prefix = options.prefix ?? "domainkit-conformance";
|
|
75
|
+
const run = (target, body) => Effect.gen(function* () {
|
|
76
|
+
const attachment = yield* attachZone(target, credential, zone);
|
|
77
|
+
const { session: session$1, target: resolved } = yield* session(attachment.id);
|
|
78
|
+
return yield* body(session$1.dns(resolved), attachment);
|
|
79
|
+
}).pipe(Effect.provideService(Service$1, principal), Effect.provide(layerMemory({
|
|
80
|
+
providers: [target],
|
|
81
|
+
resolver: Layer.succeed(Service$2)(silentResolver)
|
|
82
|
+
})));
|
|
83
|
+
const cleanup = (dns, ids) => Effect.suspend(() => Effect.forEach(ids(), (id) => dns.delete(zone, id).pipe(Effect.ignore), { discard: true }));
|
|
84
|
+
const createReadbackCleanup = run(definition, (dns, attachment) => {
|
|
85
|
+
let created = [];
|
|
86
|
+
const name = "create-readback-cleanup";
|
|
87
|
+
return Effect.gen(function* () {
|
|
88
|
+
const requirements = [cname(zone, `${prefix}-create`), txt(zone, `${prefix}-page`, "domainkit")];
|
|
89
|
+
const plan$2 = yield* plan({
|
|
90
|
+
domain: attachment.domain,
|
|
91
|
+
requirements
|
|
92
|
+
});
|
|
93
|
+
yield* expect(name, plan$2.operations.every(({ _tag }) => _tag === "Create"), "expected two Create operations");
|
|
94
|
+
const receipt = yield* apply(yield* approve(plan$2));
|
|
95
|
+
created = recordIds(receipt);
|
|
96
|
+
yield* expect(name, receipt.status === "complete" && created.length === 2, "apply must return a complete receipt with provider record ids");
|
|
97
|
+
const observed = yield* dns.list(zone);
|
|
98
|
+
yield* expect(name, requirements.every((requirement) => observed.some(({ record }) => equals(record, requirement))), "list must return every created record, including across provider pages");
|
|
99
|
+
const cleanupPlan = yield* plan$1({ receiptId: receipt.id });
|
|
100
|
+
yield* expect(name, cleanupPlan.operations.every(({ _tag }) => _tag === "Delete"), "cleanup plan must delete both records");
|
|
101
|
+
const cleaned = yield* apply$1(yield* approve$1(cleanupPlan));
|
|
102
|
+
const remaining = yield* Effect.forEach(created, (id) => dns.get(zone, id));
|
|
103
|
+
yield* expect(name, cleaned.status === "complete" && remaining.every((record) => record === null), "receipt-bound cleanup must remove every created record");
|
|
104
|
+
created = [];
|
|
105
|
+
}).pipe(Effect.ensuring(cleanup(dns, () => created)));
|
|
106
|
+
});
|
|
107
|
+
const exactNoop = run(definition, (dns, attachment) => {
|
|
108
|
+
let created = [];
|
|
109
|
+
const name = "exact-noop";
|
|
110
|
+
return Effect.gen(function* () {
|
|
111
|
+
const requirement = cname(zone, `${prefix}-exact`);
|
|
112
|
+
const { providerRecordId } = yield* dns.create(zone, requirement);
|
|
113
|
+
created = [providerRecordId];
|
|
114
|
+
const plan$3 = yield* plan({
|
|
115
|
+
domain: attachment.domain,
|
|
116
|
+
requirements: [requirement]
|
|
117
|
+
});
|
|
118
|
+
yield* expect(name, plan$3.operations[0]?._tag === "Noop", "an exact record must plan as Noop");
|
|
119
|
+
const receipt = yield* apply(yield* approve(plan$3));
|
|
120
|
+
yield* expect(name, applied(receipt).length === 0, "an exact plan must not create another record");
|
|
121
|
+
}).pipe(Effect.ensuring(cleanup(dns, () => created)));
|
|
122
|
+
});
|
|
123
|
+
const conflict = run(definition, (dns, attachment) => {
|
|
124
|
+
let created = [];
|
|
125
|
+
const name = "conflict";
|
|
126
|
+
return Effect.gen(function* () {
|
|
127
|
+
const { providerRecordId } = yield* dns.create(zone, txt(zone, `${prefix}-conflict`, "occupied"));
|
|
128
|
+
created = [providerRecordId];
|
|
129
|
+
const plan$6 = yield* plan({
|
|
130
|
+
domain: attachment.domain,
|
|
131
|
+
requirements: [cname(zone, `${prefix}-conflict`)]
|
|
132
|
+
});
|
|
133
|
+
yield* expect(name, plan$6.operations[0]?._tag === "Conflict", "a CNAME over an occupied name must plan as Conflict");
|
|
134
|
+
const rejected = yield* approve(plan$6).pipe(Effect.result);
|
|
135
|
+
yield* expect(name, rejected._tag === "Failure" && rejected.failure.reason._tag === "Conflict", "approve must fail closed on conflicts");
|
|
136
|
+
}).pipe(Effect.ensuring(cleanup(dns, () => created)));
|
|
137
|
+
});
|
|
138
|
+
const stalePlan = run(definition, (dns, attachment) => {
|
|
139
|
+
let created = [];
|
|
140
|
+
const name = "stale-plan";
|
|
141
|
+
return Effect.gen(function* () {
|
|
142
|
+
const requirement = cname(zone, `${prefix}-stale`);
|
|
143
|
+
const plan$4 = yield* plan({
|
|
144
|
+
domain: attachment.domain,
|
|
145
|
+
requirements: [requirement]
|
|
146
|
+
});
|
|
147
|
+
const approval = yield* approve(plan$4);
|
|
148
|
+
const { providerRecordId } = yield* dns.create(zone, requirement);
|
|
149
|
+
created = [providerRecordId];
|
|
150
|
+
const stale = yield* apply(approval).pipe(Effect.result);
|
|
151
|
+
yield* expect(name, stale._tag === "Failure" && stale.failure.reason._tag === "Stale", "apply must reject provider state that changed after approval");
|
|
152
|
+
}).pipe(Effect.ensuring(cleanup(dns, () => created)));
|
|
153
|
+
});
|
|
154
|
+
const partialApplyCleanup = run({
|
|
155
|
+
...definition,
|
|
156
|
+
session: (input) => {
|
|
157
|
+
const session = definition.session(input);
|
|
158
|
+
return {
|
|
159
|
+
...session,
|
|
160
|
+
dns: (target) => {
|
|
161
|
+
const dns = session.dns(target);
|
|
162
|
+
let writes = 0;
|
|
163
|
+
return {
|
|
164
|
+
...dns,
|
|
165
|
+
create: (targetZone, record) => {
|
|
166
|
+
writes += 1;
|
|
167
|
+
return writes === 2 ? fail(new ProviderUnavailable({
|
|
168
|
+
provider: definition.id,
|
|
169
|
+
message: "injected conformance failure"
|
|
170
|
+
})) : dns.create(targetZone, record);
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
}, (dns, attachment) => {
|
|
177
|
+
let created = [];
|
|
178
|
+
const name = "partial-apply-cleanup";
|
|
179
|
+
return Effect.gen(function* () {
|
|
180
|
+
const plan$5 = yield* plan({
|
|
181
|
+
domain: attachment.domain,
|
|
182
|
+
requirements: [cname(zone, `${prefix}-partial-one`), txt(zone, `${prefix}-partial-two`, "domainkit")]
|
|
183
|
+
});
|
|
184
|
+
const receipt = yield* apply(yield* approve(plan$5));
|
|
185
|
+
created = recordIds(receipt);
|
|
186
|
+
yield* expect(name, receipt.status === "partial" && created.length === 1, "a later write failure must preserve the first confirmed write in a partial receipt");
|
|
187
|
+
const cleanupPlan = yield* plan$1({ receiptId: receipt.id });
|
|
188
|
+
yield* apply$1(yield* approve$1(cleanupPlan));
|
|
189
|
+
const remaining = yield* Effect.forEach(created, (id) => dns.get(zone, id));
|
|
190
|
+
yield* expect(name, remaining.every((record) => record === null), "partial receipt cleanup must remove the confirmed write");
|
|
191
|
+
created = [];
|
|
192
|
+
}).pipe(Effect.ensuring(cleanup(dns, () => created)));
|
|
193
|
+
});
|
|
194
|
+
return Effect.all([
|
|
195
|
+
createReadbackCleanup,
|
|
196
|
+
exactNoop,
|
|
197
|
+
conflict,
|
|
198
|
+
stalePlan,
|
|
199
|
+
partialApplyCleanup
|
|
200
|
+
], { discard: true });
|
|
201
|
+
};
|
|
202
|
+
/** Verification is out of scope for the provider contract; keep the pool quiet. */
|
|
203
|
+
const silentResolver = { resolve: () => Effect.succeed([]) };
|
|
204
|
+
//#endregion
|
|
205
|
+
export { provider };
|
|
206
|
+
|
|
207
|
+
//# sourceMappingURL=provider.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.mjs","names":["Principal.make","Errors.DomainKitError","Reason.InvalidInput","Storage.Service","Custody.Service","Storage.Authorization","Provider.encodeContext","Storage.Credential","Connect.attach","Storage.Attachment","DnsRecord.cname","DnsRecord.txt","Receipt.applied","Connect.session","session","Principal.Service","DomainKit.layerMemory","Resolver.Service","plan","Provision.plan","Provision.apply","Provision.approve","DnsRecord.equals","Cleanup.plan","Cleanup.apply","Cleanup.approve","Errors.fail","Reason.ProviderUnavailable"],"sources":["../../../src/internal/conformance/provider.ts"],"sourcesContent":["import { DateTime, Effect, Layer } from \"effect\";\n\nimport * as Cleanup from \"../../Cleanup.ts\";\nimport * as Connect from \"../../Connect.ts\";\nimport * as Custody from \"../../Custody.ts\";\nimport * as DnsRecord from \"../../DnsRecord.ts\";\nimport * as Errors from \"../error.ts\";\nimport * as Reason from \"../../Reason.ts\";\nimport * as DomainKit from \"../../DomainKit.ts\";\nimport * as Principal from \"../../Principal.ts\";\nimport * as Provider from \"../../Provider.ts\";\nimport * as Provision from \"../../Provision.ts\";\nimport * as Receipt from \"../../Receipt.ts\";\nimport * as Resolver from \"../../Resolver.ts\";\nimport * as Storage from \"../../Storage.ts\";\nimport { fresh } from \"../ids.ts\";\n\nexport const cases = [\n \"create-readback-cleanup\",\n \"exact-noop\",\n \"conflict\",\n \"stale-plan\",\n \"partial-apply-cleanup\",\n] as const;\nexport type Case = (typeof cases)[number];\n\nconst principal = Principal.make({ ownerId: \"conformance\", actorId: \"conformance\" });\n\nconst failure = (name: Case, message: string) =>\n new Errors.DomainKitError({\n reason: new Reason.InvalidInput({\n message: `provider conformance ${name}: ${message}`,\n field: \"conformance\",\n }),\n });\n\nconst expect = (name: Case, condition: boolean, message: string) =>\n condition ? Effect.void : Effect.fail(failure(name, message));\n\ntype Env = Storage.Service | Connect.Service | Provision.Service | Cleanup.Service;\n\n/** Attach `zone` to a connection built straight from `credential`, bypassing `authenticate`. */\nconst attachZone = (\n definition: Provider.Definition,\n credential: Provider.Credential,\n zone: string,\n) =>\n Effect.gen(function* () {\n const storage = yield* Storage.Service;\n const custody = yield* Custody.Service;\n const now = yield* DateTime.now;\n const authorization = new Storage.Authorization({\n id: yield* fresh(\"auth\"),\n ownerId: principal.ownerId,\n provider: definition.id,\n method: \"token\",\n capabilities: [\"dns:read\", \"dns:write\"],\n context: yield* Provider.encodeContext(definition, credential.context),\n revocation: \"active\",\n createdBy: principal.actorId,\n createdAt: now,\n });\n yield* storage.authorizations.upsert({\n authorization,\n credential: new Storage.Credential({\n ciphertext: yield* custody.seal(credential.secret),\n expiresAt: null,\n rotatedAt: now,\n }),\n });\n const connection = yield* storage.connections.create(authorization.id);\n const attached = yield* Connect.attach({ connectionId: connection.id, domain: zone });\n if (!(attached instanceof Storage.Attachment)) {\n return yield* Effect.fail(\n failure(\"create-readback-cleanup\", `zone ${zone} needs a selection; pass an exact zone`),\n );\n }\n return attached;\n });\n\nconst cname = (zone: string, label: string) =>\n DnsRecord.cname({ name: `${label}.${zone}`, target: \"target.example.net\", ttl: 300 });\nconst txt = (zone: string, label: string, value: string) =>\n DnsRecord.txt({ name: `${label}.${zone}`, value, ttl: 300 });\n\nconst recordIds = (receipt: Receipt.Model) =>\n Receipt.applied(receipt).map(({ providerRecordId }) => providerRecordId);\n\n/**\n * Runs create/readback/cleanup, exact-noop, conflict, stale-plan, and partial-apply against a\n * real provider definition, through the same Provision and Cleanup services hosts use.\n */\nexport const provider = (\n definition: Provider.Definition,\n credential: Provider.Credential,\n zone: string,\n options: { readonly prefix?: string } = {},\n): Effect.Effect<void, Errors.DomainKitError> => {\n const prefix = options.prefix ?? \"domainkit-conformance\";\n const run = <A>(\n target: Provider.Definition,\n body: (\n dns: Provider.Dns,\n attachment: Storage.Attachment,\n ) => Effect.Effect<A, Errors.DomainKitError, Env | Principal.Service>,\n ) =>\n Effect.gen(function* () {\n const attachment = yield* attachZone(target, credential, zone);\n const { session, target: resolved } = yield* Connect.session(attachment.id);\n return yield* body(session.dns(resolved), attachment);\n }).pipe(\n Effect.provideService(Principal.Service, principal),\n Effect.provide(\n DomainKit.layerMemory({\n providers: [target],\n resolver: Layer.succeed(Resolver.Service)(silentResolver),\n }),\n ),\n );\n\n const cleanup = (dns: Provider.Dns, ids: () => ReadonlyArray<string>) =>\n Effect.suspend(() =>\n Effect.forEach(ids(), (id) => dns.delete(zone, id).pipe(Effect.ignore), { discard: true }),\n );\n\n const createReadbackCleanup = run(definition, (dns, attachment) => {\n let created: ReadonlyArray<string> = [];\n const name: Case = \"create-readback-cleanup\";\n return Effect.gen(function* () {\n const requirements = [\n cname(zone, `${prefix}-create`),\n txt(zone, `${prefix}-page`, \"domainkit\"),\n ];\n const plan = yield* Provision.plan({ domain: attachment.domain, requirements });\n yield* expect(\n name,\n plan.operations.every(({ _tag }) => _tag === \"Create\"),\n \"expected two Create operations\",\n );\n const receipt = yield* Provision.apply(yield* Provision.approve(plan));\n created = recordIds(receipt);\n yield* expect(\n name,\n receipt.status === \"complete\" && created.length === 2,\n \"apply must return a complete receipt with provider record ids\",\n );\n const observed = yield* dns.list(zone);\n yield* expect(\n name,\n requirements.every((requirement) =>\n observed.some(({ record }) => DnsRecord.equals(record, requirement)),\n ),\n \"list must return every created record, including across provider pages\",\n );\n const cleanupPlan = yield* Cleanup.plan({ receiptId: receipt.id });\n yield* expect(\n name,\n cleanupPlan.operations.every(({ _tag }) => _tag === \"Delete\"),\n \"cleanup plan must delete both records\",\n );\n const cleaned = yield* Cleanup.apply(yield* Cleanup.approve(cleanupPlan));\n const remaining = yield* Effect.forEach(created, (id) => dns.get(zone, id));\n yield* expect(\n name,\n cleaned.status === \"complete\" && remaining.every((record) => record === null),\n \"receipt-bound cleanup must remove every created record\",\n );\n created = [];\n }).pipe(Effect.ensuring(cleanup(dns, () => created)));\n });\n\n const exactNoop = run(definition, (dns, attachment) => {\n let created: ReadonlyArray<string> = [];\n const name: Case = \"exact-noop\";\n return Effect.gen(function* () {\n const requirement = cname(zone, `${prefix}-exact`);\n const { providerRecordId } = yield* dns.create(zone, requirement);\n created = [providerRecordId];\n const plan = yield* Provision.plan({\n domain: attachment.domain,\n requirements: [requirement],\n });\n yield* expect(name, plan.operations[0]?._tag === \"Noop\", \"an exact record must plan as Noop\");\n const receipt = yield* Provision.apply(yield* Provision.approve(plan));\n yield* expect(\n name,\n Receipt.applied(receipt).length === 0,\n \"an exact plan must not create another record\",\n );\n }).pipe(Effect.ensuring(cleanup(dns, () => created)));\n });\n\n const conflict = run(definition, (dns, attachment) => {\n let created: ReadonlyArray<string> = [];\n const name: Case = \"conflict\";\n return Effect.gen(function* () {\n const { providerRecordId } = yield* dns.create(\n zone,\n txt(zone, `${prefix}-conflict`, \"occupied\"),\n );\n created = [providerRecordId];\n const plan = yield* Provision.plan({\n domain: attachment.domain,\n requirements: [cname(zone, `${prefix}-conflict`)],\n });\n yield* expect(\n name,\n plan.operations[0]?._tag === \"Conflict\",\n \"a CNAME over an occupied name must plan as Conflict\",\n );\n const rejected = yield* Provision.approve(plan).pipe(Effect.result);\n yield* expect(\n name,\n rejected._tag === \"Failure\" && rejected.failure.reason._tag === \"Conflict\",\n \"approve must fail closed on conflicts\",\n );\n }).pipe(Effect.ensuring(cleanup(dns, () => created)));\n });\n\n const stalePlan = run(definition, (dns, attachment) => {\n let created: ReadonlyArray<string> = [];\n const name: Case = \"stale-plan\";\n return Effect.gen(function* () {\n const requirement = cname(zone, `${prefix}-stale`);\n const plan = yield* Provision.plan({\n domain: attachment.domain,\n requirements: [requirement],\n });\n const approval = yield* Provision.approve(plan);\n const { providerRecordId } = yield* dns.create(zone, requirement);\n created = [providerRecordId];\n const stale = yield* Provision.apply(approval).pipe(Effect.result);\n yield* expect(\n name,\n stale._tag === \"Failure\" && stale.failure.reason._tag === \"Stale\",\n \"apply must reject provider state that changed after approval\",\n );\n }).pipe(Effect.ensuring(cleanup(dns, () => created)));\n });\n\n const flaky: Provider.Definition = {\n ...definition,\n session: (input) => {\n const session = definition.session(input);\n return {\n ...session,\n dns: (target) => {\n const dns = session.dns(target);\n let writes = 0;\n return {\n ...dns,\n create: (targetZone, record) => {\n writes += 1;\n return writes === 2\n ? Errors.fail(\n new Reason.ProviderUnavailable({\n provider: definition.id,\n message: \"injected conformance failure\",\n }),\n )\n : dns.create(targetZone, record);\n },\n };\n },\n };\n },\n };\n\n const partialApplyCleanup = run(flaky, (dns, attachment) => {\n let created: ReadonlyArray<string> = [];\n const name: Case = \"partial-apply-cleanup\";\n return Effect.gen(function* () {\n const plan = yield* Provision.plan({\n domain: attachment.domain,\n requirements: [\n cname(zone, `${prefix}-partial-one`),\n txt(zone, `${prefix}-partial-two`, \"domainkit\"),\n ],\n });\n const receipt = yield* Provision.apply(yield* Provision.approve(plan));\n created = recordIds(receipt);\n yield* expect(\n name,\n receipt.status === \"partial\" && created.length === 1,\n \"a later write failure must preserve the first confirmed write in a partial receipt\",\n );\n const cleanupPlan = yield* Cleanup.plan({ receiptId: receipt.id });\n yield* Cleanup.apply(yield* Cleanup.approve(cleanupPlan));\n const remaining = yield* Effect.forEach(created, (id) => dns.get(zone, id));\n yield* expect(\n name,\n remaining.every((record) => record === null),\n \"partial receipt cleanup must remove the confirmed write\",\n );\n created = [];\n }).pipe(Effect.ensuring(cleanup(dns, () => created)));\n });\n\n return Effect.all([createReadbackCleanup, exactNoop, conflict, stalePlan, partialApplyCleanup], {\n discard: true,\n });\n};\n\n/** Verification is out of scope for the provider contract; keep the pool quiet. */\nconst silentResolver: Resolver.Interface = {\n resolve: () => Effect.succeed([]),\n};\n"],"mappings":"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"}
|