@gusnips/one-click-dns 0.1.1 → 0.1.3
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 +18 -0
- package/dist/{chunk-56ZUD4EQ.js → chunk-4XNRV7QR.js} +18 -15
- package/dist/index.cjs +7 -4
- package/dist/index.d.cts +15 -3
- package/dist/index.d.ts +15 -3
- package/dist/index.js +7 -4
- package/dist/server/index.cjs +41 -17
- package/dist/server/index.d.cts +13 -1
- package/dist/server/index.d.ts +13 -1
- package/dist/server/index.js +22 -2
- package/dist/writers/index.cjs +18 -15
- package/dist/writers/index.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -102,6 +102,22 @@ When the host has your template, `plan.methods` starts with a `domain-connect` m
|
|
|
102
102
|
|
|
103
103
|
Only these providers are asked, because their `_domainconnect` value and settings were read from DNS: Cloudflare, Vercel, Simply.com and GoDaddy. A name in a customer's DNS is never fetched. More providers join the same way, one value at a time.
|
|
104
104
|
|
|
105
|
+
### Which providers have switched your template on
|
|
106
|
+
|
|
107
|
+
A merged template is not a template a provider uses. Each provider turns templates on by hand, and the only way to know is to ask it. `templateCoverage` asks every provider in the list once:
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
import { templateCoverage } from "@gusnips/one-click-dns/server";
|
|
111
|
+
|
|
112
|
+
const coverage = await templateCoverage(
|
|
113
|
+
{},
|
|
114
|
+
{ providerId: "example.com", serviceId: "custom-domain" },
|
|
115
|
+
);
|
|
116
|
+
// { cloudflare: "not_supported", godaddy: "supported", vercel: "not_supported", simply: "not_supported" }
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
`"unanswered"` means the provider was busy or did not reply. It never means no. Run it now and then to see when a provider starts to support your template.
|
|
120
|
+
|
|
105
121
|
## Templates
|
|
106
122
|
|
|
107
123
|
A template is a small JSON file in the public [Domain Connect Templates](https://github.com/Domain-Connect/Templates) repository. It lists the records you may ask a host to add. The presets build one that follows the repository's rules:
|
|
@@ -125,6 +141,8 @@ console.log(templateFileName(template)); // "example.com.custom-domain.json"
|
|
|
125
141
|
|
|
126
142
|
There are presets for a CNAME, DMARC, SPF, DKIM, MX and a verification TXT.
|
|
127
143
|
|
|
144
|
+
Two of them change how a domain's email works. The MX preset replaces the MX records the domain has, so its mail goes somewhere else. The SPF preset adds a sender to the domain's SPF record. Ask the person for consent in your own screen before you offer either link, and say what changes in the template's `description`, which the DNS host shows when they approve.
|
|
145
|
+
|
|
128
146
|
A template belongs to the company that files it. It names your key domain, and the host checks every link against the key published there. So you cannot use someone else's template, and nobody can use yours. The customer's domain is not in the template: it comes with each link, so one template serves every customer.
|
|
129
147
|
|
|
130
148
|
To file a template: test it in the [online editor](https://domainconnect.paulonet.eu/dc/free/templateedit), run `dc-template-linter -merge-or-fail` on it, open a pull request, then ask each DNS host to switch it on.
|
|
@@ -5,22 +5,25 @@ import {
|
|
|
5
5
|
// src/bounded-fetch.ts
|
|
6
6
|
import { readBounded } from "@gusnips/server";
|
|
7
7
|
async function fetchBounded(doFetch, url, init, limits) {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
8
|
+
const signal = AbortSignal.timeout(limits.timeoutMs);
|
|
9
|
+
const outOfTime = new Promise((resolve) => {
|
|
10
|
+
signal.addEventListener("abort", () => resolve(null), { once: true });
|
|
11
|
+
});
|
|
12
|
+
const attempt = async () => {
|
|
13
|
+
try {
|
|
14
|
+
const res = await doFetch(url, { ...init, redirect: "manual", signal });
|
|
15
|
+
if (res.type === "opaqueredirect" || res.status >= 300 && res.status < 400) {
|
|
16
|
+
await res.body?.cancel().catch(() => void 0);
|
|
17
|
+
return { status: res.status || 302, text: "" };
|
|
18
|
+
}
|
|
19
|
+
const { bytes, truncated } = await readBounded(res.body, limits.maxBytes);
|
|
20
|
+
if (truncated) return null;
|
|
21
|
+
return { status: res.status, text: new TextDecoder().decode(bytes) };
|
|
22
|
+
} catch {
|
|
23
|
+
return null;
|
|
17
24
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
return { status: res.status, text: new TextDecoder().decode(bytes) };
|
|
21
|
-
} catch {
|
|
22
|
-
return null;
|
|
23
|
-
}
|
|
25
|
+
};
|
|
26
|
+
return Promise.race([attempt(), outOfTime]);
|
|
24
27
|
}
|
|
25
28
|
|
|
26
29
|
// src/guards.ts
|
package/dist/index.cjs
CHANGED
|
@@ -663,21 +663,24 @@ function spfTemplate(identity, options = {}) {
|
|
|
663
663
|
);
|
|
664
664
|
}
|
|
665
665
|
function dkimTemplate(identity, options = {}) {
|
|
666
|
-
const
|
|
666
|
+
const { selectors } = options;
|
|
667
|
+
const keys = selectors?.length ?? options.keys ?? 2;
|
|
667
668
|
const numbers = Array.from({ length: keys }, (_, index) => index + 1);
|
|
669
|
+
const hostOf = (n) => selectors ? `${selectors[n - 1]}._domainkey` : `%selector${n}%._domainkey`;
|
|
670
|
+
const describe = (n) => selectors ? `%target${n}%: where ${selectors[n - 1]}._domainkey points` : `%selector${n}%: the key name, ${n} of ${keys}; %target${n}%: where it points`;
|
|
668
671
|
return template(
|
|
669
672
|
identity,
|
|
670
673
|
"dkim",
|
|
671
674
|
{
|
|
672
675
|
serviceName: "Sign your email with our key",
|
|
673
676
|
description: "Adds the CNAME records that let us sign email sent from your domain.",
|
|
674
|
-
variableDescription: numbers.map(
|
|
677
|
+
variableDescription: options.variableDescription ?? numbers.map(describe).join("; ")
|
|
675
678
|
},
|
|
676
679
|
options,
|
|
677
680
|
numbers.map((n) => ({
|
|
678
681
|
type: "CNAME",
|
|
679
|
-
host:
|
|
680
|
-
pointsTo: `%target${n}%`,
|
|
682
|
+
host: hostOf(n),
|
|
683
|
+
pointsTo: options.pointsTo?.(n) ?? `%target${n}%`,
|
|
681
684
|
ttl: TTL
|
|
682
685
|
}))
|
|
683
686
|
);
|
package/dist/index.d.cts
CHANGED
|
@@ -99,12 +99,24 @@ declare function dmarcTemplate(identity: TemplateIdentity, options?: Wording & {
|
|
|
99
99
|
/** Adds one `include:` to the domain's SPF record. `SPFM` merges it into the one record the
|
|
100
100
|
* name may have; a TXT would make a second one and break SPF. The link carries `%include%`. */
|
|
101
101
|
declare function spfTemplate(identity: TemplateIdentity, options?: Wording): DomainConnectTemplate;
|
|
102
|
-
/**
|
|
103
|
-
*
|
|
102
|
+
/**
|
|
103
|
+
* DKIM as CNAMEs, for services that publish the key on their side.
|
|
104
|
+
*
|
|
105
|
+
* By default the link carries `%selector1%` and `%target1%`, then the same pair numbered up to
|
|
106
|
+
* `keys`. When your key names never change, fix them: `selectors: ["cd1", "cd2"]` writes
|
|
107
|
+
* `cd1._domainkey` and `cd2._domainkey` into the template, which the repository prefers to a
|
|
108
|
+
* variable. `pointsTo` then names where each CNAME goes (`(n) => "%dkim" + n + "%.example.net"`),
|
|
109
|
+
* and `variableDescription` says what its variables mean.
|
|
110
|
+
*/
|
|
104
111
|
declare function dkimTemplate(identity: TemplateIdentity, options?: Wording & {
|
|
105
112
|
keys?: number;
|
|
113
|
+
selectors?: readonly string[];
|
|
114
|
+
pointsTo?: (n: number) => string;
|
|
115
|
+
variableDescription?: string;
|
|
106
116
|
}): DomainConnectTemplate;
|
|
107
|
-
/** The MX record that sends the domain's mail to you.
|
|
117
|
+
/** The MX record that sends the domain's mail to you. It changes where the domain's email is
|
|
118
|
+
* delivered and replaces the MX records it has, so ask the person for consent before you offer
|
|
119
|
+
* the link; say so in `description` too. */
|
|
108
120
|
declare function mxTemplate(identity: TemplateIdentity, options: Wording & {
|
|
109
121
|
pointsTo: string;
|
|
110
122
|
priority?: number;
|
package/dist/index.d.ts
CHANGED
|
@@ -99,12 +99,24 @@ declare function dmarcTemplate(identity: TemplateIdentity, options?: Wording & {
|
|
|
99
99
|
/** Adds one `include:` to the domain's SPF record. `SPFM` merges it into the one record the
|
|
100
100
|
* name may have; a TXT would make a second one and break SPF. The link carries `%include%`. */
|
|
101
101
|
declare function spfTemplate(identity: TemplateIdentity, options?: Wording): DomainConnectTemplate;
|
|
102
|
-
/**
|
|
103
|
-
*
|
|
102
|
+
/**
|
|
103
|
+
* DKIM as CNAMEs, for services that publish the key on their side.
|
|
104
|
+
*
|
|
105
|
+
* By default the link carries `%selector1%` and `%target1%`, then the same pair numbered up to
|
|
106
|
+
* `keys`. When your key names never change, fix them: `selectors: ["cd1", "cd2"]` writes
|
|
107
|
+
* `cd1._domainkey` and `cd2._domainkey` into the template, which the repository prefers to a
|
|
108
|
+
* variable. `pointsTo` then names where each CNAME goes (`(n) => "%dkim" + n + "%.example.net"`),
|
|
109
|
+
* and `variableDescription` says what its variables mean.
|
|
110
|
+
*/
|
|
104
111
|
declare function dkimTemplate(identity: TemplateIdentity, options?: Wording & {
|
|
105
112
|
keys?: number;
|
|
113
|
+
selectors?: readonly string[];
|
|
114
|
+
pointsTo?: (n: number) => string;
|
|
115
|
+
variableDescription?: string;
|
|
106
116
|
}): DomainConnectTemplate;
|
|
107
|
-
/** The MX record that sends the domain's mail to you.
|
|
117
|
+
/** The MX record that sends the domain's mail to you. It changes where the domain's email is
|
|
118
|
+
* delivered and replaces the MX records it has, so ask the person for consent before you offer
|
|
119
|
+
* the link; say so in `description` too. */
|
|
108
120
|
declare function mxTemplate(identity: TemplateIdentity, options: Wording & {
|
|
109
121
|
pointsTo: string;
|
|
110
122
|
priority?: number;
|
package/dist/index.js
CHANGED
|
@@ -117,21 +117,24 @@ function spfTemplate(identity, options = {}) {
|
|
|
117
117
|
);
|
|
118
118
|
}
|
|
119
119
|
function dkimTemplate(identity, options = {}) {
|
|
120
|
-
const
|
|
120
|
+
const { selectors } = options;
|
|
121
|
+
const keys = selectors?.length ?? options.keys ?? 2;
|
|
121
122
|
const numbers = Array.from({ length: keys }, (_, index) => index + 1);
|
|
123
|
+
const hostOf = (n) => selectors ? `${selectors[n - 1]}._domainkey` : `%selector${n}%._domainkey`;
|
|
124
|
+
const describe = (n) => selectors ? `%target${n}%: where ${selectors[n - 1]}._domainkey points` : `%selector${n}%: the key name, ${n} of ${keys}; %target${n}%: where it points`;
|
|
122
125
|
return template(
|
|
123
126
|
identity,
|
|
124
127
|
"dkim",
|
|
125
128
|
{
|
|
126
129
|
serviceName: "Sign your email with our key",
|
|
127
130
|
description: "Adds the CNAME records that let us sign email sent from your domain.",
|
|
128
|
-
variableDescription: numbers.map(
|
|
131
|
+
variableDescription: options.variableDescription ?? numbers.map(describe).join("; ")
|
|
129
132
|
},
|
|
130
133
|
options,
|
|
131
134
|
numbers.map((n) => ({
|
|
132
135
|
type: "CNAME",
|
|
133
|
-
host:
|
|
134
|
-
pointsTo: `%target${n}%`,
|
|
136
|
+
host: hostOf(n),
|
|
137
|
+
pointsTo: options.pointsTo?.(n) ?? `%target${n}%`,
|
|
135
138
|
ttl: TTL
|
|
136
139
|
}))
|
|
137
140
|
);
|
package/dist/server/index.cjs
CHANGED
|
@@ -27,7 +27,8 @@ __export(server_exports, {
|
|
|
27
27
|
readDnsZone: () => readDnsZone,
|
|
28
28
|
signedApplyUrl: () => signedApplyUrl,
|
|
29
29
|
systemDnsLookups: () => systemDnsLookups,
|
|
30
|
-
systemResolver: () => systemResolver
|
|
30
|
+
systemResolver: () => systemResolver,
|
|
31
|
+
templateCoverage: () => templateCoverage
|
|
31
32
|
});
|
|
32
33
|
module.exports = __toCommonJS(server_exports);
|
|
33
34
|
|
|
@@ -657,22 +658,25 @@ var CONNECT_PROVIDERS = [
|
|
|
657
658
|
// src/bounded-fetch.ts
|
|
658
659
|
var import_server = require("@gusnips/server");
|
|
659
660
|
async function fetchBounded(doFetch, url, init, limits) {
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
661
|
+
const signal = AbortSignal.timeout(limits.timeoutMs);
|
|
662
|
+
const outOfTime = new Promise((resolve) => {
|
|
663
|
+
signal.addEventListener("abort", () => resolve(null), { once: true });
|
|
664
|
+
});
|
|
665
|
+
const attempt = async () => {
|
|
666
|
+
try {
|
|
667
|
+
const res = await doFetch(url, { ...init, redirect: "manual", signal });
|
|
668
|
+
if (res.type === "opaqueredirect" || res.status >= 300 && res.status < 400) {
|
|
669
|
+
await res.body?.cancel().catch(() => void 0);
|
|
670
|
+
return { status: res.status || 302, text: "" };
|
|
671
|
+
}
|
|
672
|
+
const { bytes, truncated } = await (0, import_server.readBounded)(res.body, limits.maxBytes);
|
|
673
|
+
if (truncated) return null;
|
|
674
|
+
return { status: res.status, text: new TextDecoder().decode(bytes) };
|
|
675
|
+
} catch {
|
|
676
|
+
return null;
|
|
669
677
|
}
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
return { status: res.status, text: new TextDecoder().decode(bytes) };
|
|
673
|
-
} catch {
|
|
674
|
-
return null;
|
|
675
|
-
}
|
|
678
|
+
};
|
|
679
|
+
return Promise.race([attempt(), outOfTime]);
|
|
676
680
|
}
|
|
677
681
|
|
|
678
682
|
// src/guards.ts
|
|
@@ -771,6 +775,25 @@ async function ask(io, args, deadline) {
|
|
|
771
775
|
};
|
|
772
776
|
}
|
|
773
777
|
|
|
778
|
+
// src/server/connect/coverage.ts
|
|
779
|
+
async function templateCoverage(io, args) {
|
|
780
|
+
const { providerId, serviceId, timeoutMs = 5e3, providers = CONNECT_PROVIDERS } = args;
|
|
781
|
+
const answers = await Promise.all(
|
|
782
|
+
providers.map(async (provider) => {
|
|
783
|
+
const answer = await fetchBounded(
|
|
784
|
+
io.fetch ?? fetch,
|
|
785
|
+
`${provider.api}/v2/domainTemplates/providers/${providerId}/services/${serviceId}`,
|
|
786
|
+
{ method: "GET", headers: { Accept: "application/json" } },
|
|
787
|
+
{ timeoutMs, maxBytes: 64 * 1024 }
|
|
788
|
+
);
|
|
789
|
+
if (answer?.status === 200) return [provider.id, "supported"];
|
|
790
|
+
if (answer?.status === 404) return [provider.id, "not_supported"];
|
|
791
|
+
return [provider.id, "unanswered"];
|
|
792
|
+
})
|
|
793
|
+
);
|
|
794
|
+
return Object.fromEntries(answers);
|
|
795
|
+
}
|
|
796
|
+
|
|
774
797
|
// src/server/connect/sign.ts
|
|
775
798
|
var import_node_crypto = require("crypto");
|
|
776
799
|
var encode = (value) => encodeURIComponent(value).replace(/[!'()*]/g, (char) => `%${char.charCodeAt(0).toString(16).toUpperCase()}`).replace(/%20/g, "+");
|
|
@@ -1316,5 +1339,6 @@ async function planConnection(input, io) {
|
|
|
1316
1339
|
readDnsZone,
|
|
1317
1340
|
signedApplyUrl,
|
|
1318
1341
|
systemDnsLookups,
|
|
1319
|
-
systemResolver
|
|
1342
|
+
systemResolver,
|
|
1343
|
+
templateCoverage
|
|
1320
1344
|
});
|
package/dist/server/index.d.cts
CHANGED
|
@@ -84,6 +84,18 @@ declare function discoverConnect(io: DiscoveryIo, args: {
|
|
|
84
84
|
providers?: readonly ConnectProvider[];
|
|
85
85
|
}): Promise<Discovery>;
|
|
86
86
|
|
|
87
|
+
type TemplateSupport = "supported" | "not_supported" | "unanswered";
|
|
88
|
+
/** The answer of each provider, by our id for it (`cloudflare`). A status that says "not now"
|
|
89
|
+
* (429, 5xx), a failed request or a redirect is `unanswered`, never "no". */
|
|
90
|
+
declare function templateCoverage(io: {
|
|
91
|
+
fetch?: FetchFn;
|
|
92
|
+
}, args: {
|
|
93
|
+
providerId: string;
|
|
94
|
+
serviceId: string;
|
|
95
|
+
timeoutMs?: number;
|
|
96
|
+
providers?: readonly ConnectProvider[];
|
|
97
|
+
}): Promise<Record<string, TemplateSupport>>;
|
|
98
|
+
|
|
87
99
|
/** Who is asking, as the templates name it. */
|
|
88
100
|
interface DomainConnectIdentity {
|
|
89
101
|
/** The provider id our templates are filed under in the Templates repository
|
|
@@ -142,4 +154,4 @@ interface PlanInput {
|
|
|
142
154
|
}
|
|
143
155
|
declare function planConnection(input: PlanInput, io: DiscoveryIo & ZoneLookups): Promise<ConnectPlan>;
|
|
144
156
|
|
|
145
|
-
export { CONNECT_PROVIDERS, type ConnectMethod, type ConnectPlan, type ConnectProvider, type Discovery, type DiscoveryIo, type DnsAnswer, type DnsAsk, type DnsLookups, type DomainConnectIdentity, FetchFn, type PlanInput, type ZoneLookups, discoverConnect, dnsHostOf, planConnection, readDnsZone, signedApplyUrl, systemDnsLookups, systemResolver };
|
|
157
|
+
export { CONNECT_PROVIDERS, type ConnectMethod, type ConnectPlan, type ConnectProvider, type Discovery, type DiscoveryIo, type DnsAnswer, type DnsAsk, type DnsLookups, type DomainConnectIdentity, FetchFn, type PlanInput, type TemplateSupport, type ZoneLookups, discoverConnect, dnsHostOf, planConnection, readDnsZone, signedApplyUrl, systemDnsLookups, systemResolver, templateCoverage };
|
package/dist/server/index.d.ts
CHANGED
|
@@ -84,6 +84,18 @@ declare function discoverConnect(io: DiscoveryIo, args: {
|
|
|
84
84
|
providers?: readonly ConnectProvider[];
|
|
85
85
|
}): Promise<Discovery>;
|
|
86
86
|
|
|
87
|
+
type TemplateSupport = "supported" | "not_supported" | "unanswered";
|
|
88
|
+
/** The answer of each provider, by our id for it (`cloudflare`). A status that says "not now"
|
|
89
|
+
* (429, 5xx), a failed request or a redirect is `unanswered`, never "no". */
|
|
90
|
+
declare function templateCoverage(io: {
|
|
91
|
+
fetch?: FetchFn;
|
|
92
|
+
}, args: {
|
|
93
|
+
providerId: string;
|
|
94
|
+
serviceId: string;
|
|
95
|
+
timeoutMs?: number;
|
|
96
|
+
providers?: readonly ConnectProvider[];
|
|
97
|
+
}): Promise<Record<string, TemplateSupport>>;
|
|
98
|
+
|
|
87
99
|
/** Who is asking, as the templates name it. */
|
|
88
100
|
interface DomainConnectIdentity {
|
|
89
101
|
/** The provider id our templates are filed under in the Templates repository
|
|
@@ -142,4 +154,4 @@ interface PlanInput {
|
|
|
142
154
|
}
|
|
143
155
|
declare function planConnection(input: PlanInput, io: DiscoveryIo & ZoneLookups): Promise<ConnectPlan>;
|
|
144
156
|
|
|
145
|
-
export { CONNECT_PROVIDERS, type ConnectMethod, type ConnectPlan, type ConnectProvider, type Discovery, type DiscoveryIo, type DnsAnswer, type DnsAsk, type DnsLookups, type DomainConnectIdentity, FetchFn, type PlanInput, type ZoneLookups, discoverConnect, dnsHostOf, planConnection, readDnsZone, signedApplyUrl, systemDnsLookups, systemResolver };
|
|
157
|
+
export { CONNECT_PROVIDERS, type ConnectMethod, type ConnectPlan, type ConnectProvider, type Discovery, type DiscoveryIo, type DnsAnswer, type DnsAsk, type DnsLookups, type DomainConnectIdentity, FetchFn, type PlanInput, type TemplateSupport, type ZoneLookups, discoverConnect, dnsHostOf, planConnection, readDnsZone, signedApplyUrl, systemDnsLookups, systemResolver, templateCoverage };
|
package/dist/server/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
fetchBounded,
|
|
6
6
|
isObject,
|
|
7
7
|
writers
|
|
8
|
-
} from "../chunk-
|
|
8
|
+
} from "../chunk-4XNRV7QR.js";
|
|
9
9
|
import {
|
|
10
10
|
normalizeDnsName
|
|
11
11
|
} from "../chunk-P2O7INDB.js";
|
|
@@ -211,6 +211,25 @@ async function ask(io, args, deadline) {
|
|
|
211
211
|
};
|
|
212
212
|
}
|
|
213
213
|
|
|
214
|
+
// src/server/connect/coverage.ts
|
|
215
|
+
async function templateCoverage(io, args) {
|
|
216
|
+
const { providerId, serviceId, timeoutMs = 5e3, providers = CONNECT_PROVIDERS } = args;
|
|
217
|
+
const answers = await Promise.all(
|
|
218
|
+
providers.map(async (provider) => {
|
|
219
|
+
const answer = await fetchBounded(
|
|
220
|
+
io.fetch ?? fetch,
|
|
221
|
+
`${provider.api}/v2/domainTemplates/providers/${providerId}/services/${serviceId}`,
|
|
222
|
+
{ method: "GET", headers: { Accept: "application/json" } },
|
|
223
|
+
{ timeoutMs, maxBytes: 64 * 1024 }
|
|
224
|
+
);
|
|
225
|
+
if (answer?.status === 200) return [provider.id, "supported"];
|
|
226
|
+
if (answer?.status === 404) return [provider.id, "not_supported"];
|
|
227
|
+
return [provider.id, "unanswered"];
|
|
228
|
+
})
|
|
229
|
+
);
|
|
230
|
+
return Object.fromEntries(answers);
|
|
231
|
+
}
|
|
232
|
+
|
|
214
233
|
// src/server/connect/sign.ts
|
|
215
234
|
import { createPrivateKey, sign } from "crypto";
|
|
216
235
|
var encode = (value) => encodeURIComponent(value).replace(/[!'()*]/g, (char) => `%${char.charCodeAt(0).toString(16).toUpperCase()}`).replace(/%20/g, "+");
|
|
@@ -270,5 +289,6 @@ export {
|
|
|
270
289
|
readDnsZone,
|
|
271
290
|
signedApplyUrl,
|
|
272
291
|
systemDnsLookups,
|
|
273
|
-
systemResolver
|
|
292
|
+
systemResolver,
|
|
293
|
+
templateCoverage
|
|
274
294
|
};
|
package/dist/writers/index.cjs
CHANGED
|
@@ -32,22 +32,25 @@ module.exports = __toCommonJS(writers_exports);
|
|
|
32
32
|
// src/bounded-fetch.ts
|
|
33
33
|
var import_server = require("@gusnips/server");
|
|
34
34
|
async function fetchBounded(doFetch, url, init, limits) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
35
|
+
const signal = AbortSignal.timeout(limits.timeoutMs);
|
|
36
|
+
const outOfTime = new Promise((resolve) => {
|
|
37
|
+
signal.addEventListener("abort", () => resolve(null), { once: true });
|
|
38
|
+
});
|
|
39
|
+
const attempt = async () => {
|
|
40
|
+
try {
|
|
41
|
+
const res = await doFetch(url, { ...init, redirect: "manual", signal });
|
|
42
|
+
if (res.type === "opaqueredirect" || res.status >= 300 && res.status < 400) {
|
|
43
|
+
await res.body?.cancel().catch(() => void 0);
|
|
44
|
+
return { status: res.status || 302, text: "" };
|
|
45
|
+
}
|
|
46
|
+
const { bytes, truncated } = await (0, import_server.readBounded)(res.body, limits.maxBytes);
|
|
47
|
+
if (truncated) return null;
|
|
48
|
+
return { status: res.status, text: new TextDecoder().decode(bytes) };
|
|
49
|
+
} catch {
|
|
50
|
+
return null;
|
|
44
51
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
return { status: res.status, text: new TextDecoder().decode(bytes) };
|
|
48
|
-
} catch {
|
|
49
|
-
return null;
|
|
50
|
-
}
|
|
52
|
+
};
|
|
53
|
+
return Promise.race([attempt(), outOfTime]);
|
|
51
54
|
}
|
|
52
55
|
|
|
53
56
|
// src/dns-name.ts
|
package/dist/writers/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gusnips/one-click-dns",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"packageManager": "bun@1.4.2",
|
|
5
5
|
"description": "Put DNS records in a customer's zone: find their DNS host, send them to a signed Domain Connect link, write with an API token, or show the steps for that host",
|
|
6
6
|
"type": "module",
|