Commit Graph
147 Commits
Author SHA1 Message Date
Clintchiz 2e12656060 feat: close application architecture gaps
Quality / quality (ubuntu-latest) (push) Failing after 9m56s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-23 11:45:57 +05:30
Clintchiz 64ab20cc95 feat: add application productivity foundations
Quality / quality (ubuntu-latest) (push) Failing after 22s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-23 11:13:03 +05:30
Clintchiz 46195462c3 feat: make queues durable by default and add seed helpers
Quality / quality (ubuntu-latest) (push) Failing after 11m1s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-23 09:50:25 +05:30
Clintchiz 8fb96f521f fix(cli): resolve runtime config import absolutely
Quality / quality (ubuntu-latest) (push) Failing after 9m54s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-23 09:29:26 +05:30
Clintchiz 9377a69d88 feat(queue): add typed application queue lifecycle
Quality / quality (ubuntu-latest) (push) Failing after 10m55s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-23 09:22:05 +05:30
Clintchiz 9332522614 fix: initialize configured auth in production builds
Quality / quality (ubuntu-latest) (push) Failing after 9m54s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-22 23:36:31 +05:30
Clintchiz 7c5069b5b5 fix: honor project typecheck in canonical checks
Quality / quality (ubuntu-latest) (push) Failing after 9m49s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-22 23:23:21 +05:30
Clintchiz a3ddd39b7b feat: centralize application framework primitives
Quality / quality (ubuntu-latest) (push) Failing after 14m38s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-22 23:07:46 +05:30
Clintchiz cd0dffa87d feat: complete SSR CRM and refine auth UI
Quality / quality (ubuntu-latest) (push) Failing after 11m17s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-20 16:17:59 +05:30
ClintchizandClaude Opus 5 7a1b4e5b33 fix(cli): guard the one migration write path that skips parse validation
mode-functions writes without a parse check when a mode wrapper survives
holding api entries, since that intermediate state is unparseable until
move-api-blocks runs later in the same pass. Brace balance is the invariant
a bad splice offset would break, so check that instead; nothing downstream
could tell a corrupted wrapper from an untouched one.

Also records that Task 5's example-app migration ran against an already-
migrated target and so did not prove end-to-end behaviour.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 12:24:19 +05:30
ClintchizandClaude Opus 5 aded2daab9 fix: restore the production gate after the migration tasks
- rebuild editors/vscode bundles, stale since the parser escape fix
- attach the caught ParseError as `cause` in both migration validators
- drop two unused test bindings flagged by eslint

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 12:19:53 +05:30
ClintchizandClaude Opus 5 6bb3ab5fe7 feat(cli): fail the update when a project needs manual review
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 12:12:38 +05:30
ClintchizandClaude Opus 5 4aa0973352 feat(cli): report legacy api bodies for manual migration
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 12:03:17 +05:30
ClintchizandClaude Opus 5 e616ed276e feat(cli): migrate mode-scoped helpers to shared functions
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 11:58:16 +05:30
ClintchizandClaude Opus 5 74490964ee feat(cli): migrate api entries into the apis block
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 11:51:44 +05:30
ClintchizandClaude Opus 5 7a3e55b150 feat(cli): migrate away the dead config keys
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 11:45:40 +05:30
ClintchizandClaude Opus 5 de99a2c2e0 feat(cli): assert types for every api block with declared fields
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 07:44:58 +05:30
ClintchizandClaude Opus 5 d069f5ddd7 fix: correct feature-report prose, restore update test coverage, warn on sub-0.8.0 upgrades
- generate-complete-framework-report.mjs no longer claims legacyEmit/
  legacyEventProps/legacyComponentDiscovery/stringLayouts/
  functions.legacyDefaultRuntime are usable compatibility flags; they were
  removed before the first public release and a config setting them is now
  rejected. Regenerated docs/WRNEXUS-COMPLETE-FEATURE-REPORT.md.
