The union every internal SNS consumer validates against — the SNS envelope
only.
It is NOT the schema of the surviving Hookdeck routes. The three
reconciliation routes on backoffice-api keep their own hand-written bodies,
because the legacy HTTP wire format carries no id — the publisher it was
built for sent {...event, createdAt} and never minted one — while id is
required here. Pointing a route at this union would 400 anything still
arriving over HTTP.
Static<typeof TInternalEvent> is never, and that is a trap.internalEventSchemas above is annotated TSchema[], which erases the
per-event literal types, so this is a real RUNTIME validator carrying no
usable static type. Value.Check(TInternalEvent, x) therefore narrows x to
never rather than to the wire union. Both handlers work around it by
checking inside a function with a declared AnyInternalEventEnvelope return
type — never is assignable to anything, so returning it through that
signature yields the right type with no cast. Annotating a const does NOT
work, because TypeScript re-narrows a const by its initialiser.
Do not stack a second guard in front of it either: isKnownInternalEvent
narrows to the PUBLISH union (createdAt?: Date) rather than the wire one
(createdAt: string), so the intersection of the two is empty.
A plain T.Union, not DiscriminatedUnion from @cfxlabsinc/controllers:
that helper returns a T.Unsafe carrying raw JSON-Schema oneOf, which AJV
understands but TypeBox's own Value.Check does not — and this package must
validate without a Fastify AJV instance.
The union every internal SNS consumer validates against — the SNS envelope only.
It is NOT the schema of the surviving Hookdeck routes. The three reconciliation routes on
backoffice-apikeep their own hand-written bodies, because the legacy HTTP wire format carries noid— the publisher it was built for sent{...event, createdAt}and never minted one — whileidis required here. Pointing a route at this union would 400 anything still arriving over HTTP.Static<typeof TInternalEvent>isnever, and that is a trap.internalEventSchemasabove is annotatedTSchema[], which erases the per-event literal types, so this is a real RUNTIME validator carrying no usable static type.Value.Check(TInternalEvent, x)therefore narrowsxtoneverrather than to the wire union. Both handlers work around it by checking inside a function with a declaredAnyInternalEventEnvelopereturn type —neveris assignable to anything, so returning it through that signature yields the right type with no cast. Annotating aconstdoes NOT work, because TypeScript re-narrows aconstby its initialiser.Do not stack a second guard in front of it either:
isKnownInternalEventnarrows to the PUBLISH union (createdAt?: Date) rather than the wire one (createdAt: string), so the intersection of the two is empty.A plain
T.Union, notDiscriminatedUnionfrom@cfxlabsinc/controllers: that helper returns aT.Unsafecarrying raw JSON-SchemaoneOf, which AJV understands but TypeBox's ownValue.Checkdoes not — and this package must validate without a Fastify AJV instance.