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