- Restored the update.test.ts coverage lost in the sub-0.8.0 migration
  cleanup: a 0.8.x fixture now asserts refreshFrameworkFiles' still-live
  behaviour (public/llms.txt and CLAUDE.md creation) and that
  pkg.wrnexus.version stays at its old value after an unverified update.
  The .gitignore refresh and build/start/production script backfill were
  themselves removed as part of dropping the sub-0.8.0 migrations that
  implemented them, so there is nothing left to cover for those two.
- updateApp now logs a clear warning when the detected project version is
  below 0.8.0, naming the version and stating that automated migration from
  below 0.8.0 is no longer supported, without failing the command (a
  marker-less app, like examples/basic-app, is benign and must still
  upgrade cleanly).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 02:19:23 +05:30
ClintchizandClaude Opus 5 e7e7b58160 chore: drop update migrations below 0.8.0
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 01:28:33 +05:30
Clintchiz 5c8f3ede54 refactor: delete the compatibility config surface 2026-08-20 01:11:35 +05:30
ClintchizandClaude Opus 5 b5029889a5 fix: address all seven final-gate findings for typed api blocks
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>
2026-08-19 20:38:22 +05:30
ClintchizandClaude Opus 5 04be24ddd8 fix(cli): close the extra-field gap in AssertAssignable, add tsc-based enforcement tests
AssertAssignable was one-directional ([Actual] extends [Expected]), so a
block declaring a field the contract doesn't accept passed silently
(TypeScript's excess-property check only applies to fresh object
literals, not conditional-type extends). Add a key-exactness check
(Exclude<keyof Actual, keyof Expected> extends never) alongside the
assignability check. Guard it with 'unknown extends Expected' so
untyped (no defineEndpoint contract) routes still only warn, per the
existing behaviour, instead of being forced to fail on every declared
field.

Add packages/cli/test/api-block-types.test.ts cases that regenerate a
fixture and run the real TypeScript compiler (via bunx tsc) over the
generated output, asserting on its diagnostics rather than on emitted
text: matching fields compile clean; a wrong-typed field, an extra
field (the Finding-A regression guard), and a missing required field
all fail, each pointing at the offending block's __wrn_api_check_*
line.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 16:14:13 +05:30
ClintchizandClaude Opus 5 785e8a65bd fix(cli): move api-block assertions into a real .ts file
skipLibCheck exempts .d.ts contents from being checked, so assertions
written inside wrnexus.generated.d.ts were never evaluated by tsc.
Emit them into wrnexus.generated.api-checks.ts instead, referencing
the WRNexusGenerated namespace's helper types (which stay in the
.d.ts). Track the new generated file in check:generated-types.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 16:01:07 +05:30
ClintchizandClaude Opus 5 419614d9d1 feat(cli): generate type assertions for api blocks
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 15:56:02 +05:30
ClintchizandClaude Opus 5 55fed2177a fix(compiler): strip TypeScript from client function bodies
Quality / quality (ubuntu-latest) (push) Failing after 9m53s
Quality / quality (windows-latest) (push) Canceled after 0s
`wrnexus build` failed on any client function whose body used TypeScript:

    const requestBody: Record<string, unknown> = {}
    error: Expected ";" but found ":"

Codegen copies a client function's body into the browser module verbatim.
It removes the types from the function's *signature*, which is what made
this easy to miss -- the emitted module looked transpiled, and only bodies
carried types through. The artifact is written as .mjs and read back as
plain JavaScript, so the failure surfaced as a syntax error in generated
code rather than at the .wrn line responsible.

Browser modules are now transpiled before they are written, at all three
sites that emit one (the production build and both dev-server paths).

Reproduced end to end: a page with an annotated body failed the build with
the reported errors, and after the fix builds, ships valid minified JS, and
runs -- the handler sets its state correctly in a browser.

Note: the same body is also embedded as a string for the CSP-safe fallback
interpreter, which still receives it untranspiled. The compiled module
shadows the fallback, so this is only reachable in the window before that
module loads. Left alone here because stripping it lives in codegen, which
also runs under Node in the editor bundle where the Bun transpiler is
unavailable.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 14:03:14 +05:30
ClintchizandClaude Opus 5 10421465df fix(styles): stop scanning every component for utility sources
Component discovery is not utility-source discovery: scanning all
built-in and plugin component directories made Tailwind/Iconify generate
rules for components the app never renders. Packages that need scanning
opt in through styles.source.

