domain0 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/README.md +629 -0
- package/dist/contracts.cjs +1773 -0
- package/dist/contracts.cjs.map +1 -0
- package/dist/contracts.d.cts +2 -0
- package/dist/contracts.d.mts +2 -0
- package/dist/contracts.mjs +1627 -0
- package/dist/contracts.mjs.map +1 -0
- package/dist/error-BB4-lyc4.cjs +48 -0
- package/dist/error-BB4-lyc4.cjs.map +1 -0
- package/dist/error-Cyd6a9ay.mjs +31 -0
- package/dist/error-Cyd6a9ay.mjs.map +1 -0
- package/dist/index-CtQn01os.d.cts +6930 -0
- package/dist/index-CtQn01os.d.mts +6930 -0
- package/dist/index.cjs +246 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +3 -0
- package/dist/index.d.mts +3 -0
- package/dist/index.mjs +91 -0
- package/dist/index.mjs.map +1 -0
- package/dist/sdk-7CglngBt.d.mts +196 -0
- package/dist/sdk-BHIxsxUT.mjs +3997 -0
- package/dist/sdk-BHIxsxUT.mjs.map +1 -0
- package/dist/sdk-BKtsupNk.d.cts +196 -0
- package/dist/sdk-C0q5dV-6.cjs +4074 -0
- package/dist/sdk-C0q5dV-6.cjs.map +1 -0
- package/dist/server.cjs +103 -0
- package/dist/server.cjs.map +1 -0
- package/dist/server.d.cts +19 -0
- package/dist/server.d.mts +19 -0
- package/dist/server.mjs +102 -0
- package/dist/server.mjs.map +1 -0
- package/dist/ui.cjs +12 -0
- package/dist/ui.d.cts +286 -0
- package/dist/ui.d.mts +286 -0
- package/dist/ui.mjs +2 -0
- package/package.json +112 -0
|
@@ -0,0 +1,1773 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
let zod = require("zod");
|
|
3
|
+
//#region src/contracts/primitives.ts
|
|
4
|
+
const identifier = zod.z.string().trim().min(1).max(200);
|
|
5
|
+
const ConnectionIdSchema = identifier.brand().meta({ id: "ConnectionId" });
|
|
6
|
+
const ApplicationIdSchema = identifier.brand().meta({ id: "ApplicationId" });
|
|
7
|
+
const TenantIdSchema = identifier.brand().meta({ id: "TenantId" });
|
|
8
|
+
const ProviderIdSchema = zod.z.string().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/).brand().meta({ id: "ProviderId" });
|
|
9
|
+
const CommandIdSchema = identifier.brand().meta({ id: "CommandId" });
|
|
10
|
+
const DomainNameSchema = zod.z.string().trim().toLowerCase().overwrite((value) => value.replace(/\.$/, "")).min(3).max(253).regex(/^(?=.{1,253}$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/).refine((value) => !/^\d+(?:\.\d+){3}$/.test(value), "IP addresses are not domain names").brand().meta({ id: "DomainName" });
|
|
11
|
+
const IsoDateTimeSchema = zod.z.iso.datetime({ offset: true }).meta({ id: "IsoDateTime" });
|
|
12
|
+
//#endregion
|
|
13
|
+
//#region src/contracts/dns.ts
|
|
14
|
+
const RelativeDnsHostSchema = zod.z.string().trim().toLowerCase().overwrite((value) => value.replace(/\.$/, "")).min(1).max(253).refine(isRelativeDnsHost, "Host must be @ or a valid relative DNS name");
|
|
15
|
+
const caaValuePattern = /^([0-9]{1,3})[\t ]+([A-Za-z0-9-]{1,15})[\t ]+(?:"([^\r\n"]*)"|([^\r\n"]+))$/;
|
|
16
|
+
const DnsRecordTypeSchema = zod.z.enum([
|
|
17
|
+
"A",
|
|
18
|
+
"AAAA",
|
|
19
|
+
"CNAME",
|
|
20
|
+
"CAA",
|
|
21
|
+
"MX",
|
|
22
|
+
"TXT",
|
|
23
|
+
"NS"
|
|
24
|
+
]).meta({ id: "DnsRecordType" });
|
|
25
|
+
const DmarcTagNameSchema = zod.z.string().regex(/^[a-z]{1,32}$/);
|
|
26
|
+
const DmarcTagValueSchema = zod.z.string().min(1).max(4096).refine((value) => value.trim() === value && /^[\x20-\x3a\x3c-\x7e]+$/.test(value), { message: "DMARC tag values must be trimmed printable ASCII without semicolons" });
|
|
27
|
+
const DmarcTagMapSchema = zod.z.record(DmarcTagNameSchema, DmarcTagValueSchema).refine((tags) => Object.keys(tags).length <= 32, "At most 32 DMARC tags are allowed");
|
|
28
|
+
const AdvancedDmarcOptionsSchema = zod.z.object({
|
|
29
|
+
inheritRootDmarc: zod.z.boolean().default(false),
|
|
30
|
+
overrideTags: DmarcTagMapSchema.default({}),
|
|
31
|
+
removeTags: zod.z.array(DmarcTagNameSchema).max(32).refine((tags) => new Set(tags).size === tags.length, "DMARC tags to remove must be unique").default([]),
|
|
32
|
+
addTagsIfNotExist: DmarcTagMapSchema.default({})
|
|
33
|
+
}).strict().refine((options) => options.inheritRootDmarc || Object.keys(options.overrideTags).length > 0 || options.removeTags.length > 0 || Object.keys(options.addTagsIfNotExist).length > 0, "At least one advanced DMARC option is required").superRefine((options, context) => {
|
|
34
|
+
for (const [field, tags] of [["overrideTags", options.overrideTags], ["addTagsIfNotExist", options.addTagsIfNotExist]]) for (const [name, value] of Object.entries(tags)) if (!isValidKnownDmarcTag(name, value)) context.addIssue({
|
|
35
|
+
code: "custom",
|
|
36
|
+
path: [field, name],
|
|
37
|
+
message: `Invalid value for the ${name} DMARC tag`
|
|
38
|
+
});
|
|
39
|
+
}).meta({ id: "AdvancedDmarcOptions" });
|
|
40
|
+
const CoreDnsRecordSchema = zod.z.object({
|
|
41
|
+
host: RelativeDnsHostSchema,
|
|
42
|
+
type: DnsRecordTypeSchema,
|
|
43
|
+
value: zod.z.string().trim().min(1).max(65535),
|
|
44
|
+
ttl: zod.z.number().int().min(1).max(2147483647).default(300),
|
|
45
|
+
priority: zod.z.number().int().min(0).max(65535).optional(),
|
|
46
|
+
optional: zod.z.boolean().default(false)
|
|
47
|
+
}).strict().superRefine((record, context) => {
|
|
48
|
+
if (record.type === "MX" && record.priority === void 0) context.addIssue({
|
|
49
|
+
code: "custom",
|
|
50
|
+
path: ["priority"],
|
|
51
|
+
message: "MX records require a priority"
|
|
52
|
+
});
|
|
53
|
+
if (record.type !== "MX" && record.priority !== void 0) context.addIssue({
|
|
54
|
+
code: "custom",
|
|
55
|
+
path: ["priority"],
|
|
56
|
+
message: "Only MX records accept a priority"
|
|
57
|
+
});
|
|
58
|
+
if (record.type === "A" && !zod.z.ipv4().safeParse(record.value).success) context.addIssue({
|
|
59
|
+
code: "custom",
|
|
60
|
+
path: ["value"],
|
|
61
|
+
message: "A records require an IPv4 address"
|
|
62
|
+
});
|
|
63
|
+
if (record.type === "AAAA" && !zod.z.ipv6().safeParse(record.value).success) context.addIssue({
|
|
64
|
+
code: "custom",
|
|
65
|
+
path: ["value"],
|
|
66
|
+
message: "AAAA records require an IPv6 address"
|
|
67
|
+
});
|
|
68
|
+
if ([
|
|
69
|
+
"CNAME",
|
|
70
|
+
"MX",
|
|
71
|
+
"NS"
|
|
72
|
+
].includes(record.type) && !DomainNameSchema.safeParse(record.value).success) context.addIssue({
|
|
73
|
+
code: "custom",
|
|
74
|
+
path: ["value"],
|
|
75
|
+
message: `${record.type} records require a domain name`
|
|
76
|
+
});
|
|
77
|
+
if (record.type === "CAA") {
|
|
78
|
+
const match = caaValuePattern.exec(record.value);
|
|
79
|
+
if (match === null || Number(match[1]) > 255) context.addIssue({
|
|
80
|
+
code: "custom",
|
|
81
|
+
path: ["value"],
|
|
82
|
+
message: "CAA values must contain flags, a tag, and a quoted value"
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
if (record.type === "TXT" && /[\u0000\r\n]/.test(record.value)) context.addIssue({
|
|
86
|
+
code: "custom",
|
|
87
|
+
path: ["value"],
|
|
88
|
+
message: "TXT values cannot contain NUL or newlines"
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
const ResolvedDnsRecordSchema = CoreDnsRecordSchema.meta({ id: "ResolvedDnsRecord" });
|
|
92
|
+
const DnsRecordSchema = CoreDnsRecordSchema.safeExtend({ advancedDmarcOptions: AdvancedDmarcOptionsSchema.optional() }).superRefine((record, context) => {
|
|
93
|
+
if (record.advancedDmarcOptions === void 0) return;
|
|
94
|
+
if (record.type !== "TXT" || record.host !== "_dmarc" && !record.host.startsWith("_dmarc.")) context.addIssue({
|
|
95
|
+
code: "custom",
|
|
96
|
+
path: ["advancedDmarcOptions"],
|
|
97
|
+
message: "Advanced DMARC options require a TXT record at a _dmarc host"
|
|
98
|
+
});
|
|
99
|
+
if (!isValidDmarcRecord(record.value)) context.addIssue({
|
|
100
|
+
code: "custom",
|
|
101
|
+
path: ["value"],
|
|
102
|
+
message: "Advanced DMARC options require a valid DMARC1 value with a policy tag"
|
|
103
|
+
});
|
|
104
|
+
}).meta({ id: "DnsRecord" });
|
|
105
|
+
const ExistingRecordPolicySchema = zod.z.object({
|
|
106
|
+
validateDmarc: zod.z.boolean().default(false),
|
|
107
|
+
validateCAA: zod.z.boolean().default(false)
|
|
108
|
+
}).strict().meta({ id: "ExistingRecordPolicy" });
|
|
109
|
+
const DnsIntentSchema = zod.z.object({
|
|
110
|
+
domain: DomainNameSchema,
|
|
111
|
+
records: zod.z.array(DnsRecordSchema).min(1).max(100),
|
|
112
|
+
existingRecordPolicy: ExistingRecordPolicySchema.default({
|
|
113
|
+
validateDmarc: false,
|
|
114
|
+
validateCAA: false
|
|
115
|
+
})
|
|
116
|
+
}).strict().meta({ id: "DnsIntent" });
|
|
117
|
+
const ResolvedDnsIntentSchema = zod.z.object({
|
|
118
|
+
domain: DomainNameSchema,
|
|
119
|
+
records: zod.z.array(ResolvedDnsRecordSchema).max(100)
|
|
120
|
+
}).strict().meta({ id: "ResolvedDnsIntent" });
|
|
121
|
+
const CheckRecordsInputSchema = ResolvedDnsIntentSchema.meta({ id: "CheckRecordsInput" });
|
|
122
|
+
const CheckedRecordSchema = zod.z.object({
|
|
123
|
+
record: ResolvedDnsRecordSchema,
|
|
124
|
+
authoritativeServers: zod.z.number().int().min(1).max(16),
|
|
125
|
+
matchingServers: zod.z.number().int().min(0).max(16),
|
|
126
|
+
propagated: zod.z.boolean()
|
|
127
|
+
}).strict().superRefine((result, context) => {
|
|
128
|
+
if (result.matchingServers > result.authoritativeServers) context.addIssue({
|
|
129
|
+
code: "custom",
|
|
130
|
+
path: ["matchingServers"],
|
|
131
|
+
message: "Matching nameservers cannot exceed authoritative nameservers"
|
|
132
|
+
});
|
|
133
|
+
if (result.propagated !== (result.matchingServers === result.authoritativeServers)) context.addIssue({
|
|
134
|
+
code: "custom",
|
|
135
|
+
path: ["propagated"],
|
|
136
|
+
message: "Propagation state must match the authoritative nameserver counts"
|
|
137
|
+
});
|
|
138
|
+
}).meta({ id: "CheckedRecord" });
|
|
139
|
+
const CheckRecordsResponseSchema = zod.z.object({
|
|
140
|
+
domain: DomainNameSchema,
|
|
141
|
+
allPropagated: zod.z.boolean(),
|
|
142
|
+
records: zod.z.array(CheckedRecordSchema).min(1).max(100)
|
|
143
|
+
}).strict().superRefine((result, context) => {
|
|
144
|
+
if (result.allPropagated !== result.records.every((record) => record.propagated)) context.addIssue({
|
|
145
|
+
code: "custom",
|
|
146
|
+
path: ["allPropagated"],
|
|
147
|
+
message: "Overall propagation state must match every record result"
|
|
148
|
+
});
|
|
149
|
+
}).meta({ id: "CheckRecordsResponse" });
|
|
150
|
+
const ConflictPolicySchema = zod.z.enum([
|
|
151
|
+
"preserve",
|
|
152
|
+
"replace_same_name_and_type",
|
|
153
|
+
"replace_all_at_name"
|
|
154
|
+
]).meta({ id: "ConflictPolicy" });
|
|
155
|
+
const SpfConflictPolicySchema = zod.z.enum(["merge", "replace"]).meta({ id: "SpfConflictPolicy" });
|
|
156
|
+
const ChangeActionSchema = zod.z.enum([
|
|
157
|
+
"create",
|
|
158
|
+
"replace",
|
|
159
|
+
"delete"
|
|
160
|
+
]).meta({ id: "ChangeAction" });
|
|
161
|
+
const PlannedChangeSchema = zod.z.object({
|
|
162
|
+
action: ChangeActionSchema,
|
|
163
|
+
before: ResolvedDnsRecordSchema.optional(),
|
|
164
|
+
after: ResolvedDnsRecordSchema.optional(),
|
|
165
|
+
destructive: zod.z.boolean()
|
|
166
|
+
}).strict().superRefine((change, context) => {
|
|
167
|
+
if (change.action === "create" && change.after === void 0) context.addIssue({
|
|
168
|
+
code: "custom",
|
|
169
|
+
path: ["after"],
|
|
170
|
+
message: "Create requires the resulting record"
|
|
171
|
+
});
|
|
172
|
+
if (change.action === "replace" && (change.before === void 0 || change.after === void 0)) context.addIssue({
|
|
173
|
+
code: "custom",
|
|
174
|
+
message: "Replace requires both current and resulting records"
|
|
175
|
+
});
|
|
176
|
+
if (change.action === "delete" && change.before === void 0) context.addIssue({
|
|
177
|
+
code: "custom",
|
|
178
|
+
path: ["before"],
|
|
179
|
+
message: "Delete requires the current record"
|
|
180
|
+
});
|
|
181
|
+
}).meta({ id: "PlannedChange" });
|
|
182
|
+
function isRelativeDnsHost(value) {
|
|
183
|
+
if (value === "@") return true;
|
|
184
|
+
return value.split(".").every((label, index) => {
|
|
185
|
+
if (label === "*") return index === 0;
|
|
186
|
+
return label.length >= 1 && label.length <= 63 && !label.startsWith("-") && !label.endsWith("-") && /^[a-z0-9_-]+$/.test(label);
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
function isValidDmarcRecord(value) {
|
|
190
|
+
const parts = value.split(";").map((part) => part.trim()).filter(Boolean);
|
|
191
|
+
const seen = /* @__PURE__ */ new Set();
|
|
192
|
+
if (parts.length < 2) return false;
|
|
193
|
+
for (const [index, part] of parts.entries()) {
|
|
194
|
+
const separator = part.indexOf("=");
|
|
195
|
+
if (separator <= 0) return false;
|
|
196
|
+
const name = part.slice(0, separator).trim().toLowerCase();
|
|
197
|
+
const tagValue = part.slice(separator + 1).trim();
|
|
198
|
+
if (!DmarcTagNameSchema.safeParse(name).success || !DmarcTagValueSchema.safeParse(tagValue).success || seen.has(name)) return false;
|
|
199
|
+
if (index === 0 && (name !== "v" || tagValue !== "DMARC1")) return false;
|
|
200
|
+
if (!isValidKnownDmarcTag(name, tagValue)) return false;
|
|
201
|
+
seen.add(name);
|
|
202
|
+
}
|
|
203
|
+
return seen.has("p");
|
|
204
|
+
}
|
|
205
|
+
function isValidKnownDmarcTag(name, value) {
|
|
206
|
+
if (name === "v") return value === "DMARC1";
|
|
207
|
+
if ([
|
|
208
|
+
"p",
|
|
209
|
+
"sp",
|
|
210
|
+
"np"
|
|
211
|
+
].includes(name)) return [
|
|
212
|
+
"none",
|
|
213
|
+
"quarantine",
|
|
214
|
+
"reject"
|
|
215
|
+
].includes(value);
|
|
216
|
+
if (["adkim", "aspf"].includes(name)) return value === "r" || value === "s";
|
|
217
|
+
if (name === "pct") return /^\d{1,3}$/.test(value) && Number(value) <= 100;
|
|
218
|
+
if (name === "ri") return /^[1-9]\d*$/.test(value);
|
|
219
|
+
if (name === "rf") return value === "afrf";
|
|
220
|
+
if (name === "fo") {
|
|
221
|
+
const options = value.split(":");
|
|
222
|
+
return options.length > 0 && new Set(options).size === options.length && options.every((option) => [
|
|
223
|
+
"0",
|
|
224
|
+
"1",
|
|
225
|
+
"d",
|
|
226
|
+
"s"
|
|
227
|
+
].includes(option)) && !(options.includes("0") && options.includes("1"));
|
|
228
|
+
}
|
|
229
|
+
return true;
|
|
230
|
+
}
|
|
231
|
+
//#endregion
|
|
232
|
+
//#region src/contracts/provider.ts
|
|
233
|
+
const ProviderImplementationSchema = zod.z.enum([
|
|
234
|
+
"automatic",
|
|
235
|
+
"domain_connect",
|
|
236
|
+
"manual",
|
|
237
|
+
"unverified"
|
|
238
|
+
]).meta({ id: "ProviderImplementation" });
|
|
239
|
+
const AuthorizationMethodSchema = zod.z.enum([
|
|
240
|
+
"oauth2_pkce",
|
|
241
|
+
"api_token",
|
|
242
|
+
"api_key",
|
|
243
|
+
"basic",
|
|
244
|
+
"session",
|
|
245
|
+
"domain_connect",
|
|
246
|
+
"manual"
|
|
247
|
+
]).meta({ id: "AuthorizationMethod" });
|
|
248
|
+
const ProviderCapabilitiesSchema = zod.z.strictObject({ rootNSModification: zod.z.enum([
|
|
249
|
+
"supported",
|
|
250
|
+
"unsupported",
|
|
251
|
+
"unverified"
|
|
252
|
+
]) }).meta({ id: "ProviderCapabilities" });
|
|
253
|
+
const ProviderDetectionUnavailableReasonCodeSchema = zod.z.enum(["no_stable_public_provider_identity"]).meta({ id: "ProviderDetectionUnavailableReasonCode" });
|
|
254
|
+
const ProviderDetectionSchema = zod.z.discriminatedUnion("method", [zod.z.strictObject({ method: zod.z.enum([
|
|
255
|
+
"nameserver",
|
|
256
|
+
"domain_connect",
|
|
257
|
+
"nameserver_or_domain_connect"
|
|
258
|
+
]) }), zod.z.strictObject({
|
|
259
|
+
method: zod.z.literal("manual"),
|
|
260
|
+
reasonCode: ProviderDetectionUnavailableReasonCodeSchema
|
|
261
|
+
})]).meta({ id: "ProviderDetection" });
|
|
262
|
+
const ProviderReferenceUrlSchema = zod.z.url({ protocol: /^https$/ }).regex(/^https:\/\//, "Provider reference URL must use HTTPS").refine((value) => {
|
|
263
|
+
const url = new URL(value);
|
|
264
|
+
return url.username === "" && url.password === "";
|
|
265
|
+
}, "Provider reference URL must not contain credentials").meta({ format: "uri" });
|
|
266
|
+
const ProviderSchema = zod.z.object({
|
|
267
|
+
id: ProviderIdSchema,
|
|
268
|
+
name: zod.z.string().trim().min(1),
|
|
269
|
+
referenceUrl: ProviderReferenceUrlSchema,
|
|
270
|
+
implementation: ProviderImplementationSchema,
|
|
271
|
+
availableAuthorizationMethods: zod.z.array(AuthorizationMethodSchema).refine((methods) => new Set(methods).size === methods.length, "Authorization methods must be unique"),
|
|
272
|
+
capabilities: ProviderCapabilitiesSchema,
|
|
273
|
+
detection: ProviderDetectionSchema,
|
|
274
|
+
limitations: zod.z.array(zod.z.string().min(1))
|
|
275
|
+
}).strict().superRefine((provider, context) => {
|
|
276
|
+
const methods = provider.availableAuthorizationMethods;
|
|
277
|
+
if (!(provider.implementation === "automatic" ? methods.every((method) => method !== "domain_connect" && method !== "manual") : provider.implementation === "domain_connect" ? methods.every((method) => method === "domain_connect") : provider.implementation === "manual" ? methods.every((method) => method === "manual") : methods.length === 0)) context.addIssue({
|
|
278
|
+
code: "custom",
|
|
279
|
+
path: ["availableAuthorizationMethods"],
|
|
280
|
+
message: "Authorization methods must match the verified provider implementation"
|
|
281
|
+
});
|
|
282
|
+
}).meta({ id: "Provider" });
|
|
283
|
+
const ListProvidersResponseSchema = zod.z.object({ providers: zod.z.array(ProviderSchema) }).strict().meta({ id: "ListProvidersResponse" });
|
|
284
|
+
const ProviderHealthStatusSchema = zod.z.enum(["operational", "unavailable"]).meta({ id: "ProviderHealthStatus" });
|
|
285
|
+
const ProviderHealthReasonSchema = zod.z.enum([
|
|
286
|
+
"automatic_ready",
|
|
287
|
+
"domain_connect_ready",
|
|
288
|
+
"runtime_authorization_unavailable",
|
|
289
|
+
"manual_only",
|
|
290
|
+
"unverified"
|
|
291
|
+
]).meta({ id: "ProviderHealthReason" });
|
|
292
|
+
const ProviderHealthSchema = zod.z.object({
|
|
293
|
+
providerId: ProviderIdSchema,
|
|
294
|
+
providerName: zod.z.string().trim().min(1).max(200),
|
|
295
|
+
enabled: zod.z.boolean(),
|
|
296
|
+
status: ProviderHealthStatusSchema,
|
|
297
|
+
reason: ProviderHealthReasonSchema
|
|
298
|
+
}).strict().superRefine((health, context) => {
|
|
299
|
+
const readyReason = health.reason === "automatic_ready" || health.reason === "domain_connect_ready";
|
|
300
|
+
const matchingStatus = health.enabled ? health.status === "operational" : health.status === "unavailable";
|
|
301
|
+
if (health.enabled !== readyReason || !matchingStatus) context.addIssue({
|
|
302
|
+
code: "custom",
|
|
303
|
+
path: ["enabled"],
|
|
304
|
+
message: "Enabled, status, and reason must describe the same provider health state"
|
|
305
|
+
});
|
|
306
|
+
}).meta({ id: "ProviderHealth" });
|
|
307
|
+
const ProviderHealthResponseSchema = zod.z.object({
|
|
308
|
+
observedAt: IsoDateTimeSchema,
|
|
309
|
+
refreshAfter: IsoDateTimeSchema,
|
|
310
|
+
providers: zod.z.array(ProviderHealthSchema)
|
|
311
|
+
}).strict().superRefine((report, context) => {
|
|
312
|
+
if (Date.parse(report.refreshAfter) <= Date.parse(report.observedAt)) context.addIssue({
|
|
313
|
+
code: "custom",
|
|
314
|
+
path: ["refreshAfter"],
|
|
315
|
+
message: "Provider health refresh deadline must be after its observation time"
|
|
316
|
+
});
|
|
317
|
+
const providerIds = report.providers.map((provider) => provider.providerId);
|
|
318
|
+
if (new Set(providerIds).size !== providerIds.length) context.addIssue({
|
|
319
|
+
code: "custom",
|
|
320
|
+
path: ["providers"],
|
|
321
|
+
message: "Provider health entries must have unique provider IDs"
|
|
322
|
+
});
|
|
323
|
+
}).meta({ id: "ProviderHealthResponse" });
|
|
324
|
+
//#endregion
|
|
325
|
+
//#region src/contracts/connection.ts
|
|
326
|
+
const ConnectionStateSchema = zod.z.enum([
|
|
327
|
+
"requested",
|
|
328
|
+
"detecting_provider",
|
|
329
|
+
"provider_selected",
|
|
330
|
+
"domain_connect_pending",
|
|
331
|
+
"authorization_pending",
|
|
332
|
+
"authorized",
|
|
333
|
+
"planning",
|
|
334
|
+
"awaiting_confirmation",
|
|
335
|
+
"applying",
|
|
336
|
+
"propagation_pending",
|
|
337
|
+
"active",
|
|
338
|
+
"manual_required",
|
|
339
|
+
"failed_retryable",
|
|
340
|
+
"failed_terminal",
|
|
341
|
+
"cancelled"
|
|
342
|
+
]).meta({ id: "ConnectionState" });
|
|
343
|
+
const RetryResumeStateSchema = zod.z.enum([
|
|
344
|
+
"authorized",
|
|
345
|
+
"awaiting_confirmation",
|
|
346
|
+
"applying"
|
|
347
|
+
]).meta({ id: "RetryResumeState" });
|
|
348
|
+
const ConnectionFailureCodeSchema = zod.z.enum(["provider_snapshot_timeout", "provider_apply_timeout"]).meta({ id: "ConnectionFailureCode" });
|
|
349
|
+
const ConnectionFailureSchema = zod.z.object({
|
|
350
|
+
code: ConnectionFailureCodeSchema,
|
|
351
|
+
retryable: zod.z.literal(true),
|
|
352
|
+
resumeState: RetryResumeStateSchema,
|
|
353
|
+
occurredAt: IsoDateTimeSchema
|
|
354
|
+
}).strict().meta({ id: "ConnectionFailure" });
|
|
355
|
+
const PlanWarningCodeSchema = zod.z.enum(["spf_policy_replaced", "spf_policy_merged"]).meta({ id: "PlanWarningCode" });
|
|
356
|
+
const ChangePlanSchema = zod.z.object({
|
|
357
|
+
digest: zod.z.string().regex(/^[a-f0-9]{64}$/),
|
|
358
|
+
changes: zod.z.array(PlannedChangeSchema).min(1),
|
|
359
|
+
warnings: zod.z.array(PlanWarningCodeSchema),
|
|
360
|
+
expiresAt: IsoDateTimeSchema
|
|
361
|
+
}).strict().meta({ id: "ChangePlan" });
|
|
362
|
+
const ConnectionSchema = zod.z.object({
|
|
363
|
+
id: ConnectionIdSchema,
|
|
364
|
+
applicationId: ApplicationIdSchema,
|
|
365
|
+
tenantId: TenantIdSchema,
|
|
366
|
+
intent: DnsIntentSchema,
|
|
367
|
+
effectiveIntent: ResolvedDnsIntentSchema.optional(),
|
|
368
|
+
state: ConnectionStateSchema,
|
|
369
|
+
provider: ProviderSchema.optional(),
|
|
370
|
+
plan: ChangePlanSchema.optional(),
|
|
371
|
+
failure: ConnectionFailureSchema.optional(),
|
|
372
|
+
optionalFailures: zod.z.array(zod.z.string()),
|
|
373
|
+
createdAt: IsoDateTimeSchema,
|
|
374
|
+
updatedAt: IsoDateTimeSchema
|
|
375
|
+
}).strict().superRefine((connection, context) => {
|
|
376
|
+
const requiresFailure = connection.state === "failed_retryable";
|
|
377
|
+
if (requiresFailure !== (connection.failure !== void 0)) context.addIssue({
|
|
378
|
+
code: "custom",
|
|
379
|
+
path: ["failure"],
|
|
380
|
+
message: requiresFailure ? "A retryable failure state requires failure details" : "Failure details are allowed only in a retryable failure state"
|
|
381
|
+
});
|
|
382
|
+
if (connection.effectiveIntent !== void 0 && connection.effectiveIntent.domain !== connection.intent.domain) context.addIssue({
|
|
383
|
+
code: "custom",
|
|
384
|
+
path: ["effectiveIntent", "domain"],
|
|
385
|
+
message: "Effective intent must use the requested intent domain"
|
|
386
|
+
});
|
|
387
|
+
}).meta({ id: "Connection" });
|
|
388
|
+
const CreateConnectionInputSchema = zod.z.object({
|
|
389
|
+
applicationId: ApplicationIdSchema,
|
|
390
|
+
tenantId: TenantIdSchema,
|
|
391
|
+
intent: DnsIntentSchema,
|
|
392
|
+
commandId: CommandIdSchema
|
|
393
|
+
}).strict().meta({ id: "CreateConnectionInput" });
|
|
394
|
+
const CreateConnectionResponseSchema = zod.z.object({ connection: ConnectionSchema }).strict().meta({ id: "ConnectionResponse" });
|
|
395
|
+
const GetConnectionResponseSchema = CreateConnectionResponseSchema;
|
|
396
|
+
const ProviderCandidateSchema = zod.z.object({
|
|
397
|
+
provider: ProviderSchema,
|
|
398
|
+
confidence: zod.z.number().min(0).max(1)
|
|
399
|
+
}).strict().meta({ id: "ProviderCandidate" });
|
|
400
|
+
const DetectProviderResponseSchema = zod.z.object({
|
|
401
|
+
connection: ConnectionSchema,
|
|
402
|
+
candidates: zod.z.array(ProviderCandidateSchema)
|
|
403
|
+
}).strict().meta({ id: "DetectProviderResponse" });
|
|
404
|
+
const SelectProviderInputSchema = zod.z.object({
|
|
405
|
+
providerId: ProviderIdSchema,
|
|
406
|
+
commandId: CommandIdSchema
|
|
407
|
+
}).strict().meta({ id: "SelectProviderInput" });
|
|
408
|
+
const DomainConnectIdSchema = zod.z.string().regex(/^[A-Za-z0-9._-]{1,63}$/);
|
|
409
|
+
const DomainConnectPropertyNameSchema = zod.z.string().regex(/^[A-Za-z][A-Za-z0-9_-]{0,62}$/);
|
|
410
|
+
const reservedDomainConnectProperties = /* @__PURE__ */ new Set([
|
|
411
|
+
"domain",
|
|
412
|
+
"host",
|
|
413
|
+
"groupId",
|
|
414
|
+
"providerName",
|
|
415
|
+
"serviceName",
|
|
416
|
+
"instanceId",
|
|
417
|
+
"redirect_uri",
|
|
418
|
+
"state",
|
|
419
|
+
"sig",
|
|
420
|
+
"key"
|
|
421
|
+
]);
|
|
422
|
+
const DomainConnectPropertiesSchema = zod.z.record(DomainConnectPropertyNameSchema, zod.z.string().min(1).max(4096).refine((value) => value.trim() === value && !/[\u0000\r\n]/.test(value), "Domain Connect property values must be trimmed and single-line")).refine((properties) => Object.keys(properties).length <= 64, "At most 64 Domain Connect properties are allowed").refine((properties) => Object.keys(properties).every((name) => !reservedDomainConnectProperties.has(name)), "Domain Connect properties must not override reserved protocol parameters");
|
|
423
|
+
const DomainConnectTemplateSchema = zod.z.object({
|
|
424
|
+
serviceProviderId: DomainConnectIdSchema,
|
|
425
|
+
serviceId: DomainConnectIdSchema,
|
|
426
|
+
host: zod.z.string().max(253).regex(/^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)(?:\.(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?))*$/).optional(),
|
|
427
|
+
groupId: DomainConnectIdSchema.optional(),
|
|
428
|
+
providerName: zod.z.string().trim().min(1).max(200).optional(),
|
|
429
|
+
serviceName: zod.z.string().trim().min(1).max(200).optional(),
|
|
430
|
+
instanceId: DomainConnectIdSchema.optional(),
|
|
431
|
+
properties: DomainConnectPropertiesSchema
|
|
432
|
+
}).strict().meta({ id: "DomainConnectTemplate" });
|
|
433
|
+
const StartDomainConnectInputSchema = DomainConnectTemplateSchema.extend({ commandId: CommandIdSchema }).strict().meta({ id: "StartDomainConnectInput" });
|
|
434
|
+
const DomainConnectHandoffUrlSchema = zod.z.url({ protocol: /^https$/ }).refine((value) => {
|
|
435
|
+
const url = new URL(value);
|
|
436
|
+
return url.username === "" && url.password === "" && url.hash === "";
|
|
437
|
+
}, "Domain Connect handoff URL must be HTTPS without credentials or a fragment");
|
|
438
|
+
const StartDomainConnectResponseSchema = zod.z.object({
|
|
439
|
+
connection: ConnectionSchema,
|
|
440
|
+
handoffUrl: DomainConnectHandoffUrlSchema
|
|
441
|
+
}).strict().meta({ id: "StartDomainConnectResponse" });
|
|
442
|
+
const SubmitDomainConnectCompletionInputSchema = zod.z.object({ commandId: CommandIdSchema }).strict().meta({ id: "SubmitDomainConnectCompletionInput" });
|
|
443
|
+
const ConfirmPlanInputSchema = zod.z.object({
|
|
444
|
+
digest: zod.z.string().regex(/^[a-f0-9]{64}$/),
|
|
445
|
+
allowDestructive: zod.z.boolean(),
|
|
446
|
+
commandId: CommandIdSchema
|
|
447
|
+
}).strict().meta({ id: "ConfirmPlanInput" });
|
|
448
|
+
const StartOAuthInputSchema = zod.z.object({ commandId: CommandIdSchema }).strict().meta({ id: "StartOAuthInput" });
|
|
449
|
+
const StartOAuthResponseSchema = zod.z.object({
|
|
450
|
+
connection: ConnectionSchema,
|
|
451
|
+
authorizationUrl: zod.z.url({ protocol: /^https$/ }),
|
|
452
|
+
expiresAt: IsoDateTimeSchema
|
|
453
|
+
}).strict().meta({ id: "StartOAuthResponse" });
|
|
454
|
+
const SecretSchema = zod.z.string().min(1).max(16384).refine((value) => value.trim() === value, "Credential values must not have leading or trailing whitespace").meta({
|
|
455
|
+
format: "password",
|
|
456
|
+
writeOnly: true
|
|
457
|
+
});
|
|
458
|
+
const ApiTokenCredentialsSchema = zod.z.object({
|
|
459
|
+
kind: zod.z.literal("api_token"),
|
|
460
|
+
token: SecretSchema
|
|
461
|
+
}).strict().meta({ id: "ApiTokenCredentials" });
|
|
462
|
+
const AccessKeyCredentialsSchema = zod.z.object({
|
|
463
|
+
kind: zod.z.literal("access_key"),
|
|
464
|
+
keyId: SecretSchema,
|
|
465
|
+
secret: SecretSchema
|
|
466
|
+
}).strict().meta({ id: "AccessKeyCredentials" });
|
|
467
|
+
const UsernamePasswordCredentialsSchema = zod.z.object({
|
|
468
|
+
kind: zod.z.literal("username_password"),
|
|
469
|
+
username: SecretSchema,
|
|
470
|
+
password: SecretSchema
|
|
471
|
+
}).strict().meta({ id: "UsernamePasswordCredentials" });
|
|
472
|
+
const AccountTokenCredentialsSchema = zod.z.object({
|
|
473
|
+
kind: zod.z.literal("account_token"),
|
|
474
|
+
accountId: SecretSchema,
|
|
475
|
+
token: SecretSchema
|
|
476
|
+
}).strict().meta({ id: "AccountTokenCredentials" });
|
|
477
|
+
const UsernameTokenCredentialsSchema = zod.z.object({
|
|
478
|
+
kind: zod.z.literal("username_token"),
|
|
479
|
+
username: SecretSchema,
|
|
480
|
+
token: SecretSchema
|
|
481
|
+
}).strict().meta({ id: "UsernameTokenCredentials" });
|
|
482
|
+
const OvhCredentialsSchema = zod.z.object({
|
|
483
|
+
kind: zod.z.literal("ovh"),
|
|
484
|
+
endpoint: zod.z.enum([
|
|
485
|
+
"ovh-eu",
|
|
486
|
+
"ovh-us",
|
|
487
|
+
"ovh-ca",
|
|
488
|
+
"kimsufi-eu",
|
|
489
|
+
"kimsufi-ca",
|
|
490
|
+
"soyoustart-eu",
|
|
491
|
+
"soyoustart-ca"
|
|
492
|
+
]),
|
|
493
|
+
applicationKey: SecretSchema,
|
|
494
|
+
applicationSecret: SecretSchema,
|
|
495
|
+
consumerKey: SecretSchema
|
|
496
|
+
}).strict().meta({ id: "OvhCredentials" });
|
|
497
|
+
const PrivateKeyCredentialsSchema = zod.z.object({
|
|
498
|
+
kind: zod.z.literal("private_key"),
|
|
499
|
+
login: SecretSchema,
|
|
500
|
+
privateKey: SecretSchema.refine((value) => /^(-----BEGIN (?:RSA )?PRIVATE KEY-----)[\s\S]+(-----END (?:RSA )?PRIVATE KEY-----)$/.test(value), "A PKCS#8 or PKCS#1 PEM private key is required")
|
|
501
|
+
}).strict().meta({ id: "PrivateKeyCredentials" });
|
|
502
|
+
const AwsSessionCredentialsSchema = zod.z.object({
|
|
503
|
+
kind: zod.z.literal("aws_session"),
|
|
504
|
+
keyId: SecretSchema.regex(/^[A-Z0-9]{16,128}$/),
|
|
505
|
+
secret: SecretSchema,
|
|
506
|
+
sessionToken: SecretSchema,
|
|
507
|
+
hostedZoneId: zod.z.string().regex(/^Z[A-Z0-9]{1,63}$/),
|
|
508
|
+
expiresAt: IsoDateTimeSchema
|
|
509
|
+
}).strict().meta({ id: "AwsSessionCredentials" });
|
|
510
|
+
const CPanelEndpointSchema = zod.z.url({ protocol: /^https$/ }).refine((value) => {
|
|
511
|
+
const url = new URL(value);
|
|
512
|
+
return url.port === "2083" && url.username === "" && url.password === "" && (url.pathname === "" || url.pathname === "/") && url.search === "" && url.hash === "";
|
|
513
|
+
}, "cPanel endpoint must be an HTTPS origin on port 2083");
|
|
514
|
+
const CPanelCredentialsSchema = zod.z.object({
|
|
515
|
+
kind: zod.z.literal("cpanel"),
|
|
516
|
+
endpoint: CPanelEndpointSchema,
|
|
517
|
+
username: SecretSchema,
|
|
518
|
+
token: SecretSchema
|
|
519
|
+
}).strict().meta({ id: "CPanelCredentials" });
|
|
520
|
+
const ClientCredentialsSchema = zod.z.object({
|
|
521
|
+
kind: zod.z.literal("client_credentials"),
|
|
522
|
+
clientId: SecretSchema,
|
|
523
|
+
clientSecret: SecretSchema
|
|
524
|
+
}).strict().meta({ id: "ClientCredentials" });
|
|
525
|
+
function credentialInput(providerId, credentials) {
|
|
526
|
+
return zod.z.object({
|
|
527
|
+
providerId: zod.z.literal(providerId),
|
|
528
|
+
credentials,
|
|
529
|
+
commandId: CommandIdSchema
|
|
530
|
+
}).strict();
|
|
531
|
+
}
|
|
532
|
+
const directCredentialAuthorizationOptions = [
|
|
533
|
+
credentialInput("alibaba-cloud", AccessKeyCredentialsSchema),
|
|
534
|
+
credentialInput("all-inkl", UsernamePasswordCredentialsSchema),
|
|
535
|
+
credentialInput("amazon-route-53", AwsSessionCredentialsSchema),
|
|
536
|
+
credentialInput("cloudflare", ApiTokenCredentialsSchema),
|
|
537
|
+
credentialInput("cloudns", AccountTokenCredentialsSchema),
|
|
538
|
+
credentialInput("digitalocean", ApiTokenCredentialsSchema),
|
|
539
|
+
credentialInput("dnsimple", AccountTokenCredentialsSchema),
|
|
540
|
+
credentialInput("dreamhost", ApiTokenCredentialsSchema),
|
|
541
|
+
credentialInput("dynadot", AccessKeyCredentialsSchema),
|
|
542
|
+
credentialInput("easydns", AccessKeyCredentialsSchema),
|
|
543
|
+
credentialInput("gandi", ApiTokenCredentialsSchema),
|
|
544
|
+
credentialInput("godaddy", ApiTokenCredentialsSchema),
|
|
545
|
+
credentialInput("hetzner", ApiTokenCredentialsSchema),
|
|
546
|
+
credentialInput("hostgator", CPanelCredentialsSchema),
|
|
547
|
+
credentialInput("hostinger", ApiTokenCredentialsSchema),
|
|
548
|
+
credentialInput("hosting-com", CPanelCredentialsSchema),
|
|
549
|
+
credentialInput("inmotion-hosting", CPanelCredentialsSchema),
|
|
550
|
+
credentialInput("ionos", ApiTokenCredentialsSchema),
|
|
551
|
+
credentialInput("name-com", UsernameTokenCredentialsSchema),
|
|
552
|
+
credentialInput("namesilo", ApiTokenCredentialsSchema),
|
|
553
|
+
credentialInput("netlify", ApiTokenCredentialsSchema),
|
|
554
|
+
credentialInput("o2switch", CPanelCredentialsSchema),
|
|
555
|
+
credentialInput("openprovider", ApiTokenCredentialsSchema),
|
|
556
|
+
credentialInput("opensrs", ClientCredentialsSchema),
|
|
557
|
+
credentialInput("ovh", OvhCredentialsSchema),
|
|
558
|
+
credentialInput("porkbun", AccessKeyCredentialsSchema),
|
|
559
|
+
credentialInput("simply", ApiTokenCredentialsSchema),
|
|
560
|
+
credentialInput("spaceship", AccessKeyCredentialsSchema),
|
|
561
|
+
credentialInput("transip", PrivateKeyCredentialsSchema),
|
|
562
|
+
credentialInput("united-domains", ApiTokenCredentialsSchema),
|
|
563
|
+
credentialInput("vercel", ApiTokenCredentialsSchema),
|
|
564
|
+
credentialInput("wix", AccountTokenCredentialsSchema)
|
|
565
|
+
];
|
|
566
|
+
const directCredentialKindByProvider = Object.freeze(Object.fromEntries(directCredentialAuthorizationOptions.map((option) => [option.shape.providerId.value, option.shape.credentials.shape.kind.value])));
|
|
567
|
+
const AuthorizeWithCredentialInputSchema = zod.z.discriminatedUnion("providerId", directCredentialAuthorizationOptions).meta({ id: "AuthorizeWithCredentialInput" });
|
|
568
|
+
const OAuthCallbackResponseSchema = zod.z.object({ completed: zod.z.literal(true) }).strict().meta({ id: "OAuthCallbackResponse" });
|
|
569
|
+
const PreparePlanInputSchema = zod.z.object({
|
|
570
|
+
conflictPolicy: ConflictPolicySchema,
|
|
571
|
+
spfPolicy: SpfConflictPolicySchema.default("merge"),
|
|
572
|
+
commandId: CommandIdSchema
|
|
573
|
+
}).strict().meta({ id: "PreparePlanInput" });
|
|
574
|
+
const ApplyPlanInputSchema = zod.z.object({ commandId: CommandIdSchema }).strict().meta({ id: "ApplyPlanInput" });
|
|
575
|
+
const VerifyPropagationInputSchema = zod.z.object({ commandId: CommandIdSchema }).strict().meta({ id: "VerifyPropagationInput" });
|
|
576
|
+
const StartManualConfigurationInputSchema = zod.z.object({ commandId: CommandIdSchema }).strict().meta({ id: "StartManualConfigurationInput" });
|
|
577
|
+
const SubmitManualCompletionInputSchema = zod.z.object({ commandId: CommandIdSchema }).strict().meta({ id: "SubmitManualCompletionInput" });
|
|
578
|
+
const CancelConnectionInputSchema = zod.z.object({ commandId: CommandIdSchema }).strict().meta({ id: "CancelConnectionInput" });
|
|
579
|
+
const RetryConnectionInputSchema = zod.z.object({ commandId: CommandIdSchema }).strict().meta({ id: "RetryConnectionInput" });
|
|
580
|
+
const IssueConnectionTokenInputSchema = zod.z.object({ origin: zod.z.url().refine((value) => {
|
|
581
|
+
const url = new URL(value);
|
|
582
|
+
return url.username === "" && url.password === "" && url.search === "" && url.hash === "" && (url.pathname === "" || url.pathname === "/") && (url.protocol === "https:" || url.protocol === "http:" && isLoopbackHost(url.hostname));
|
|
583
|
+
}, "Origin must be an HTTPS origin without a path, query, or fragment") }).strict().meta({ id: "IssueConnectionTokenInput" });
|
|
584
|
+
const IssueConnectionTokenResponseSchema = zod.z.object({
|
|
585
|
+
accessToken: zod.z.string().min(1).max(8192),
|
|
586
|
+
tokenType: zod.z.literal("Bearer"),
|
|
587
|
+
expiresAt: IsoDateTimeSchema
|
|
588
|
+
}).strict().meta({ id: "IssueConnectionTokenResponse" });
|
|
589
|
+
function isLoopbackHost(hostname) {
|
|
590
|
+
return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "[::1]";
|
|
591
|
+
}
|
|
592
|
+
//#endregion
|
|
593
|
+
//#region src/contracts/connection-flow.ts
|
|
594
|
+
const ConnectionFlowIdSchema = zod.z.string().trim().min(1).max(200).meta({ id: "ConnectionFlowId" });
|
|
595
|
+
const ConditionalDnsRecordsSchema = zod.z.strictObject({
|
|
596
|
+
domain: zod.z.array(DnsRecordSchema).min(1).max(100),
|
|
597
|
+
subDomain: zod.z.array(DnsRecordSchema).min(1).max(100).optional(),
|
|
598
|
+
rootNS: zod.z.array(DnsRecordSchema).min(1).max(100).optional()
|
|
599
|
+
}).superRefine((records, context) => {
|
|
600
|
+
records.rootNS?.forEach((record, index) => {
|
|
601
|
+
if (record.host !== "@" || record.type !== "NS") context.addIssue({
|
|
602
|
+
code: "custom",
|
|
603
|
+
path: ["rootNS", index],
|
|
604
|
+
message: "rootNS accepts only NS records at the zone apex"
|
|
605
|
+
});
|
|
606
|
+
});
|
|
607
|
+
}).meta({ id: "ConditionalDnsRecords" });
|
|
608
|
+
const DirectConnectionFlowTargetInputSchema = zod.z.strictObject({
|
|
609
|
+
domain: DomainNameSchema,
|
|
610
|
+
records: zod.z.array(DnsRecordSchema).min(1).max(100)
|
|
611
|
+
});
|
|
612
|
+
const ConditionalConnectionFlowTargetInputSchema = zod.z.strictObject({
|
|
613
|
+
domain: DomainNameSchema,
|
|
614
|
+
conditionalRecords: ConditionalDnsRecordsSchema
|
|
615
|
+
});
|
|
616
|
+
const ConnectionFlowTargetInputSchema = zod.z.union([DirectConnectionFlowTargetInputSchema, ConditionalConnectionFlowTargetInputSchema]).meta({ id: "ConnectionFlowTargetInput" });
|
|
617
|
+
const CreateConnectionFlowInputSchema = zod.z.strictObject({
|
|
618
|
+
applicationId: ApplicationIdSchema,
|
|
619
|
+
tenantId: TenantIdSchema,
|
|
620
|
+
existingRecordPolicy: ExistingRecordPolicySchema.default({
|
|
621
|
+
validateDmarc: false,
|
|
622
|
+
validateCAA: false
|
|
623
|
+
}),
|
|
624
|
+
targets: zod.z.array(ConnectionFlowTargetInputSchema).min(2).max(100).superRefine((targets, context) => {
|
|
625
|
+
const seen = /* @__PURE__ */ new Set();
|
|
626
|
+
targets.forEach((target, index) => {
|
|
627
|
+
if (seen.has(target.domain)) context.addIssue({
|
|
628
|
+
code: "custom",
|
|
629
|
+
path: [index, "domain"],
|
|
630
|
+
message: "Flow target domains must be unique"
|
|
631
|
+
});
|
|
632
|
+
seen.add(target.domain);
|
|
633
|
+
});
|
|
634
|
+
}),
|
|
635
|
+
commandId: CommandIdSchema
|
|
636
|
+
}).meta({ id: "CreateConnectionFlowInput" });
|
|
637
|
+
const ConnectionFlowTargetSchema = zod.z.strictObject({
|
|
638
|
+
domain: DomainNameSchema,
|
|
639
|
+
state: zod.z.enum(["pending", "provisioned"]),
|
|
640
|
+
connectionId: ConnectionIdSchema.optional()
|
|
641
|
+
}).superRefine((target, context) => {
|
|
642
|
+
if (target.state === "provisioned" !== (target.connectionId !== void 0)) context.addIssue({
|
|
643
|
+
code: "custom",
|
|
644
|
+
path: ["connectionId"],
|
|
645
|
+
message: target.state === "provisioned" ? "A provisioned target requires a connection id" : "A pending target cannot have a connection id"
|
|
646
|
+
});
|
|
647
|
+
}).meta({ id: "ConnectionFlowTarget" });
|
|
648
|
+
const ConnectionFlowSchema = zod.z.strictObject({
|
|
649
|
+
id: ConnectionFlowIdSchema,
|
|
650
|
+
applicationId: ApplicationIdSchema,
|
|
651
|
+
tenantId: TenantIdSchema,
|
|
652
|
+
state: zod.z.enum(["provisioning", "ready"]),
|
|
653
|
+
targets: zod.z.array(ConnectionFlowTargetSchema).min(2).max(100),
|
|
654
|
+
createdAt: IsoDateTimeSchema,
|
|
655
|
+
updatedAt: IsoDateTimeSchema
|
|
656
|
+
}).superRefine((flow, context) => {
|
|
657
|
+
const allProvisioned = flow.targets.every((target) => target.state === "provisioned");
|
|
658
|
+
if (flow.state === "ready" !== allProvisioned) context.addIssue({
|
|
659
|
+
code: "custom",
|
|
660
|
+
path: ["state"],
|
|
661
|
+
message: "Flow state must match target provisioning state"
|
|
662
|
+
});
|
|
663
|
+
}).meta({ id: "ConnectionFlow" });
|
|
664
|
+
const ConnectionFlowResponseSchema = zod.z.strictObject({ flow: ConnectionFlowSchema }).meta({ id: "ConnectionFlowResponse" });
|
|
665
|
+
//#endregion
|
|
666
|
+
//#region src/contracts/shared-flow.ts
|
|
667
|
+
const SharedFlowIdSchema = zod.z.string().min(1).max(200).brand();
|
|
668
|
+
const SharedFlowTokenSchema = zod.z.string().regex(/^[A-Za-z0-9_-]{43}$/).brand();
|
|
669
|
+
const CreateSharedFlowInputSchema = zod.z.strictObject({
|
|
670
|
+
connectionId: ConnectionIdSchema,
|
|
671
|
+
expiresAt: IsoDateTimeSchema
|
|
672
|
+
});
|
|
673
|
+
const CreateSharedFlowResponseSchema = zod.z.strictObject({
|
|
674
|
+
sharedFlowId: SharedFlowIdSchema,
|
|
675
|
+
token: SharedFlowTokenSchema,
|
|
676
|
+
expiresAt: IsoDateTimeSchema
|
|
677
|
+
});
|
|
678
|
+
const ResolveSharedFlowInputSchema = zod.z.strictObject({
|
|
679
|
+
token: SharedFlowTokenSchema,
|
|
680
|
+
origin: zod.z.string().url().max(2048)
|
|
681
|
+
});
|
|
682
|
+
const ResolveSharedFlowResponseSchema = zod.z.strictObject({
|
|
683
|
+
sharedFlowId: SharedFlowIdSchema,
|
|
684
|
+
connectionId: ConnectionIdSchema,
|
|
685
|
+
accessToken: zod.z.string().min(1).max(8192),
|
|
686
|
+
expiresAt: IsoDateTimeSchema
|
|
687
|
+
});
|
|
688
|
+
const SharedFlowUrlSchema = zod.z.string().url().max(4096);
|
|
689
|
+
const SharedFlowInvitationUrlSchema = SharedFlowUrlSchema.superRefine((value, context) => {
|
|
690
|
+
const url = new URL(value);
|
|
691
|
+
const secure = url.protocol === "https:";
|
|
692
|
+
const loopback = url.protocol === "http:" && isLoopbackHostname(url.hostname);
|
|
693
|
+
if (!secure && !loopback) context.addIssue({
|
|
694
|
+
code: "custom",
|
|
695
|
+
message: "Shared-flow invitation URLs must use HTTPS, except on loopback development hosts"
|
|
696
|
+
});
|
|
697
|
+
if (url.username !== "" || url.password !== "") context.addIssue({
|
|
698
|
+
code: "custom",
|
|
699
|
+
message: "Shared-flow invitation URLs cannot contain user information"
|
|
700
|
+
});
|
|
701
|
+
const fragment = new URLSearchParams(url.hash.slice(1));
|
|
702
|
+
const token = fragment.get("domain0");
|
|
703
|
+
if (fragment.size !== 1 || !SharedFlowTokenSchema.safeParse(token).success) context.addIssue({
|
|
704
|
+
code: "custom",
|
|
705
|
+
message: "Shared-flow invitation URLs require one valid domain0 capability in the fragment"
|
|
706
|
+
});
|
|
707
|
+
}).meta({ id: "SharedFlowInvitationUrl" });
|
|
708
|
+
const SharedFlowInvitationSchema = zod.z.object({
|
|
709
|
+
sharedFlowId: SharedFlowIdSchema,
|
|
710
|
+
url: SharedFlowInvitationUrlSchema,
|
|
711
|
+
expiresAt: IsoDateTimeSchema
|
|
712
|
+
}).strict().meta({ id: "SharedFlowInvitation" });
|
|
713
|
+
function isLoopbackHostname(hostname) {
|
|
714
|
+
const normalized = hostname.toLowerCase();
|
|
715
|
+
return normalized === "localhost" || normalized === "[::1]" || /^127(?:\.[0-9]{1,3}){3}$/.test(normalized);
|
|
716
|
+
}
|
|
717
|
+
//#endregion
|
|
718
|
+
//#region src/contracts/dkim-guidance.ts
|
|
719
|
+
const DkimSelectorSchema = zod.z.string().trim().toLowerCase().min(1).max(63).regex(/^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/).brand().meta({ id: "DkimSelector" });
|
|
720
|
+
const DkimGuidanceInputSchema = zod.z.object({ selectors: zod.z.array(DkimSelectorSchema).max(10).default([]) }).strict().superRefine((input, context) => {
|
|
721
|
+
if (new Set(input.selectors).size !== input.selectors.length) context.addIssue({
|
|
722
|
+
code: "custom",
|
|
723
|
+
path: ["selectors"],
|
|
724
|
+
message: "DKIM selectors must be unique"
|
|
725
|
+
});
|
|
726
|
+
}).meta({ id: "DkimGuidanceInput" });
|
|
727
|
+
const Domain0DkimGuidanceOptionsSchema = zod.z.object({
|
|
728
|
+
enableDkim: zod.z.boolean().default(false),
|
|
729
|
+
dkimSelectors: zod.z.array(DkimSelectorSchema).max(10).default([])
|
|
730
|
+
}).strict().superRefine((input, context) => {
|
|
731
|
+
if (new Set(input.dkimSelectors).size !== input.dkimSelectors.length) context.addIssue({
|
|
732
|
+
code: "custom",
|
|
733
|
+
path: ["dkimSelectors"],
|
|
734
|
+
message: "DKIM selectors must be unique"
|
|
735
|
+
});
|
|
736
|
+
}).meta({ id: "Domain0DkimGuidanceOptions" });
|
|
737
|
+
const GoogleWorkspaceEmailProviderSchema = zod.z.object({
|
|
738
|
+
id: zod.z.literal("google_workspace"),
|
|
739
|
+
name: zod.z.literal("Google Workspace"),
|
|
740
|
+
guideUrl: zod.z.literal("https://support.google.com/a/answer/174124")
|
|
741
|
+
}).strict();
|
|
742
|
+
const Microsoft365EmailProviderSchema = zod.z.object({
|
|
743
|
+
id: zod.z.literal("microsoft_365"),
|
|
744
|
+
name: zod.z.literal("Microsoft 365"),
|
|
745
|
+
guideUrl: zod.z.literal("https://learn.microsoft.com/en-us/defender-office-365/email-authentication-dkim-configure")
|
|
746
|
+
}).strict();
|
|
747
|
+
const ZohoMailEmailProviderSchema = zod.z.object({
|
|
748
|
+
id: zod.z.literal("zoho_mail"),
|
|
749
|
+
name: zod.z.literal("Zoho Mail"),
|
|
750
|
+
guideUrl: zod.z.literal("https://www.zoho.com/mail/help/adminconsole/dkim-configuration.html")
|
|
751
|
+
}).strict();
|
|
752
|
+
const EmailProviderSchema = zod.z.discriminatedUnion("id", [
|
|
753
|
+
GoogleWorkspaceEmailProviderSchema,
|
|
754
|
+
Microsoft365EmailProviderSchema,
|
|
755
|
+
ZohoMailEmailProviderSchema
|
|
756
|
+
]).meta({ id: "EmailProvider" });
|
|
757
|
+
const EmailProviderDetectionStatusSchema = zod.z.enum([
|
|
758
|
+
"detected",
|
|
759
|
+
"ambiguous",
|
|
760
|
+
"unknown"
|
|
761
|
+
]).meta({ id: "EmailProviderDetectionStatus" });
|
|
762
|
+
const DkimDnsRecordTypeSchema = zod.z.enum(["TXT", "CNAME"]).meta({ id: "DkimDnsRecordType" });
|
|
763
|
+
const DkimDnsEvidenceStatusSchema = zod.z.enum([
|
|
764
|
+
"record_observed",
|
|
765
|
+
"no_record_observed",
|
|
766
|
+
"not_checked",
|
|
767
|
+
"lookup_unavailable"
|
|
768
|
+
]).meta({ id: "DkimDnsEvidenceStatus" });
|
|
769
|
+
const DkimDnsRecordEvidenceSchema = zod.z.object({
|
|
770
|
+
selector: DkimSelectorSchema,
|
|
771
|
+
types: zod.z.array(DkimDnsRecordTypeSchema).min(1).max(2).refine((types) => new Set(types).size === types.length, "Observed DKIM DNS record types must be unique")
|
|
772
|
+
}).strict().meta({ id: "DkimDnsRecordEvidence" });
|
|
773
|
+
const DkimDnsEvidenceSchema = zod.z.object({
|
|
774
|
+
status: DkimDnsEvidenceStatusSchema,
|
|
775
|
+
selectorsChecked: zod.z.array(DkimSelectorSchema).max(12).refine((selectors) => new Set(selectors).size === selectors.length, "Checked DKIM selectors must be unique"),
|
|
776
|
+
records: zod.z.array(DkimDnsRecordEvidenceSchema).max(12)
|
|
777
|
+
}).strict().superRefine((evidence, context) => {
|
|
778
|
+
const checked = new Set(evidence.selectorsChecked);
|
|
779
|
+
if (evidence.records.some((record) => !checked.has(record.selector))) context.addIssue({
|
|
780
|
+
code: "custom",
|
|
781
|
+
path: ["records"],
|
|
782
|
+
message: "Observed records must belong to checked selectors"
|
|
783
|
+
});
|
|
784
|
+
if (evidence.status === "not_checked" && (evidence.selectorsChecked.length !== 0 || evidence.records.length !== 0)) context.addIssue({
|
|
785
|
+
code: "custom",
|
|
786
|
+
path: ["status"],
|
|
787
|
+
message: "Not-checked evidence cannot include selector observations"
|
|
788
|
+
});
|
|
789
|
+
if (evidence.status === "record_observed" && evidence.records.length === 0) context.addIssue({
|
|
790
|
+
code: "custom",
|
|
791
|
+
path: ["records"],
|
|
792
|
+
message: "Observed status requires at least one DNS record"
|
|
793
|
+
});
|
|
794
|
+
if (evidence.status !== "record_observed" && evidence.records.length !== 0) context.addIssue({
|
|
795
|
+
code: "custom",
|
|
796
|
+
path: ["records"],
|
|
797
|
+
message: "Only observed status can include DNS records"
|
|
798
|
+
});
|
|
799
|
+
if (["no_record_observed", "lookup_unavailable"].includes(evidence.status) && evidence.selectorsChecked.length === 0) context.addIssue({
|
|
800
|
+
code: "custom",
|
|
801
|
+
path: ["selectorsChecked"],
|
|
802
|
+
message: "A DNS lookup status requires at least one checked selector"
|
|
803
|
+
});
|
|
804
|
+
}).meta({ id: "DkimDnsEvidence" });
|
|
805
|
+
const DkimGuidanceResponseSchema = zod.z.object({
|
|
806
|
+
domain: DomainNameSchema,
|
|
807
|
+
detectionStatus: EmailProviderDetectionStatusSchema,
|
|
808
|
+
candidates: zod.z.array(EmailProviderSchema).max(3),
|
|
809
|
+
provider: EmailProviderSchema.optional(),
|
|
810
|
+
mxHosts: zod.z.array(DomainNameSchema).max(20).refine((hosts) => new Set(hosts).size === hosts.length, "MX hosts must be unique"),
|
|
811
|
+
dkimDns: DkimDnsEvidenceSchema,
|
|
812
|
+
observedAt: IsoDateTimeSchema
|
|
813
|
+
}).strict().superRefine((result, context) => {
|
|
814
|
+
const candidateIds = result.candidates.map((candidate) => candidate.id);
|
|
815
|
+
if (new Set(candidateIds).size !== candidateIds.length) context.addIssue({
|
|
816
|
+
code: "custom",
|
|
817
|
+
path: ["candidates"],
|
|
818
|
+
message: "Email provider candidates must be unique"
|
|
819
|
+
});
|
|
820
|
+
if (result.detectionStatus === "detected" && (result.candidates.length !== 1 || result.provider?.id !== result.candidates[0]?.id)) context.addIssue({
|
|
821
|
+
code: "custom",
|
|
822
|
+
path: ["provider"],
|
|
823
|
+
message: "Detected status requires exactly one matching provider"
|
|
824
|
+
});
|
|
825
|
+
if (result.detectionStatus === "ambiguous" && (result.candidates.length < 2 || result.provider !== void 0)) context.addIssue({
|
|
826
|
+
code: "custom",
|
|
827
|
+
path: ["candidates"],
|
|
828
|
+
message: "Ambiguous status requires multiple candidates and no resolved provider"
|
|
829
|
+
});
|
|
830
|
+
if (result.detectionStatus === "unknown" && (result.candidates.length !== 0 || result.provider !== void 0)) context.addIssue({
|
|
831
|
+
code: "custom",
|
|
832
|
+
path: ["candidates"],
|
|
833
|
+
message: "Unknown status cannot include an email provider"
|
|
834
|
+
});
|
|
835
|
+
}).meta({ id: "DkimGuidanceResponse" });
|
|
836
|
+
//#endregion
|
|
837
|
+
//#region src/contracts/connect-events.ts
|
|
838
|
+
const Domain0ConnectStepSchema = zod.z.union([zod.z.enum([
|
|
839
|
+
"loading",
|
|
840
|
+
"provider_detection",
|
|
841
|
+
"provider_selection",
|
|
842
|
+
"cancellation_confirmation"
|
|
843
|
+
]), ConnectionStateSchema]).meta({ id: "Domain0ConnectStep" });
|
|
844
|
+
const Domain0ConnectCloseReasonSchema = zod.z.enum([
|
|
845
|
+
"close_button",
|
|
846
|
+
"escape",
|
|
847
|
+
"programmatic",
|
|
848
|
+
"destroyed",
|
|
849
|
+
"success"
|
|
850
|
+
]).meta({ id: "Domain0ConnectCloseReason" });
|
|
851
|
+
const ActiveConnectionSchema = ConnectionSchema.and(zod.z.object({ state: zod.z.literal("active") }));
|
|
852
|
+
const ManualConnectionSchema = ConnectionSchema.and(zod.z.object({ state: zod.z.literal("manual_required") }));
|
|
853
|
+
const Domain0ConnectSuccessEventSchema = zod.z.object({
|
|
854
|
+
type: zod.z.literal("success"),
|
|
855
|
+
connection: ActiveConnectionSchema
|
|
856
|
+
}).strict().meta({ id: "Domain0ConnectSuccessEvent" });
|
|
857
|
+
const Domain0ConnectCloseEventSchema = zod.z.object({
|
|
858
|
+
type: zod.z.literal("close"),
|
|
859
|
+
reason: Domain0ConnectCloseReasonSchema,
|
|
860
|
+
connection: ConnectionSchema.optional(),
|
|
861
|
+
step: Domain0ConnectStepSchema.optional()
|
|
862
|
+
}).strict().meta({ id: "Domain0ConnectCloseEvent" });
|
|
863
|
+
const Domain0ConnectStepChangeEventSchema = zod.z.object({
|
|
864
|
+
type: zod.z.literal("step_change"),
|
|
865
|
+
step: Domain0ConnectStepSchema,
|
|
866
|
+
previousStep: Domain0ConnectStepSchema.optional(),
|
|
867
|
+
connection: ConnectionSchema.optional()
|
|
868
|
+
}).strict().meta({ id: "Domain0ConnectStepChangeEvent" });
|
|
869
|
+
const Domain0ManualSetupDocumentationClickEventSchema = zod.z.object({
|
|
870
|
+
type: zod.z.literal("manual_setup_documentation_click"),
|
|
871
|
+
connection: ManualConnectionSchema
|
|
872
|
+
}).strict().meta({ id: "Domain0ManualSetupDocumentationClickEvent" });
|
|
873
|
+
const Domain0RequestCloseEventSchema = zod.z.object({
|
|
874
|
+
type: zod.z.literal("request_close"),
|
|
875
|
+
connection: ConnectionSchema.optional(),
|
|
876
|
+
step: Domain0ConnectStepSchema
|
|
877
|
+
}).strict().meta({ id: "Domain0RequestCloseEvent" });
|
|
878
|
+
const Domain0SharedFlowSentEventSchema = zod.z.object({
|
|
879
|
+
type: zod.z.literal("shared_flow_sent"),
|
|
880
|
+
connection: ManualConnectionSchema,
|
|
881
|
+
sharedFlowId: SharedFlowIdSchema,
|
|
882
|
+
url: SharedFlowInvitationUrlSchema,
|
|
883
|
+
expiresAt: IsoDateTimeSchema
|
|
884
|
+
}).strict().meta({ id: "Domain0SharedFlowSentEvent" });
|
|
885
|
+
const Domain0DkimSetupDocumentationClickEventSchema = zod.z.object({
|
|
886
|
+
type: zod.z.literal("dkim_setup_documentation_click"),
|
|
887
|
+
connection: ActiveConnectionSchema,
|
|
888
|
+
emailProvider: EmailProviderSchema
|
|
889
|
+
}).strict().meta({ id: "Domain0DkimSetupDocumentationClickEvent" });
|
|
890
|
+
const Domain0ConnectEventSchema = zod.z.discriminatedUnion("type", [
|
|
891
|
+
Domain0ConnectSuccessEventSchema,
|
|
892
|
+
Domain0ConnectCloseEventSchema,
|
|
893
|
+
Domain0ConnectStepChangeEventSchema,
|
|
894
|
+
Domain0ManualSetupDocumentationClickEventSchema,
|
|
895
|
+
Domain0RequestCloseEventSchema,
|
|
896
|
+
Domain0SharedFlowSentEventSchema,
|
|
897
|
+
Domain0DkimSetupDocumentationClickEventSchema
|
|
898
|
+
]).meta({ id: "Domain0ConnectEvent" });
|
|
899
|
+
//#endregion
|
|
900
|
+
//#region src/contracts/setup-policy.ts
|
|
901
|
+
const Domain0SetupPolicySchema = zod.z.object({ mode: zod.z.enum(["automatic", "manual"]).default("automatic") }).strict();
|
|
902
|
+
//#endregion
|
|
903
|
+
//#region src/contracts/white-label.ts
|
|
904
|
+
const opaqueHexColorPattern = /^#[0-9A-F]{6}$/;
|
|
905
|
+
const backdropHexColorPattern = /^#[0-9A-F]{8}$/;
|
|
906
|
+
const OpaqueHexColorSchema = zod.z.string().trim().toUpperCase().regex(opaqueHexColorPattern, "Expected an opaque #RRGGBB color");
|
|
907
|
+
const BackdropHexColorSchema = zod.z.string().trim().toUpperCase().regex(backdropHexColorPattern, "Expected a #RRGGBBAA backdrop color");
|
|
908
|
+
const WhiteLabelTextSchema = zod.z.string().trim().min(1).max(500).refine((value) => !/[<>\u0000-\u001F\u007F]/.test(value), "White-label copy must be plain text without markup or control characters");
|
|
909
|
+
const Domain0ApplicationNameSchema = WhiteLabelTextSchema.max(120).meta({ id: "Domain0ApplicationName" });
|
|
910
|
+
const Domain0WhiteLabelLogoUrlSchema = zod.z.string().trim().min(1).max(2048).refine((value) => {
|
|
911
|
+
try {
|
|
912
|
+
const parsed = new URL(value);
|
|
913
|
+
return parsed.protocol === "https:" && parsed.username === "" && parsed.password === "";
|
|
914
|
+
} catch {
|
|
915
|
+
return false;
|
|
916
|
+
}
|
|
917
|
+
}, "Logo must be an HTTPS URL without embedded credentials").meta({ id: "Domain0WhiteLabelLogoUrl" });
|
|
918
|
+
function copyWithPlaceholders(...allowed) {
|
|
919
|
+
return WhiteLabelTextSchema.refine((value) => {
|
|
920
|
+
const withoutAllowedPlaceholders = allowed.reduce((copy, placeholder) => copy.replaceAll(placeholder, ""), value);
|
|
921
|
+
return !/[{}]/.test(withoutAllowedPlaceholders);
|
|
922
|
+
}, `Only these placeholders are allowed: ${allowed.join(", ") || "none"}`);
|
|
923
|
+
}
|
|
924
|
+
function localizedCopy(message) {
|
|
925
|
+
const localized = zod.z.object({
|
|
926
|
+
en: message,
|
|
927
|
+
es: message.optional(),
|
|
928
|
+
pt: message.optional(),
|
|
929
|
+
"pt-br": message.optional(),
|
|
930
|
+
"pt-pt": message.optional(),
|
|
931
|
+
fr: message.optional(),
|
|
932
|
+
it: message.optional(),
|
|
933
|
+
de: message.optional(),
|
|
934
|
+
nl: message.optional(),
|
|
935
|
+
pl: message.optional(),
|
|
936
|
+
tr: message.optional(),
|
|
937
|
+
ja: message.optional(),
|
|
938
|
+
da: message.optional(),
|
|
939
|
+
sv: message.optional()
|
|
940
|
+
}).strict();
|
|
941
|
+
return zod.z.union([message.transform((en) => ({ en })), localized]);
|
|
942
|
+
}
|
|
943
|
+
function colorContrastRatio(foreground, background) {
|
|
944
|
+
return parsedColorContrastRatio(OpaqueHexColorSchema.parse(foreground), OpaqueHexColorSchema.parse(background));
|
|
945
|
+
}
|
|
946
|
+
function parsedColorContrastRatio(foreground, background) {
|
|
947
|
+
const foregroundLuminance = relativeLuminance(foreground);
|
|
948
|
+
const backgroundLuminance = relativeLuminance(background);
|
|
949
|
+
const lighter = Math.max(foregroundLuminance, backgroundLuminance);
|
|
950
|
+
const darker = Math.min(foregroundLuminance, backgroundLuminance);
|
|
951
|
+
return (lighter + .05) / (darker + .05);
|
|
952
|
+
}
|
|
953
|
+
function isSafeFontFamilyStack(value) {
|
|
954
|
+
const families = value.split(",");
|
|
955
|
+
return families.length <= 10 && families.every((family) => {
|
|
956
|
+
const trimmed = family.trim();
|
|
957
|
+
if (trimmed.length === 0) return false;
|
|
958
|
+
const quote = trimmed.at(0);
|
|
959
|
+
if (quote === "\"" || quote === "'") return trimmed.at(-1) === quote && /^[A-Za-z0-9 -]+$/.test(trimmed.slice(1, -1));
|
|
960
|
+
return /^-?[A-Za-z][A-Za-z0-9 -]*$/.test(trimmed);
|
|
961
|
+
});
|
|
962
|
+
}
|
|
963
|
+
function relativeLuminance(color) {
|
|
964
|
+
const [red, green, blue] = [
|
|
965
|
+
1,
|
|
966
|
+
3,
|
|
967
|
+
5
|
|
968
|
+
].map((start) => Number.parseInt(color.slice(start, start + 2), 16) / 255).map((channel) => channel <= .04045 ? channel / 12.92 : ((channel + .055) / 1.055) ** 2.4);
|
|
969
|
+
return .2126 * red + .7152 * green + .0722 * blue;
|
|
970
|
+
}
|
|
971
|
+
const contrastPairs = [
|
|
972
|
+
{
|
|
973
|
+
foreground: "text",
|
|
974
|
+
background: "surface",
|
|
975
|
+
minimum: 4.5,
|
|
976
|
+
purpose: "body text"
|
|
977
|
+
},
|
|
978
|
+
{
|
|
979
|
+
foreground: "mutedText",
|
|
980
|
+
background: "surface",
|
|
981
|
+
minimum: 4.5,
|
|
982
|
+
purpose: "secondary text"
|
|
983
|
+
},
|
|
984
|
+
{
|
|
985
|
+
foreground: "onPrimary",
|
|
986
|
+
background: "primary",
|
|
987
|
+
minimum: 4.5,
|
|
988
|
+
purpose: "primary control text"
|
|
989
|
+
},
|
|
990
|
+
{
|
|
991
|
+
foreground: "danger",
|
|
992
|
+
background: "surface",
|
|
993
|
+
minimum: 4.5,
|
|
994
|
+
purpose: "error text"
|
|
995
|
+
},
|
|
996
|
+
{
|
|
997
|
+
foreground: "onDanger",
|
|
998
|
+
background: "danger",
|
|
999
|
+
minimum: 4.5,
|
|
1000
|
+
purpose: "danger control text"
|
|
1001
|
+
},
|
|
1002
|
+
{
|
|
1003
|
+
foreground: "link",
|
|
1004
|
+
background: "surface",
|
|
1005
|
+
minimum: 4.5,
|
|
1006
|
+
purpose: "link text"
|
|
1007
|
+
},
|
|
1008
|
+
{
|
|
1009
|
+
foreground: "border",
|
|
1010
|
+
background: "surface",
|
|
1011
|
+
minimum: 3,
|
|
1012
|
+
purpose: "control boundaries"
|
|
1013
|
+
},
|
|
1014
|
+
{
|
|
1015
|
+
foreground: "primary",
|
|
1016
|
+
background: "surface",
|
|
1017
|
+
minimum: 3,
|
|
1018
|
+
purpose: "primary control boundaries"
|
|
1019
|
+
},
|
|
1020
|
+
{
|
|
1021
|
+
foreground: "focus",
|
|
1022
|
+
background: "surface",
|
|
1023
|
+
minimum: 3,
|
|
1024
|
+
purpose: "focus indicators"
|
|
1025
|
+
}
|
|
1026
|
+
];
|
|
1027
|
+
const lightPaletteDefaults = {
|
|
1028
|
+
surface: "#FFFFFF",
|
|
1029
|
+
text: "#0A0A0A",
|
|
1030
|
+
mutedText: "#6B6B6B",
|
|
1031
|
+
border: "#73737C",
|
|
1032
|
+
primary: "#0A0A0A",
|
|
1033
|
+
onPrimary: "#FFFFFF",
|
|
1034
|
+
link: "#2B21FF",
|
|
1035
|
+
focus: "#2B21FF",
|
|
1036
|
+
danger: "#B3261E",
|
|
1037
|
+
onDanger: "#FFFFFF"
|
|
1038
|
+
};
|
|
1039
|
+
const darkPaletteDefaults = {
|
|
1040
|
+
surface: "#111827",
|
|
1041
|
+
text: "#F9FAFB",
|
|
1042
|
+
mutedText: "#D1D5DB",
|
|
1043
|
+
border: "#D1D5DB",
|
|
1044
|
+
primary: "#BFDBFE",
|
|
1045
|
+
onPrimary: "#172554",
|
|
1046
|
+
link: "#BFDBFE",
|
|
1047
|
+
focus: "#FBBF24",
|
|
1048
|
+
danger: "#FCA5A5",
|
|
1049
|
+
onDanger: "#450A0A"
|
|
1050
|
+
};
|
|
1051
|
+
function themePaletteSchema(defaults) {
|
|
1052
|
+
return zod.z.object({
|
|
1053
|
+
surface: OpaqueHexColorSchema.default(defaults.surface),
|
|
1054
|
+
text: OpaqueHexColorSchema.default(defaults.text),
|
|
1055
|
+
mutedText: OpaqueHexColorSchema.default(defaults.mutedText),
|
|
1056
|
+
border: OpaqueHexColorSchema.default(defaults.border),
|
|
1057
|
+
primary: OpaqueHexColorSchema.default(defaults.primary),
|
|
1058
|
+
onPrimary: OpaqueHexColorSchema.default(defaults.onPrimary),
|
|
1059
|
+
link: OpaqueHexColorSchema.default(defaults.link),
|
|
1060
|
+
focus: OpaqueHexColorSchema.default(defaults.focus),
|
|
1061
|
+
danger: OpaqueHexColorSchema.default(defaults.danger),
|
|
1062
|
+
onDanger: OpaqueHexColorSchema.default(defaults.onDanger)
|
|
1063
|
+
}).strict().superRefine((palette, context) => {
|
|
1064
|
+
for (const pair of contrastPairs) {
|
|
1065
|
+
const ratio = parsedColorContrastRatio(palette[pair.foreground], palette[pair.background]);
|
|
1066
|
+
if (ratio < pair.minimum) context.addIssue({
|
|
1067
|
+
code: "custom",
|
|
1068
|
+
path: [pair.foreground],
|
|
1069
|
+
message: `${pair.purpose} requires ${pair.minimum}:1 contrast against ${pair.background}; received ${ratio.toFixed(2)}:1`
|
|
1070
|
+
});
|
|
1071
|
+
}
|
|
1072
|
+
});
|
|
1073
|
+
}
|
|
1074
|
+
const LightThemePaletteSchema = themePaletteSchema(lightPaletteDefaults);
|
|
1075
|
+
const DarkThemePaletteSchema = themePaletteSchema(darkPaletteDefaults);
|
|
1076
|
+
const Domain0WhiteLabelThemeSchema = zod.z.object({
|
|
1077
|
+
colorMode: zod.z.enum([
|
|
1078
|
+
"light",
|
|
1079
|
+
"dark",
|
|
1080
|
+
"system"
|
|
1081
|
+
]).default("system"),
|
|
1082
|
+
light: LightThemePaletteSchema.prefault({}),
|
|
1083
|
+
dark: DarkThemePaletteSchema.prefault({}),
|
|
1084
|
+
backdrop: BackdropHexColorSchema.default("#111827B8"),
|
|
1085
|
+
widthPx: zod.z.number().int().min(320).max(960).default(672),
|
|
1086
|
+
dialogRadiusPx: zod.z.number().int().min(0).max(48).default(20),
|
|
1087
|
+
buttonRadiusPx: zod.z.number().int().min(0).max(999).default(999),
|
|
1088
|
+
inputRadiusPx: zod.z.number().int().min(0).max(32).default(12),
|
|
1089
|
+
fontFamily: zod.z.string().trim().min(1).max(200).refine(isSafeFontFamilyStack, "Use a comma-separated system font stack without URLs or CSS syntax").default("\"PP Neue Montreal\", \"Inter Tight\", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif"),
|
|
1090
|
+
fontWeight: zod.z.number().int().min(100).max(900).multipleOf(50).default(400),
|
|
1091
|
+
boldFontWeight: zod.z.number().int().min(100).max(900).multipleOf(50).default(500)
|
|
1092
|
+
}).strict().superRefine((theme, context) => {
|
|
1093
|
+
if (theme.boldFontWeight < theme.fontWeight) context.addIssue({
|
|
1094
|
+
code: "custom",
|
|
1095
|
+
path: ["boldFontWeight"],
|
|
1096
|
+
message: "Bold font weight must be at least the normal font weight"
|
|
1097
|
+
});
|
|
1098
|
+
});
|
|
1099
|
+
const Domain0WhiteLabelCopySchema = zod.z.object({
|
|
1100
|
+
initialSubtitle: localizedCopy(copyWithPlaceholders()).optional(),
|
|
1101
|
+
providerLoginMessage: localizedCopy(copyWithPlaceholders("{PROVIDER}")).optional(),
|
|
1102
|
+
successTitle: localizedCopy(copyWithPlaceholders("{DOMAIN}")).optional(),
|
|
1103
|
+
successDescription: localizedCopy(copyWithPlaceholders("{DOMAIN}")).optional(),
|
|
1104
|
+
successButton: localizedCopy(copyWithPlaceholders()).optional(),
|
|
1105
|
+
manuallyScreen: zod.z.object({
|
|
1106
|
+
disableManualSetupDocumentationLink: zod.z.boolean().default(false),
|
|
1107
|
+
stepByStepGuide: localizedCopy(copyWithPlaceholders()).optional()
|
|
1108
|
+
}).strict().prefault({})
|
|
1109
|
+
}).strict();
|
|
1110
|
+
const DisabledControlSchema = zod.z.object({ disable: zod.z.boolean().default(false) }).strict();
|
|
1111
|
+
const Domain0WhiteLabelCustomPropertiesSchema = zod.z.object({
|
|
1112
|
+
manualConfiguration: zod.z.object({ disableScreen: zod.z.boolean().default(false) }).strict().prefault({}),
|
|
1113
|
+
providerLogin: zod.z.object({
|
|
1114
|
+
forwardLink: DisabledControlSchema.prefault({}),
|
|
1115
|
+
gotoManualLink: DisabledControlSchema.prefault({})
|
|
1116
|
+
}).strict().prefault({})
|
|
1117
|
+
}).strict();
|
|
1118
|
+
const Domain0WhiteLabelSchema = zod.z.object({
|
|
1119
|
+
theme: Domain0WhiteLabelThemeSchema.prefault({}),
|
|
1120
|
+
copy: Domain0WhiteLabelCopySchema.prefault({}),
|
|
1121
|
+
logo: Domain0WhiteLabelLogoUrlSchema.optional(),
|
|
1122
|
+
logoBackgroundColor: OpaqueHexColorSchema.optional(),
|
|
1123
|
+
removeLogoBorder: zod.z.boolean().default(false),
|
|
1124
|
+
hideCompanyLogo: zod.z.boolean().default(false),
|
|
1125
|
+
hideCompanyName: zod.z.boolean().default(false),
|
|
1126
|
+
removeShareLogin: zod.z.boolean().default(false),
|
|
1127
|
+
skipCongratulationsScreen: zod.z.boolean().default(false),
|
|
1128
|
+
customProperties: Domain0WhiteLabelCustomPropertiesSchema.prefault({})
|
|
1129
|
+
}).strict();
|
|
1130
|
+
//#endregion
|
|
1131
|
+
//#region src/contracts/manual-setup-documentation.ts
|
|
1132
|
+
const ManualSetupDocumentationUrlSchema = zod.z.url({ protocol: /^https$/ }).max(2048).refine((value) => {
|
|
1133
|
+
const url = new URL(value);
|
|
1134
|
+
return url.username === "" && url.password === "";
|
|
1135
|
+
}, "Manual setup documentation URL must not contain credentials").brand().meta({ id: "ManualSetupDocumentationUrl" });
|
|
1136
|
+
//#endregion
|
|
1137
|
+
//#region src/contracts/connect-configuration.ts
|
|
1138
|
+
const Domain0ConnectConfigurationSchema = zod.z.object({
|
|
1139
|
+
applicationName: Domain0ApplicationNameSchema.optional(),
|
|
1140
|
+
whiteLabel: Domain0WhiteLabelSchema.prefault({}),
|
|
1141
|
+
setupPolicy: Domain0SetupPolicySchema.prefault({}),
|
|
1142
|
+
dkim: Domain0DkimGuidanceOptionsSchema.prefault({}),
|
|
1143
|
+
manualSetupDocumentation: ManualSetupDocumentationUrlSchema.optional()
|
|
1144
|
+
}).strict().superRefine((configuration, context) => {
|
|
1145
|
+
const { customProperties } = configuration.whiteLabel;
|
|
1146
|
+
const forcedManual = configuration.setupPolicy.mode === "manual";
|
|
1147
|
+
if (forcedManual && customProperties.manualConfiguration.disableScreen) context.addIssue({
|
|
1148
|
+
code: "custom",
|
|
1149
|
+
path: [
|
|
1150
|
+
"whiteLabel",
|
|
1151
|
+
"customProperties",
|
|
1152
|
+
"manualConfiguration",
|
|
1153
|
+
"disableScreen"
|
|
1154
|
+
],
|
|
1155
|
+
message: "Forced manual setup requires the manual configuration screen"
|
|
1156
|
+
});
|
|
1157
|
+
if (forcedManual && customProperties.providerLogin.gotoManualLink.disable) context.addIssue({
|
|
1158
|
+
code: "custom",
|
|
1159
|
+
path: [
|
|
1160
|
+
"whiteLabel",
|
|
1161
|
+
"customProperties",
|
|
1162
|
+
"providerLogin",
|
|
1163
|
+
"gotoManualLink",
|
|
1164
|
+
"disable"
|
|
1165
|
+
],
|
|
1166
|
+
message: "Forced manual setup cannot disable entry into manual configuration"
|
|
1167
|
+
});
|
|
1168
|
+
if (configuration.dkim.enableDkim && configuration.whiteLabel.skipCongratulationsScreen) context.addIssue({
|
|
1169
|
+
code: "custom",
|
|
1170
|
+
path: ["whiteLabel", "skipCongratulationsScreen"],
|
|
1171
|
+
message: "DKIM guidance requires the success screen"
|
|
1172
|
+
});
|
|
1173
|
+
}).meta({ id: "Domain0ConnectConfiguration" });
|
|
1174
|
+
//#endregion
|
|
1175
|
+
//#region src/contracts/conformance.ts
|
|
1176
|
+
const CompiledProviderIdSchema = zod.z.enum([
|
|
1177
|
+
"alibaba-cloud",
|
|
1178
|
+
"all-inkl",
|
|
1179
|
+
"amazon-route-53",
|
|
1180
|
+
"cloudflare",
|
|
1181
|
+
"cloudns",
|
|
1182
|
+
"digitalocean",
|
|
1183
|
+
"dnsimple",
|
|
1184
|
+
"dreamhost",
|
|
1185
|
+
"dynadot",
|
|
1186
|
+
"easydns",
|
|
1187
|
+
"gandi",
|
|
1188
|
+
"godaddy",
|
|
1189
|
+
"hetzner",
|
|
1190
|
+
"hostgator",
|
|
1191
|
+
"hostinger",
|
|
1192
|
+
"hosting-com",
|
|
1193
|
+
"inmotion-hosting",
|
|
1194
|
+
"ionos",
|
|
1195
|
+
"name-com",
|
|
1196
|
+
"namecheap",
|
|
1197
|
+
"namesilo",
|
|
1198
|
+
"netlify",
|
|
1199
|
+
"o2switch",
|
|
1200
|
+
"openprovider",
|
|
1201
|
+
"opensrs",
|
|
1202
|
+
"ovh",
|
|
1203
|
+
"porkbun",
|
|
1204
|
+
"simply",
|
|
1205
|
+
"spaceship",
|
|
1206
|
+
"transip",
|
|
1207
|
+
"united-domains",
|
|
1208
|
+
"vercel",
|
|
1209
|
+
"wix"
|
|
1210
|
+
]).meta({ id: "CompiledProviderId" });
|
|
1211
|
+
const ProviderConformanceCheckNameSchema = zod.z.enum([
|
|
1212
|
+
"authenticated_snapshot",
|
|
1213
|
+
"reserved_name_available",
|
|
1214
|
+
"create_sibling",
|
|
1215
|
+
"create_target",
|
|
1216
|
+
"rrset_sibling_preservation",
|
|
1217
|
+
"idempotent_replay",
|
|
1218
|
+
"replace_target",
|
|
1219
|
+
"authoritative_propagation",
|
|
1220
|
+
"delete_target",
|
|
1221
|
+
"scoped_cleanup"
|
|
1222
|
+
]).meta({ id: "ProviderConformanceCheckName" });
|
|
1223
|
+
const expectedProviderConformanceChecks = [
|
|
1224
|
+
"authenticated_snapshot",
|
|
1225
|
+
"reserved_name_available",
|
|
1226
|
+
"create_sibling",
|
|
1227
|
+
"create_target",
|
|
1228
|
+
"rrset_sibling_preservation",
|
|
1229
|
+
"idempotent_replay",
|
|
1230
|
+
"replace_target",
|
|
1231
|
+
"authoritative_propagation",
|
|
1232
|
+
"delete_target",
|
|
1233
|
+
"scoped_cleanup"
|
|
1234
|
+
];
|
|
1235
|
+
const ProviderConformanceCheckSchema = zod.z.object({ name: ProviderConformanceCheckNameSchema }).strict();
|
|
1236
|
+
const SourceRevisionSchema = zod.z.union([zod.z.literal("unknown"), zod.z.string().regex(/^[a-f0-9]{40,64}$/)]);
|
|
1237
|
+
const ProviderConformanceEvidenceSchema = zod.z.object({
|
|
1238
|
+
schemaVersion: zod.z.literal(1),
|
|
1239
|
+
providerId: CompiledProviderIdSchema,
|
|
1240
|
+
adapterVersion: zod.z.string().min(1).max(200).refine(isTrimmedSingleLine),
|
|
1241
|
+
sourceRevision: SourceRevisionSchema,
|
|
1242
|
+
sourceModified: zod.z.boolean(),
|
|
1243
|
+
binarySha256: zod.z.string().regex(/^[a-f0-9]{64}$/),
|
|
1244
|
+
zone: DomainNameSchema,
|
|
1245
|
+
runId: zod.z.string().regex(/^[a-f0-9]{16,64}$/),
|
|
1246
|
+
host: zod.z.string(),
|
|
1247
|
+
startedAt: IsoDateTimeSchema,
|
|
1248
|
+
completedAt: IsoDateTimeSchema,
|
|
1249
|
+
passed: zod.z.boolean(),
|
|
1250
|
+
checks: zod.z.array(ProviderConformanceCheckSchema).max(expectedProviderConformanceChecks.length),
|
|
1251
|
+
cleanupAttempted: zod.z.boolean(),
|
|
1252
|
+
cleanupCompleted: zod.z.boolean()
|
|
1253
|
+
}).strict().superRefine((evidence, context) => {
|
|
1254
|
+
if (evidence.host !== `_domain0-conformance-${evidence.runId}`) context.addIssue({
|
|
1255
|
+
code: "custom",
|
|
1256
|
+
path: ["host"],
|
|
1257
|
+
message: "Conformance hostname must be derived from the random run ID"
|
|
1258
|
+
});
|
|
1259
|
+
if (Date.parse(evidence.completedAt) < Date.parse(evidence.startedAt)) context.addIssue({
|
|
1260
|
+
code: "custom",
|
|
1261
|
+
path: ["completedAt"],
|
|
1262
|
+
message: "Conformance completion must not precede its start"
|
|
1263
|
+
});
|
|
1264
|
+
const names = evidence.checks.map((check) => check.name);
|
|
1265
|
+
if (!validCheckSequence(names)) context.addIssue({
|
|
1266
|
+
code: "custom",
|
|
1267
|
+
path: ["checks"],
|
|
1268
|
+
message: "Conformance checks must be an ordered prefix followed optionally by scoped cleanup"
|
|
1269
|
+
});
|
|
1270
|
+
const cleanupCheck = names.includes("scoped_cleanup");
|
|
1271
|
+
if (evidence.cleanupCompleted && !evidence.cleanupAttempted || cleanupCheck !== evidence.cleanupCompleted) context.addIssue({
|
|
1272
|
+
code: "custom",
|
|
1273
|
+
path: ["cleanupCompleted"],
|
|
1274
|
+
message: "Completed cleanup requires an attempted cleanup and the scoped-cleanup check"
|
|
1275
|
+
});
|
|
1276
|
+
}).meta({ id: "ProviderConformanceEvidence" });
|
|
1277
|
+
const VerifiedProviderConformanceEvidenceSchema = ProviderConformanceEvidenceSchema.refine((evidence) => evidence.passed && !evidence.sourceModified && evidence.sourceRevision !== "unknown" && evidence.cleanupAttempted && evidence.cleanupCompleted && evidence.checks.every((check, index) => check.name === expectedProviderConformanceChecks[index]) && evidence.checks.length === expectedProviderConformanceChecks.length, "Verified live-suite evidence requires clean traceable source, every ordered check, and completed cleanup").meta({ id: "VerifiedProviderConformanceEvidence" });
|
|
1278
|
+
function validCheckSequence(checks) {
|
|
1279
|
+
let position = 0;
|
|
1280
|
+
let cleanupSeen = false;
|
|
1281
|
+
for (const check of checks) {
|
|
1282
|
+
if (check === "scoped_cleanup") {
|
|
1283
|
+
if (cleanupSeen) return false;
|
|
1284
|
+
cleanupSeen = true;
|
|
1285
|
+
continue;
|
|
1286
|
+
}
|
|
1287
|
+
if (cleanupSeen || check !== expectedProviderConformanceChecks[position]) return false;
|
|
1288
|
+
position += 1;
|
|
1289
|
+
}
|
|
1290
|
+
return true;
|
|
1291
|
+
}
|
|
1292
|
+
function isTrimmedSingleLine(value) {
|
|
1293
|
+
return value.trim() === value && !/[\u0000\r\n]/.test(value);
|
|
1294
|
+
}
|
|
1295
|
+
//#endregion
|
|
1296
|
+
//#region src/contracts/domain-check.ts
|
|
1297
|
+
const DomainDelegationStatusSchema = zod.z.enum([
|
|
1298
|
+
"delegated",
|
|
1299
|
+
"undelegated",
|
|
1300
|
+
"unknown"
|
|
1301
|
+
]).meta({ id: "DomainDelegationStatus" });
|
|
1302
|
+
const DomainSetupSupportSchema = zod.z.enum([
|
|
1303
|
+
"automatic",
|
|
1304
|
+
"manual",
|
|
1305
|
+
"unavailable",
|
|
1306
|
+
"ambiguous",
|
|
1307
|
+
"unknown"
|
|
1308
|
+
]).meta({ id: "DomainSetupSupport" });
|
|
1309
|
+
const RecordConflictReasonSchema = zod.z.enum([
|
|
1310
|
+
"same_name_and_type",
|
|
1311
|
+
"cname_collision",
|
|
1312
|
+
"mx_set_collision",
|
|
1313
|
+
"spf_collision",
|
|
1314
|
+
"ns_delegation"
|
|
1315
|
+
]).meta({ id: "RecordConflictReason" });
|
|
1316
|
+
const CheckDomainInputSchema = zod.z.object({
|
|
1317
|
+
domain: DomainNameSchema,
|
|
1318
|
+
records: zod.z.array(ResolvedDnsRecordSchema).max(100).default([]),
|
|
1319
|
+
checkConflicts: zod.z.boolean().default(false)
|
|
1320
|
+
}).strict().superRefine((input, context) => {
|
|
1321
|
+
if (input.checkConflicts && input.records.length === 0) context.addIssue({
|
|
1322
|
+
code: "custom",
|
|
1323
|
+
path: ["records"],
|
|
1324
|
+
message: "Record conflict analysis requires at least one requested record"
|
|
1325
|
+
});
|
|
1326
|
+
}).meta({ id: "CheckDomainInput" });
|
|
1327
|
+
const DomainProviderCandidateSchema = zod.z.object({
|
|
1328
|
+
provider: ProviderSchema,
|
|
1329
|
+
confidence: zod.z.number().min(0).max(1),
|
|
1330
|
+
exact: zod.z.boolean()
|
|
1331
|
+
}).strict().meta({ id: "DomainProviderCandidate" });
|
|
1332
|
+
const DomainRecordConflictSchema = zod.z.object({
|
|
1333
|
+
requested: ResolvedDnsRecordSchema,
|
|
1334
|
+
existing: zod.z.array(ResolvedDnsRecordSchema).max(1e3),
|
|
1335
|
+
reasons: zod.z.array(RecordConflictReasonSchema).min(1).max(5).refine((reasons) => new Set(reasons).size === reasons.length, "Conflict reasons must be unique")
|
|
1336
|
+
}).strict().meta({ id: "DomainRecordConflict" });
|
|
1337
|
+
const ConflictAnalysisSchema = zod.z.object({
|
|
1338
|
+
status: zod.z.enum([
|
|
1339
|
+
"not_requested",
|
|
1340
|
+
"complete",
|
|
1341
|
+
"partial"
|
|
1342
|
+
]),
|
|
1343
|
+
authoritativeServers: zod.z.number().int().min(0).max(16),
|
|
1344
|
+
respondingServers: zod.z.number().int().min(0).max(16),
|
|
1345
|
+
conflicts: zod.z.array(DomainRecordConflictSchema).max(100)
|
|
1346
|
+
}).strict().superRefine((analysis, context) => {
|
|
1347
|
+
if (analysis.respondingServers > analysis.authoritativeServers) context.addIssue({
|
|
1348
|
+
code: "custom",
|
|
1349
|
+
path: ["respondingServers"],
|
|
1350
|
+
message: "Responding nameservers cannot exceed authoritative nameservers"
|
|
1351
|
+
});
|
|
1352
|
+
if (analysis.status === "complete" && analysis.respondingServers !== analysis.authoritativeServers) context.addIssue({
|
|
1353
|
+
code: "custom",
|
|
1354
|
+
path: ["status"],
|
|
1355
|
+
message: "Complete analysis requires every authoritative nameserver to respond"
|
|
1356
|
+
});
|
|
1357
|
+
if (analysis.status === "partial" && (analysis.respondingServers === 0 || analysis.respondingServers >= analysis.authoritativeServers)) context.addIssue({
|
|
1358
|
+
code: "custom",
|
|
1359
|
+
path: ["status"],
|
|
1360
|
+
message: "Partial analysis requires some but not all authoritative nameservers to respond"
|
|
1361
|
+
});
|
|
1362
|
+
if (analysis.status === "not_requested" && analysis.conflicts.length !== 0) context.addIssue({
|
|
1363
|
+
code: "custom",
|
|
1364
|
+
path: ["conflicts"],
|
|
1365
|
+
message: "Conflict results cannot exist when analysis was not requested"
|
|
1366
|
+
});
|
|
1367
|
+
}).meta({ id: "ConflictAnalysis" });
|
|
1368
|
+
const CheckDomainResponseSchema = zod.z.object({
|
|
1369
|
+
domain: DomainNameSchema,
|
|
1370
|
+
delegationStatus: DomainDelegationStatusSchema,
|
|
1371
|
+
candidates: zod.z.array(DomainProviderCandidateSchema).max(65),
|
|
1372
|
+
provider: ProviderSchema.optional(),
|
|
1373
|
+
setupSupport: DomainSetupSupportSchema,
|
|
1374
|
+
conflictAnalysis: ConflictAnalysisSchema
|
|
1375
|
+
}).strict().superRefine((result, context) => {
|
|
1376
|
+
const candidateIds = result.candidates.map((candidate) => candidate.provider.id);
|
|
1377
|
+
if (new Set(candidateIds).size !== candidateIds.length) context.addIssue({
|
|
1378
|
+
code: "custom",
|
|
1379
|
+
path: ["candidates"],
|
|
1380
|
+
message: "Provider candidates must be unique"
|
|
1381
|
+
});
|
|
1382
|
+
if (result.delegationStatus === "undelegated" && (result.candidates.length !== 0 || result.provider !== void 0 || result.setupSupport !== "unavailable")) context.addIssue({
|
|
1383
|
+
code: "custom",
|
|
1384
|
+
path: ["delegationStatus"],
|
|
1385
|
+
message: "Undelegated domains cannot have provider support"
|
|
1386
|
+
});
|
|
1387
|
+
if (result.provider !== void 0 && !result.candidates.some((candidate) => candidate.exact && candidate.provider.id === result.provider?.id)) context.addIssue({
|
|
1388
|
+
code: "custom",
|
|
1389
|
+
path: ["provider"],
|
|
1390
|
+
message: "Resolved provider must be an exact provider candidate"
|
|
1391
|
+
});
|
|
1392
|
+
if (["automatic", "manual"].includes(result.setupSupport) && result.provider === void 0) context.addIssue({
|
|
1393
|
+
code: "custom",
|
|
1394
|
+
path: ["setupSupport"],
|
|
1395
|
+
message: "Available setup support requires a resolved provider"
|
|
1396
|
+
});
|
|
1397
|
+
if (result.setupSupport === "automatic" && !result.provider?.availableAuthorizationMethods.some((method) => method !== "manual")) context.addIssue({
|
|
1398
|
+
code: "custom",
|
|
1399
|
+
path: ["setupSupport"],
|
|
1400
|
+
message: "Automatic setup requires a configured non-manual authorization method"
|
|
1401
|
+
});
|
|
1402
|
+
if (result.setupSupport === "manual" && (result.provider?.availableAuthorizationMethods.length !== 1 || result.provider.availableAuthorizationMethods[0] !== "manual")) context.addIssue({
|
|
1403
|
+
code: "custom",
|
|
1404
|
+
path: ["setupSupport"],
|
|
1405
|
+
message: "Manual setup requires the manual authorization method"
|
|
1406
|
+
});
|
|
1407
|
+
if (result.setupSupport === "ambiguous" && result.candidates.length === 0) context.addIssue({
|
|
1408
|
+
code: "custom",
|
|
1409
|
+
path: ["candidates"],
|
|
1410
|
+
message: "Ambiguous setup requires provider candidates"
|
|
1411
|
+
});
|
|
1412
|
+
if (result.setupSupport === "unknown" && result.candidates.length !== 0) context.addIssue({
|
|
1413
|
+
code: "custom",
|
|
1414
|
+
path: ["candidates"],
|
|
1415
|
+
message: "Unknown setup cannot include provider candidates"
|
|
1416
|
+
});
|
|
1417
|
+
if (["ambiguous", "unknown"].includes(result.setupSupport) && result.provider !== void 0) context.addIssue({
|
|
1418
|
+
code: "custom",
|
|
1419
|
+
path: ["provider"],
|
|
1420
|
+
message: "Ambiguous or unknown support cannot resolve one provider"
|
|
1421
|
+
});
|
|
1422
|
+
}).meta({ id: "CheckDomainResponse" });
|
|
1423
|
+
//#endregion
|
|
1424
|
+
//#region src/contracts/error.ts
|
|
1425
|
+
const Domain0ErrorCodeSchema = zod.z.enum([
|
|
1426
|
+
"invalid_request",
|
|
1427
|
+
"unauthorized",
|
|
1428
|
+
"forbidden",
|
|
1429
|
+
"not_found",
|
|
1430
|
+
"conflict",
|
|
1431
|
+
"rate_limited",
|
|
1432
|
+
"dns_unavailable",
|
|
1433
|
+
"provider_unavailable",
|
|
1434
|
+
"provider_authorization_failed",
|
|
1435
|
+
"provider_limitation",
|
|
1436
|
+
"plan_expired",
|
|
1437
|
+
"propagation_pending",
|
|
1438
|
+
"internal_error"
|
|
1439
|
+
]).meta({ id: "Domain0ErrorCode" });
|
|
1440
|
+
const Domain0ErrorResponseSchema = zod.z.object({ error: zod.z.object({
|
|
1441
|
+
code: Domain0ErrorCodeSchema,
|
|
1442
|
+
message: zod.z.string().min(1),
|
|
1443
|
+
requestId: zod.z.string().min(1),
|
|
1444
|
+
retryable: zod.z.boolean(),
|
|
1445
|
+
details: zod.z.record(zod.z.string(), zod.z.unknown()).optional()
|
|
1446
|
+
}).strict() }).strict().meta({ id: "ErrorResponse" });
|
|
1447
|
+
//#endregion
|
|
1448
|
+
//#region src/contracts/localization.ts
|
|
1449
|
+
/** Locale identifiers supported by Entri Connect's public configuration contract. */
|
|
1450
|
+
const domain0Locales = [
|
|
1451
|
+
"en",
|
|
1452
|
+
"es",
|
|
1453
|
+
"pt",
|
|
1454
|
+
"pt-br",
|
|
1455
|
+
"pt-pt",
|
|
1456
|
+
"fr",
|
|
1457
|
+
"it",
|
|
1458
|
+
"de",
|
|
1459
|
+
"nl",
|
|
1460
|
+
"pl",
|
|
1461
|
+
"tr",
|
|
1462
|
+
"ja",
|
|
1463
|
+
"da",
|
|
1464
|
+
"sv"
|
|
1465
|
+
];
|
|
1466
|
+
const Domain0LocaleSchema = zod.z.enum(domain0Locales).meta({ id: "Domain0Locale" });
|
|
1467
|
+
//#endregion
|
|
1468
|
+
//#region src/contracts/platform.ts
|
|
1469
|
+
/** Public platform request. Application and tenant identity come from the API key. */
|
|
1470
|
+
const PlatformCreateConnectionInputSchema = zod.z.strictObject({ intent: DnsIntentSchema }).meta({ id: "PlatformCreateConnectionInput" });
|
|
1471
|
+
//#endregion
|
|
1472
|
+
//#region src/contracts/webhook.ts
|
|
1473
|
+
const Domain0WebhookEventTypeSchema = zod.z.enum([
|
|
1474
|
+
"connection.requested",
|
|
1475
|
+
"provider.detection_started",
|
|
1476
|
+
"provider.selected",
|
|
1477
|
+
"authorization.required",
|
|
1478
|
+
"domain_connect.started",
|
|
1479
|
+
"domain_connect.submitted",
|
|
1480
|
+
"authorization.completed",
|
|
1481
|
+
"connection.planning_started",
|
|
1482
|
+
"connection.plan_prepared",
|
|
1483
|
+
"connection.planning_restarted",
|
|
1484
|
+
"connection.already_configured",
|
|
1485
|
+
"connection.plan_confirmed",
|
|
1486
|
+
"connection.changes_applied",
|
|
1487
|
+
"connection.propagation_checked",
|
|
1488
|
+
"connection.activated",
|
|
1489
|
+
"connection.manual_required",
|
|
1490
|
+
"connection.manual_submitted",
|
|
1491
|
+
"connection.failed_retryable",
|
|
1492
|
+
"connection.retry_started",
|
|
1493
|
+
"connection.cancelled"
|
|
1494
|
+
]).meta({ id: "Domain0WebhookEventType" });
|
|
1495
|
+
const Domain0WebhookEventIdSchema = zod.z.string().regex(/^[0-9a-f]{64}$/).brand().meta({ id: "Domain0WebhookEventId" });
|
|
1496
|
+
const Domain0WebhookAttributesSchema = zod.z.record(zod.z.string().min(1).max(100), zod.z.string().max(4e3)).superRefine((attributes, context) => {
|
|
1497
|
+
if (Object.keys(attributes).length > 64) context.addIssue({
|
|
1498
|
+
code: "custom",
|
|
1499
|
+
message: "Webhook attributes must contain at most 64 entries"
|
|
1500
|
+
});
|
|
1501
|
+
}).meta({ id: "Domain0WebhookAttributes" });
|
|
1502
|
+
const Domain0WebhookEventSchema = zod.z.strictObject({
|
|
1503
|
+
schemaVersion: zod.z.literal(1),
|
|
1504
|
+
eventId: Domain0WebhookEventIdSchema,
|
|
1505
|
+
type: Domain0WebhookEventTypeSchema,
|
|
1506
|
+
connectionId: ConnectionIdSchema,
|
|
1507
|
+
applicationId: ApplicationIdSchema,
|
|
1508
|
+
tenantId: TenantIdSchema,
|
|
1509
|
+
sequence: zod.z.number().int().positive().max(Number.MAX_SAFE_INTEGER),
|
|
1510
|
+
occurredAt: IsoDateTimeSchema,
|
|
1511
|
+
attributes: Domain0WebhookAttributesSchema
|
|
1512
|
+
}).meta({ id: "Domain0WebhookEvent" });
|
|
1513
|
+
const Domain0WebhookVerificationErrorCodeSchema = zod.z.enum([
|
|
1514
|
+
"invalid_headers",
|
|
1515
|
+
"stale_timestamp",
|
|
1516
|
+
"invalid_signature",
|
|
1517
|
+
"invalid_payload",
|
|
1518
|
+
"crypto_unavailable"
|
|
1519
|
+
]).meta({ id: "Domain0WebhookVerificationErrorCode" });
|
|
1520
|
+
var Domain0WebhookVerificationError = class extends Error {
|
|
1521
|
+
code;
|
|
1522
|
+
name = "Domain0WebhookVerificationError";
|
|
1523
|
+
constructor(code, message, options) {
|
|
1524
|
+
super(message, options);
|
|
1525
|
+
this.code = code;
|
|
1526
|
+
}
|
|
1527
|
+
};
|
|
1528
|
+
const DOMAIN0_WEBHOOK_HEADER_NAMES = {
|
|
1529
|
+
eventId: "x-domain0-event-id",
|
|
1530
|
+
eventType: "x-domain0-event-type",
|
|
1531
|
+
deliveryAttempt: "x-domain0-delivery-attempt",
|
|
1532
|
+
schemaVersion: "x-domain0-schema-version",
|
|
1533
|
+
signatureKeyId: "x-domain0-signature-key-id",
|
|
1534
|
+
timestamp: "x-domain0-timestamp",
|
|
1535
|
+
signature: "x-domain0-signature"
|
|
1536
|
+
};
|
|
1537
|
+
const normalizedHeadersSchema = zod.z.strictObject({
|
|
1538
|
+
eventId: Domain0WebhookEventIdSchema,
|
|
1539
|
+
eventType: zod.z.string().regex(/^domain0\.[a-z_]+\.[a-z_]+$/).max(100),
|
|
1540
|
+
deliveryAttempt: zod.z.string().regex(/^[1-9][0-9]{0,8}$/).transform(Number),
|
|
1541
|
+
schemaVersion: zod.z.literal("1"),
|
|
1542
|
+
signatureKeyId: zod.z.string().regex(/^[A-Za-z0-9._-]{1,64}$/),
|
|
1543
|
+
timestamp: zod.z.string().regex(/^[0-9]{1,12}$/).transform(Number),
|
|
1544
|
+
signature: zod.z.string().regex(/^v1=[0-9a-f]{64}$/)
|
|
1545
|
+
});
|
|
1546
|
+
const textEncoder = new TextEncoder();
|
|
1547
|
+
async function verifyDomain0Webhook(input) {
|
|
1548
|
+
const body = typeof input.body === "string" ? textEncoder.encode(input.body) : new Uint8Array(input.body);
|
|
1549
|
+
const maxBodyBytes = boundedPositiveInteger(input.maxBodyBytes ?? 1048576, 16777216);
|
|
1550
|
+
if (!(body instanceof Uint8Array) || body.byteLength === 0 || body.byteLength > maxBodyBytes) throw verificationError("invalid_payload", "Webhook body is empty or exceeds the configured limit");
|
|
1551
|
+
const headers = parseHeaders(input.headers);
|
|
1552
|
+
const now = boundedNonNegativeInteger(input.nowUnixSeconds ?? Math.floor(Date.now() / 1e3), Number.MAX_SAFE_INTEGER);
|
|
1553
|
+
const maxAgeSeconds = boundedPositiveInteger(input.maxAgeSeconds ?? 300, 86400);
|
|
1554
|
+
if (Math.abs(now - headers.timestamp) > maxAgeSeconds) throw verificationError("stale_timestamp", "Webhook timestamp is outside the accepted replay window");
|
|
1555
|
+
const secret = Object.prototype.hasOwnProperty.call(input.secretKeyring, headers.signatureKeyId) ? input.secretKeyring[headers.signatureKeyId] : void 0;
|
|
1556
|
+
if (!(secret instanceof Uint8Array) || secret.byteLength !== 32) throw verificationError("invalid_signature", "Webhook signature is invalid");
|
|
1557
|
+
const crypto = input.crypto ?? globalThis.crypto;
|
|
1558
|
+
if (crypto?.subtle === void 0) throw verificationError("crypto_unavailable", "Web Crypto is unavailable");
|
|
1559
|
+
const signature = hexToBytes(headers.signature.slice(3));
|
|
1560
|
+
const prefix = textEncoder.encode(`v1.${headers.signatureKeyId}.${headers.timestamp}.`);
|
|
1561
|
+
const signedBytes = new Uint8Array(prefix.byteLength + body.byteLength);
|
|
1562
|
+
signedBytes.set(prefix);
|
|
1563
|
+
signedBytes.set(body, prefix.byteLength);
|
|
1564
|
+
const key = await crypto.subtle.importKey("raw", new Uint8Array(secret), {
|
|
1565
|
+
name: "HMAC",
|
|
1566
|
+
hash: "SHA-256"
|
|
1567
|
+
}, false, ["verify"]);
|
|
1568
|
+
if (!await crypto.subtle.verify("HMAC", key, new Uint8Array(signature), signedBytes)) throw verificationError("invalid_signature", "Webhook signature is invalid");
|
|
1569
|
+
let decoded;
|
|
1570
|
+
try {
|
|
1571
|
+
const text = new TextDecoder("utf-8", { fatal: true }).decode(body);
|
|
1572
|
+
decoded = JSON.parse(text);
|
|
1573
|
+
} catch (error) {
|
|
1574
|
+
throw verificationError("invalid_payload", "Verified webhook body is not valid UTF-8 JSON", error);
|
|
1575
|
+
}
|
|
1576
|
+
const parsed = Domain0WebhookEventSchema.safeParse(decoded);
|
|
1577
|
+
if (!parsed.success) throw verificationError("invalid_payload", "Verified webhook body does not match schema version 1", parsed.error);
|
|
1578
|
+
if (parsed.data.eventId !== headers.eventId || `domain0.${parsed.data.type}` !== headers.eventType || String(parsed.data.schemaVersion) !== headers.schemaVersion) throw verificationError("invalid_payload", "Verified webhook headers do not match the event body");
|
|
1579
|
+
return parsed.data;
|
|
1580
|
+
}
|
|
1581
|
+
function parseHeaders(source) {
|
|
1582
|
+
const values = {
|
|
1583
|
+
eventId: void 0,
|
|
1584
|
+
eventType: void 0,
|
|
1585
|
+
deliveryAttempt: void 0,
|
|
1586
|
+
schemaVersion: void 0,
|
|
1587
|
+
signatureKeyId: void 0,
|
|
1588
|
+
timestamp: void 0,
|
|
1589
|
+
signature: void 0
|
|
1590
|
+
};
|
|
1591
|
+
if ("get" in source && typeof source.get === "function") for (const [key, name] of Object.entries(DOMAIN0_WEBHOOK_HEADER_NAMES)) values[key] = source.get(name) ?? void 0;
|
|
1592
|
+
else {
|
|
1593
|
+
const accepted = /* @__PURE__ */ new Map();
|
|
1594
|
+
for (const key of Object.keys(DOMAIN0_WEBHOOK_HEADER_NAMES)) accepted.set(DOMAIN0_WEBHOOK_HEADER_NAMES[key], key);
|
|
1595
|
+
const observed = /* @__PURE__ */ new Set();
|
|
1596
|
+
for (const [providedName, providedValue] of Object.entries(source)) {
|
|
1597
|
+
const name = providedName.toLowerCase();
|
|
1598
|
+
const key = accepted.get(name);
|
|
1599
|
+
if (key === void 0) continue;
|
|
1600
|
+
if (observed.has(name) || typeof providedValue !== "string") throw verificationError("invalid_headers", "Webhook headers are missing, duplicated, or malformed");
|
|
1601
|
+
observed.add(name);
|
|
1602
|
+
values[key] = providedValue;
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
const parsed = normalizedHeadersSchema.safeParse(values);
|
|
1606
|
+
if (!parsed.success) throw verificationError("invalid_headers", "Webhook headers are missing, duplicated, or malformed", parsed.error);
|
|
1607
|
+
return parsed.data;
|
|
1608
|
+
}
|
|
1609
|
+
function boundedPositiveInteger(value, maximum) {
|
|
1610
|
+
if (!Number.isSafeInteger(value) || value < 1 || value > maximum) throw verificationError("invalid_headers", "Webhook verification limits are invalid");
|
|
1611
|
+
return value;
|
|
1612
|
+
}
|
|
1613
|
+
function boundedNonNegativeInteger(value, maximum) {
|
|
1614
|
+
if (!Number.isSafeInteger(value) || value < 0 || value > maximum) throw verificationError("invalid_headers", "Webhook verification time is invalid");
|
|
1615
|
+
return value;
|
|
1616
|
+
}
|
|
1617
|
+
function hexToBytes(value) {
|
|
1618
|
+
const result = new Uint8Array(value.length / 2);
|
|
1619
|
+
for (let index = 0; index < value.length; index += 2) result[index / 2] = Number.parseInt(value.slice(index, index + 2), 16);
|
|
1620
|
+
return result;
|
|
1621
|
+
}
|
|
1622
|
+
function verificationError(code, message, cause) {
|
|
1623
|
+
return new Domain0WebhookVerificationError(code, message, cause === void 0 ? void 0 : { cause });
|
|
1624
|
+
}
|
|
1625
|
+
//#endregion
|
|
1626
|
+
exports.AccessKeyCredentialsSchema = AccessKeyCredentialsSchema;
|
|
1627
|
+
exports.AccountTokenCredentialsSchema = AccountTokenCredentialsSchema;
|
|
1628
|
+
exports.AdvancedDmarcOptionsSchema = AdvancedDmarcOptionsSchema;
|
|
1629
|
+
exports.ApiTokenCredentialsSchema = ApiTokenCredentialsSchema;
|
|
1630
|
+
exports.ApplicationIdSchema = ApplicationIdSchema;
|
|
1631
|
+
exports.ApplyPlanInputSchema = ApplyPlanInputSchema;
|
|
1632
|
+
exports.AuthorizationMethodSchema = AuthorizationMethodSchema;
|
|
1633
|
+
exports.AuthorizeWithCredentialInputSchema = AuthorizeWithCredentialInputSchema;
|
|
1634
|
+
exports.AwsSessionCredentialsSchema = AwsSessionCredentialsSchema;
|
|
1635
|
+
exports.CPanelCredentialsSchema = CPanelCredentialsSchema;
|
|
1636
|
+
exports.CancelConnectionInputSchema = CancelConnectionInputSchema;
|
|
1637
|
+
exports.ChangeActionSchema = ChangeActionSchema;
|
|
1638
|
+
exports.ChangePlanSchema = ChangePlanSchema;
|
|
1639
|
+
exports.CheckDomainInputSchema = CheckDomainInputSchema;
|
|
1640
|
+
exports.CheckDomainResponseSchema = CheckDomainResponseSchema;
|
|
1641
|
+
exports.CheckRecordsInputSchema = CheckRecordsInputSchema;
|
|
1642
|
+
exports.CheckRecordsResponseSchema = CheckRecordsResponseSchema;
|
|
1643
|
+
exports.CheckedRecordSchema = CheckedRecordSchema;
|
|
1644
|
+
exports.ClientCredentialsSchema = ClientCredentialsSchema;
|
|
1645
|
+
exports.CommandIdSchema = CommandIdSchema;
|
|
1646
|
+
exports.CompiledProviderIdSchema = CompiledProviderIdSchema;
|
|
1647
|
+
exports.ConditionalDnsRecordsSchema = ConditionalDnsRecordsSchema;
|
|
1648
|
+
exports.ConfirmPlanInputSchema = ConfirmPlanInputSchema;
|
|
1649
|
+
exports.ConflictAnalysisSchema = ConflictAnalysisSchema;
|
|
1650
|
+
exports.ConflictPolicySchema = ConflictPolicySchema;
|
|
1651
|
+
exports.ConnectionFailureCodeSchema = ConnectionFailureCodeSchema;
|
|
1652
|
+
exports.ConnectionFailureSchema = ConnectionFailureSchema;
|
|
1653
|
+
exports.ConnectionFlowIdSchema = ConnectionFlowIdSchema;
|
|
1654
|
+
exports.ConnectionFlowResponseSchema = ConnectionFlowResponseSchema;
|
|
1655
|
+
exports.ConnectionFlowSchema = ConnectionFlowSchema;
|
|
1656
|
+
exports.ConnectionFlowTargetInputSchema = ConnectionFlowTargetInputSchema;
|
|
1657
|
+
exports.ConnectionFlowTargetSchema = ConnectionFlowTargetSchema;
|
|
1658
|
+
exports.ConnectionIdSchema = ConnectionIdSchema;
|
|
1659
|
+
exports.ConnectionSchema = ConnectionSchema;
|
|
1660
|
+
exports.ConnectionStateSchema = ConnectionStateSchema;
|
|
1661
|
+
exports.CreateConnectionFlowInputSchema = CreateConnectionFlowInputSchema;
|
|
1662
|
+
exports.CreateConnectionInputSchema = CreateConnectionInputSchema;
|
|
1663
|
+
exports.CreateConnectionResponseSchema = CreateConnectionResponseSchema;
|
|
1664
|
+
exports.CreateSharedFlowInputSchema = CreateSharedFlowInputSchema;
|
|
1665
|
+
exports.CreateSharedFlowResponseSchema = CreateSharedFlowResponseSchema;
|
|
1666
|
+
exports.DOMAIN0_WEBHOOK_HEADER_NAMES = DOMAIN0_WEBHOOK_HEADER_NAMES;
|
|
1667
|
+
exports.DetectProviderResponseSchema = DetectProviderResponseSchema;
|
|
1668
|
+
exports.DkimDnsEvidenceSchema = DkimDnsEvidenceSchema;
|
|
1669
|
+
exports.DkimDnsEvidenceStatusSchema = DkimDnsEvidenceStatusSchema;
|
|
1670
|
+
exports.DkimDnsRecordEvidenceSchema = DkimDnsRecordEvidenceSchema;
|
|
1671
|
+
exports.DkimDnsRecordTypeSchema = DkimDnsRecordTypeSchema;
|
|
1672
|
+
exports.DkimGuidanceInputSchema = DkimGuidanceInputSchema;
|
|
1673
|
+
exports.DkimGuidanceResponseSchema = DkimGuidanceResponseSchema;
|
|
1674
|
+
exports.DkimSelectorSchema = DkimSelectorSchema;
|
|
1675
|
+
exports.DnsIntentSchema = DnsIntentSchema;
|
|
1676
|
+
exports.DnsRecordSchema = DnsRecordSchema;
|
|
1677
|
+
exports.DnsRecordTypeSchema = DnsRecordTypeSchema;
|
|
1678
|
+
exports.Domain0ApplicationNameSchema = Domain0ApplicationNameSchema;
|
|
1679
|
+
exports.Domain0ConnectCloseEventSchema = Domain0ConnectCloseEventSchema;
|
|
1680
|
+
exports.Domain0ConnectCloseReasonSchema = Domain0ConnectCloseReasonSchema;
|
|
1681
|
+
exports.Domain0ConnectConfigurationSchema = Domain0ConnectConfigurationSchema;
|
|
1682
|
+
exports.Domain0ConnectEventSchema = Domain0ConnectEventSchema;
|
|
1683
|
+
exports.Domain0ConnectStepChangeEventSchema = Domain0ConnectStepChangeEventSchema;
|
|
1684
|
+
exports.Domain0ConnectStepSchema = Domain0ConnectStepSchema;
|
|
1685
|
+
exports.Domain0ConnectSuccessEventSchema = Domain0ConnectSuccessEventSchema;
|
|
1686
|
+
exports.Domain0DkimGuidanceOptionsSchema = Domain0DkimGuidanceOptionsSchema;
|
|
1687
|
+
exports.Domain0DkimSetupDocumentationClickEventSchema = Domain0DkimSetupDocumentationClickEventSchema;
|
|
1688
|
+
exports.Domain0ErrorCodeSchema = Domain0ErrorCodeSchema;
|
|
1689
|
+
exports.Domain0ErrorResponseSchema = Domain0ErrorResponseSchema;
|
|
1690
|
+
exports.Domain0LocaleSchema = Domain0LocaleSchema;
|
|
1691
|
+
exports.Domain0ManualSetupDocumentationClickEventSchema = Domain0ManualSetupDocumentationClickEventSchema;
|
|
1692
|
+
exports.Domain0RequestCloseEventSchema = Domain0RequestCloseEventSchema;
|
|
1693
|
+
exports.Domain0SetupPolicySchema = Domain0SetupPolicySchema;
|
|
1694
|
+
exports.Domain0SharedFlowSentEventSchema = Domain0SharedFlowSentEventSchema;
|
|
1695
|
+
exports.Domain0WebhookAttributesSchema = Domain0WebhookAttributesSchema;
|
|
1696
|
+
exports.Domain0WebhookEventIdSchema = Domain0WebhookEventIdSchema;
|
|
1697
|
+
exports.Domain0WebhookEventSchema = Domain0WebhookEventSchema;
|
|
1698
|
+
exports.Domain0WebhookEventTypeSchema = Domain0WebhookEventTypeSchema;
|
|
1699
|
+
exports.Domain0WebhookVerificationError = Domain0WebhookVerificationError;
|
|
1700
|
+
exports.Domain0WebhookVerificationErrorCodeSchema = Domain0WebhookVerificationErrorCodeSchema;
|
|
1701
|
+
exports.Domain0WhiteLabelCopySchema = Domain0WhiteLabelCopySchema;
|
|
1702
|
+
exports.Domain0WhiteLabelCustomPropertiesSchema = Domain0WhiteLabelCustomPropertiesSchema;
|
|
1703
|
+
exports.Domain0WhiteLabelLogoUrlSchema = Domain0WhiteLabelLogoUrlSchema;
|
|
1704
|
+
exports.Domain0WhiteLabelSchema = Domain0WhiteLabelSchema;
|
|
1705
|
+
exports.Domain0WhiteLabelThemeSchema = Domain0WhiteLabelThemeSchema;
|
|
1706
|
+
exports.DomainConnectTemplateSchema = DomainConnectTemplateSchema;
|
|
1707
|
+
exports.DomainDelegationStatusSchema = DomainDelegationStatusSchema;
|
|
1708
|
+
exports.DomainNameSchema = DomainNameSchema;
|
|
1709
|
+
exports.DomainProviderCandidateSchema = DomainProviderCandidateSchema;
|
|
1710
|
+
exports.DomainRecordConflictSchema = DomainRecordConflictSchema;
|
|
1711
|
+
exports.DomainSetupSupportSchema = DomainSetupSupportSchema;
|
|
1712
|
+
exports.EmailProviderDetectionStatusSchema = EmailProviderDetectionStatusSchema;
|
|
1713
|
+
exports.EmailProviderSchema = EmailProviderSchema;
|
|
1714
|
+
exports.ExistingRecordPolicySchema = ExistingRecordPolicySchema;
|
|
1715
|
+
exports.GetConnectionResponseSchema = GetConnectionResponseSchema;
|
|
1716
|
+
exports.IsoDateTimeSchema = IsoDateTimeSchema;
|
|
1717
|
+
exports.IssueConnectionTokenInputSchema = IssueConnectionTokenInputSchema;
|
|
1718
|
+
exports.IssueConnectionTokenResponseSchema = IssueConnectionTokenResponseSchema;
|
|
1719
|
+
exports.ListProvidersResponseSchema = ListProvidersResponseSchema;
|
|
1720
|
+
exports.ManualSetupDocumentationUrlSchema = ManualSetupDocumentationUrlSchema;
|
|
1721
|
+
exports.OAuthCallbackResponseSchema = OAuthCallbackResponseSchema;
|
|
1722
|
+
exports.OvhCredentialsSchema = OvhCredentialsSchema;
|
|
1723
|
+
exports.PlanWarningCodeSchema = PlanWarningCodeSchema;
|
|
1724
|
+
exports.PlannedChangeSchema = PlannedChangeSchema;
|
|
1725
|
+
exports.PlatformCreateConnectionInputSchema = PlatformCreateConnectionInputSchema;
|
|
1726
|
+
exports.PreparePlanInputSchema = PreparePlanInputSchema;
|
|
1727
|
+
exports.PrivateKeyCredentialsSchema = PrivateKeyCredentialsSchema;
|
|
1728
|
+
exports.ProviderCandidateSchema = ProviderCandidateSchema;
|
|
1729
|
+
exports.ProviderCapabilitiesSchema = ProviderCapabilitiesSchema;
|
|
1730
|
+
exports.ProviderConformanceCheckNameSchema = ProviderConformanceCheckNameSchema;
|
|
1731
|
+
exports.ProviderConformanceEvidenceSchema = ProviderConformanceEvidenceSchema;
|
|
1732
|
+
exports.ProviderDetectionSchema = ProviderDetectionSchema;
|
|
1733
|
+
exports.ProviderDetectionUnavailableReasonCodeSchema = ProviderDetectionUnavailableReasonCodeSchema;
|
|
1734
|
+
exports.ProviderHealthReasonSchema = ProviderHealthReasonSchema;
|
|
1735
|
+
exports.ProviderHealthResponseSchema = ProviderHealthResponseSchema;
|
|
1736
|
+
exports.ProviderHealthSchema = ProviderHealthSchema;
|
|
1737
|
+
exports.ProviderHealthStatusSchema = ProviderHealthStatusSchema;
|
|
1738
|
+
exports.ProviderIdSchema = ProviderIdSchema;
|
|
1739
|
+
exports.ProviderImplementationSchema = ProviderImplementationSchema;
|
|
1740
|
+
exports.ProviderSchema = ProviderSchema;
|
|
1741
|
+
exports.RecordConflictReasonSchema = RecordConflictReasonSchema;
|
|
1742
|
+
exports.ResolveSharedFlowInputSchema = ResolveSharedFlowInputSchema;
|
|
1743
|
+
exports.ResolveSharedFlowResponseSchema = ResolveSharedFlowResponseSchema;
|
|
1744
|
+
exports.ResolvedDnsIntentSchema = ResolvedDnsIntentSchema;
|
|
1745
|
+
exports.ResolvedDnsRecordSchema = ResolvedDnsRecordSchema;
|
|
1746
|
+
exports.RetryConnectionInputSchema = RetryConnectionInputSchema;
|
|
1747
|
+
exports.RetryResumeStateSchema = RetryResumeStateSchema;
|
|
1748
|
+
exports.SelectProviderInputSchema = SelectProviderInputSchema;
|
|
1749
|
+
exports.SharedFlowIdSchema = SharedFlowIdSchema;
|
|
1750
|
+
exports.SharedFlowInvitationSchema = SharedFlowInvitationSchema;
|
|
1751
|
+
exports.SharedFlowInvitationUrlSchema = SharedFlowInvitationUrlSchema;
|
|
1752
|
+
exports.SharedFlowTokenSchema = SharedFlowTokenSchema;
|
|
1753
|
+
exports.SharedFlowUrlSchema = SharedFlowUrlSchema;
|
|
1754
|
+
exports.SpfConflictPolicySchema = SpfConflictPolicySchema;
|
|
1755
|
+
exports.StartDomainConnectInputSchema = StartDomainConnectInputSchema;
|
|
1756
|
+
exports.StartDomainConnectResponseSchema = StartDomainConnectResponseSchema;
|
|
1757
|
+
exports.StartManualConfigurationInputSchema = StartManualConfigurationInputSchema;
|
|
1758
|
+
exports.StartOAuthInputSchema = StartOAuthInputSchema;
|
|
1759
|
+
exports.StartOAuthResponseSchema = StartOAuthResponseSchema;
|
|
1760
|
+
exports.SubmitDomainConnectCompletionInputSchema = SubmitDomainConnectCompletionInputSchema;
|
|
1761
|
+
exports.SubmitManualCompletionInputSchema = SubmitManualCompletionInputSchema;
|
|
1762
|
+
exports.TenantIdSchema = TenantIdSchema;
|
|
1763
|
+
exports.UsernamePasswordCredentialsSchema = UsernamePasswordCredentialsSchema;
|
|
1764
|
+
exports.UsernameTokenCredentialsSchema = UsernameTokenCredentialsSchema;
|
|
1765
|
+
exports.VerifiedProviderConformanceEvidenceSchema = VerifiedProviderConformanceEvidenceSchema;
|
|
1766
|
+
exports.VerifyPropagationInputSchema = VerifyPropagationInputSchema;
|
|
1767
|
+
exports.colorContrastRatio = colorContrastRatio;
|
|
1768
|
+
exports.directCredentialKindByProvider = directCredentialKindByProvider;
|
|
1769
|
+
exports.domain0Locales = domain0Locales;
|
|
1770
|
+
exports.expectedProviderConformanceChecks = expectedProviderConformanceChecks;
|
|
1771
|
+
exports.verifyDomain0Webhook = verifyDomain0Webhook;
|
|
1772
|
+
|
|
1773
|
+
//# sourceMappingURL=contracts.cjs.map
|