diff --git a/scripts/generate-zod.mjs b/scripts/generate-zod.mjs index 58b694b..010d817 100644 --- a/scripts/generate-zod.mjs +++ b/scripts/generate-zod.mjs @@ -125,8 +125,21 @@ async function main() { console.log('Reading spec from', SPEC_PATH); const spec = JSON.parse(readFileSync(SPEC_PATH, 'utf8')); - const { flattened, inlinedDiscriminators, inlinedNullableDeductions } = - preprocessSpec(spec); + // Snapshot the unmodified spec for `generateSpecFacts` BEFORE + // preprocessing mutates it. The Postel's-Law relaxer drops multi-value + // enums on response-shape DTOs (e.g. `AlertChannelDto.channelType`), + // and a few facts are anchored on response DTOs as fallbacks. Reading + // facts off the original spec keeps the canonical value lists complete + // even after the relaxer runs, while the generated Zod schemas (which + // use `spec`) stay tolerant on the wire. + const originalSpec = JSON.parse(JSON.stringify(spec)); + + const { + flattened, + inlinedDiscriminators, + inlinedNullableDeductions, + relaxedEnums, + } = preprocessSpec(spec); console.log(`Preprocessed spec (${Object.keys(spec.components?.schemas ?? {}).length} schemas)`); if (flattened.length > 0) { console.log(` Flattened circular oneOf: ${flattened.join(', ')}`); @@ -143,8 +156,13 @@ async function main() { ` Inlined nullable deduction refs for: ${inlinedNullableDeductions.join(', ')}`, ); } + if (relaxedEnums && relaxedEnums.length > 0) { + console.log( + ` Relaxed response-DTO enums (Postel's Law): ${relaxedEnums.length} fields`, + ); + } - generateSpecFacts(spec); + generateSpecFacts(originalSpec); mkdirSync(dirname(OUTPUT_PATH), { recursive: true }); diff --git a/scripts/lib/preprocess.mjs b/scripts/lib/preprocess.mjs index 0b6fd80..6193148 100644 --- a/scripts/lib/preprocess.mjs +++ b/scripts/lib/preprocess.mjs @@ -314,6 +314,75 @@ export function inlineNullableDeductionRefs(spec) { return Array.from(rewritten); } +/** + * Drop `enum` constraints from response-shape DTO properties so the + * generated Zod schemas decode unknown future enum values as plain + * strings (Postel's Law contract — see + * `mini/runbooks/api-contract.md` § 2.2 + § 3). + * + * Selection rules — must match the request-vs-response naming convention + * shared with mini's `relaxResponseEnums` post-processor: + * + * - Walks `components.schemas`. A schema is "response-shape" if its + * name matches `*Dto`, `*Response`, `SingleValueResponse*`, + * `TableValueResult*`, or `CursorPage*`. + * - A schema is "request-shape" (left alone) if its name matches + * `*Request` / `*Params` or starts lower-case (helpers). + * - Inside a response-shape schema, every property whose schema + * carries a multi-value `enum` (length ≥ 2) gets the `enum` key + * dropped so codegens emit `z.string()` / `str` / `string`. + * - SINGLE-VALUE enums are PRESERVED — those are the discriminator + * tags installed by `inlineDiscriminatorSubtypesWithInfo`. + * - Array-typed properties get the same treatment on `items.enum`. + * + * Idempotent: re-running on a relaxed spec is a no-op. Returns the list + * of `Schema.field` paths that were relaxed. + */ +export function relaxResponseEnumsInSpec(spec) { + const schemas = getSchemas(spec); + const relaxed = []; + + function isResponseShape(name) { + if (/^[a-z]/.test(name)) return false; + if (/(Request|Params)$/.test(name)) return false; + return ( + /(Dto|Response)$/.test(name) || + /^(SingleValueResponse|TableValueResult|CursorPage)/.test(name) + ); + } + + function relaxProps(schemaName, properties) { + if (!properties) return; + for (const [propName, raw] of Object.entries(properties)) { + if (!isSchemaObj(raw)) continue; + if (Array.isArray(raw.enum) && raw.enum.length >= 2) { + delete raw.enum; + relaxed.push(`${schemaName}.${propName}`); + } + if (raw.items && isSchemaObj(raw.items)) { + if (Array.isArray(raw.items.enum) && raw.items.enum.length >= 2) { + delete raw.items.enum; + relaxed.push(`${schemaName}.${propName}[]`); + } + } + } + } + + for (const [schemaName, schema] of Object.entries(schemas)) { + if (!isResponseShape(schemaName)) continue; + relaxProps(schemaName, schema.properties); + if (Array.isArray(schema.allOf)) { + for (const member of schema.allOf) { + if (isSchemaObj(member)) { + relaxProps(schemaName, member.properties); + } + } + } + } + + return relaxed; +} + export function preprocessSpec(spec) { setRequiredFields(spec); setRequiredOnAllOfMembers(spec); @@ -326,7 +395,17 @@ export function preprocessSpec(spec) { // discriminator-based parents as abstract/empty ones. const inlinedNullableDeductions = inlineNullableDeductionRefs(spec); const flattened = flattenCircularOneOf(spec); - return { flattened, inlinedDiscriminators, inlinedNullableDeductions }; + // Postel's Law: drop multi-value enums on response-shape DTOs so all + // codegens (Zod, Pydantic, Go) emit tolerant readers. MUST run AFTER + // discriminator inlining so we don't accidentally relax single-value + // discriminator tags (those are length-1 enums and skipped by design). + const relaxedEnums = relaxResponseEnumsInSpec(spec); + return { + flattened, + inlinedDiscriminators, + inlinedNullableDeductions, + relaxedEnums, + }; } /** diff --git a/skills/devhelm-communicate/references/_generated/status-page-components.fields.md b/skills/devhelm-communicate/references/_generated/status-page-components.fields.md index ee7dbab..224370e 100644 --- a/skills/devhelm-communicate/references/_generated/status-page-components.fields.md +++ b/skills/devhelm-communicate/references/_generated/status-page-components.fields.md @@ -40,10 +40,10 @@ | `groupId` | string (uuid) | | ✓ | | | `name` | string | ✓ | | | | `description` | string | | ✓ | | -| `type` | "MONITOR" \| "GROUP" \| "STATIC" | ✓ | | | +| `type` | string | ✓ | | | | `monitorId` | string (uuid) | | ✓ | | | `resourceGroupId` | string (uuid) | | ✓ | | -| `currentStatus` | "OPERATIONAL" \| "DEGRADED_PERFORMANCE" \| "PARTIAL_OUTAGE" \| "MAJOR_OUTAGE" \| "UNDER_MAINTENANCE" | ✓ | | | +| `currentStatus` | string | ✓ | | | | `showUptime` | boolean | ✓ | | | | `displayOrder` | integer (int32) | ✓ | | | | `pageOrder` | integer (int32) | ✓ | | | diff --git a/skills/devhelm-communicate/references/_generated/status-page-incidents.fields.md b/skills/devhelm-communicate/references/_generated/status-page-incidents.fields.md index 16ac994..539c852 100644 --- a/skills/devhelm-communicate/references/_generated/status-page-incidents.fields.md +++ b/skills/devhelm-communicate/references/_generated/status-page-incidents.fields.md @@ -36,8 +36,8 @@ | `id` | string (uuid) | ✓ | | | | `statusPageId` | string (uuid) | ✓ | | | | `title` | string | ✓ | | | -| `status` | "INVESTIGATING" \| "IDENTIFIED" \| "MONITORING" \| "RESOLVED" | ✓ | | | -| `impact` | "NONE" \| "MINOR" \| "MAJOR" \| "CRITICAL" | ✓ | | | +| `status` | string | ✓ | | | +| `impact` | string | ✓ | | | | `scheduled` | boolean | ✓ | | | | `scheduledFor` | string (date-time) | | ✓ | | | `scheduledUntil` | string (date-time) | | ✓ | | diff --git a/skills/devhelm-communicate/references/_generated/status-pages.fields.md b/skills/devhelm-communicate/references/_generated/status-pages.fields.md index 69b201d..c016c0b 100644 --- a/skills/devhelm-communicate/references/_generated/status-pages.fields.md +++ b/skills/devhelm-communicate/references/_generated/status-pages.fields.md @@ -39,13 +39,13 @@ | `slug` | string | ✓ | | | | `description` | string | | ✓ | | | `branding` | StatusPageBranding | ✓ | | | -| `visibility` | "PUBLIC" \| "PASSWORD" \| "IP_RESTRICTED" | ✓ | | | +| `visibility` | string | ✓ | | | | `enabled` | boolean | ✓ | | | -| `incidentMode` | "MANUAL" \| "REVIEW" \| "AUTOMATIC" | ✓ | | | +| `incidentMode` | string | ✓ | | | | `componentCount` | integer (int32) | | ✓ | | | `subscriberCount` | integer (int64) | | ✓ | | -| `overallStatus` | "OPERATIONAL" \| "DEGRADED_PERFORMANCE" \| "PARTIAL_OUTAGE" \| "MAJOR_OUTAGE" \| "UNDER_MAINTENANCE" | | ✓ | | -| `managedBy` | "DASHBOARD" \| "CLI" \| "TERRAFORM" \| "MCP" \| "API" | | ✓ | Source that created/owns this status page: DASHBOARD, CLI, TERRAFORM, MCP, or API. Null on pages created before this attribution column existed. | +| `overallStatus` | string | | ✓ | | +| `managedBy` | string | | ✓ | Source that created/owns this status page: DASHBOARD, CLI, TERRAFORM, MCP, or API. Null on pages created before this attribution column existed. | | `createdAt` | string (date-time) | ✓ | | | | `updatedAt` | string (date-time) | ✓ | | | diff --git a/skills/devhelm-configure/references/_generated/alert-channels.fields.md b/skills/devhelm-configure/references/_generated/alert-channels.fields.md index e85c6f1..8ef0f39 100644 --- a/skills/devhelm-configure/references/_generated/alert-channels.fields.md +++ b/skills/devhelm-configure/references/_generated/alert-channels.fields.md @@ -25,12 +25,12 @@ |---|---|---|---|---| | `id` | string (uuid) | ✓ | | Unique alert channel identifier | | `name` | string | ✓ | | Human-readable channel name | -| `channelType` | "email" \| "webhook" \| "slack" \| "pagerduty" \| "opsgenie" \| "teams" \| "discord" | ✓ | | Channel integration type (e.g. SLACK, PAGERDUTY, EMAIL) | +| `channelType` | string | ✓ | | Channel integration type (e.g. SLACK, PAGERDUTY, EMAIL) | | `displayConfig` | any | | ✓ | | | `createdAt` | string (date-time) | ✓ | | Timestamp when the channel was created | | `updatedAt` | string (date-time) | ✓ | | Timestamp when the channel was last updated | | `configHash` | string | | ✓ | SHA-256 hash of the channel config; use for change detection | -| `managedBy` | "DASHBOARD" \| "CLI" \| "TERRAFORM" \| "MCP" \| "API" | | ✓ | Source that created/owns this channel: DASHBOARD, CLI, TERRAFORM, MCP, or API. Null on channels created before this attribution column existed. | +| `managedBy` | string | | ✓ | Source that created/owns this channel: DASHBOARD, CLI, TERRAFORM, MCP, or API. Null on channels created before this attribution column existed. | | `lastDeliveryAt` | string (date-time) | | ✓ | Timestamp of the most recent delivery attempt | | `lastDeliveryStatus` | string | | ✓ | Outcome of the most recent delivery (SUCCESS, FAILED, etc.) | diff --git a/skills/devhelm-configure/references/_generated/monitors.fields.md b/skills/devhelm-configure/references/_generated/monitors.fields.md index 7dcf172..82c0046 100644 --- a/skills/devhelm-configure/references/_generated/monitors.fields.md +++ b/skills/devhelm-configure/references/_generated/monitors.fields.md @@ -10,9 +10,9 @@ | `name` | string | ✓ | | Human-readable name for this monitor | | `type` | "HTTP" \| "DNS" \| "MCP_SERVER" \| "TCP" \| "ICMP" \| "HEARTBEAT" | ✓ | | Monitor protocol type | | `config` | any | ✓ | | | -| `frequencySeconds` | integer (int32) | | ✓ | Check frequency in seconds (30–86400); null defaults to plan minimum (60s on most paid plans) | +| `frequencySeconds` | integer (int32) | | ✓ | Check frequency in seconds (10–86400); null defaults to plan minimum (60s on most paid plans) | | `enabled` | boolean | | ✓ | Whether the monitor is active (default: true) | -| `regions` | string[] | | ✓ | Probe regions to run checks from, e.g. us-east, eu-west | +| `regions` | string[] | | ✓ | Probe regions to run checks from. Allowed values are deployment-dependent; production: us-east, us-west, eu-west, ap-south. | | `managedBy` | "DASHBOARD" \| "CLI" \| "TERRAFORM" \| "MCP" \| "API" | | ✓ | Source that created/owns this monitor: DASHBOARD, CLI, TERRAFORM, MCP, or API. Defaults to API when omitted; set to your surface so audit logs, drift detection, and analytics attribute correctly. | | `environmentId` | string (uuid) | | ✓ | Environment to associate with this monitor | | `assertions` | CreateAssertionRequest[] | | ✓ | Assertions to evaluate against each check result | @@ -27,9 +27,9 @@ |---|---|---|---|---| | `name` | string | | ✓ | New monitor name; null preserves current | | `config` | any | | ✓ | | -| `frequencySeconds` | integer (int32) | | ✓ | New check frequency in seconds (30–86400); null preserves current | +| `frequencySeconds` | integer (int32) | | ✓ | New check frequency in seconds (10–86400); null preserves current | | `enabled` | boolean | | ✓ | Enable or disable the monitor; null preserves current | -| `regions` | string[] | | ✓ | New probe regions; null preserves current | +| `regions` | string[] | | ✓ | New probe regions; null preserves current. Allowed values are deployment-dependent. | | `managedBy` | "DASHBOARD" \| "CLI" \| "TERRAFORM" \| "MCP" \| "API" | | ✓ | New ownership source: DASHBOARD, CLI, TERRAFORM, MCP, or API; null preserves current value | | `environmentId` | string (uuid) | | ✓ | New environment ID; null preserves current (use clearEnvironmentId to unset) | | `clearEnvironmentId` | boolean | | ✓ | Set to true to remove the environment association | @@ -47,12 +47,12 @@ | `id` | string (uuid) | ✓ | | Unique monitor identifier | | `organizationId` | integer (int32) | ✓ | | Organization this monitor belongs to | | `name` | string | ✓ | | Human-readable name for this monitor | -| `type` | "HTTP" \| "DNS" \| "MCP_SERVER" \| "TCP" \| "ICMP" \| "HEARTBEAT" | ✓ | | | +| `type` | string | ✓ | | | | `config` | any | ✓ | | | | `frequencySeconds` | integer (int32) | ✓ | | Check frequency in seconds (30–86400) | | `enabled` | boolean | ✓ | | Whether the monitor is active | | `regions` | string[] | ✓ | | Probe regions where checks are executed | -| `managedBy` | "DASHBOARD" \| "CLI" \| "TERRAFORM" \| "MCP" \| "API" | ✓ | | Source that created/owns this monitor: DASHBOARD, CLI, TERRAFORM, MCP, or API | +| `managedBy` | string | ✓ | | Source that created/owns this monitor: DASHBOARD, CLI, TERRAFORM, MCP, or API | | `createdAt` | string (date-time) | ✓ | | Timestamp when the monitor was created | | `updatedAt` | string (date-time) | ✓ | | Timestamp when the monitor was last updated | | `assertions` | MonitorAssertionDto[] | | ✓ | Assertions evaluated against each check result; null on list responses | @@ -62,5 +62,5 @@ | `auth` | any | | ✓ | | | `incidentPolicy` | any | | ✓ | | | `alertChannelIds` | string (uuid)[] | | ✓ | Alert channel IDs linked to this monitor; populated on single-monitor responses | -| `currentStatus` | "up" \| "degraded" \| "down" \| "paused" \| "unknown" | | ✓ | Current operational state — UP, DOWN, DEGRADED, PAUSED, or UNKNOWN if no probe data yet | +| `currentStatus` | string | | ✓ | Current operational state — UP, DOWN, DEGRADED, PAUSED, or UNKNOWN if no probe data yet | diff --git a/skills/devhelm-configure/references/_generated/resource-groups.fields.md b/skills/devhelm-configure/references/_generated/resource-groups.fields.md index e95a7a8..3916aac 100644 --- a/skills/devhelm-configure/references/_generated/resource-groups.fields.md +++ b/skills/devhelm-configure/references/_generated/resource-groups.fields.md @@ -56,14 +56,14 @@ | `defaultRetryStrategy` | any | | ✓ | | | `defaultAlertChannels` | string (uuid)[] | | ✓ | Default alert channel IDs for member monitors | | `defaultEnvironmentId` | string (uuid) | | ✓ | Default environment ID for member monitors | -| `healthThresholdType` | "COUNT" \| "PERCENTAGE" | | ✓ | Health threshold type: COUNT or PERCENTAGE | +| `healthThresholdType` | string | | ✓ | Health threshold type: COUNT or PERCENTAGE | | `healthThresholdValue` | number | | ✓ | Health threshold value | | `suppressMemberAlerts` | boolean | ✓ | | When true, member-level incidents skip notification dispatch; only group alerts fire | | `confirmationDelaySeconds` | integer (int32) | | ✓ | Seconds to wait after health threshold breach before creating group incident | | `recoveryCooldownMinutes` | integer (int32) | | ✓ | Cooldown minutes after group incident resolves before a new one can open | | `health` | ResourceGroupHealthDto | ✓ | | | | `members` | ResourceGroupMemberDto[] | | ✓ | Member list with individual statuses; populated on detail GET only | -| `managedBy` | "DASHBOARD" \| "CLI" \| "TERRAFORM" \| "MCP" \| "API" | | ✓ | Source that created/owns this group: DASHBOARD, CLI, TERRAFORM, MCP, or API. Null on groups created before this attribution column existed. | +| `managedBy` | string | | ✓ | Source that created/owns this group: DASHBOARD, CLI, TERRAFORM, MCP, or API. Null on groups created before this attribution column existed. | | `createdAt` | string (date-time) | ✓ | | Timestamp when the group was created | | `updatedAt` | string (date-time) | ✓ | | Timestamp when the group was last updated | diff --git a/skills/devhelm-investigate/references/_generated/incidents.fields.md b/skills/devhelm-investigate/references/_generated/incidents.fields.md index 7e3a235..a183ddc 100644 --- a/skills/devhelm-investigate/references/_generated/incidents.fields.md +++ b/skills/devhelm-investigate/references/_generated/incidents.fields.md @@ -10,9 +10,9 @@ | `id` | string (uuid) | ✓ | | Unique incident identifier | | `monitorId` | string (uuid) | | ✓ | Monitor that triggered the incident; null for service or manual incidents | | `organizationId` | integer (int32) | ✓ | | Organization this incident belongs to | -| `source` | "AUTOMATIC" \| "MANUAL" \| "MONITORS" \| "STATUS_DATA" \| "RESOURCE_GROUP" | ✓ | | Incident origin: MONITOR, SERVICE, or MANUAL | -| `status` | "WATCHING" \| "TRIGGERED" \| "CONFIRMED" \| "RESOLVED" | ✓ | | Current lifecycle status (OPEN, RESOLVED, etc.) | -| `severity` | "DOWN" \| "DEGRADED" \| "MAINTENANCE" | ✓ | | Severity level: DOWN, DEGRADED, or MAINTENANCE | +| `source` | string | ✓ | | Incident origin: MONITOR, SERVICE, or MANUAL | +| `status` | string | ✓ | | Current lifecycle status (OPEN, RESOLVED, etc.) | +| `severity` | string | ✓ | | Severity level: DOWN, DEGRADED, or MAINTENANCE | | `title` | string | | ✓ | Short summary of the incident; null for auto-generated incidents | | `triggeredByRule` | string | | ✓ | Human-readable description of the trigger rule that fired | | `affectedRegions` | string[] | ✓ | | Probe regions that observed the failure | @@ -24,7 +24,7 @@ | `externalRef` | string | | ✓ | External reference ID (e.g. PagerDuty incident ID) | | `affectedComponents` | string[] | | ✓ | Service components affected by this incident | | `shortlink` | string | | ✓ | Short URL linking to the incident details | -| `resolutionReason` | "MANUAL" \| "AUTO_RECOVERED" \| "AUTO_RESOLVED" | | ✓ | How the incident was resolved (AUTO_RECOVERED, MANUAL, etc.) | +| `resolutionReason` | string | | ✓ | How the incident was resolved (AUTO_RECOVERED, MANUAL, etc.) | | `startedAt` | string (date-time) | | ✓ | Timestamp when the incident was detected or created | | `confirmedAt` | string (date-time) | | ✓ | Timestamp when the incident was confirmed (multi-region confirmation) | | `resolvedAt` | string (date-time) | | ✓ | Timestamp when the incident was resolved | diff --git a/src/lib/api-zod.generated.ts b/src/lib/api-zod.generated.ts index 5968a5d..f8a5fbc 100644 --- a/src/lib/api-zod.generated.ts +++ b/src/lib/api-zod.generated.ts @@ -675,7 +675,7 @@ const CreateMonitorRequest = z McpServerMonitorConfig, TcpMonitorConfig, ]), - frequencySeconds: z.number().int().nullish(), + frequencySeconds: z.number().int().gte(10).lte(86400).nullish(), enabled: z.boolean().nullish(), regions: z.array(z.string()).nullish(), managedBy: z @@ -702,7 +702,7 @@ const UpdateMonitorRequest = z TcpMonitorConfig, ]) .nullable(), - frequencySeconds: z.number().int().nullable(), + frequencySeconds: z.number().int().gte(10).lte(86400).nullable(), enabled: z.boolean().nullable(), regions: z.array(z.string()).nullable(), managedBy: z @@ -1294,22 +1294,12 @@ const AlertChannelDto = z .object({ id: z.string().uuid(), name: z.string(), - channelType: z.enum([ - "email", - "webhook", - "slack", - "pagerduty", - "opsgenie", - "teams", - "discord", - ]), + channelType: z.string(), displayConfig: AlertChannelDisplayConfig.nullish(), createdAt: z.string().datetime({ offset: true }), updatedAt: z.string().datetime({ offset: true }), configHash: z.string().nullish(), - managedBy: z - .enum(["DASHBOARD", "CLI", "TERRAFORM", "MCP", "API"]) - .nullish(), + managedBy: z.string().nullish(), lastDeliveryAt: z.string().datetime({ offset: true }).nullish(), lastDeliveryStatus: z.string().nullish(), }) @@ -1322,18 +1312,8 @@ const AlertDeliveryDto = z channelId: z.string().uuid(), channel: z.string(), channelType: z.string(), - status: z.enum([ - "PENDING", - "DELIVERED", - "RETRY_PENDING", - "FAILED", - "CANCELLED", - ]), - eventType: z.enum([ - "INCIDENT_CREATED", - "INCIDENT_RESOLVED", - "INCIDENT_REOPENED", - ]), + status: z.string(), + eventType: z.string(), stepNumber: z.number().int(), fireCount: z.number().int(), attemptCount: z.number().int(), @@ -1369,7 +1349,7 @@ const AssertionResultDto = z .object({ type: z.string(), passed: z.boolean(), - severity: z.enum(["fail", "warn"]), + severity: z.string(), message: z.string().nullish(), expected: z.string().nullish(), actual: z.string().nullish(), @@ -1377,52 +1357,9 @@ const AssertionResultDto = z .strict(); const AssertionTestResultDto = z .object({ - assertionType: z.enum([ - "status_code", - "response_time", - "body_contains", - "json_path", - "header_value", - "regex_body", - "dns_resolves", - "dns_response_time", - "dns_expected_ips", - "dns_expected_cname", - "dns_record_contains", - "dns_record_equals", - "dns_txt_contains", - "dns_min_answers", - "dns_max_answers", - "dns_response_time_warn", - "dns_ttl_low", - "dns_ttl_high", - "mcp_connects", - "mcp_response_time", - "mcp_has_capability", - "mcp_tool_available", - "mcp_min_tools", - "mcp_protocol_version", - "mcp_response_time_warn", - "mcp_tool_count_changed", - "ssl_expiry", - "response_size", - "redirect_count", - "redirect_target", - "response_time_warn", - "tcp_connects", - "tcp_response_time", - "tcp_response_time_warn", - "icmp_reachable", - "icmp_response_time", - "icmp_response_time_warn", - "icmp_packet_loss", - "heartbeat_received", - "heartbeat_max_interval", - "heartbeat_interval_drift", - "heartbeat_payload_contains", - ]), + assertionType: z.string(), passed: z.boolean(), - severity: z.enum(["fail", "warn"]), + severity: z.string(), message: z.string(), expected: z.string().nullish(), actual: z.string().nullish(), @@ -1894,15 +1831,9 @@ const IncidentDto = z id: z.string().uuid(), monitorId: z.string().uuid().nullish(), organizationId: z.number().int(), - source: z.enum([ - "AUTOMATIC", - "MANUAL", - "MONITORS", - "STATUS_DATA", - "RESOURCE_GROUP", - ]), - status: z.enum(["WATCHING", "TRIGGERED", "CONFIRMED", "RESOLVED"]), - severity: z.enum(["DOWN", "DEGRADED", "MAINTENANCE"]), + source: z.string(), + status: z.string(), + severity: z.string(), title: z.string().nullish(), triggeredByRule: z.string().nullish(), affectedRegions: z.array(z.string()), @@ -1914,9 +1845,7 @@ const IncidentDto = z externalRef: z.string().nullish(), affectedComponents: z.array(z.string()).nullish(), shortlink: z.string().nullish(), - resolutionReason: z - .enum(["MANUAL", "AUTO_RECOVERED", "AUTO_RESOLVED"]) - .nullish(), + resolutionReason: z.string().nullish(), startedAt: z.string().datetime({ offset: true }).nullish(), confirmedAt: z.string().datetime({ offset: true }).nullish(), resolvedAt: z.string().datetime({ offset: true }).nullish(), @@ -1939,14 +1868,10 @@ const IncidentUpdateDto = z .object({ id: z.string().uuid(), incidentId: z.string().uuid(), - oldStatus: z - .enum(["WATCHING", "TRIGGERED", "CONFIRMED", "RESOLVED"]) - .nullish(), - newStatus: z - .enum(["WATCHING", "TRIGGERED", "CONFIRMED", "RESOLVED"]) - .nullish(), + oldStatus: z.string().nullish(), + newStatus: z.string().nullish(), body: z.string().nullish(), - createdBy: z.enum(["SYSTEM", "USER"]).nullish(), + createdBy: z.string().nullish(), notifySubscribers: z.boolean(), createdAt: z.string().datetime({ offset: true }), }) @@ -1958,8 +1883,8 @@ const LinkedStatusPageIncidentDto = z statusPageName: z.string(), statusPageSlug: z.string(), title: z.string(), - status: z.enum(["INVESTIGATING", "IDENTIFIED", "MONITORING", "RESOLVED"]), - impact: z.enum(["NONE", "MINOR", "MAJOR", "CRITICAL"]), + status: z.string(), + impact: z.string(), scheduled: z.boolean(), publishedAt: z.string().datetime({ offset: true }).nullish(), }) @@ -2043,14 +1968,7 @@ const IntegrationDto = z description: z.string(), logoUrl: z.string(), authType: z.string(), - tierAvailability: z.enum([ - "FREE", - "STARTER", - "PRO", - "TEAM", - "BUSINESS", - "ENTERPRISE", - ]), + tierAvailability: z.string(), lifecycle: z.string(), setupGuideUrl: z.string(), configSchema: IntegrationConfigSchemaDto, @@ -2060,7 +1978,7 @@ const InviteDto = z .object({ inviteId: z.number().int(), email: z.string(), - roleOffered: z.enum(["OWNER", "ADMIN", "MEMBER"]), + roleOffered: z.string(), expiresAt: z.string().datetime({ offset: true }), consumedAt: z.string().datetime({ offset: true }).nullish(), revokedAt: z.string().datetime({ offset: true }).nullish(), @@ -2095,15 +2013,8 @@ const MemberDto = z userId: z.number().int(), email: z.string(), name: z.string().nullish(), - orgRole: z.enum(["OWNER", "ADMIN", "MEMBER"]), - status: z.enum([ - "INVITED", - "ACTIVE", - "SUSPENDED", - "LEFT", - "REMOVED", - "DECLINED", - ]), + orgRole: z.string(), + status: z.string(), createdAt: z.string().datetime({ offset: true }), }) .strict(); @@ -2111,50 +2022,7 @@ const MonitorAssertionDto = z .object({ id: z.string().uuid(), monitorId: z.string().uuid(), - assertionType: z.enum([ - "status_code", - "response_time", - "body_contains", - "json_path", - "header_value", - "regex_body", - "dns_resolves", - "dns_response_time", - "dns_expected_ips", - "dns_expected_cname", - "dns_record_contains", - "dns_record_equals", - "dns_txt_contains", - "dns_min_answers", - "dns_max_answers", - "dns_response_time_warn", - "dns_ttl_low", - "dns_ttl_high", - "mcp_connects", - "mcp_response_time", - "mcp_has_capability", - "mcp_tool_available", - "mcp_min_tools", - "mcp_protocol_version", - "mcp_response_time_warn", - "mcp_tool_count_changed", - "ssl_expiry", - "response_size", - "redirect_count", - "redirect_target", - "response_time_warn", - "tcp_connects", - "tcp_response_time", - "tcp_response_time_warn", - "icmp_reachable", - "icmp_response_time", - "icmp_response_time_warn", - "icmp_packet_loss", - "heartbeat_received", - "heartbeat_max_interval", - "heartbeat_interval_drift", - "heartbeat_payload_contains", - ]), + assertionType: z.string(), config: z.union([ BodyContainsAssertion, DnsExpectedCnameAssertion, @@ -2199,14 +2067,14 @@ const MonitorAssertionDto = z TcpResponseTimeAssertion, TcpResponseTimeWarnAssertion, ]), - severity: z.enum(["fail", "warn"]), + severity: z.string(), }) .strict(); const MonitorAuthDto = z .object({ id: z.string().uuid(), monitorId: z.string().uuid(), - authType: z.enum(["bearer", "basic", "header", "api_key"]), + authType: z.string(), config: z.discriminatedUnion("type", [ApiKeyAuthConfig, BasicAuthConfig, BearerAuthConfig, HeaderAuthConfig]), }) .strict(); @@ -2232,7 +2100,7 @@ const MonitorDto = z id: z.string().uuid(), organizationId: z.number().int(), name: z.string().min(1), - type: z.enum(["HTTP", "DNS", "MCP_SERVER", "TCP", "ICMP", "HEARTBEAT"]), + type: z.string(), config: z.union([ DnsMonitorConfig, HeartbeatMonitorConfig, @@ -2244,7 +2112,7 @@ const MonitorDto = z frequencySeconds: z.number().int(), enabled: z.boolean(), regions: z.array(z.string()), - managedBy: z.enum(["DASHBOARD", "CLI", "TERRAFORM", "MCP", "API"]), + managedBy: z.string(), createdAt: z.string().datetime({ offset: true }), updatedAt: z.string().datetime({ offset: true }), assertions: z.array(MonitorAssertionDto).nullish(), @@ -2254,9 +2122,7 @@ const MonitorDto = z auth: MonitorAuthConfig.nullish(), incidentPolicy: IncidentPolicyDto.nullish(), alertChannelIds: z.array(z.string().uuid()).nullish(), - currentStatus: z - .enum(["up", "degraded", "down", "paused", "unknown"]) - .nullish(), + currentStatus: z.string().nullish(), }) .strict(); const MonitorReference = z @@ -2286,7 +2152,7 @@ const MonitorVersionDto = z version: z.number().int(), snapshot: MonitorDto, changedById: z.number().int().nullish(), - changedVia: z.enum(["API", "DASHBOARD", "CLI", "TERRAFORM"]), + changedVia: z.string(), changeSummary: z.string().nullish(), createdAt: z.string().datetime({ offset: true }), }) @@ -2297,15 +2163,8 @@ const NotificationDispatchDto = z incidentId: z.string().uuid(), policyId: z.string().uuid(), policyName: z.string().nullish(), - status: z.enum([ - "PENDING", - "DISPATCHING", - "DELIVERED", - "ESCALATING", - "ACKNOWLEDGED", - "COMPLETED", - ]), - completionReason: z.enum(["EXHAUSTED", "RESOLVED", "NO_STEPS"]).nullish(), + status: z.string(), + completionReason: z.string().nullish(), currentStep: z.number().int(), totalSteps: z.number().int().nullish(), acknowledgedAt: z.string().datetime({ offset: true }).nullish(), @@ -2377,11 +2236,11 @@ const RegionStatusDto = z .strict(); const ResourceGroupHealthDto = z .object({ - status: z.enum(["operational", "maintenance", "degraded", "down"]), + status: z.string(), totalMembers: z.number().int(), operationalCount: z.number().int(), activeIncidents: z.number().int(), - thresholdStatus: z.enum(["healthy", "degraded", "down"]).nullish(), + thresholdStatus: z.string().nullish(), failingCount: z.number().int().nullish(), }) .strict(); @@ -2395,7 +2254,7 @@ const ResourceGroupMemberDto = z name: z.string().nullish(), slug: z.string().nullish(), subscriptionId: z.string().uuid().nullish(), - status: z.enum(["operational", "maintenance", "degraded", "down"]), + status: z.string(), effectiveFrequency: z.string().nullish(), createdAt: z.string().datetime({ offset: true }), uptime24h: z.number().nullish(), @@ -2420,23 +2279,21 @@ const ResourceGroupDto = z defaultRetryStrategy: RetryStrategy.nullish(), defaultAlertChannels: z.array(z.string().uuid()).nullish(), defaultEnvironmentId: z.string().uuid().nullish(), - healthThresholdType: z.enum(["COUNT", "PERCENTAGE"]).nullish(), + healthThresholdType: z.string().nullish(), healthThresholdValue: z.number().nullish(), suppressMemberAlerts: z.boolean(), confirmationDelaySeconds: z.number().int().nullish(), recoveryCooldownMinutes: z.number().int().nullish(), health: ResourceGroupHealthDto, members: z.array(ResourceGroupMemberDto).nullish(), - managedBy: z - .enum(["DASHBOARD", "CLI", "TERRAFORM", "MCP", "API"]) - .nullish(), + managedBy: z.string().nullish(), createdAt: z.string().datetime({ offset: true }), updatedAt: z.string().datetime({ offset: true }), }) .strict(); const ResultSummaryDto = z .object({ - currentStatus: z.enum(["up", "degraded", "down", "paused", "unknown"]), + currentStatus: z.string(), latestPerRegion: z.array(RegionStatusDto), chartData: z.array(ChartBucketDto), uptime24h: z.number().nullish(), @@ -2620,7 +2477,7 @@ const ServiceSubscriptionDto = z overallStatus: z.string().nullish(), componentId: z.string().uuid().nullish(), component: ServiceComponentDto.nullish(), - alertSensitivity: z.enum(["ALL", "INCIDENTS_ONLY", "MAJOR_ONLY"]), + alertSensitivity: z.string().min(1), subscribedAt: z.string().datetime({ offset: true }), }) .strict(); @@ -2760,16 +2617,10 @@ const StatusPageComponentDto = z groupId: z.string().uuid().nullish(), name: z.string().min(1), description: z.string().nullish(), - type: z.enum(["MONITOR", "GROUP", "STATIC"]), + type: z.string(), monitorId: z.string().uuid().nullish(), resourceGroupId: z.string().uuid().nullish(), - currentStatus: z.enum([ - "OPERATIONAL", - "DEGRADED_PERFORMANCE", - "PARTIAL_OUTAGE", - "MAJOR_OUTAGE", - "UNDER_MAINTENANCE", - ]), + currentStatus: z.string(), showUptime: z.boolean(), displayOrder: z.number().int(), pageOrder: z.number().int(), @@ -2803,16 +2654,8 @@ const StatusPageCustomDomainDto = z .object({ id: z.string().uuid(), hostname: z.string(), - status: z.enum([ - "PENDING_VERIFICATION", - "VERIFICATION_FAILED", - "VERIFIED", - "SSL_PENDING", - "ACTIVE", - "FAILED", - "REMOVED", - ]), - verificationMethod: z.enum(["CNAME", "TXT"]), + status: z.string(), + verificationMethod: z.string(), verificationToken: z.string(), verificationCnameTarget: z.string(), verifiedAt: z.string().datetime({ offset: true }).nullish(), @@ -2837,23 +2680,13 @@ const StatusPageDto = z slug: z.string().min(1), description: z.string().nullish(), branding: StatusPageBranding, - visibility: z.enum(["PUBLIC", "PASSWORD", "IP_RESTRICTED"]), + visibility: z.string(), enabled: z.boolean(), - incidentMode: z.enum(["MANUAL", "REVIEW", "AUTOMATIC"]), + incidentMode: z.string(), componentCount: z.number().int().nullish(), subscriberCount: z.number().int().nullish(), - overallStatus: z - .enum([ - "OPERATIONAL", - "DEGRADED_PERFORMANCE", - "PARTIAL_OUTAGE", - "MAJOR_OUTAGE", - "UNDER_MAINTENANCE", - ]) - .nullish(), - managedBy: z - .enum(["DASHBOARD", "CLI", "TERRAFORM", "MCP", "API"]) - .nullish(), + overallStatus: z.string().nullish(), + managedBy: z.string().nullish(), createdAt: z.string().datetime({ offset: true }), updatedAt: z.string().datetime({ offset: true }), }) @@ -2864,22 +2697,16 @@ const SingleValueResponseStatusPageDto = z const StatusPageIncidentComponentDto = z .object({ statusPageComponentId: z.string().uuid(), - componentStatus: z.enum([ - "OPERATIONAL", - "DEGRADED_PERFORMANCE", - "PARTIAL_OUTAGE", - "MAJOR_OUTAGE", - "UNDER_MAINTENANCE", - ]), + componentStatus: z.string(), componentName: z.string(), }) .strict(); const StatusPageIncidentUpdateDto = z .object({ id: z.string().uuid(), - status: z.enum(["INVESTIGATING", "IDENTIFIED", "MONITORING", "RESOLVED"]), + status: z.string(), body: z.string(), - createdBy: z.enum(["USER", "SYSTEM"]).nullish(), + createdBy: z.string().nullish(), createdByUserId: z.number().int().nullish(), notifySubscribers: z.boolean(), createdAt: z.string().datetime({ offset: true }), @@ -2890,8 +2717,8 @@ const StatusPageIncidentDto = z id: z.string().uuid(), statusPageId: z.string().uuid(), title: z.string().min(1), - status: z.enum(["INVESTIGATING", "IDENTIFIED", "MONITORING", "RESOLVED"]), - impact: z.enum(["NONE", "MINOR", "MAJOR", "CRITICAL"]), + status: z.string(), + impact: z.string(), scheduled: z.boolean(), scheduledFor: z.string().datetime({ offset: true }).nullish(), scheduledUntil: z.string().datetime({ offset: true }).nullish(), diff --git a/src/lib/descriptions.generated.ts b/src/lib/descriptions.generated.ts index 59908f9..a29bee2 100644 --- a/src/lib/descriptions.generated.ts +++ b/src/lib/descriptions.generated.ts @@ -6,9 +6,9 @@ export const fieldDescriptions: Record> = "CreateMonitorRequest": { "name": "Human-readable name for this monitor", "type": "Monitor protocol type", - "frequencySeconds": "Check frequency in seconds (30–86400); null defaults to plan minimum (60s on most paid plans)", + "frequencySeconds": "Check frequency in seconds (10–86400); null defaults to plan minimum (60s on most paid plans)", "enabled": "Whether the monitor is active (default: true)", - "regions": "Probe regions to run checks from, e.g. us-east, eu-west", + "regions": "Probe regions to run checks from. Allowed values are deployment-dependent; production: us-east, us-west, eu-west, ap-south.", "managedBy": "Source that created/owns this monitor: DASHBOARD, CLI, TERRAFORM, MCP, or API. Defaults to API when omitted; set to your surface so audit logs, drift detection, and analytics attribute correctly.", "environmentId": "Environment to associate with this monitor", "assertions": "Assertions to evaluate against each check result", @@ -16,9 +16,9 @@ export const fieldDescriptions: Record> = }, "UpdateMonitorRequest": { "name": "New monitor name; null preserves current", - "frequencySeconds": "New check frequency in seconds (30–86400); null preserves current", + "frequencySeconds": "New check frequency in seconds (10–86400); null preserves current", "enabled": "Enable or disable the monitor; null preserves current", - "regions": "New probe regions; null preserves current", + "regions": "New probe regions; null preserves current. Allowed values are deployment-dependent.", "managedBy": "New ownership source: DASHBOARD, CLI, TERRAFORM, MCP, or API; null preserves current value", "environmentId": "New environment ID; null preserves current (use clearEnvironmentId to unset)", "clearEnvironmentId": "Set to true to remove the environment association",