Pre-existing working-tree change, committed as-is rather than authored
here.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 19:16:53 +05:30
ClintchizandClaude Opus 5 17aa3b98eb feat(islands): wire islands end to end
The island pieces existed but nothing connected .wrn compilation to island
emission. Now:

- codegen emits a data-wrn-island placeholder for component tags bound to
  .tsx imports, keeping .wrn components on the normal mount path
- the dev pipeline and static build resolve island imports, thread the
  names into codegen, and build the bundles
- collectScripts adds /__wrnexus/islands.js only when island markup is
  present, so island-free pages still ship nothing
- island routes classify as static-interactive via hasIslands

Three bugs found by driving a real page in the browser:

1. The mount runtime was never built anywhere, so the bootstrap 404'd and
   no island mounted.
2. Building the runtime separately from the islands gave each its own copy
   of React: "Cannot read properties of null (reading 'useState')". The
   runtime is now an entrypoint of the same build so React stays in one
   shared chunk. The existing single-React test only compared bundles
   within one build and could not see across build boundaries.
3. Island props arrived as attribute strings, so start={3} was "3" and
   incrementing produced "31" then "311". Props now follow JSX semantics:
   {…} parses as JSON, quoted values stay strings, and a runtime
   expression is a WRN-ISLAND-PROPS build error rather than a silent
   wrong value.

island-codegen.ts no longer imports @wrnexus/core. Compiler modules are
bundled into the Node-only VS Code extension, which contains no other
packages, so a runtime import of core broke the editor compiler; the two
helpers are implemented locally and the core dependency is dropped again.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 16:16:03 +05:30
ClintchizandClaude Opus 5 a184f1a3be feat(islands): serve the island runtime in dev, prod, and static builds
Adds /__wrnexus/islands.js (the bootstrap) and the /__wrnexus/island/
prefix (mount runtime, island bundles, shared chunks) to all three
serving paths.

Dev reuses the browserArtifactPaths registry pattern from pipeline.ts.
Prod mirrors the clientModulesDir handler, including its filename
allowlist, so island names cannot escape the output directory.

The bootstrap is inert without a data-wrn-island marker, so island-free
pages still download nothing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 15:28:10 +05:30
Clintchiz 5106256401 fix(production): preserve emitted plugin runtimes 2026-08-13 18:00:30 +05:30
Clintchiz b508b49058 fix(dev): consolidate generated cache directories
Quality / quality (ubuntu-latest) (push) Failing after 9m50s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-12 22:25:12 +05:30
Clintchiz e638c6be6a fix(cli): bundle current WRN typechecker
Quality / quality (ubuntu-latest) (push) Failing after 9m50s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-12 22:11:32 +05:30
Clintchiz 5de792f359 feat(config): add shared browser cookie policy
Quality / quality (ubuntu-latest) (push) Failing after 9m46s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-12 21:53:07 +05:30
Clintchiz 2c960fc1dc refactor: migrate legacy wire namespace to wrn
Quality / quality (ubuntu-latest) (push) Failing after 9m49s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-12 18:51:15 +05:30
Clintchiz bf0d866793 fix(cli): scaffold installable framework versions
Quality / quality (ubuntu-latest) (push) Failing after 9m53s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-12 17:08:38 +05:30
Clintchiz b1086d14e9 fix(auth): secure hydration and align password forms
Quality / quality (ubuntu-latest) (push) Failing after 6m7s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-11 17:05:04 +05:30
Clintchiz 8d044565e3 feat: publish changed packages independently
Quality / quality (ubuntu-latest) (push) Failing after 21s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-11 15:32:36 +05:30
Clintchiz feff30a2b5 release: prepare WRNexusJS 0.8.8
Quality / quality (ubuntu-latest) (push) Failing after 9m49s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-11 13:40:03 +05:30
Clintchiz bca9549f3a finish CSS delivery and generated type remediation
Quality / quality (ubuntu-latest) (push) Failing after 12m54s
Quality / quality (windows-latest) (push) Canceled after 0s
2026-08-10 00:17:25 +05:30
Clintchiz 232d8e6734 complete performance and reliability follow-ups 2026-08-09 23:04:36 +05:30
Clintchiz 8f19a5eb2b complete UI CSS ownership remediation 2026-08-09 21:42:30 +05:30
Clintchiz 1660044ed2 perf(csr): load component controllers on demand 2026-08-09 14:10:15 +05:30
Clintchiz 5c2c6a0a8f refactor(ui): replace Tailwind utilities with local styles 2026-08-09 13:58:13 +05:30
ClintchizandClaude Opus 5 5e65627305 fix(csr,ui): deliver component outputs to parent bindings
Quality / quality (ubuntu-latest) (push) Failing after 13m39s
Quality / quality (windows-latest) (push) Canceled after 0s
An output only reaches a parent @binding when the component calls
output.<name>(). Two separate faults meant most of the library never got
there, and both failed silently at each end.

