Skip to content

chore: typing audit, validator helpers, and stronger spec parity tests#10

Merged
caballeto merged 10 commits intomainfrom
chore/end-1152-consolidate-enum-arrays
Apr 20, 2026
Merged

chore: typing audit, validator helpers, and stronger spec parity tests#10
caballeto merged 10 commits intomainfrom
chore/end-1152-consolidate-enum-arrays

Conversation

@caballeto
Copy link
Copy Markdown
Contributor

Summary

Batch of CLI quality / parity tickets cleaning up boilerplate, removing the hand-rolled openapi.d.ts shim, and tightening the test suite.

  • Drop the stale 11k-line src/types/openapi.d.ts; downstream commands now import generated path types directly.
  • Introduce src/lib/validators.ts with shared Zod-validator helpers used by every command flag (URL / cron / time-window / etc.).
  • Refactor every command + the lib/{api-client,base-command,crud-commands,resources}.ts surface to use the new validators and consistent error wrapping; matching test/lib/ harness covers the new helpers.
  • yaml/: tighten parser / resolver / interpolation / state / transform / zod-schemas.ts typing; expand applier + interpolation + spec-field parity + entitlements + zod-schemas tests; entitlements helper now builds full /auth/me responses so Zod validation passes.
  • Switch to a flat-config eslint setup with a dedicated tsconfig.eslint.json so test/ files type-check too.
  • Refresh version command + contexts-file / full-stack fixture / version test data.

