@gusnips/one-click-dns 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +142 -2
- package/dist/chunk-2T2PEF4H.js +521 -0
- package/dist/chunk-FGM7S7GQ.js +222 -0
- package/dist/chunk-P2O7INDB.js +8 -0
- package/dist/hosts-B0_V4Ned.d.cts +106 -0
- package/dist/hosts-B0_V4Ned.d.ts +106 -0
- package/dist/index.cjs +745 -0
- package/dist/index.d.cts +119 -0
- package/dist/index.d.ts +119 -0
- package/dist/index.js +198 -0
- package/dist/server/index.cjs +1020 -0
- package/dist/server/index.d.cts +144 -0
- package/dist/server/index.d.ts +144 -0
- package/dist/server/index.js +274 -0
- package/dist/writers/index.cjs +249 -0
- package/dist/writers/index.d.cts +57 -0
- package/dist/writers/index.d.ts +57 -0
- package/dist/writers/index.js +11 -0
- package/package.json +93 -4
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/writers/index.ts
|
|
21
|
+
var writers_exports = {};
|
|
22
|
+
__export(writers_exports, {
|
|
23
|
+
DnsWriteError: () => DnsWriteError,
|
|
24
|
+
cloudflare: () => cloudflare,
|
|
25
|
+
writers: () => writers
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(writers_exports);
|
|
28
|
+
|
|
29
|
+
// src/bounded-fetch.ts
|
|
30
|
+
var import_server = require("@gusnips/server");
|
|
31
|
+
async function fetchBounded(doFetch, url, init, limits) {
|
|
32
|
+
try {
|
|
33
|
+
const res = await doFetch(url, {
|
|
34
|
+
...init,
|
|
35
|
+
redirect: "manual",
|
|
36
|
+
signal: AbortSignal.timeout(limits.timeoutMs)
|
|
37
|
+
});
|
|
38
|
+
if (res.type === "opaqueredirect" || res.status >= 300 && res.status < 400) {
|
|
39
|
+
await res.body?.cancel().catch(() => void 0);
|
|
40
|
+
return { status: res.status || 302, text: "" };
|
|
41
|
+
}
|
|
42
|
+
const { bytes, truncated } = await (0, import_server.readBounded)(res.body, limits.maxBytes);
|
|
43
|
+
if (truncated) return null;
|
|
44
|
+
return { status: res.status, text: new TextDecoder().decode(bytes) };
|
|
45
|
+
} catch {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// src/dns-name.ts
|
|
51
|
+
function normalizeDnsName(name) {
|
|
52
|
+
return name.trim().toLowerCase().replace(/\.$/, "");
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// src/guards.ts
|
|
56
|
+
function isObject(value) {
|
|
57
|
+
return typeof value === "object" && value !== null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// src/writers/types.ts
|
|
61
|
+
var DnsWriteError = class extends Error {
|
|
62
|
+
constructor(code, params = {}) {
|
|
63
|
+
super(MESSAGES[code](params));
|
|
64
|
+
this.code = code;
|
|
65
|
+
this.params = params;
|
|
66
|
+
this.name = "DnsWriteError";
|
|
67
|
+
}
|
|
68
|
+
code;
|
|
69
|
+
params;
|
|
70
|
+
};
|
|
71
|
+
var MESSAGES = {
|
|
72
|
+
token_invalid: () => "The DNS host didn't accept that token",
|
|
73
|
+
zone_read_missing: () => "That token can't read your domains",
|
|
74
|
+
dns_edit_missing: () => "That token can't change DNS records",
|
|
75
|
+
zone_not_found: (p) => `No zone holds ${p.hostname ?? "that name"}`,
|
|
76
|
+
conflict: (p) => `${p.hostname ?? "That name"} already has a record (type ${p.type ?? "?"})`,
|
|
77
|
+
unavailable: () => "Couldn't reach the DNS host"
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
// src/writers/cloudflare.ts
|
|
81
|
+
var API = "https://api.cloudflare.com/client/v4";
|
|
82
|
+
var TIMEOUT_MS = 1e4;
|
|
83
|
+
var MAX_BODY_BYTES = 256 * 1024;
|
|
84
|
+
var TOKEN_CODES = /* @__PURE__ */ new Set([1e3, 6003, 6111, 1e4]);
|
|
85
|
+
var CONFLICT_CODES = /* @__PURE__ */ new Set([81053, 81054, 81057]);
|
|
86
|
+
var ID = /^[A-Za-z0-9]{1,64}$/;
|
|
87
|
+
var RECORD_TYPE = /^[A-Z0-9]{1,12}$/;
|
|
88
|
+
function readEnvelope(json) {
|
|
89
|
+
if (!isObject(json) || typeof json.success !== "boolean") return null;
|
|
90
|
+
const codes = [];
|
|
91
|
+
if (Array.isArray(json.errors)) {
|
|
92
|
+
for (const error of json.errors) {
|
|
93
|
+
if (!isObject(error)) return null;
|
|
94
|
+
if (typeof error.code === "number") codes.push(error.code);
|
|
95
|
+
if (Array.isArray(error.error_chain)) {
|
|
96
|
+
for (const link of error.error_chain)
|
|
97
|
+
if (isObject(link) && typeof link.code === "number") codes.push(link.code);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return { success: json.success, codes, result: json.result };
|
|
102
|
+
}
|
|
103
|
+
async function call(doFetch, token, path, send) {
|
|
104
|
+
const res = await fetchBounded(
|
|
105
|
+
doFetch,
|
|
106
|
+
`${API}${path}`,
|
|
107
|
+
{
|
|
108
|
+
method: send?.method ?? "GET",
|
|
109
|
+
headers: {
|
|
110
|
+
Authorization: `Bearer ${token}`,
|
|
111
|
+
Accept: "application/json",
|
|
112
|
+
...send ? { "Content-Type": "application/json" } : {}
|
|
113
|
+
},
|
|
114
|
+
body: send ? JSON.stringify(send.body) : void 0
|
|
115
|
+
},
|
|
116
|
+
{ timeoutMs: TIMEOUT_MS, maxBytes: MAX_BODY_BYTES }
|
|
117
|
+
);
|
|
118
|
+
if (!res) return { status: 0, body: null };
|
|
119
|
+
let json = null;
|
|
120
|
+
try {
|
|
121
|
+
json = JSON.parse(res.text);
|
|
122
|
+
} catch {
|
|
123
|
+
}
|
|
124
|
+
return { status: res.status, body: readEnvelope(json) };
|
|
125
|
+
}
|
|
126
|
+
function refusal(answer, stage, subject) {
|
|
127
|
+
const codes = answer.body?.codes ?? [];
|
|
128
|
+
const refusedToken = answer.status === 401 || codes.some((c) => TOKEN_CODES.has(c));
|
|
129
|
+
if (stage === "zones" && refusedToken) return new DnsWriteError("token_invalid");
|
|
130
|
+
if (codes.some((c) => CONFLICT_CODES.has(c)))
|
|
131
|
+
return new DnsWriteError("conflict", { ...subject });
|
|
132
|
+
if (answer.status === 403 || refusedToken) {
|
|
133
|
+
if (stage === "zones") return new DnsWriteError("zone_read_missing");
|
|
134
|
+
return new DnsWriteError("dns_edit_missing");
|
|
135
|
+
}
|
|
136
|
+
return new DnsWriteError("unavailable");
|
|
137
|
+
}
|
|
138
|
+
function resultOf(answer, stage, subject) {
|
|
139
|
+
const ok = answer.status >= 200 && answer.status < 300 && answer.body?.success === true;
|
|
140
|
+
if (!ok) throw refusal(answer, stage, subject);
|
|
141
|
+
return answer.body?.result;
|
|
142
|
+
}
|
|
143
|
+
function zonesOf(result) {
|
|
144
|
+
if (!Array.isArray(result)) throw new DnsWriteError("unavailable");
|
|
145
|
+
return result.map((row) => {
|
|
146
|
+
if (!isObject(row) || typeof row.id !== "string" || typeof row.name !== "string")
|
|
147
|
+
throw new DnsWriteError("unavailable");
|
|
148
|
+
if (!ID.test(row.id)) throw new DnsWriteError("unavailable");
|
|
149
|
+
return { id: row.id, name: row.name };
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
function recordsOf(result) {
|
|
153
|
+
if (!Array.isArray(result)) throw new DnsWriteError("unavailable");
|
|
154
|
+
return result.map((row) => {
|
|
155
|
+
if (!isObject(row) || typeof row.id !== "string" || typeof row.type !== "string" || typeof row.content !== "string" || !ID.test(row.id) || !RECORD_TYPE.test(row.type))
|
|
156
|
+
throw new DnsWriteError("unavailable");
|
|
157
|
+
return {
|
|
158
|
+
id: row.id,
|
|
159
|
+
type: row.type,
|
|
160
|
+
content: row.content,
|
|
161
|
+
proxied: typeof row.proxied === "boolean" ? row.proxied : void 0
|
|
162
|
+
};
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
function zoneCandidates(hostname) {
|
|
166
|
+
const labels = hostname.split(".");
|
|
167
|
+
const names = [];
|
|
168
|
+
for (let start = 0; start < labels.length - 1; start++) names.push(labels.slice(start).join("."));
|
|
169
|
+
return names;
|
|
170
|
+
}
|
|
171
|
+
var unquoted = (content) => content.replace(/"\s+"/g, "").replace(/^"|"$/g, "");
|
|
172
|
+
var sameValue = (record, existing) => record.type === "CNAME" ? normalizeDnsName(existing.content) === normalizeDnsName(record.value) : unquoted(existing.content) === record.value;
|
|
173
|
+
var isSpf = (text) => /^v=spf1(?:\s|$)/i.test(unquoted(text).trim());
|
|
174
|
+
function blocks(record, existing) {
|
|
175
|
+
if (record.type === "CNAME" || existing.type === "CNAME") return true;
|
|
176
|
+
return existing.type === "TXT" && isSpf(record.value) && isSpf(existing.content);
|
|
177
|
+
}
|
|
178
|
+
var cloudflare = {
|
|
179
|
+
host: "cloudflare",
|
|
180
|
+
/** Add the record (DNS only) to the Cloudflare zone that holds its name. An identical record
|
|
181
|
+
* is left as it is; the same CNAME behind the proxy gets the proxy turned off. */
|
|
182
|
+
async addRecord({ token, record, comment, fetch: doFetch = fetch }) {
|
|
183
|
+
const hostname = normalizeDnsName(record.name);
|
|
184
|
+
const subject = { hostname, type: record.type };
|
|
185
|
+
let zoneId;
|
|
186
|
+
for (const candidate of zoneCandidates(hostname)) {
|
|
187
|
+
const query = new URLSearchParams({ name: candidate });
|
|
188
|
+
const zones = zonesOf(
|
|
189
|
+
resultOf(await call(doFetch, token, `/zones?${query}`), "zones", subject)
|
|
190
|
+
);
|
|
191
|
+
zoneId = zones.find((z) => normalizeDnsName(z.name) === candidate)?.id;
|
|
192
|
+
if (zoneId) break;
|
|
193
|
+
}
|
|
194
|
+
if (!zoneId) throw new DnsWriteError("zone_not_found", { hostname });
|
|
195
|
+
const existing = recordsOf(
|
|
196
|
+
resultOf(
|
|
197
|
+
await call(
|
|
198
|
+
doFetch,
|
|
199
|
+
token,
|
|
200
|
+
`/zones/${zoneId}/dns_records?${new URLSearchParams({ name: hostname })}`
|
|
201
|
+
),
|
|
202
|
+
"records",
|
|
203
|
+
subject
|
|
204
|
+
)
|
|
205
|
+
);
|
|
206
|
+
const ours = existing.find((r) => r.type === record.type && sameValue(record, r));
|
|
207
|
+
if (ours) {
|
|
208
|
+
if (record.type !== "CNAME" || ours.proxied !== true) return "unchanged";
|
|
209
|
+
resultOf(
|
|
210
|
+
await call(doFetch, token, `/zones/${zoneId}/dns_records/${ours.id}`, {
|
|
211
|
+
method: "PATCH",
|
|
212
|
+
body: { proxied: false }
|
|
213
|
+
}),
|
|
214
|
+
"records",
|
|
215
|
+
subject
|
|
216
|
+
);
|
|
217
|
+
return "unproxied";
|
|
218
|
+
}
|
|
219
|
+
const blocker = existing.find((r) => blocks(record, r));
|
|
220
|
+
if (blocker) throw new DnsWriteError("conflict", { hostname, type: blocker.type });
|
|
221
|
+
resultOf(
|
|
222
|
+
await call(doFetch, token, `/zones/${zoneId}/dns_records`, {
|
|
223
|
+
method: "POST",
|
|
224
|
+
body: {
|
|
225
|
+
type: record.type,
|
|
226
|
+
name: hostname,
|
|
227
|
+
content: record.value,
|
|
228
|
+
// A proxied record hides the CNAME our checker reads.
|
|
229
|
+
...record.type === "CNAME" ? { proxied: false } : {},
|
|
230
|
+
// 1 is Cloudflare's "automatic".
|
|
231
|
+
ttl: 1,
|
|
232
|
+
...comment ? { comment } : {}
|
|
233
|
+
}
|
|
234
|
+
}),
|
|
235
|
+
"records",
|
|
236
|
+
subject
|
|
237
|
+
);
|
|
238
|
+
return "created";
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
// src/writers/index.ts
|
|
243
|
+
var writers = { cloudflare };
|
|
244
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
245
|
+
0 && (module.exports = {
|
|
246
|
+
DnsWriteError,
|
|
247
|
+
cloudflare,
|
|
248
|
+
writers
|
|
249
|
+
});
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { D as DnsHostId } from '../hosts-B0_V4Ned.cjs';
|
|
2
|
+
|
|
3
|
+
interface DnsWriteRecord {
|
|
4
|
+
type: "CNAME" | "TXT";
|
|
5
|
+
/** The record's whole name: `help.acme.com`. */
|
|
6
|
+
name: string;
|
|
7
|
+
/** The target (CNAME) or the text (TXT), as the app means it, with no quotes added. */
|
|
8
|
+
value: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* - `created`: the record is there now;
|
|
12
|
+
* - `unchanged`: the same record was already there;
|
|
13
|
+
* - `unproxied`: the same record was there behind the host's proxy, and the proxy is off now,
|
|
14
|
+
* because a proxied record hides the CNAME a checker reads (Cloudflare).
|
|
15
|
+
*/
|
|
16
|
+
type DnsWriteOutcome = "created" | "unchanged" | "unproxied";
|
|
17
|
+
/**
|
|
18
|
+
* Why a write was refused. A person can act on each one:
|
|
19
|
+
* - `token_invalid`: the host didn't accept the token (paste it again);
|
|
20
|
+
* - `zone_read_missing`: the token can't list the domains (give it that permission);
|
|
21
|
+
* - `dns_edit_missing`: the token can't change DNS records (give it that permission);
|
|
22
|
+
* - `zone_not_found`: no zone the token can see holds the name (the domain is elsewhere, or the
|
|
23
|
+
* token is for another account);
|
|
24
|
+
* - `conflict`: the name already has a record that isn't ours, and it is the owner's to resolve;
|
|
25
|
+
* - `unavailable`: the host didn't answer in a usable way (try again).
|
|
26
|
+
*/
|
|
27
|
+
type DnsWriteErrorCode = "token_invalid" | "zone_read_missing" | "dns_edit_missing" | "zone_not_found" | "conflict" | "unavailable";
|
|
28
|
+
/** A refusal with a fixed message and no `cause`: an error from the host or from `fetch` can
|
|
29
|
+
* quote the token, so none of it is kept. */
|
|
30
|
+
declare class DnsWriteError extends Error {
|
|
31
|
+
readonly code: DnsWriteErrorCode;
|
|
32
|
+
/** `conflict` carries `hostname` and the `type` of the record in the way. */
|
|
33
|
+
readonly params: Readonly<Record<string, string>>;
|
|
34
|
+
constructor(code: DnsWriteErrorCode,
|
|
35
|
+
/** `conflict` carries `hostname` and the `type` of the record in the way. */
|
|
36
|
+
params?: Readonly<Record<string, string>>);
|
|
37
|
+
}
|
|
38
|
+
interface DnsWriter {
|
|
39
|
+
host: DnsHostId;
|
|
40
|
+
/** Add the record to the person's own zone with a token they pasted. The token lives only in
|
|
41
|
+
* this call: it is never stored, queued, logged, put in a URL or put in an error. A record
|
|
42
|
+
* the person already has is never overwritten or deleted. */
|
|
43
|
+
addRecord(input: {
|
|
44
|
+
token: string;
|
|
45
|
+
record: DnsWriteRecord;
|
|
46
|
+
/** Shown beside the record in the host's panel, where the host has such a field. */
|
|
47
|
+
comment?: string;
|
|
48
|
+
fetch?: typeof fetch;
|
|
49
|
+
}): Promise<DnsWriteOutcome>;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
declare const cloudflare: DnsWriter;
|
|
53
|
+
|
|
54
|
+
/** The hosts that take a pasted token, by host id: `writers[host]` is undefined for the rest. */
|
|
55
|
+
declare const writers: Partial<Record<DnsHostId, DnsWriter>>;
|
|
56
|
+
|
|
57
|
+
export { DnsWriteError, type DnsWriteErrorCode, type DnsWriteOutcome, type DnsWriteRecord, type DnsWriter, cloudflare, writers };
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { D as DnsHostId } from '../hosts-B0_V4Ned.js';
|
|
2
|
+
|
|
3
|
+
interface DnsWriteRecord {
|
|
4
|
+
type: "CNAME" | "TXT";
|
|
5
|
+
/** The record's whole name: `help.acme.com`. */
|
|
6
|
+
name: string;
|
|
7
|
+
/** The target (CNAME) or the text (TXT), as the app means it, with no quotes added. */
|
|
8
|
+
value: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* - `created`: the record is there now;
|
|
12
|
+
* - `unchanged`: the same record was already there;
|
|
13
|
+
* - `unproxied`: the same record was there behind the host's proxy, and the proxy is off now,
|
|
14
|
+
* because a proxied record hides the CNAME a checker reads (Cloudflare).
|
|
15
|
+
*/
|
|
16
|
+
type DnsWriteOutcome = "created" | "unchanged" | "unproxied";
|
|
17
|
+
/**
|
|
18
|
+
* Why a write was refused. A person can act on each one:
|
|
19
|
+
* - `token_invalid`: the host didn't accept the token (paste it again);
|
|
20
|
+
* - `zone_read_missing`: the token can't list the domains (give it that permission);
|
|
21
|
+
* - `dns_edit_missing`: the token can't change DNS records (give it that permission);
|
|
22
|
+
* - `zone_not_found`: no zone the token can see holds the name (the domain is elsewhere, or the
|
|
23
|
+
* token is for another account);
|
|
24
|
+
* - `conflict`: the name already has a record that isn't ours, and it is the owner's to resolve;
|
|
25
|
+
* - `unavailable`: the host didn't answer in a usable way (try again).
|
|
26
|
+
*/
|
|
27
|
+
type DnsWriteErrorCode = "token_invalid" | "zone_read_missing" | "dns_edit_missing" | "zone_not_found" | "conflict" | "unavailable";
|
|
28
|
+
/** A refusal with a fixed message and no `cause`: an error from the host or from `fetch` can
|
|
29
|
+
* quote the token, so none of it is kept. */
|
|
30
|
+
declare class DnsWriteError extends Error {
|
|
31
|
+
readonly code: DnsWriteErrorCode;
|
|
32
|
+
/** `conflict` carries `hostname` and the `type` of the record in the way. */
|
|
33
|
+
readonly params: Readonly<Record<string, string>>;
|
|
34
|
+
constructor(code: DnsWriteErrorCode,
|
|
35
|
+
/** `conflict` carries `hostname` and the `type` of the record in the way. */
|
|
36
|
+
params?: Readonly<Record<string, string>>);
|
|
37
|
+
}
|
|
38
|
+
interface DnsWriter {
|
|
39
|
+
host: DnsHostId;
|
|
40
|
+
/** Add the record to the person's own zone with a token they pasted. The token lives only in
|
|
41
|
+
* this call: it is never stored, queued, logged, put in a URL or put in an error. A record
|
|
42
|
+
* the person already has is never overwritten or deleted. */
|
|
43
|
+
addRecord(input: {
|
|
44
|
+
token: string;
|
|
45
|
+
record: DnsWriteRecord;
|
|
46
|
+
/** Shown beside the record in the host's panel, where the host has such a field. */
|
|
47
|
+
comment?: string;
|
|
48
|
+
fetch?: typeof fetch;
|
|
49
|
+
}): Promise<DnsWriteOutcome>;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
declare const cloudflare: DnsWriter;
|
|
53
|
+
|
|
54
|
+
/** The hosts that take a pasted token, by host id: `writers[host]` is undefined for the rest. */
|
|
55
|
+
declare const writers: Partial<Record<DnsHostId, DnsWriter>>;
|
|
56
|
+
|
|
57
|
+
export { DnsWriteError, type DnsWriteErrorCode, type DnsWriteOutcome, type DnsWriteRecord, type DnsWriter, cloudflare, writers };
|
package/package.json
CHANGED
|
@@ -1,6 +1,95 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gusnips/one-click-dns",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"description": "
|
|
6
|
-
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"packageManager": "bun@1.4.2",
|
|
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
|
+
"type": "module",
|
|
7
|
+
"main": "dist/index.cjs",
|
|
8
|
+
"module": "dist/index.js",
|
|
9
|
+
"types": "dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"import": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"default": "./dist/index.js"
|
|
15
|
+
},
|
|
16
|
+
"require": {
|
|
17
|
+
"types": "./dist/index.d.cts",
|
|
18
|
+
"default": "./dist/index.cjs"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"./server": {
|
|
22
|
+
"import": {
|
|
23
|
+
"types": "./dist/server/index.d.ts",
|
|
24
|
+
"default": "./dist/server/index.js"
|
|
25
|
+
},
|
|
26
|
+
"require": {
|
|
27
|
+
"types": "./dist/server/index.d.cts",
|
|
28
|
+
"default": "./dist/server/index.cjs"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"./writers": {
|
|
32
|
+
"import": {
|
|
33
|
+
"types": "./dist/writers/index.d.ts",
|
|
34
|
+
"default": "./dist/writers/index.js"
|
|
35
|
+
},
|
|
36
|
+
"require": {
|
|
37
|
+
"types": "./dist/writers/index.d.cts",
|
|
38
|
+
"default": "./dist/writers/index.cjs"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"files": [
|
|
43
|
+
"dist",
|
|
44
|
+
"README.md",
|
|
45
|
+
"LICENSE"
|
|
46
|
+
],
|
|
47
|
+
"scripts": {
|
|
48
|
+
"build": "tsup src/index.ts src/server/index.ts src/writers/index.ts --format cjs,esm --dts --clean",
|
|
49
|
+
"typecheck": "tsc --noEmit",
|
|
50
|
+
"lint": "eslint src",
|
|
51
|
+
"format": "prettier --write .",
|
|
52
|
+
"test": "bun test",
|
|
53
|
+
"prepublishOnly": "bun run lint && bun run typecheck && bun run build"
|
|
54
|
+
},
|
|
55
|
+
"keywords": [
|
|
56
|
+
"dns",
|
|
57
|
+
"domain-connect",
|
|
58
|
+
"custom-domain",
|
|
59
|
+
"cloudflare",
|
|
60
|
+
"godaddy",
|
|
61
|
+
"name-com",
|
|
62
|
+
"registro-br",
|
|
63
|
+
"typescript"
|
|
64
|
+
],
|
|
65
|
+
"author": "Gustavo Salomé (gusnips)",
|
|
66
|
+
"license": "MIT",
|
|
67
|
+
"repository": {
|
|
68
|
+
"type": "git",
|
|
69
|
+
"url": "git+https://github.com/gusnips/one-click-dns.git"
|
|
70
|
+
},
|
|
71
|
+
"bugs": {
|
|
72
|
+
"url": "https://github.com/gusnips/one-click-dns/issues"
|
|
73
|
+
},
|
|
74
|
+
"homepage": "https://github.com/gusnips/one-click-dns#readme",
|
|
75
|
+
"engines": {
|
|
76
|
+
"node": ">=20"
|
|
77
|
+
},
|
|
78
|
+
"publishConfig": {
|
|
79
|
+
"access": "public"
|
|
80
|
+
},
|
|
81
|
+
"peerDependencies": {
|
|
82
|
+
"@gusnips/server": ">=0.15.0"
|
|
83
|
+
},
|
|
84
|
+
"devDependencies": {
|
|
85
|
+
"@eslint/js": "^10.0.1",
|
|
86
|
+
"@gusnips/server": "^0.15.0",
|
|
87
|
+
"@types/bun": "^1.4.2",
|
|
88
|
+
"@types/node": "^26.6.5",
|
|
89
|
+
"eslint": "^10.12.0",
|
|
90
|
+
"prettier": "^3.9.10",
|
|
91
|
+
"tsup": "^8.5.1",
|
|
92
|
+
"typescript": "^5.9.3",
|
|
93
|
+
"typescript-eslint": "^8.71.1"
|
|
94
|
+
}
|
|
95
|
+
}
|