HTML lowercases attribute names, so a parent's @sizeChange registered under
"sizechange" while the component emitted "sizeChange". The lookup missed, fell
through to a DOM dispatch, and the binding was never invoked. That made all 17
camelCase outputs undeliverable -- DataTable.pageChange and .rowClick,
Map.markerClick, ChatBubble.messageClick, LayoutSplitter.sizeChange and the
rest. invokeComponentOutput now falls back to a case-insensitive lookup, and a
csr test fails without it.

Separately, 18 components dispatched hand-built CustomEvents rather than
calling output.*. A bubbling event on the component's own root never reaches a
binding, because parent handlers live in a registry only the output proxy
reads. Card, Footer, Breadcrumb, Accordion, alert, Badge, AnnouncementBar,
AvatarGroup, ToggleCount and InputNumber now emit properly; Marquee, Map,
Timeline, List and SearchBox additionally declare the outputs they were
already firing. Dispatches on window are left alone -- that is how Toaster,
Modal and DataTable signal across component boundaries.

Verified in a browser both ways before and after: an AnnouncementBar
dispatching its own bubbling "dismiss" never reached a page-level @dismiss,
and reached it immediately once it called output.dismiss().

This corrects the audit, which called the LayoutSplitter failure "narrow and
unexplained" and read 32 dead outputs as 16 components needing a rebuild.
"Outputs work elsewhere" was an assumption; the components that worked
happened to use lowercase names and output.*. The dead-output ratchet drops
from 32 to 22, and a new test forbids the raw-CustomEvent pattern outright.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-09 01:40:39 +05:30
ClintchizandClaude Opus 5 84dc4e06a2 docs: audit the ui library and record the 0.8.6 migration
Quality / quality (ubuntu-latest) (push) Failing after 12m40s
Quality / quality (windows-latest) (push) Canceled after 0s
A measured pass rather than a bulk rewrite. Numbers come from the source and
from a browser.

The migration entry covers what has accumulated since 0.8.5 and would
otherwise reach upgraders unannounced: the Tabs output contract, the Sidebar
BEM rename, the layout components leaving Tailwind so their rendered class
lists changed, LayoutSplitter and CustomScrollbar changing props and outputs,
the ui.css families that were removed, and the theme tokens that now paint
where they previously resolved to nothing.

The audit records what is still wrong, with counts: 32 outputs across 16
components that nothing emits, 23 components still on the scaffold pattern, 66
without a local style block and therefore dependent on ui.css, and 10 still
using Tailwind. A test pins the dead-output count at 32 as a ceiling that only
moves down, so rebuilding a component tightens it and no new one can be added
quietly.