Test plan

  • npm run lint
  • npm run typecheck
  • npm test (846 tests pass)
  • CI green
  • Surface integration test (mono#246 runs make test-surface SURFACE=cli against this branch)

Made with Cursor

Replace inline preprocessing functions in generate-zod.mjs with
import from @devhelm/openapi-tools/preprocess. Adds missing
flattenCircularOneOf step and fixes default-field skip logic.

Made-with: Cursor
…rity tests

Replace 14 hand-maintained enum arrays in zod-schemas.ts and schema.ts
with imports from spec-facts.generated.ts, which is extracted from the
OpenAPI spec during `npm run zodgen`. Add spec-field-parity.test.ts
(39 tests) that verify every YAML field maps to an API request DTO field
and vice versa, catching schema drift at compile time.

Made-with: Cursor
Add 9 new parity tests (39→48) covering:
- statusPageComponentDesiredSnapshot fields vs StatusPageComponentDto
- statusPageGroupDesiredSnapshot fields vs StatusPageComponentGroupDto
- StatusPageBranding bidirectional field check
- EscalationStep fields (channels↔channelIds mapping)
- MatchRule fields (monitorNames↔monitorIds mapping)

Ensures API schema additions are caught at compile time before they
cause silent drift in the reconciliation loop.

Made-with: Cursor
- Add AuthMeResponseSchema and DashboardOverviewSchema with safeParse validation
- Remove all `{} as SomeDto` fallbacks — fail explicitly on empty responses
- Document checkedFetch trade-off (compile-time only, critical paths validated)
- Validate auth/me response for entitlement checks that gate deploy behavior

Made-with: Cursor
…BILITIES (END-1152)

The CLI keeps a few enum tuples that duplicate values from the OpenAPI
spec. Two clear consolidations were still outstanding:

1. ``COMPARISON_OPERATORS`` was hand-rolled in ``schema.ts`` and only
   loosely typed (``readonly string[]``), even though the same enum is
   inlined four times in ``api-zod.generated.ts`` (StatusCodeAssertion,
   HeaderValueAssertion, JsonPathAssertion, ResponseSizeAssertion). If
   the API ever added a comparison operator, the validator would silently
   accept it without ever updating its error messages. Add
   ``COMPARISON_OPERATORS`` to ``generate-zod.mjs``'s spec-facts
   extraction (sourced from StatusCodeAssertion.operator) and re-export
   it from ``schema.ts`` so the validator and any future Zod consumers
   get a single, spec-derived source of truth typed as a literal tuple.

2. ``STATUS_PAGE_VISIBILITIES`` was intentionally narrowed to
   ``['PUBLIC']`` (the API also accepts PASSWORD and IP_RESTRICTED, but
   they are not yet wired to storage / enforcement). The narrowed tuple
   was previously declared in **both** ``schema.ts`` and
   ``yaml/zod-schemas.ts`` independently — a future contributor flipping
   one of them on (e.g. when PASSWORD ships) would silently desync the
   validator from the Zod layer. Promote ``schema.ts``'s declaration to
   ``as const`` and derive ``StatusPageVisibility`` from it so the Zod
   layer can re-import the same tuple (follow-up commit) instead of
   redeclaring it.

Mechanical changes:

- ``scripts/generate-zod.mjs``: extend ``generateSpecFacts`` to extract
  ``COMPARISON_OPERATORS`` from ``StatusCodeAssertion.operator``.
- ``src/lib/spec-facts.generated.ts``: regenerated; now exports
  ``COMPARISON_OPERATORS`` as a typed tuple plus
  ``ComparisonOperators`` literal union.
- ``src/lib/yaml/schema.ts``:
  * Import ``COMPARISON_OPERATORS`` from spec-facts and re-export it
    alongside the other generated enum tuples.
  * Drop the hand-rolled ``readonly string[]`` declaration so consumers
    pick up the spec-derived literal tuple type.
  * Convert ``STATUS_PAGE_VISIBILITIES`` to ``['PUBLIC'] as const`` and
    derive ``StatusPageVisibility`` from the tuple, with a comment
    explaining the intentional narrow vs the spec.
- ``src/lib/yaml/validator.ts``: cast ``COMPARISON_OPERATORS`` to
  ``readonly string[]`` at the call site for ``Array.includes`` because
  it now has a literal-tuple element type rather than ``string``.

``npm run zodgen`` produces a stable diff (only the new
``COMPARISON_OPERATORS`` block); ``npm run typecheck`` and
``npm run lint`` are clean. Pre-existing failing tests in
``test/yaml/entitlements.test.ts`` are unrelated.

Made-with: Cursor
Batch of CLI quality / parity tickets cleaning up boilerplate, removing
the hand-rolled openapi.d.ts shim, and tightening the test suite.

- Drop the stale 11k-line src/types/openapi.d.ts; downstream commands now
  import generated path types directly.
- Introduce src/lib/validators.ts with shared Zod-validator helpers used
  by every command flag (URL / cron / Joi-style time windows / etc.).
- Refactor every command + the lib/{api-client,base-command,
  crud-commands,resources}.ts surface to use the new validators and
  consistent error wrapping; matching test/lib/ harness covers the new
  helpers.
- yaml/: tighten parser / resolver / interpolation / state / transform /
  zod-schemas.ts typing, expand applier + interpolation + spec-field
  parity + entitlements + zod-schemas tests; entitlements helper now
  builds full /auth/me responses so Zod validation passes.
- Switch to a flat-config eslint setup with a dedicated
  tsconfig.eslint.json so test/ files type-check too.
- Update the version command to consume the spec-derived metadata and
  refresh the contexts-file / full-stack fixture / version test data
  to match.

Lint, typecheck (npm run typecheck), and the full vitest suite (846
tests) all pass locally.

Made-with: Cursor
…-tools

The release / spec-check workflows were failing on every CI run with:

  Error [ERR_MODULE_NOT_FOUND]: Cannot find package
  '@devhelm/openapi-tools' imported from scripts/generate-zod.mjs

because the package was declared as
``file:../mini/packages/openapi-tools`` — that only ever resolves on the
maintainer's workstation, where the mono repo happens to live one
directory up.

Vendor the same preprocessing logic into ``scripts/lib/preprocess.mjs``
(byte-for-byte identical to the upstream module) and drop the broken
devDependency. The build is now self-contained and ``npm ci`` works on
any clean checkout.

Made-with: Cursor
API spec change makes RetryStrategy.maxRetries/interval and
CreateEnvironmentRequest.isDefault required (they were always required at
the API level — they're primitive int/boolean in Java DTOs). Update YAML
schema and transformers to match. Refresh generated types/zod against the
corrected monitoring-api.json (MonitorConfig polymorphism inlined).

Made-with: Cursor
Mirrors the upstream @devhelm/openapi-tools changes so the CLI's vendored
preprocessing pipeline emits proper Zod discriminated unions for
polymorphic types (MonitorAuthConfig, CheckTypeDetailsDto in the public
surface) and strict objects everywhere.

scripts/lib/preprocess.mjs:
- Add inlineDiscriminatorSubtypesWithInfo that merges parent properties
  into each subtype, replaces the discriminator field with z.literal,
  drops the allOf back-reference, then rewrites the parent itself as a
  oneOf+discriminator union.
- Add rewriteUnionsAsDiscriminated post-processor that swaps z.union
  for z.discriminatedUnion when members match a known subtype set.
- preprocessSpec now returns { flattened, inlinedDiscriminators } so the
  caller can drive the post-process.

scripts/generate-zod.mjs:
- Pass additionalPropertiesDefaultValue: false to openapi-zod-client so
  generated objects no longer carry .passthrough() (zaps the noisy
  `unknown` widening that broke YAML validator narrowing).
- Wire the post-processor into the generated source.

Refresh docs/openapi/monitoring-api.json from the upstream public spec
and regenerate src/lib/api-zod.generated.ts. lint, typecheck, and the
full vitest suite (846 tests) pass.

Made-with: Cursor
…nions

Port the `inlineNullableDeductionRefs` preprocessor pass from the mono
repo so `UpdateMonitorRequest.config` (a `@Nullable MonitorConfig`)
resolves to a proper `z.union([...]).nullable()` instead of an empty
object that silently strips the entire config body.

Also:
- Enable `strictObjects: true` in openapi-zod-client so union resolution
  picks the correct subtype rather than matching the first partial fit
  and stripping extras (the TCP `{host,port}` → IcmpMonitorConfig bug).
- Widen `enumsFrom` in generate-zod.mjs to pick up enum values from
  direct `items.enum` on array-typed properties (e.g.
  `DnsMonitorConfig.recordTypes`), so `DNS_RECORD_TYPES` no longer
  generates a `WARNING: enum not found in spec` comment that breaks
  the downstream import in `yaml/schema.ts`.
- Refresh vendored spec + regenerate Zod + TS types.

Made-with: Cursor
@caballeto caballeto merged commit 917dd0d into main Apr 20, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant