Commit 2a2dd11
feat(ssl): property-scoped assertion grammar (#1404)
* refactor(cli): property-scoped SSL assertion grammar
Replace the flat per-source SSL assertion builder with a property-scoped
model mirroring the API check grammar and the monorepo redesign.
Sources are now CERTIFICATE | CONNECTION | RESPONSE_TIME | JSON_RESPONSE |
TEXT_RESPONSE. SslAssertionBuilder exposes certificate(property),
connection(property), responseTime(), jsonResponse(jsonPath) and
textResponse(regex?), reusing GeneralAssertionBuilder / NumericAssertionBuilder.
The TlsVersion / SignatureAlgorithm / CipherSuite constant maps are kept as
assertion targets, with JSDoc updated to the new API.
Validation is rekeyed by (source, property): CERTIFICATE/CONNECTION whitelist
comparisons per property and flag unknown properties; RESPONSE_TIME /
JSON_RESPONSE / TEXT_RESPONSE whitelist per source. Boolean-target validation
is retained for boolean properties. Both GREATER_THAN_OR_EQUAL and the regex
(MATCHES) operator are removed for SSL; the now-unused SSL-only boolean/matches
codegen helpers are dropped from the shared internal codegen.
Codegen emits the five new sources and all specs (ssl-assertion,
ssl-assertion-codegen, ssl-monitor, uptime-monitor-codegen) plus the
ai-context example are migrated to the new grammar.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013AYwkvoaANQTH5dGStbBj2
* fix(ssl): align resolvedIp to the full string comparison set
resolvedIp allowed EQUALS/NOT_EQUALS/CONTAINS but not NOT_CONTAINS, diverging
from the webapp/backend string value type. Use the shared STRING set for parity.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013AYwkvoaANQTH5dGStbBj2
* fix(cli): restore type narrowing for SSL typed-constant assertions
Convert SslAssertionBuilder.certificate/connection to typed overloads so
property names are narrowed to known unions (typos are compile errors +
autocomplete), and the two typed-constant properties narrow the builder
target type: connection('tlsVersion') -> TlsVersionValue,
certificate('signatureAlgorithm') -> SignatureAlgorithmValue. cipherSuite
stays unconstrained by prior decision.
Add type-level @ts-expect-error coverage proving the narrowing, plus
runtime validation coverage for the removed SSL-only operators
(MATCHES, GREATER_THAN_OR_EQUAL) and the per-property comparison
whitelist (sans/serialNumber/ocspStatus rejects; sans/resolvedIp accepts).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013AYwkvoaANQTH5dGStbBj2
* style(cli): comma member-delimiter in ssl-monitor test type literals
Fixes the lint job (@stylistic/member-delimiter-style): single-line type
literals use a comma, not a semicolon.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013AYwkvoaANQTH5dGStbBj2
* fix(examples): port SSL check templates to property-scoped grammar
The advanced-project and advanced-project-js templates still called the
flat SslAssertionBuilder methods (certExpiresInDays, chainTrusted,
hostnameVerified, tlsVersion) removed by the property-scoped grammar
refactor.
These are not documentation-only: create-checkly downloads them as
github:checkly/checkly-cli/examples/<template>#<version> and pins
checkly to 'latest', so once the new grammar ships, scaffolding the
recommended template and running checkly test/deploy would fail with
"SslAssertionBuilder.certExpiresInDays is not a function".
Semantics are unchanged; the synthesized wire payload is equivalent.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(cli): key SSL property whitelists by their property unions
The runtime whitelists in ssl-assertion-validation.ts were typed
Record<string, PropertyRule>, so nothing tied them to the property
unions in ssl-assertion.ts that they mirror. A union member added
without a matching rule compiled clean and autocompleted, then failed
at the user's checkly deploy with "unknown property".
Key them by the unions instead, matching traceroute-assertion-validation.ts
and this file's own source-level rule. A missing or misspelled entry is
now a compile error.
The unions are exported to make that possible, which also lets users name
them when building typed wrappers, as the traceroute analogue already
allows. They are prefixed Ssl- to suit the flat public export namespace,
following the IcmpLatencyProperty / TracerouteResponseTimeProperty
convention; both names are new to the public API, so nothing breaks.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* refactor(cli): one operator class per SSL assertion property
certificate(p)/connection(p) returned a GeneralAssertionBuilder exposing
every operator with string|number|boolean targets, so
certificate('daysUntilExpiry').greaterThan('30 days') compiled, passed
deploy validation, and then silently never fired.
Give each property its own operator class exposing exactly the
comparisons the backend accepts for it, with its target typed. The
operator maps are the declaration point: the property unions derive from
their keys, and the value-type and comparison tables are keyed by those
unions, so a property cannot be added to one and forgotten in another.
An unknown property still resolves, and is reported by validation rather
than throwing.
Validation now reports a non-numeric target for numeric properties and
for RESPONSE_TIME, closing the same silent-never-fires hole on the
object-literal path that bypasses the builder. Targets are checked only
where the accepted set is universal; the enumerated sets stay with the
builder's types to avoid restating the value unions.
Codegen emits numeric and boolean targets as bare literals so the
generated call matches the operators, sharing its numeric predicate with
validation so nothing the CLI calls valid is emitted as code that does
not compile. Targets outside a property's contract keep their string
form rather than being coerced, which would rewrite the assertion on the
next deploy.
valueForNumericAssertion keeps parseInt for its other callers, whose
targets may carry units; SSL opts into Number via the new parse option.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(ssl): carry the textResponse regex in the assertion property slot
The backend Joi schema and the go-runner (EvaluateRegExp) read the
TEXT_RESPONSE extraction pattern from `property`; the `regex` field is
never consulted. textResponse(pattern) previously emitted the pattern into
`regex`, so it validated fine but was silently ignored at evaluation —
the comparison ran against the whole serialized response document. Emit it
as `property` (builder + import codegen) and pin the wire shape in tests.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DXP63MTUyPnuc48ZWGovhR
* refactor(cli): derive the SSL assertion grammar from one declaration
The certificate/connection grammar was stated in four hand-synced places
— 19 operator classes, two operator maps, a value-type table, and a
comparison whitelist. Review found three ways they could drift while
compiling clean: an operator added to a class but not the whitelist, a
value type changed without the class's target type, and a property
literal copy-pasted onto the wrong class. TypeScript's Record keying
caught a missing entry but never a mismatched one.
Declare each property once, as data: the operators it exposes and its
target type. The property unions, the typed builder methods, the
validation whitelist, and codegen's literal kind are all derived from
that row, so the three drifts become structurally impossible — a
property cannot expose an operator its whitelist rejects, or a target
type codegen renders wrong, and there are no repeated literals to
mis-copy.
The generic machinery (operator catalog, target specs, the mapped type
that turns a row into a builder surface) lives in a monitor-agnostic
internal/assertion-grammar.ts; the SSL grammar tables and the runtime
lookups validation and codegen read live in ssl-assertion-grammar.ts.
Both stay out of the public API. Public call shapes, the property unions,
wire output, and per-property operator and target narrowing are all
unchanged: the pre-existing SSL tests pass untouched.
A new consistency spec iterates the grammar and asserts the builder,
validation and codegen agree for every declared (property, operator),
and that number and boolean properties declare only operators codegen
renders as literals — guarding the one drift the type system cannot,
using codegen's own predicates rather than restating them.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* refactor(cli): derive the traceroute assertion grammar from one declaration
Migrate the traceroute assertion builder onto the single-declaration
grammar system SSL already uses. The three hand-rolled builder classes
and the hand-maintained validation tables (property list and per-source
comparison whitelists) are replaced by grammar tables in
traceroute-assertion-grammar.ts, from which the builder surface, the
property union, and the validation whitelists all derive.
Traceroute exercises both grammar shapes: RESPONSE_TIME is
property-scoped over avg/min/max/stdDev, built via operatorsForProperty;
HOP_COUNT and PACKET_LOSS are single scalar sources whose backend grammar
omits notEquals, built via a new operatorsForSource helper. RESPONSE_TIME
comparisons are derived per statistical property, so a property that later
diverges is validated against its own row rather than a sibling's.
The builder call shapes, the property union, wire output, and per-source
operator narrowing are unchanged; the pre-existing traceroute tests pass
untouched, and the diagnostic messages ("has an invalid property", "must
not specify a property", "unknown source") are preserved. A consistency
spec iterates the grammar and asserts the builder and validation agree
for every declared (source, property, operator).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Simo Kinnunen <simo@checklyhq.com>1 parent 23abb3d commit 2a2dd11
19 files changed
Lines changed: 1368 additions & 580 deletions
File tree
- examples
- advanced-project-js/src/__checks__/uptime
- advanced-project/src/__checks__/uptime
- packages/cli/src
- ai-context
- constructs
- __tests__
- internal
Lines changed: 4 additions & 4 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
18 | 18 | | |
19 | 19 | | |
20 | 20 | | |
21 | | - | |
22 | | - | |
23 | | - | |
24 | | - | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
25 | 25 | | |
26 | 26 | | |
27 | 27 | | |
Lines changed: 4 additions & 4 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
18 | 18 | | |
19 | 19 | | |
20 | 20 | | |
21 | | - | |
22 | | - | |
23 | | - | |
24 | | - | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
25 | 25 | | |
26 | 26 | | |
27 | 27 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
290 | 290 | | |
291 | 291 | | |
292 | 292 | | |
293 | | - | |
294 | | - | |
295 | | - | |
296 | | - | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
297 | 297 | | |
298 | 298 | | |
299 | 299 | | |
| |||
Lines changed: 99 additions & 14 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
13 | 13 | | |
14 | 14 | | |
15 | 15 | | |
16 | | - | |
| 16 | + | |
17 | 17 | | |
18 | | - | |
| 18 | + | |
| 19 | + | |
19 | 20 | | |
20 | | - | |
21 | | - | |
| 21 | + | |
| 22 | + | |
22 | 23 | | |
23 | 24 | | |
24 | | - | |
25 | | - | |
| 25 | + | |
| 26 | + | |
26 | 27 | | |
| 28 | + | |
27 | 29 | | |
28 | | - | |
29 | | - | |
| 30 | + | |
| 31 | + | |
30 | 32 | | |
31 | | - | |
32 | 33 | | |
33 | | - | |
34 | | - | |
| 34 | + | |
| 35 | + | |
35 | 36 | | |
36 | | - | |
37 | 37 | | |
38 | | - | |
39 | | - | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
40 | 59 | | |
41 | 60 | | |
42 | 61 | | |
43 | 62 | | |
44 | 63 | | |
45 | 64 | | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
46 | 131 | | |
Lines changed: 122 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
0 commit comments