The old assertions only searched REACTIVE_RUNTIME for substrings; they never
touched buildApiRequest and were not anchored to the content-type line they
claimed to guard, so they could not detect drift on either side. Replace
with a fixture-driven test that runs both implementations on the same
(path, method, input) cases and compares the actual request they produce.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Typed, callable api blocks for .wrn files: sectioned request/response/error,
callable from client code as api.name(input), type-checked by tsc against the
route contracts.
Also two fixes found along the way: defineEndpoint never received its input
through the real router, so every typed endpoint was validating undefined; and
v.boolean() silently coerced any unrecognised string to false.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Client codegen chained .then().catch(), so a .catch() after .then()
caught exceptions thrown by the response body too. Switched to the
two-argument then(onFulfilled, onRejected) form, whose rejection
handler cannot see errors from the fulfilment handler.
SSR codegen wrapped both the transport call and the response-body eval
in the same try; only __wrnexusCallApi is now inside the try, and
__wrnexusEvalData runs after it, outside.
Also verifies (and locks in with a regression test) that GET query
numbers already coerce correctly through defineEndpoint + checkField,
and documents that in the typed-api-block spec.
B1: qualify each generated __wrn_api_check_* assertion name with a short
hash of the page's path (relative to app/, for reproducibility across
checkouts) so two pages declaring a same-named block no longer collide
with an identical type alias (TS2300).
B2: skip assertion emission for any block that is not client-mode, or
that has zero declared request fields. ssr sectioned blocks can never
declare a request and always fell back to Record<string, never>, whose
keyof is `string` -- making the key-exactness arm of AssertAssignable
evaluate to false unconditionally (TS2344) on every ssr sectioned block
regardless of correctness. Chose to skip both non-client blocks and
zero-field client blocks, since neither has anything meaningful to
assert type-safety about.
B3: only resolve the endpoint's input (query params / ctx.req.json())
when the endpoint declares an input schema. Previously the router-set
fix accidentally read the request body unconditionally, so a handler
with no input schema that parses the request itself hit
ERR_BODY_ALREADY_USED.
B4: run response/error bodies in client-mode api blocks through
eraseFunctionTypes, matching every other browser-bound body in
client-codegen.ts, so a TypeScript-only construct inside one (e.g. an
annotated locally-declared function) doesn't reach the .mjs artifact.
B5: only exclude "api" from state/prop destructuring in the generated
browser module when the page actually has client-mode api blocks (i.e.
there is a real `api` binding to shadow). Previously "api" was always
excluded, so a page with `state api` and no api blocks got an
undeclared `api` reference (ReferenceError) in client code.
B6: prefix each emitted assertion with `export`, so it isn't flagged as
an unused local under a downstream project's noUnusedLocals (TS6196).
B7: wrnexusCallApi now resolves with undefined for an ok 204/205
response, or an ok response with an empty/unparseable body, instead of
rejecting with "Response was not valid JSON" -- matching the spec's
failure table (error path only for non-2xx, network failure, or an
actually unparseable body on a non-empty response).
Regenerated examples/basic-app's generated types and editor bundles to
match. Confirmed the example's type gate still fails when an
unaccepted field is added to a request body, and passes cleanly
otherwise.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Every existing test in endpoint-schema.test.ts passed rawInput
explicitly, so the branch added to endpoint.ts's fix (GET/HEAD query
parsing, JSON body parsing, malformed/absent body fallback) was
exercised by nothing but a manual curl. Add coverage that calls the
endpoint with only a context, matching the real router's calling
convention:
- GET with query parameters populates input from ctx.url.searchParams.
- POST with a JSON body populates input from the parsed body.
- POST with a malformed or absent body does not throw; the schema's
own validation decides the outcome (asserted on the real response).
- An explicit rawInput argument still wins and the request is never
read (the body is drained first, so a second .json() call would
reject if the endpoint tried to read it again) -- the regression
guard for the branch intentionally left untouched.
Confirmed the GET and POST-body tests fail against the pre-fix
endpoint.ts (input resolves as undefined/null instead of the sent
value); the malformed/absent-body test does not distinguish pre- and
post-fix, because in that specific edge case both normalize to an
effectively empty input -- noted in the report rather than forced.
Added a CHANGELOG entry documenting the behavior change for
downstream apps: a request that previously passed vacuous validation
on a defineEndpoint route can now legitimately fail.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
directory.ts previously exported a plain (ctx: Context) => ... handler.
With that shape ApiInput<> resolved to unknown, so the generated
__wrn_api_check assertion for the demo page passed trivially even with
a field the endpoint does not accept -- the worked example did not
demonstrate the type safety it exists to demonstrate.
Rewrite directory.ts to use defineEndpoint with a schema (matching
typed-user.ts), which gives the generated assertion a real input type
to check against. Confirmed: adding an unaccepted field to the block's
request body now fails typecheck naming
__wrn_api_check_searchDirectory; removing it passes with zero net
diff.
Fix a real bug this surfaced: packages/core/src/endpoint.ts only read
its input from a second 'rawInput' argument, but the actual HTTP
router (packages/dev-server/src/runtime.ts handleApi) invokes route
handlers as handler(ctx) with no second argument. Every
defineEndpoint-based route -- including the pre-existing typed-user.ts
example -- silently received an empty/undefined input through the
real router (confirmed via curl: valid typed-user payloads were
rejected as 'Required'; directory's name filter matched every record
regardless of query). Fixed by having the endpoint wrapper parse the
request itself (query params for GET/HEAD, JSON body otherwise) when
no rawInput is explicitly supplied, while still honoring an explicit
rawInput for direct/unit-test callers.
Also update api-block-demo.wrn's response section: defineEndpoint
wraps handler output as { data: ... }, so the block's raw response
body is now { data: { users: [...] } } -- response reads
data.data.users instead of data.users.
Re-verified in a real browser after the endpoint rewrite and the
router fix: search returns exactly "Ajay, Asha", exactly one
POST /api/directory carrying x-csrf-token, and the error section
still runs cleanly (no exception, empty result) on a missing route.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Bumps all 47 packages, the root manifest and the VS Code extension to 0.8.6,
and rebuilds the editor compiler, language server and extension bundles that
embed the version.
The release carries the output delivery fix: camelCase outputs now reach
parent bindings, and 18 components emit through output.* instead of
hand-built CustomEvents. See the 0.8.6 migration entry for what changes for
consumers.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Bumps every @wrnexus package 0.8.4 -> 0.8.5 and adds the matching update
migration. The migration is documentation only: moving off <Table> to
<DataTable> and off the @wrnexus/ui main entry to @wrnexus/ui/registry are
source changes no codemod can make safely.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>