It also records what is not worth doing. Splitting the runtime saves 3 to 4 kB
gzipped on a first visit to a file cached for a year, and hydration costs
1.5 ms for 21 scopes across 4325 elements, so neither is a real problem.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 17:16:57 +05:30
ClintchizandClaude Opus 5 1fb1a8d2d0 chore(release): prepare 0.8.5
Quality / quality (ubuntu-latest) (push) Failing after 12m21s
Quality / quality (windows-latest) (push) Canceled after 0s
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>
2026-08-07 15:08:05 +05:30
ClintchizandClaude Opus 5 949cf78636 feat(ui): add DataTable and Toaster, drop the legacy Table, fix overlay dialogs
Quality / quality (ubuntu-latest) (push) Failing after 13m40s
Quality / quality (windows-latest) (push) Canceled after 0s
DataTable replaces the 20-line Table scaffold entirely: columns, sorting,
filtering, pagination, selection, bulk actions, comparison layout, sticky
first column, custom HTML cells, and a remote source driven by a `request`
output rather than a function prop (props travel as HTML attributes, so a
function arrives as its own source text).

Toaster replaces the hand-rolled status div: tone icons, actions, hover
pause/resume and a progress bar.

Overlays audit -- Modal and Drawer declared aria-modal="true" but nothing
ever moved focus into the panel, so the @keydown handler on their root
never ran and closeOnEscape did nothing. Focus, focus restore, a Tab trap
and a body scroll lock now live in the reactive runtime, shared by both.

ContextMenu placed pointer menus by subtracting a guessed 340x420 from the
viewport, which pushed every menu that was not that size away from the
pointer; it now positions at the pointer and lets the anchored clamp pull
it back once it can be measured.

The reactive runtime size budget moves 150k -> 175k to cover anchored
overlays, dialog behaviour, the toaster and the DataTable client half.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 14:59:58 +05:30
ClintchizandClaude Opus 5 6aaf21aa06 feat(rpc): add the caller-side example and close remaining coverage gaps
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>
2026-08-05 20:21:41 +05:30
Clintchiz 57097c8204 fix(authz): fix prod boot-order (C1), dev HMR staleness (I2), add prod coverage (I4)
Fix round 2 for Task 14, addressing a critical review finding reproduced on
a real built server.

C1 (critical): the generated production entry set the authz catalog inside
createProductionServer's BODY, but ES modules evaluate every static import
(including app middleware, emitted as a static import) before the importing
module's body runs. Middleware reading getAuthzCatalog() at module scope —
the same eager shape authzMiddleware({ catalog, ... }) itself requires, and
the pattern app/middleware/logger.ts's `export default requestLogger({...})`
already uses — saw an unset catalog and crashed the whole process at import
time, after every other gate (typecheck/lint/tests/a plain `bun run build`)
stayed green.

