Raises the typescript devDependency across the workspace, bumps package
versions, re-adds ignoreDeprecations, and repoints the @wrnexus registry.
These were pre-existing working-tree changes, committed as-is rather than
authored here. The .npmrc change redirects @wrnexus publishes from
registry.npmjs.org to registry.workroot.in — confirm that is intended
before publishing from this branch.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Expands 3.1 and 3.2 so the work can be done without re-deriving anything.
3.1 now records what 0.8.6 already fixed, separated into the ten components
that were miswired and the five that gained outputs they had been firing
undeclared, with the caveat that Map's three were converted but never confirmed
in a browser. For the 22 that remain it adds the finding that changes the
decision: all nine are pure scaffolds with no state, functions or handlers, and
five of them duplicate a component that already works -- FileUpload against
FileInput and FileUploadProgress, Toast and ToastNotifications against Toaster,
AdvancedDatePicker against DatePicker, AdvancedRangeSlider against RangeSlider.
Superseding those is a migration entry rather than new code, and leaves Chart,
TreeView, Confetti and CopyMarkup as the only ones needing to be built.
3.2 corrects the scaffold count from 23 to 28; the earlier figure used a looser
rule. Nine of the 28 are the 3.1 components, so the two items must be planned
together, and several of the rest are primitives that need only their styles
moved out of ui.css rather than any behaviour.
Also corrects the dead-output component count from 11 to 9 in both documents.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Adds a per-subsystem measurement of reactive.js, made by minifying it
repeatedly with one subsystem removed rather than counting source bytes.
This corrects the earlier audit on both figures and on the conclusion drawn
from them. Component controllers are 23,722 bytes minified / 6,660 gzipped --
30.6% of transfer, not the "about 18%" previously claimed -- and splitting them
out saves 6.6 kB gzipped on a typical page, not "3-4 kB". Measured against the
example app, / and /login use none of the ten controllers and /layout uses one,
so most pages download and parse the lot for nothing.
The larger finding is that the runtime is not where the weight is. One page
parses 490,212 decoded bytes across 11 generated client modules while
transferring 21,026, and the largest module is 89.8% duplicated lines: the
state-restore prologue appears 162 times because client-codegen.ts inlines the
sync into every peer alias of every client function. Gzip hides it on the wire,
but parse cost follows decoded bytes.
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>
packages/csr's actions.test.ts and reactive.test.ts both leave
globalThis.fetch mutated across bun test files (reactive.test.ts's
'cache invalidation refetches...' test replaces it and never restores
it). Since bun test runs files sequentially rather than importing all
of them up front, a module-level capture of fetch in this file would
already observe csr's leftover mock (csr sorts before rpc).
Route the real-socket assertion through a small node:http-backed fetch
implementation instead of relying on globalThis.fetch at all, keeping
the test's actual target - httpTransport()'s default resolveOrigin -
unaffected by any other suite's global mutation.
- Resolve RPC call origins via a new WRNEXUS_INTERNAL_ORIGINS map (loopback
origins the gateway hands each child before spawning it), falling back to
the public appOrigin only when it is absent. Calls previously always went
to the public gateway origin, which the gateway unconditionally 404s on
the RPC prefix by design — every real cross-app call failed.
- Stop loadServices() from running ahead of routing and stop memoizing a
rejected load: one bad file under app/services/ no longer permanently
breaks every route in the app. A failed load logs loudly, is retried on
the next RPC request, and the RPC path gets a structured RPC_UNKNOWN
instead of an unhandled throw.
- Reject a service whose contract.name does not match the filename it is
mounted under, naming both, instead of silently mounting under the
filename while the typed client calls by contract name.
- Let ServiceError accept an explicit retryable and have the client pass the
wire value through, instead of recomputing (and silently flipping) it from
the error code alone.
- Document the gateway/X-Forwarded-* deployment requirement in the RPC
README.
Each of the three code blockers has a new/extended test that was verified to
fail when its fix was reverted (rpc/test/integration.test.ts,
dev-server/test/rpc-services-loading.test.ts).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Adds examples/auth-showcase/app/services/greeter-client.ts so the showcase
demonstrates both halves - the review noted the example was callee-only, so a
developer had no working reference for making a call.
Raises integration coverage to the planned 3 tests and adds the missing
rpc-endpoint cases. Also wires the prod build path for services.
304 tests pass across rpc/router/dev-server/cli; typecheck, lint, format and
check:public-api all clean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Critical:
- router: fail loudly (WRN-SERVICE-COLLISION) when two app/services files
scan to the same service name, instead of silently letting directory-walk
order pick a winner.
Important:
- server.ts: wrap a throwing input schema so its raw message cannot escape
invoke(); returns RPC_INVALID and logs server-side instead.
- client.ts: race timeoutMs against transport.call so a stalled transport
cannot hang the caller; rejects with a ServiceError(RPC_TRANSPORT).
- client.ts: the proxy returns undefined for undeclared properties (incl.
then/catch/finally) instead of a function that throws, closing the
await-client thenable trap.
- gateway.ts / rpc-dispatch.ts: import RPC_PATH_PREFIX / RPC_INTERNAL_HEADER
from @wrnexus/rpc instead of hardcoding local copies.
- gateway.test.ts: cover the RPC-prefix edge block and internal-header
stripping across casing variants.
- http.test.ts / client.test.ts: cover anonymous-call header omission, the
internal marker, the retryable-status sweep, network/malformed/HTML
failures, AbortSignal propagation, the timeout path, and timer cleanup.
Minor:
- transport.ts: Object.hasOwn for handler lookup; note the entry-only abort
check.
- client.ts: wrap a missing/invalid WRNEXUS_RPC_SECRET as a ServiceError
(RPC_IDENTITY) instead of a bare Error.
- rpc/package.json: drop the unused @wrnexus/authz dependency.
- server.ts: implement() now throws at construction time if a declared
procedure has no own handler.
Verified: reverting the service-collision check and the client timeout race
each make their new test fail, then restore green.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
C1 CRITICAL: implement() looked up procedures/handlers with plain property
indexing, so any Object.prototype member name (constructor, toString, etc.)
resolved truthy and skipped the permission gate entirely. Fixed with
Object.hasOwn checks in packages/rpc/src/server.ts. Defense-in-depth guard
added in packages/dev-server/src/rpc-dispatch.ts constraining URL path
segments to a safe charset before they reach service/procedure lookups.
Added missing direct test coverage for packages/rpc/src/transport.ts,
server.ts and client.ts (previously untested), including a prototype-name
sweep in both server.test.ts and dev-server's rpc-endpoint.test.ts.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Brings the uncommitted body of work under version control so it cannot be
lost. Gates are green: 152 tests pass across rpc/router/dev-server,
typecheck, lint, format and check:public-api all clean.
NOT YET REVIEWED. None of Tasks 5-11 has had an independent task review, and
Task 4's second fix round was never re-reviewed either.
Known gaps against the plan, recorded here rather than discovered later:
- packages/rpc/test/{transport,server,client}.test.ts are ABSENT. The plan
required a test file for each. server.ts holds the fail-closed identity and
permission checks and currently has no direct coverage at all.
- rpc-endpoint.test.ts has 3 tests where the plan specified 9. Missing:
unknown service, non-POST, malformed body, non-rpc passthrough, and the
isInternalCaller sweep. This is the task where a reachable
/__wrnexus/rpc/* makes every permission check in the workspace bypassable.
- http.test.ts has 3 of 7; integration.test.ts 2 of 3;
services-discovery.test.ts 1 of 4.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
verifyJwt gates its maxAge check on iat being a number, so a token forged
without iat was honoured at any maxAgeSeconds - the same shape as the
audience and exp fail-opens closed in the previous round. A future-dated iat
did the same via a negative age. Both refused now.
The import side never checked aud was a single string, and verifyJwt compares
with includes(), so a multi-audience token verified at several apps. The
mint-side guard's invariant now holds where it is enforced.
ctx.tenant present with a null id minted an authenticated credential with no
tenant claim, which the callee reads as global. Absent ctx.tenant means
untenanted; a present tenant with an unusable id is an error.
Adds six tests pinning behaviours that mutation testing showed were free to
delete without any test noticing: no-exp, no-iat, the 300s default max age,
an array audience on import, a non-string tenant claim on import, and a null
tenant id at mint.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- importSubjectContext now rejects a non-string/empty selfApp before
verifying. verifyJwt skips the audience check entirely when audience
is undefined, so an unvalidated selfApp (the natural shape of
currentAppName(): string | undefined) accepted every token from every
app for every audience.
- exportSubjectContext now rejects a non-string/empty targetApp, so an
array can no longer mint one token valid at multiple apps.
- Both directions now reject a present-but-non-string tenant id instead
of silently dropping it (was: callee reads missing tenantId as
global/unscoped -> cross-tenant exposure).
- importSubjectContext now requires exp to be present and independently
bounds accepted token age via a new maxAge/ImportOptions.maxAgeSeconds
(default 300s), so a caller cannot mint a long-lived token via a huge
ttlSeconds and have it honoured indefinitely.
- SubjectContext.callerApp doc now states it is self-asserted (the
signing secret is workspace-wide) and must never be an authz input.
- index.ts also exports the new ImportOptions type.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- isRetryableStatus now fails closed for out-of-range values (600+, negative,
NaN) by bounding the 5xx check on both sides (>= 500 && <= 599), instead of
an unbounded >= 500 that classified garbage statuses like 1000 as retryable.
- 408 Request Timeout is now retryable, matching the RPC_TRANSPORT doc
comment (connection, timeout, 5xx) — a timeout surfaced as 408 is no longer
treated differently from the same timeout surfaced as 504.
- Add RPC_MALFORMED: the callee answered, but not with a ServiceResult (HTML
error page, truncated body, unexpected shape). Distinct from RPC_TRANSPORT
since something DID respond; non-retryable via the existing retryableFor,
no new branch needed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Drop the redundant eslint-disable on AnyProcedures; no-explicit-any
is off repo-wide so the directive itself was the warning. Doc
comment now explains why none is needed.
- Rename test's schema binding to _schema per the lint config's
underscore-prefix rule for read-only-as-type bindings.