Fix: packages/cli/src/build.ts now emits a small side-effecting
`.authz-setup.ts` module containing the static imports of every
app/authz/*.ts declaration plus a call to the new
applyAuthzManifestEarly(entries) (packages/dev-server/src/prod.ts), and
imports THAT MODULE FIRST in the generated entry — before pages, api,
realtime, middleware, components, and layouts. applyAuthzManifestEarly is
deliberately silent (no missing-default-export warnings, though a genuine
conflict still throws and fails the boot at import time); createProductionHandlers
keeps its own unconditional merge+set as an idempotent, always-warning second
pass, so an adapter that bypasses the generated entry and calls it directly
still gets a correctly merged, validated catalog, and so the function stays
independently testable.

I3: corrected packages/authz/src/client.ts's WRN-AUTHZ-SETUP message, which
claimed prod always sets the catalog before middleware runs — true again for
the generated entry after the C1 fix, but not for a custom entry that calls
createProductionHandlers directly.

I2: dev HMR editing app/authz/*.ts reloaded the page while the OLD catalog
stayed authoritative (watch.ts classifies any non-CSS change as "server";
hotUpdate had no authz/ branch) — a false security signal, since tightening
or removing a permission looked like it took effect but didn't until a
restart. Added the branch (packages/dev-server/src/index.ts), and gave
loadAppAuthzCatalog (authz-boot.ts) an injectable importer: a raw import()
would have silently no-op'd on the re-import (Bun caches local TS/JS modules
by filesystem path and ignores query strings), so the hot path routes through
loadModule (pipeline.ts) instead, which copies the edited file to a versioned
sibling specifically to defeat that cache.

I4: added direct createProductionHandlers/applyAuthzManifestEarly tests
(packages/dev-server/test/authz-prod.test.ts: conflict throws naming both
files, missing default export warns and skips, empty array yields an empty
catalog, a second call re-validates rather than trusting a stale singleton)
and the regression test that matters most
(packages/cli/test/authz-prod-coldstart.test.ts): a real `runBuild` + a real
`bun dist/server.js` boot, with a middleware module reading
getAuthzCatalog() at module scope, asserting it actually serves a request.

M5: startServer built its own router once, then loadAppAuthzCatalog built a
second one from scratch on every dev boot and every authz/ hot reload.
loadAppAuthzCatalog now accepts either an appDir (still used standalone, e.g.
by the test suite) or an already-built Router, and both call sites in
index.ts now pass the router they already have.

Every fix in this round was verified non-vacuous by sabotaging it and
confirming the corresponding test fails, then reverting.
2026-08-04 23:16:41 +05:30
Clintchiz 226217ecbf feat(authz): reach the merged catalog from boot via a process-wide singleton
Fix round 1 for Task 14 — closes the gap flagged in the last report:
loadAppAuthzCatalog existed but nothing called it.

- packages/authz/src/client.ts (new): setAuthzCatalog/getAuthzCatalog/
  hasAuthzCatalog, mirroring @wrnexus/db's client.ts. App middleware runs
  at module-eval time and needs the catalog then, so ctx cannot carry it;
  getAuthzCatalog() throws a setup error naming the fix, like getDb() does.
  Exported from packages/authz/src/index.ts.
- packages/dev-server/src/index.ts: startServer calls loadAppAuthzCatalog +
  setAuthzCatalog before middleware is resolved (schemasJs precedent),
  and populates the new RuntimeDeps.authz field.
- packages/dev-server/src/runtime.ts: RuntimeDeps gains authz?: AuthzCatalog.
- packages/cli/src/build.ts: emits static imports of each app/authz/*.ts
  file into the generated entry (components/layouts precedent) and passes
  { source, module } pairs through ProdOptions.authz — the catalog holds
  policy functions, so it cannot be JSON-baked like schemasJs.
- packages/dev-server/src/prod.ts: createProductionHandlers merges those
  declarations and calls setAuthzCatalog before the server accepts
  traffic, so a conflict fails the boot instead of surfacing on the first
  request. Runs for every deployment adapter, not only Bun.serve.

The framework never installs authzMiddleware itself; the app still
registers it with its own store.

Verified end-to-end: added a temporary app/authz declaration to
examples/basic-app, ran `bun run build`, inspected the generated entry's
static import + authz array, and booted dist/server.js to confirm the
merge/setAuthzCatalog call succeeds against real bundled code (reverted
before commit).
2026-08-04 22:40:42 +05:30
Clintchiz bc5437063d fix(cli): declare @wrnexus/authz dependency, exit cleanly on bad authz input
Round-1 review fixes for Task 13:

- packages/cli/package.json was missing @wrnexus/authz, and
  packages/authz/package.json was missing @wrnexus/core despite importing
  its types in index.ts/middleware.ts/advanced.ts. Both only worked
  in-repo because bare "@wrnexus/*" specifiers resolve through the root
  tsconfig.json paths map; a standalone install of @wrnexus/cli or
  @wrnexus/authz would fail at runtime.
- authz.ts's unknown/missing-subcommand and bad --dialect paths now
  console.error + process.exit(1), matching db.ts's convention, instead
  of throwing — index.ts's top-level catch previously printed those as a
  raw stack trace. Added a subprocess-level test that spawns the real CLI
  and asserts stderr has the usage line with no stack frame.
- nextMigrationNumber now extracts the leading-digit run the same way
  db/migrate.ts's nextNumber does, instead of a fixed slice(0, 4) that
  would have undercounted once a migration number passed 9999.
2026-08-04 21:51:38 +05:30