diff --git a/.prettierignore b/.prettierignore index bd023dc1..243e1cfc 100644 --- a/.prettierignore +++ b/.prettierignore @@ -4,7 +4,7 @@ dist/ **/.wrnexus/** **/.wrnexus-*/** .publish/ -**/.wirefw/ +**/.wrnfw/ coverage/ bun.lock bun.lockb @@ -14,7 +14,7 @@ bun.lockb **/*.gen.ts **/*.generated.d.ts -# Bundled .wire compiler for the VS Code extension (generated) +# Bundled .wrn compiler for the VS Code extension (generated) editors/vscode/src/compiler.cjs editors/vscode/src/language-server.cjs editors/vscode/src/extension.bundle.cjs diff --git a/.publish/ai/package.json b/.publish/ai/package.json index 0d642cbc..f8c8a749 100644 --- a/.publish/ai/package.json +++ b/.publish/ai/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/ai", - "version": "0.8.8", + "version": "0.8.9", "type": "module", "description": "Zero-dependency Claude (Anthropic) client for WrNexus apps.", "license": "MIT", diff --git a/.publish/authz/package.json b/.publish/authz/package.json index 122e944e..b6aabcf9 100644 --- a/.publish/authz/package.json +++ b/.publish/authz/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/authz", - "version": "0.8.8", + "version": "0.8.9", "type": "module", "description": "@wrnexus/authz — part of the WrNexus framework.", "license": "MIT", @@ -41,8 +41,8 @@ } }, "dependencies": { - "@wrnexus/core": "^0.8.8", - "@wrnexus/db": "^0.8.8" + "@wrnexus/core": "^0.8.9", + "@wrnexus/db": "^0.8.9" }, "files": [ "dist", diff --git a/.publish/cli/README.md b/.publish/cli/README.md index 591332fb..34792727 100644 --- a/.publish/cli/README.md +++ b/.publish/cli/README.md @@ -79,7 +79,7 @@ Every command accepts an optional `[app-dir]` (defaults to `.`). Commands that r | `wrnexus generate docker` | Scaffold `Dockerfile`, `.dockerignore`, and `docker-compose.yml`. | | `wrnexus generate mobile` | Scaffold a Capacitor shell for iOS and Android. | | `wrnexus mobile add ` | Install Capacitor plugins and sync native projects. | -| `wrnexus eject ` | Copy Wire UI component `.wrn` sources into `app/components/`. | +| `wrnexus eject ` | Copy WrNexus UI component `.wrn` sources into `app/components/`. | | `wrnexus db ` | Database migrations and tooling (see [db](#wrnexus-db)). | | `wrnexus test [app-dir] [--watch]` | Run the app's tests via `bun test` (defaults to the `test` profile). | | `wrnexus profiles [app-dir]` | List config profiles and their `.env` files, marking the active one. | @@ -179,7 +179,7 @@ WrNexus response pages. ### `wrnexus eject` -Copies a Wire UI component's `.wrn` source out of `@wrnexus/ui` into `app/components/`, so the app owns and can edit it (the app copy shadows the library one by name). Run with no names to list available components. It skips components that already exist in the app. +Copies a WrNexus UI component's `.wrn` source out of `@wrnexus/ui` into `app/components/`, so the app owns and can edit it (the app copy shadows the library one by name). Run with no names to list available components. It skips components that already exist in the app. ```bash wrnexus eject button card modal @@ -322,5 +322,5 @@ export default config; ## Requirements / Notes - **Bun-only.** The CLI runs on Bun, spawns the Bun binary for the dev child and `bun test`, and the production build uses `Bun.build`. Node is not supported. -- Orchestrates the rest of the framework: `@wrnexus/dev-server` (dev/prod server + gateway), `@wrnexus/router` (route + typed-routes codegen), `@wrnexus/compiler` (`.wrn` → `.ts`), `@wrnexus/db` (migrations, typed queries), `@wrnexus/styles` (config, profiles, `.env`, themes, styles), `@wrnexus/ui` (ejectable Wire UI components), `@wrnexus/validation`, `@wrnexus/csr`, and `@wrnexus/i18n`. +- Orchestrates the rest of the framework: `@wrnexus/dev-server` (dev/prod server + gateway), `@wrnexus/router` (route + typed-routes codegen), `@wrnexus/compiler` (`.wrn` → `.ts`), `@wrnexus/db` (migrations, typed queries), `@wrnexus/styles` (config, profiles, `.env`, themes, styles), `@wrnexus/ui` (ejectable WrNexus UI components), `@wrnexus/validation`, `@wrnexus/csr`, and `@wrnexus/i18n`. - Reads `wrnexus.config.ts` for `db` / `databases`, `theme`, `styles`, `seo`, `security`, `i18n`, and `profiles`, and `wrnexus.workspace.ts` for the gateway. diff --git a/.publish/cli/package.json b/.publish/cli/package.json index 49898fc4..d9893b8e 100644 --- a/.publish/cli/package.json +++ b/.publish/cli/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/cli", - "version": "0.8.8", + "version": "0.8.17", "type": "module", "description": "@wrnexus/cli — part of the WrNexus framework.", "license": "MIT", @@ -44,19 +44,19 @@ "wrnexus": "./dist/index.js" }, "dependencies": { - "@wrnexus/core": "^0.8.8", - "@wrnexus/router": "^0.8.8", - "@wrnexus/csr": "^0.8.8", - "@wrnexus/compiler": "^0.8.8", - "@wrnexus/styles": "^0.8.8", - "@wrnexus/dev-server": "^0.8.8", - "@wrnexus/ui": "^0.8.8", - "@wrnexus/validation": "^0.8.8", - "@wrnexus/i18n": "^0.8.8", + "@wrnexus/core": "^0.8.9", + "@wrnexus/router": "^0.8.9", + "@wrnexus/csr": "^0.8.16", + "@wrnexus/compiler": "^0.8.9", + "@wrnexus/styles": "^0.8.10", + "@wrnexus/dev-server": "^0.8.16", + "@wrnexus/ui": "^0.8.12", + "@wrnexus/validation": "^0.8.9", + "@wrnexus/i18n": "^0.8.9", "@wrnexus/mcp": "^0.8.8", "@wrnexus/playground": "^0.8.8", - "@wrnexus/db": "^0.8.8", - "@wrnexus/authz": "^0.8.8", + "@wrnexus/db": "^0.8.9", + "@wrnexus/authz": "^0.8.9", "@wrnexus/plugin": "^0.8.8", "@wrnexus/syntax": "^0.8.8", "@wrnexus/typecheck": "^0.8.8", diff --git a/.publish/compiler/README.md b/.publish/compiler/README.md index 7eeeea82..70854969 100644 --- a/.publish/compiler/README.md +++ b/.publish/compiler/README.md @@ -69,7 +69,7 @@ bun add @wrnexus/compiler All exports come from the package root (`@wrnexus/compiler`). -### `compileWireFile(source: string): string` +### `compileWrnFile(source: string): string` Compile `.wrn` source to a TypeScript module string. Throws `ParseError` on invalid input. The output is prefixed with a `// compiled from .wrn` comment. @@ -115,10 +115,10 @@ class Lexer { ### Errors -| Class | Thrown by | Meaning | -| ------------ | ------------------------------------------------- | --------------------------------------------------------------- | -| `ParseError` | `parse`, `compile`, `compileWireFile`, `generate` | Invalid `.wrn` grammar or (rewrapped) lex failure. | -| `LexError` | `Lexer` | Unexpected character / unterminated string / unbalanced braces. | +| Class | Thrown by | Meaning | +| ------------ | ------------------------------------------------ | --------------------------------------------------------------- | +| `ParseError` | `parse`, `compile`, `compileWrnFile`, `generate` | Invalid `.wrn` grammar or (rewrapped) lex failure. | +| `LexError` | `Lexer` | Unexpected character / unterminated string / unbalanced braces. | ### AST types @@ -143,9 +143,9 @@ Exported type-only symbols describing the parsed tree: Compile a page: ```ts -import { compileWireFile } from "@wrnexus/compiler"; +import { compileWrnFile } from "@wrnexus/compiler"; -const ts = compileWireFile(` +const ts = compileWrnFile(` page Home { state count = 0 seo { title = "Home" description = "Welcome" } @@ -214,4 +214,4 @@ A file opens with `page ` or `component ` followed by a `{ ... }` bo ## Requirements / Notes - Pure TypeScript with no runtime dependencies; runs under **Bun** as part of the WrNexus toolchain (Node is not supported). -- Generated modules target WrNexus runtime primitives (`data-scope`, `data-text`, `data-on-*`, `data-for`, `data-component`, `__wrnexus*`/`__wire*` helpers) — consume the output within a WrNexus app, e.g. via `@wrnexus/core`'s dev loader. +- Generated modules target WrNexus runtime primitives (`data-scope`, `data-text`, `data-on-*`, `data-for`, `data-component`, `__wrnexus*`/`__wrn*` helpers) — consume the output within a WrNexus app, e.g. via `@wrnexus/core`'s dev loader. diff --git a/.publish/compiler/package.json b/.publish/compiler/package.json index 0ed98c56..80c5cc4a 100644 --- a/.publish/compiler/package.json +++ b/.publish/compiler/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/compiler", - "version": "0.8.8", + "version": "0.8.9", "type": "module", "description": "@wrnexus/compiler — part of the WrNexus framework.", "license": "MIT", @@ -37,10 +37,10 @@ } }, "dependencies": { - "@wrnexus/csr": "^0.8.8", + "@wrnexus/csr": "^0.8.16", "@wrnexus/syntax": "^0.8.8", "@wrnexus/store": "^0.8.8", - "@wrnexus/validation": "^0.8.8" + "@wrnexus/validation": "^0.8.9" }, "files": [ "dist", diff --git a/.publish/core/README.md b/.publish/core/README.md index 0a08481e..4fd267da 100644 --- a/.publish/core/README.md +++ b/.publish/core/README.md @@ -37,7 +37,7 @@ The `Context` (`ctx`) is the single value passed to middleware and handlers. | `Context` | type | Per-request object: `req`, `url`, `lang`, `t`, `params`, `locals`, `user?`, `ip?`, `cookies`, `session`, `localStorage`. | | `Next` | type | `() => Promise \| Response` — invokes the next middleware/handler. | | `Middleware` | type | `(ctx, next) => Promise \| Response`. Return `next()` to continue, or a `Response` to short-circuit. | -| `createContext(req, url)` | fn | Build a fresh `Context` for an incoming request (wires up cookies, session, localStorage snapshot). | +| `createContext(req, url)` | fn | Build a fresh `Context` for an incoming request (connects up cookies, session, localStorage snapshot). | | `withContextHeaders(ctx, res)` | fn | Apply accumulated headers (e.g. `Set-Cookie`) from the context onto a response. | | `PageComponent` | type | `(ctx) => string \| Promise` — a page module's default export. | | `PageMeta` / `SeoConfig` | type | `` metadata: `title`, `description`, `canonical`, `robots`, `image`, `twitterCard`, `themeColor`, … | @@ -70,15 +70,15 @@ cookie-backed `SessionStore`. ### CSRF — `@wrnexus/core` -Double-submit cookie pattern: a readable `wire-csrf` cookie is echoed in an +Double-submit cookie pattern: a readable `wrn-csrf` cookie is echoed in an `x-csrf-token` header on unsafe requests. -| Export | Signature | Notes | -| ----------------------------- | -------------------------------- | ---------------------------------------------------------------------------------------------------------------- | -| `csrfToken(ctx)` | `(Context) => string` | Ensures the CSRF cookie exists and returns its token. | -| `verifyCsrf(ctx)` | `(Context) => boolean` | Safe methods (GET/HEAD/OPTIONS) pass; otherwise header/`ctx.locals._csrf` must match the cookie (constant-time). | -| `csrfProtection()` | `() => Middleware` | 403s unsafe requests with a missing/mismatched token. | -| `CSRF_COOKIE` / `CSRF_HEADER` | `"wire-csrf"` / `"x-csrf-token"` | Cookie & header names. | +| Export | Signature | Notes | +| ----------------------------- | ------------------------------- | ---------------------------------------------------------------------------------------------------------------- | +| `csrfToken(ctx)` | `(Context) => string` | Ensures the CSRF cookie exists and returns its token. | +| `verifyCsrf(ctx)` | `(Context) => boolean` | Safe methods (GET/HEAD/OPTIONS) pass; otherwise header/`ctx.locals._csrf` must match the cookie (constant-time). | +| `csrfProtection()` | `() => Middleware` | 403s unsafe requests with a missing/mismatched token. | +| `CSRF_COOKIE` / `CSRF_HEADER` | `"wrn-csrf"` / `"x-csrf-token"` | Cookie & header names. | ### Rate limiting — `@wrnexus/core` diff --git a/.publish/core/package.json b/.publish/core/package.json index 9442ed72..0b912234 100644 --- a/.publish/core/package.json +++ b/.publish/core/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/core", - "version": "0.8.8", + "version": "0.8.9", "type": "module", "description": "@wrnexus/core — part of the WrNexus framework.", "license": "MIT", diff --git a/.publish/csr/README.md b/.publish/csr/README.md index 7a0288eb..66c295f1 100644 --- a/.publish/csr/README.md +++ b/.publish/csr/README.md @@ -111,11 +111,11 @@ Intercepts same-origin `` clicks, fetches the target page, and swaps the `#ap Connects to `/realtime/` over WebSocket (`ws`/`wss` chosen from `location.protocol`). Two usage modes. -Programmatic API via `window.wire`: +Programmatic API via `window.wrn`: ```ts -wire.room(name): Room // open (or reuse) a room connection -wire.bindRooms(root?) // (re)bind declarative [data-room] containers +wrn.room(name): Room // open (or reuse) a room connection +wrn.bindRooms(root?) // (re)bind declarative [data-room] containers interface Room { name: string; @@ -211,7 +211,7 @@ A realtime chat, fully declarative: Or drive a room from code: ```ts -const room = wire.room("lobby"); +const room = wrn.room("lobby"); room.on("chat", (msg) => console.log(msg.user, msg.text)); room.send({ type: "chat", user: "ada", text: "hi" }); ``` diff --git a/.publish/csr/package.json b/.publish/csr/package.json index 20662018..ab224c78 100644 --- a/.publish/csr/package.json +++ b/.publish/csr/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/csr", - "version": "0.8.8", + "version": "0.8.16", "type": "module", "description": "@wrnexus/csr — part of the WrNexus framework.", "license": "MIT", @@ -37,7 +37,7 @@ } }, "dependencies": { - "@wrnexus/core": "^0.8.8" + "@wrnexus/core": "^0.8.9" }, "files": [ "dist", diff --git a/.publish/db/README.md b/.publish/db/README.md index ee7f906b..7fb4f5e6 100644 --- a/.publish/db/README.md +++ b/.publish/db/README.md @@ -128,7 +128,7 @@ const events = await getDb("analytics").all("SELECT * FROM hits"); Migrations are `.sql` files (in e.g. `app/db/migrations`), each split into `-- +up` and `-- +down` sections. A file with no markers is treated entirely as -`up`. Applied names are recorded in a `_wire_migrations` table so each runs once. +`up`. Applied names are recorded in a `_wrn_migrations` table so each runs once. - `parseMigration(name, content)` → `Migration` (`{ name, up, down }`). - `loadMigrations(dir)` — parse all `.sql` files, sorted by filename. @@ -249,7 +249,7 @@ SQL driver — use `@wrnexus/db/mongo` directly. - **Bun-only.** Uses `bun:sqlite` (SQLite adapter + session store) and `Bun.SQL` (Postgres/MySQL). Migrations/scaffolding use `node:fs`/`node:path`. - Works with `@wrnexus/core` — `sqliteSessionStore` implements its - `SessionBackend`; `getDb`/`setDb` are wired by the WrNexus runtime from + `SessionBackend`; `getDb`/`setDb` are connected by the WrNexus runtime from `wrnexus.config.ts`. - The `mongodb` npm package is an optional, lazily-imported peer — install it only if you use `@wrnexus/db/mongo`. The core package stays dependency-free. diff --git a/.publish/db/package.json b/.publish/db/package.json index e94a52b7..614b2ee4 100644 --- a/.publish/db/package.json +++ b/.publish/db/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/db", - "version": "0.8.8", + "version": "0.8.9", "type": "module", "description": "Typed database drivers, migrations, instrumentation, repositories, pagination, and transaction helpers.", "license": "MIT", diff --git a/.publish/dev-server/README.md b/.publish/dev-server/README.md index 9159b79e..9290e0ad 100644 --- a/.publish/dev-server/README.md +++ b/.publish/dev-server/README.md @@ -6,7 +6,7 @@ Part of the **WrNexus** framework — an SSR-first, Bun-native full-stack web fr ## Overview -This package is the server runtime that powers a WrNexus app in both development and production. A single **request runtime** (`createHandlers`) owns HTTP/WebSocket dispatch and SSR document assembly; it knows nothing about _how_ modules and assets are produced, so the dev and prod entry points wire in different backends: dev uses dynamic module loading plus on-the-fly bundling and injects a live-reload client; prod uses a static, pre-built manifest with cache-immutable assets. The package also ships a multi-app **gateway** (route several apps by `Host` header behind one port) and a portable `node:http` adapter for WinterCG hosts. It is entirely server-side and Bun-native (`Bun.serve`, `Bun.file`, `Bun.gzipSync`). +This package is the server runtime that powers a WrNexus app in both development and production. A single **request runtime** (`createHandlers`) owns HTTP/WebSocket dispatch and SSR document assembly; it knows nothing about _how_ modules and assets are produced, so the dev and prod entry points wrn in different backends: dev uses dynamic module loading plus on-the-fly bundling and injects a live-reload client; prod uses a static, pre-built manifest with cache-immutable assets. The package also ships a multi-app **gateway** (route several apps by `Host` header behind one port) and a portable `node:http` adapter for WinterCG hosts. It is entirely server-side and Bun-native (`Bun.serve`, `Bun.file`, `Bun.gzipSync`). ## Installation @@ -21,16 +21,16 @@ bun add @wrnexus/dev-server ### Main entry (`@wrnexus/dev-server`) -| Export | Kind | Purpose | -| --------------------------------------------------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | -| `startServer(opts: ServeOptions)` | `Promise` | Start the dev server on `Bun.serve`: builds the router, connects/migrates databases, wires assets + HMR, and starts the file watcher. | -| `createHandlers(deps: RuntimeDeps)` | `Handlers` | The shared request runtime (fetch + websocket handlers). Re-exported from `runtime.ts`. | -| `createProductionServer(manifest, opts)` | `Bun.Server` | Start the production server from a precompiled manifest. | -| `createProductionHandlers(manifest, opts)` | `Handlers` | Build the portable prod fetch/websocket handlers with no server bound (the deployment-adapter seam). | -| `startGateway(opts: GatewayOptions)` | `Promise` | Boot multiple apps as child processes and route by `Host`. | -| `toRequest`, `writeResponse`, `nodeListener`, `serveNode` | functions | `node:http` ↔ WinterCG `Request`/`Response` adapter. | -| `RESTART_EXIT_CODE` | `number` (`97`) | Exit code the dev child uses to ask the supervisor for a fresh process. | -| `STYLES_HREF`, `HMR_CLIENT_JS` | constants | The global stylesheet URL and the inline HMR client script. | +| Export | Kind | Purpose | +| --------------------------------------------------------- | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | +| `startServer(opts: ServeOptions)` | `Promise` | Start the dev server on `Bun.serve`: builds the router, connects/migrates databases, connects assets + HMR, and starts the file watcher. | +| `createHandlers(deps: RuntimeDeps)` | `Handlers` | The shared request runtime (fetch + websocket handlers). Re-exported from `runtime.ts`. | +| `createProductionServer(manifest, opts)` | `Bun.Server` | Start the production server from a precompiled manifest. | +| `createProductionHandlers(manifest, opts)` | `Handlers` | Build the portable prod fetch/websocket handlers with no server bound (the deployment-adapter seam). | +| `startGateway(opts: GatewayOptions)` | `Promise` | Boot multiple apps as child processes and route by `Host`. | +| `toRequest`, `writeResponse`, `nodeListener`, `serveNode` | functions | `node:http` ↔ WinterCG `Request`/`Response` adapter. | +| `RESTART_EXIT_CODE` | `number` (`97`) | Exit code the dev child uses to ask the supervisor for a fresh process. | +| `STYLES_HREF`, `HMR_CLIENT_JS` | constants | The global stylesheet URL and the inline HMR client script. | Exported types: `ServeOptions`, `RunningServer`, `RuntimeDeps`, `AssetServer`, `WsData`, `GatewayApp`, `GatewayOptions`, `GatewayAuth`, `GatewaySecurity`, `RunningGateway`, `FetchHandler`. @@ -85,7 +85,7 @@ interface RuntimeDeps { getMiddleware(): Promise; assets: AssetServer; // serves /__wrnexus/* (islands, reactive, hmr) hasStyles?: boolean; // inject the global stylesheet link - hasUi?: boolean; // inject the Wire UI stylesheet (/__wrnexus/ui.css) + hasUi?: boolean; // inject the WrNexus UI stylesheet (/__wrnexus/ui.css) theme?: ResolvedTheme; // enables /__wrnexus/theme.css + i18n?: ResolvedI18n; // enables ctx.t, , {t:key} markers inlineStyles?: string; // inline small prod stylesheets into diff --git a/.publish/dev-server/package.json b/.publish/dev-server/package.json index e44bc699..59235b0d 100644 --- a/.publish/dev-server/package.json +++ b/.publish/dev-server/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/dev-server", - "version": "0.8.8", + "version": "0.8.16", "type": "module", "description": "@wrnexus/dev-server — part of the WrNexus framework.", "license": "MIT", @@ -41,21 +41,21 @@ } }, "dependencies": { - "@wrnexus/authz": "^0.8.8", - "@wrnexus/rpc": "^0.8.8", - "@wrnexus/core": "^0.8.8", - "@wrnexus/dev-toolbar": "^0.8.8", - "@wrnexus/router": "^0.8.8", - "@wrnexus/ssr": "^0.8.8", - "@wrnexus/csr": "^0.8.8", - "@wrnexus/compiler": "^0.8.8", - "@wrnexus/styles": "^0.8.8", - "@wrnexus/ui": "^0.8.8", - "@wrnexus/validation": "^0.8.8", - "@wrnexus/i18n": "^0.8.8", - "@wrnexus/db": "^0.8.8", - "@wrnexus/pubsub": "^0.8.8", - "@wrnexus/uploader": "^0.8.8", + "@wrnexus/authz": "^0.8.9", + "@wrnexus/rpc": "^0.8.9", + "@wrnexus/core": "^0.8.9", + "@wrnexus/dev-toolbar": "^0.8.9", + "@wrnexus/router": "^0.8.9", + "@wrnexus/ssr": "^0.8.9", + "@wrnexus/csr": "^0.8.16", + "@wrnexus/compiler": "^0.8.9", + "@wrnexus/styles": "^0.8.10", + "@wrnexus/ui": "^0.8.12", + "@wrnexus/validation": "^0.8.9", + "@wrnexus/i18n": "^0.8.9", + "@wrnexus/db": "^0.8.9", + "@wrnexus/pubsub": "^0.8.9", + "@wrnexus/uploader": "^0.8.9", "@wrnexus/plugin": "^0.8.8", "@wrnexus/store": "^0.8.8", "@wrnexus/security": "^0.8.8", diff --git a/.publish/dev-toolbar/package.json b/.publish/dev-toolbar/package.json index a50940cd..5e493651 100644 --- a/.publish/dev-toolbar/package.json +++ b/.publish/dev-toolbar/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/dev-toolbar", - "version": "0.8.8", + "version": "0.8.9", "type": "module", "description": "@wrnexus/dev-toolbar — part of the WrNexus framework.", "license": "MIT", diff --git a/.publish/encryption/README.md b/.publish/encryption/README.md deleted file mode 100644 index e58e9196..00000000 --- a/.publish/encryption/README.md +++ /dev/null @@ -1,67 +0,0 @@ -# @wrnexus/encryption - -Authenticated encryption, hashing, HMAC, key rotation, and optional encrypted HTTP exchanges for WRNexusJS. - -## Core helpers - -- `generateKey()` — random 256-bit AES key encoded as base64. -- `deriveKey(password, salt)` — PBKDF2-derived AES key. -- `encrypt(plaintext, key)` / `decrypt(payload, key)` — AES-256-GCM. -- `sha256(data)` — SHA-256 digest. -- `hmacSign(data, secret)` / `hmacVerify(...)` — HMAC-SHA256. -- `createKeyring(keys)` — active/previous key management. -- `seal()` / `open()` — versioned ciphertext with key ID. - -## Encrypted HTTP envelope - -```ts -import { - createEncryptedRequest, - createKeyring, - createMemoryReplayStore, - decryptEncryptedResponse, - encryptedExchange, -} from "@wrnexus/encryption"; - -const keyring = createKeyring([{ id: "2026-08", secret: process.env.API_BODY_KEY!, active: true }]); - -const replayStore = createMemoryReplayStore(); - -// Server middleware. -app.use( - encryptedExchange({ - keyring, - replayStore, - maxAgeMs: 60_000, - maxBodyBytes: 1_048_576, - }), -); - -// Controlled service/native client. -const request = await createEncryptedRequest( - "https://api.example.com/private/report", - { reportId: "report-1" }, - { method: "POST", keyring }, -); -const response = await fetch(request); -const result = await decryptEncryptedResponse(response, request, { keyring }); -``` - -The envelope binds authenticated ciphertext to: - -- HTTP method -- URL path and query -- request ID -- timestamp and expiry window -- encryption key ID -- optional replay-store consumption - -`encryptedBody()` decrypts request bodies only. `encryptedExchange()` also encrypts successful downstream responses while allowing application exceptions to propagate normally. `encryptedFetch()` provides a convenient controlled-client call. - -## Security boundary - -Encrypted HTTP bodies **do not replace TLS/HTTPS**. Always use HTTPS. - -This layer is appropriate for service-to-service traffic, native/mobile applications, controlled agents, and selected fields protected with server-managed keys. It cannot conceal data from an end user when browser JavaScript receives the decryption key. Never ship a long-lived server encryption key to a browser. - -Use a shared replay store such as Redis in multi-instance deployments. The memory replay store is process-local. diff --git a/.publish/encryption/package.json b/.publish/encryption/package.json deleted file mode 100644 index 457f8fcf..00000000 --- a/.publish/encryption/package.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": "@wrnexus/encryption", - "version": "0.8.8", - "type": "module", - "description": "Authenticated encryption, key rotation, hashing, and optional application-layer encrypted HTTP envelopes.", - "license": "MIT", - "repository": { - "type": "git", - "url": "https://git.workroot.in/WorkRoot/WRNexusJS.git", - "directory": "packages/encryption" - }, - "homepage": "https://wrnexusjs.dev/packages/encryption", - "bugs": { - "url": "https://git.workroot.in/WorkRoot/WRNexusJS/issues" - }, - "keywords": [ - "wrnexus", - "bun", - "typescript", - "encryption" - ], - "sideEffects": false, - "main": "./dist/index.js", - "module": "./dist/index.js", - "types": "./dist/index.d.ts", - "engines": { - "bun": ">=1.3.0" - }, - "publishConfig": { - "registry": "https://registry.npmjs.org/", - "access": "restricted" - }, - "exports": { - ".": { - "types": "./dist/index.d.ts", - "import": "./dist/index.js" - } - }, - "dependencies": { - "@wrnexus/core": "^0.8.8" - }, - "files": [ - "dist", - "README.md" - ] -} diff --git a/.publish/helpers/README.md b/.publish/helpers/README.md deleted file mode 100644 index 7881480f..00000000 --- a/.publish/helpers/README.md +++ /dev/null @@ -1,81 +0,0 @@ -# @wrnexus/helpers - -Safe convenience helpers for common WrNexus application flows. The package uses -standard `Context`, `URL`, and `Response` values and has no runtime dependency beyond -`@wrnexus/core`. - -## Installation - -```bash -bun add @wrnexus/helpers -``` - -The package is private, so the machine must be authenticated to the `wrnexus` npm -organization. - -## Usage - -### Redirect an unauthenticated forward-auth request - -The gateway calls an SSO verifier on a different URL from the original application. -These helpers reconstruct the original URL from the gateway headers and safely place it -in the login redirect: - -```ts -import type { Context } from "@wrnexus/core"; -import { redirectToLogin } from "@wrnexus/helpers"; - -export const GET = async (ctx: Context) => { - if (await hasValidSession(ctx)) { - return new Response(null, { status: 204 }); - } - - return redirectToLogin(ctx, "/login", { - allowedHosts: ["admin.localhost:3000", "reports.localhost:3000"], - }); -}; -``` - -This creates a response such as: - -```text -Location: http://sso.localhost:3000/login?returnTo=http%3A%2F%2Fadmin.localhost%3A3000%2F -``` - -Always list the application hosts that are valid redirect destinations. Forwarded host -headers are rejected when `allowedHosts` is absent or does not match, preventing an open -redirect. - -The SSO hostname is the login destination, not an `allowedHosts` entry. For example, -when protecting `admin.localhost:3000`, keep `admin.localhost:3000` in the allowlist even -though the verifier runs at `sso.localhost:3000`. WRNexus preserves both hosts across a -nested gateway request. - -### Support dynamic tenant domains - -```ts -import type { Context } from "@wrnexus/core"; -import { getOriginalRequestOrigin, redirectToLogin } from "@wrnexus/helpers"; - -export const GET = async (ctx: Context) => { - const allowedHosts = (host: string) => host === "example.test" || host.endsWith(".example.test"); - - console.info("Authentication requested by", getOriginalRequestOrigin(ctx, { allowedHosts })); - return redirectToLogin(ctx, "https://auth.example.test/login", { - allowedHosts, - returnToParam: "continue", - status: 303, - }); -}; -``` - -## API - -- `getOriginalRequestUrl(ctx, options): URL` — reconstruct the gateway URL. -- `getOriginalRequestOrigin(ctx, options): string` — return only its origin. -- `getOriginalRequestPath(ctx): string` — return its path and query string. -- `getOriginalRequestMethod(ctx): string` — return its HTTP method. -- `redirectToLogin(ctx, loginUrl, options): Response` — create a login redirect with an - encoded `returnTo` parameter. - -For direct requests without gateway headers, URL helpers use `ctx.url`. diff --git a/.publish/helpers/package.json b/.publish/helpers/package.json deleted file mode 100644 index d8b263aa..00000000 --- a/.publish/helpers/package.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": "@wrnexus/helpers", - "version": "0.8.8", - "type": "module", - "description": "Safe convenience helpers for WrNexus request contexts and common application flows.", - "license": "MIT", - "repository": { - "type": "git", - "url": "https://git.workroot.in/WorkRoot/WRNexusJS.git", - "directory": "packages/helpers" - }, - "homepage": "https://wrnexusjs.dev/packages/helpers", - "bugs": { - "url": "https://git.workroot.in/WorkRoot/WRNexusJS/issues" - }, - "keywords": [ - "wrnexus", - "bun", - "typescript", - "helpers" - ], - "sideEffects": false, - "main": "./dist/index.js", - "module": "./dist/index.js", - "types": "./dist/index.d.ts", - "engines": { - "bun": ">=1.3.0" - }, - "publishConfig": { - "registry": "https://registry.npmjs.org/", - "access": "restricted" - }, - "exports": { - ".": { - "types": "./dist/index.d.ts", - "import": "./dist/index.js" - } - }, - "dependencies": { - "@wrnexus/core": "^0.8.8" - }, - "files": [ - "dist", - "README.md" - ] -} diff --git a/.publish/i18n/README.md b/.publish/i18n/README.md index e173a22e..fda680ae 100644 --- a/.publish/i18n/README.md +++ b/.publish/i18n/README.md @@ -22,7 +22,7 @@ const i18n = resolveI18n(loadLocales("app/locales", { strict: true }), { default: "en", locales: ["en", "mr", "hi"], fallbacks: { "mr-IN": ["mr", "en"] }, - cookie: { name: "wire-lang", sameSite: "Lax", secure: true }, + cookie: { name: "wrn-lang", sameSite: "Lax", secure: true }, }); const lang = resolveLang(i18n, cookieValue, request.headers.get("accept-language")); @@ -57,7 +57,7 @@ Enable `i18nPlugin()` to use: - `` - `` -`LanguageSwitcher` renders a native `select[data-wire-lang]`. The packaged runtime validates the +`LanguageSwitcher` renders a native `select[data-wrn-lang]`. The packaged runtime validates the selection against the configured locales, writes the configured language cookie, updates the document `lang`/`dir` attributes, emits `wrnexus:language-change`, and reloads so the next SSR request uses the same cookie. No application-owned browser script is required. diff --git a/.publish/i18n/package.json b/.publish/i18n/package.json index 2b75181e..efa01fd8 100644 --- a/.publish/i18n/package.json +++ b/.publish/i18n/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/i18n", - "version": "0.8.8", + "version": "0.8.9", "type": "module", "description": "Locale loading, fallback resolution, SSR/browser translations, formatters, and WRNexusJS language components.", "license": "MIT", @@ -42,9 +42,9 @@ "./components/*": "./components/*" }, "dependencies": { - "@wrnexus/core": "^0.8.8", + "@wrnexus/core": "^0.8.9", "@wrnexus/plugin": "^0.8.8", - "@wrnexus/ui": "^0.8.8" + "@wrnexus/ui": "^0.8.12" }, "wrnexus": { "plugin": { diff --git a/.publish/jwt/README.md b/.publish/jwt/README.md deleted file mode 100644 index 2b5b0903..00000000 --- a/.publish/jwt/README.md +++ /dev/null @@ -1,191 +0,0 @@ -# @wrnexus/jwt - -> Dependency-free JSON Web Tokens (HS256) via Web Crypto, plus a bearer-token auth middleware for WrNexus. - -Part of the **WrNexus** framework — an SSR-first, Bun-native full-stack web framework. - -## Overview - -`@wrnexus/jwt` signs and verifies stateless JSON Web Tokens using the **HS256** -(HMAC-SHA-256) algorithm. It has no runtime dependencies — signing and -verification are implemented directly on the standard **Web Crypto** API -(`crypto.subtle`), which Bun provides natively. It runs server-side and pairs -with the session-based auth in `@wrnexus/core`, giving you a stateless option -for API and mobile clients. Reach for it when you need bearer-token auth rather -than cookie sessions. - -## Installation - -```bash -bun add @wrnexus/jwt -``` - -> Private package — the machine must be authenticated to the `wrnexus` npm org -> (a read token in `~/.npmrc`). Requires **Bun** (Node is not supported). - -## API - -Single entry point (`@wrnexus/jwt`). All functions are async and return Promises. - -| Export | Kind | Description | -| --------------------------------------- | --------- | --------------------------------------------------------------- | -| `signJwt(payload, secret, options?)` | function | Sign claims into an HS256 token string. | -| `verifyJwt(token, secret, options?)` | function | Verify a token and return its claims, or throw. | -| `jwtAuth(options)` | function | Middleware that verifies a bearer JWT and sets `ctx.user`. | -| `JwtError` | class | Error thrown on any signature/payload/expiry failure. | -| `JwtClaims` | interface | Claims shape (`sub`, `iat`, `exp`, `nbf`, plus arbitrary keys). | -| `SignOptions` | interface | Options for `signJwt`. | -| `JwtAuthOptions` | interface | Options for `jwtAuth`. | - -### `signJwt(payload, secret, options?)` - -```ts -function signJwt(payload: JwtClaims, secret: string, options?: SignOptions): Promise; -``` - -Signs `payload` with `secret` using HS256 and returns the encoded token -(`header.body.signature`). An `iat` (issued-at) claim is always added. - -`SignOptions`: - -- `expiresIn?: number` — seconds until expiry; sets the `exp` claim. -- `now?: number` — override the issued-at time (seconds), useful for testing. - -### `verifyJwt(token, secret, options?)` - -```ts -function verifyJwt( - token: string, - secret: string, - options?: { now?: number }, -): Promise; -``` - -Verifies the HS256 signature and returns the decoded claims typed as `T`. -Throws `JwtError` when the token is malformed, the signature is invalid, the -payload is not valid JSON, the token is expired (`exp`), or not yet valid -(`nbf`). Pass `now` (seconds) to override the reference time for the `exp`/`nbf` -checks. - -### `jwtAuth(options)` - -```ts -function jwtAuth(options: JwtAuthOptions): Middleware; -``` - -Returns a WrNexus `Middleware` that reads a token, verifies it, and assigns the -claims to `ctx.user`. - -`JwtAuthOptions`: - -- `secret: string` — the HMAC secret used to verify tokens. -- `getToken?: (ctx: Context) => string | undefined` — how to extract the token. - Defaults to reading `Authorization: Bearer `. -- `required?: boolean` — when `true` (default), a missing or invalid token - responds with `401 { ok: false, error: "Unauthorized" }`. When `false`, - requests pass through and `ctx.user` is only set if a valid token is present. - -## Usage - -```ts -import { signJwt, verifyJwt, jwtAuth, JwtError } from "@wrnexus/jwt"; - -const secret = process.env.JWT_SECRET!; - -// Sign a token that expires in one hour -const token = await signJwt({ sub: user.id, role: "admin" }, secret, { - expiresIn: 3600, -}); - -// Verify it later -try { - const claims = await verifyJwt<{ sub: string; role: string }>(token, secret); - console.log(claims.sub, claims.role); -} catch (err) { - if (err instanceof JwtError) { - // invalid signature, expired, malformed, etc. - } -} -``` - -Protecting routes with the middleware: - -```ts -import { jwtAuth } from "@wrnexus/jwt"; - -// Require a valid bearer token; ctx.user holds the verified claims -app.use(jwtAuth({ secret: process.env.JWT_SECRET! })); - -// Optional auth — populate ctx.user when present, but don't 401 -app.use(jwtAuth({ secret: process.env.JWT_SECRET!, required: false })); -``` - -## Requirements / Notes - -- **Bun-only.** Uses the standard Web Crypto API (`crypto.subtle.importKey`, - `sign`, `verify`) plus `btoa`/`atob` and `TextEncoder`/`TextDecoder` — all - provided by Bun. No third-party crypto dependency. -- **Algorithm:** HS256 (HMAC with SHA-256) only. Asymmetric algorithms (RS/ES) - are not supported. -- Integrates with [`@wrnexus/core`](../core) for `Context`, `Middleware`, and - `ctx.user`; it complements the framework's cookie/session auth with a - stateless bearer-token flow for API and mobile clients. - -## Access, refresh, scope, and cookie helpers - -```ts -import { - createAccessToken, - createRefreshToken, - verifyAccessToken, - verifyRefreshToken, - extractBearerToken, - requireScopes, - jwtCookie, -} from "@wrnexus/jwt"; -``` - -The helpers add explicit `type: "access" | "refresh"` claims, scope checks, refresh-token family metadata, no-store token responses, and secure cookie defaults. `__Host-` cookies are rejected unless they use `Path=/` and `Secure`; `SameSite=None` is rejected without `Secure`. - -## 0.8 helper kit - -```ts -import { - createTokenPair, - verifyAccessToken, - verifyRefreshToken, - extractBearerToken, - readJwtCookie, - jwtCookie, - clearJwtCookie, - requireScopes, -} from "@wrnexus/jwt"; - -const pair = await createTokenPair(user.id, { - accessSecret: process.env.JWT_ACCESS_SECRET!, - refreshSecret: process.env.JWT_REFRESH_SECRET!, - scopes: ["profile:read"], - family: sessionFamily, -}); -``` - -The helper kit validates `__Host-` cookie invariants, cookie names and paths, `SameSite=None` security, typed access/refresh token types, scope requirements, and no-store token responses. -In addition to local HS256 secrets/keyrings, the package verifies standards-based -RS256 tokens through bounded remote JWKS caches: - -```ts -import { createRemoteJwks, verifyJwtWithJwks } from "@wrnexus/jwt"; - -const jwks = createRemoteJwks("https://issuer.example/.well-known/jwks.json"); -const claims = await verifyJwtWithJwks(token, jwks, { - issuer: "https://issuer.example", - audience: "my-api", - maxAge: 300, -}); -``` - -JWKS URLs must use HTTPS. Responses have key-count/byte limits, accept only -RS256 signing RSA keys, deduplicate concurrent refreshes, cache imported public -keys, and force an immediate refresh for an unknown `kid` so issuer rotation -does not wait for cache expiry. Never use decoded-but-unverified claims for an -authorization decision. diff --git a/.publish/jwt/package.json b/.publish/jwt/package.json deleted file mode 100644 index 03c9c13b..00000000 --- a/.publish/jwt/package.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": "@wrnexus/jwt", - "version": "0.8.8", - "type": "module", - "description": "HS256 JSON Web Tokens, key rotation, access/refresh helpers, scopes, cookies, and auth middleware.", - "license": "MIT", - "repository": { - "type": "git", - "url": "https://git.workroot.in/WorkRoot/WRNexusJS.git", - "directory": "packages/jwt" - }, - "homepage": "https://wrnexusjs.dev/packages/jwt", - "bugs": { - "url": "https://git.workroot.in/WorkRoot/WRNexusJS/issues" - }, - "keywords": [ - "wrnexus", - "bun", - "typescript", - "jwt" - ], - "sideEffects": false, - "main": "./dist/index.js", - "module": "./dist/index.js", - "types": "./dist/index.d.ts", - "engines": { - "bun": ">=1.3.0" - }, - "publishConfig": { - "registry": "https://registry.npmjs.org/", - "access": "restricted" - }, - "exports": { - ".": { - "types": "./dist/index.d.ts", - "import": "./dist/index.js" - } - }, - "dependencies": { - "@wrnexus/core": "^0.8.8" - }, - "files": [ - "dist", - "README.md" - ] -} diff --git a/.publish/mobile/README.md b/.publish/mobile/README.md deleted file mode 100644 index a8a9a6e4..00000000 --- a/.publish/mobile/README.md +++ /dev/null @@ -1,91 +0,0 @@ -# @wrnexus/mobile - -> SSR-safe access to Capacitor plugins from WRNexusJS browser code. - -## Overview - -`@wrnexus/mobile` keeps optional native imports out of server rendering while giving -browser-owned modules one consistent registry for Capacitor plugins. During SSR, -`mobile.isNative()` is `false` and `mobile.platform()` is `"web"`. - -## Installation - -Install a plugin through the WRNexusJS CLI so the web and native projects stay aligned: - -```bash -wrnexus mobile add @capacitor/camera @capacitor/haptics -``` - -## Usage - -### Register and invoke a Capacitor plugin - -Import Capacitor packages only from browser-owned code, never from API routes or SSR -helpers. - -```ts -import { Camera, CameraResultType } from "@capacitor/camera"; -import { mobile } from "@wrnexus/mobile"; - -mobile.registerPlugin("Camera", Camera); - -export async function takePhoto() { - if (!mobile.isNative()) return null; - return mobile.invoke("Camera", "getPhoto", { - quality: 85, - resultType: CameraResultType.Uri, - }); -} -``` - -### Provide a browser fallback - -`whenNative` runs the first callback only in a Capacitor WebView and can return a -web/SSR-safe fallback everywhere else. - -```ts -import { Haptics, ImpactStyle } from "@capacitor/haptics"; -import { mobile } from "@wrnexus/mobile"; - -mobile.registerPlugin("Haptics", Haptics); - -export const confirmAction = () => - mobile.whenNative( - () => mobile.invoke("Haptics", "impact", { style: ImpactStyle.Medium }), - () => navigator.vibrate?.(30), - ); -``` - -### Read an optional plugin without throwing - -```ts -import type { NetworkPlugin } from "@capacitor/network"; -import { mobile } from "@wrnexus/mobile"; - -const network = mobile.plugin("Network"); -const status = network ? await network.getStatus() : { connected: true, connectionType: "unknown" }; -``` - -## API - -- `registerPlugin(name, instance)` registers a browser-imported plugin. -- `plugin(name)` returns a plugin or `undefined`; `requirePlugin(name)` throws when absent. -- `invoke(plugin, method, options?)` calls a registered method and returns its result. -- `whenNative(native, fallback?)` selects native behavior without breaking SSR. -- `isNative()` and `platform()` report the current Capacitor environment. - -Unavailable required plugins throw `MobileUnavailableError` with an actionable message. - -The package also provides portable application-facing primitives: - -- `listenDeepLinks` normalizes initial and live links with an allowed-scheme list. -- `PushNotifications` performs permission gating and validates registrations. -- `SecureStorage` namespaces and validates keys over an application-supplied encrypted - Keychain/Keystore adapter; it does not mislabel browser `localStorage` as secure. -- `OfflineQueue` persists bounded sync batches through a pluggable durable store. - -## Requirements / Notes - -- Capacitor plugin imports must remain in browser-owned modules. -- `@wrnexus/mobile` re-exports `native` from `@wrnexus/native` for applications that - prefer the higher-level cross-platform capability API. diff --git a/.publish/mobile/package.json b/.publish/mobile/package.json deleted file mode 100644 index 6b3f5e15..00000000 --- a/.publish/mobile/package.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": "@wrnexus/mobile", - "version": "0.8.8", - "type": "module", - "description": "@wrnexus/mobile — part of the WrNexus framework.", - "license": "MIT", - "repository": { - "type": "git", - "url": "https://git.workroot.in/WorkRoot/WRNexusJS.git", - "directory": "packages/mobile" - }, - "homepage": "https://wrnexusjs.dev/packages/mobile", - "bugs": { - "url": "https://git.workroot.in/WorkRoot/WRNexusJS/issues" - }, - "keywords": [ - "wrnexus", - "bun", - "typescript", - "mobile" - ], - "sideEffects": false, - "main": "./dist/index.js", - "module": "./dist/index.js", - "types": "./dist/index.d.ts", - "engines": { - "bun": ">=1.3.0" - }, - "publishConfig": { - "registry": "https://registry.npmjs.org/", - "access": "restricted" - }, - "exports": { - ".": { - "types": "./dist/index.d.ts", - "import": "./dist/index.js" - } - }, - "dependencies": { - "@wrnexus/native": "^0.8.8" - }, - "files": [ - "dist", - "README.md" - ] -} diff --git a/.publish/native/README.md b/.publish/native/README.md deleted file mode 100644 index aa659c46..00000000 --- a/.publish/native/README.md +++ /dev/null @@ -1,88 +0,0 @@ -# @wrnexus/native - -> Cross-platform capabilities for browsers, Capacitor WebViews, and compiled native apps. - -## Overview - -`@wrnexus/native` exposes capabilities by name so application code can ask what the -current platform supports before presenting an action. Browser capabilities use Web -APIs; mobile capabilities use installed Capacitor plugins. `platform()` returns -`"server"` during SSR, `"browser"` on the web, and the Capacitor platform in a native -WebView. - -## Installation - -```bash -bun add @wrnexus/native -``` - -## Usage - -### Share a page when the platform supports it - -```ts -import { native } from "@wrnexus/native"; - -export async function shareCurrentPage() { - if (!native.supports("share")) return false; - await native.run("share", { - title: document.title, - url: location.href, - }); - return true; -} -``` - -### Register an application-specific capability - -`register` returns an unregister function, which is useful for tests and temporary -feature modules. - -```ts -import { native } from "@wrnexus/native"; - -const unregister = native.register("orders.scan", { - browser: { - supported: () => typeof window !== "undefined", - run: async ({ orderId }: { orderId: string }) => { - const code = window.prompt(`Scan code for order ${orderId}`); - return { code }; - }, - }, -}); - -const result = await native.run<{ code: string | null }>("orders.scan", { orderId: "ord_42" }); -unregister(); -``` - -### Target browser or mobile behavior explicitly - -```ts -import { native } from "@wrnexus/native"; - -const canUseMobileCamera = native.supports("camera", "mobile"); -const position = await native.run( - "geolocation", - { enableHighAccuracy: true }, - { target: "browser" }, -); -``` - -## API - -- `supports(name, target?)` checks availability without running the capability. -- `run(name, options?, runOptions?)` executes it or rejects with `NativeUnavailableError`. -- `register(name, capability)` adds or overrides a capability and returns cleanup. -- `registered()` lists capability names; `clearRegistry()` resets the registry. -- `isMobile()` and `platform()` report the current target safely during SSR. - -Built-ins include `camera`, `clipboard.write`, `share`, `geolocation`, `network`, -`haptics`, storage, filesystem, notifications, and device information. - -`defineNativeManifest` declares required capabilities and typed permissions, while -`PermissionManager` normalizes permission query/request flows across platform adapters. - -## Requirements / Notes - -Use `supports()` before showing optional controls. Mobile capabilities require their -matching Capacitor plugins to be installed and registered by the application. diff --git a/.publish/native/package.json b/.publish/native/package.json deleted file mode 100644 index 2c4e78c3..00000000 --- a/.publish/native/package.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "name": "@wrnexus/native", - "version": "0.8.8", - "type": "module", - "description": "@wrnexus/native — part of the WrNexus framework.", - "license": "MIT", - "repository": { - "type": "git", - "url": "https://git.workroot.in/WorkRoot/WRNexusJS.git", - "directory": "packages/native" - }, - "homepage": "https://wrnexusjs.dev/packages/native", - "bugs": { - "url": "https://git.workroot.in/WorkRoot/WRNexusJS/issues" - }, - "keywords": [ - "wrnexus", - "bun", - "typescript", - "native" - ], - "sideEffects": false, - "main": "./dist/index.js", - "module": "./dist/index.js", - "types": "./dist/index.d.ts", - "engines": { - "bun": ">=1.3.0" - }, - "publishConfig": { - "registry": "https://registry.npmjs.org/", - "access": "restricted" - }, - "exports": { - ".": { - "types": "./dist/index.d.ts", - "import": "./dist/index.js" - }, - "./browser": { - "types": "./dist/browser.d.ts", - "import": "./dist/browser.js" - }, - "./mobile": { - "types": "./dist/mobile.d.ts", - "import": "./dist/mobile.js" - } - }, - "files": [ - "dist", - "README.md" - ] -} diff --git a/.publish/oauth/README.md b/.publish/oauth/README.md deleted file mode 100644 index badbe154..00000000 --- a/.publish/oauth/README.md +++ /dev/null @@ -1,217 +0,0 @@ -# @wrnexus/oauth - -> Dependency-free OAuth 2.0 sign-in for any provider, with PKCE and presets for Google, GitHub, and Discord. - -Part of the **WrNexus** framework — an SSR-first, Bun-native full-stack web framework. - -## Overview - -`@wrnexus/oauth` implements the OAuth 2.0 Authorization Code flow (with PKCE) for -server-side sign-in. It ships ready-made provider presets and a `defineProvider` -helper for custom providers, then gives you two flow functions — `startAuth` -(build the redirect) and `completeAuth` (exchange the code and fetch the user's -profile). It has no runtime dependencies: it uses the platform `fetch` and -WebCrypto only. Pairs naturally with `@wrnexus/core`'s `logIn` to establish a -session once you have a normalized profile. - -## Installation - -```bash -bun add @wrnexus/oauth -``` - -> Private package — the machine must be authenticated to the `wrnexus` npm org -> (a read token in `~/.npmrc`). Requires **Bun** (Node is not supported). - -## API - -### Providers - -Each preset takes `ProviderCredentials` and returns an `OAuthProvider`. - -```ts -interface ProviderCredentials { - clientId: string; - clientSecret: string; - scopes?: string[]; // override the preset's default scopes -} -``` - -| Export | Default scopes | Notes | -| ------------------------ | ---------------------------- | ------------------------------------------------------------------ | -| `google(creds)` | `openid`, `email`, `profile` | Sets `access_type: offline` for refresh tokens. | -| `github(creds)` | `read:user`, `user:email` | Maps `name` (falls back to `login`) and `avatar_url`. | -| `discord(creds)` | `identify`, `email` | Builds the avatar CDN URL from the user id + hash. | -| `defineProvider(config)` | — | Pass a full `OAuthProvider` to define a custom OAuth 2.0 provider. | - -An `OAuthProvider` describes the endpoints, scopes, credentials, optional extra -authorize params, and a `mapProfile` normalizer: - -```ts -interface OAuthProvider { - name: string; - authorizeUrl: string; - tokenUrl: string; - userInfoUrl: string; - scopes: string[]; - clientId: string; - clientSecret: string; - authorizeParams?: Record; // e.g. access_type, prompt - mapProfile: (raw: Record) => OAuthProfile; -} -``` - -### Flow - -#### `startAuth(provider, options): Promise` - -Builds the authorize redirect URL with a generated PKCE challenge and CSRF -`state`. Store the returned `state` and `verifier` (session/cookie), then 302 the -user to `url`. - -```ts -interface StartAuthOptions { - redirectUri: string; - state?: string; // reuse a state instead of generating one - params?: Record; // extra authorize params, merged last -} - -interface StartAuthResult { - url: string; // authorize URL to redirect to - state: string; // CSRF state — verify on callback - verifier: string; // PKCE code verifier — pass to completeAuth -} -``` - -#### `completeAuth(provider, options): Promise<{ tokens, profile }>` - -On the callback: exchanges the authorization `code` for tokens, then fetches and -normalizes the user profile. Convenience wrapper over `exchangeCode` + -`fetchProfile`. - -```ts -interface CompleteAuthOptions { - code: string; - redirectUri: string; - verifier?: string; // the PKCE verifier from startAuth - fetch?: typeof fetch; // inject a fetch implementation (tests) -} -``` - -#### Lower-level helpers - -| Export | Signature | Purpose | -| ---------------------------------------- | ------------------------- | --------------------------------------------------------------- | -| `exchangeCode(provider, options)` | `→ Promise` | Exchange an authorization code for tokens. | -| `fetchProfile(provider, tokens, fetch?)` | `→ Promise` | Fetch + normalize the user's profile. | -| `randomToken(bytes?)` | `→ string` | Random URL-safe token (default 32 bytes) for `state`/verifiers. | - -### Types - -```ts -interface OAuthTokens { - access_token: string; - token_type?: string; - refresh_token?: string; - expires_in?: number; - id_token?: string; - scope?: string; -} - -interface OAuthProfile { - id: string; - email?: string; - name?: string; - avatar?: string; - raw: Record; -} -``` - -## Usage - -```ts -import { google, startAuth, completeAuth } from "@wrnexus/oauth"; -import { logIn } from "@wrnexus/core"; - -const provider = google({ - clientId: process.env.GOOGLE_CLIENT_ID!, - clientSecret: process.env.GOOGLE_CLIENT_SECRET!, -}); - -const redirectUri = "https://example.com/auth/callback"; - -// 1. Kick off sign-in: redirect the user to the provider. -async function beginLogin(ctx) { - const { url, state, verifier } = await startAuth(provider, { redirectUri }); - // Persist state + verifier in the session, then redirect. - ctx.session.set("oauth_state", state); - ctx.session.set("oauth_verifier", verifier); - return Response.redirect(url, 302); -} - -// 2. Handle the callback. -async function handleCallback(ctx, code: string, state: string) { - if (state !== ctx.session.get("oauth_state")) throw new Error("bad state"); - - const { profile } = await completeAuth(provider, { - code, - redirectUri, - verifier: ctx.session.get("oauth_verifier"), - }); - - logIn(ctx, { id: profile.id, email: profile.email }); -} -``` - -Custom provider with `defineProvider`: - -```ts -import { defineProvider, startAuth } from "@wrnexus/oauth"; - -const gitlab = defineProvider({ - name: "gitlab", - authorizeUrl: "https://gitlab.com/oauth/authorize", - tokenUrl: "https://gitlab.com/oauth/token", - userInfoUrl: "https://gitlab.com/api/v4/user", - scopes: ["read_user"], - clientId: process.env.GITLAB_CLIENT_ID!, - clientSecret: process.env.GITLAB_CLIENT_SECRET!, - mapProfile: (raw) => ({ - id: String(raw.id), - email: raw.email as string | undefined, - name: raw.name as string | undefined, - avatar: raw.avatar_url as string | undefined, - raw, - }), -}); -``` - -## Requirements / Notes - -- **Bun-only.** Relies on the global `fetch` and WebCrypto (`crypto.getRandomValues`, - `crypto.subtle.digest`) — no other runtime dependencies. -- The flow is stateless by design: you are responsible for storing `state` and - `verifier` between `startAuth` and `completeAuth` (session or signed cookie). -- Pairs with [`@wrnexus/core`](../core) — feed the normalized `OAuthProfile` into - `logIn` to establish a session. - OIDC integrations can combine strict discovery with the rotating JWKS verifier: - -```ts -import { createRemoteJwks } from "@wrnexus/jwt"; -import { discoverOidc, verifyOidcIdToken } from "@wrnexus/oauth"; - -const metadata = await discoverOidc("https://issuer.example"); -const jwks = createRemoteJwks(metadata.jwks_uri); -const claims = await verifyOidcIdToken(idToken, { - issuer: metadata.issuer, - clientId: "client-id", - jwks, - nonce: expectedNonce, - accessToken, -}); -``` - -Discovery requires an exact normalized issuer and HTTPS endpoints without URL -credentials/fragments. ID-token verification checks the RS256 signature, -expiry/not-before, issuer, audience, required OIDC claims, nonce, multi-audience -`azp`, optional token age, and optional `at_hash` binding. diff --git a/.publish/oauth/package.json b/.publish/oauth/package.json deleted file mode 100644 index 94b3d556..00000000 --- a/.publish/oauth/package.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": "@wrnexus/oauth", - "version": "0.8.8", - "type": "module", - "description": "@wrnexus/oauth — part of the WrNexus framework.", - "license": "MIT", - "repository": { - "type": "git", - "url": "https://git.workroot.in/WorkRoot/WRNexusJS.git", - "directory": "packages/oauth" - }, - "homepage": "https://wrnexusjs.dev/packages/oauth", - "bugs": { - "url": "https://git.workroot.in/WorkRoot/WRNexusJS/issues" - }, - "keywords": [ - "wrnexus", - "bun", - "typescript", - "oauth" - ], - "sideEffects": false, - "main": "./dist/index.js", - "module": "./dist/index.js", - "types": "./dist/index.d.ts", - "engines": { - "bun": ">=1.3.0" - }, - "publishConfig": { - "registry": "https://registry.npmjs.org/", - "access": "restricted" - }, - "exports": { - ".": { - "types": "./dist/index.d.ts", - "import": "./dist/index.js" - } - }, - "dependencies": { - "@wrnexus/jwt": "^0.8.8" - }, - "files": [ - "dist", - "README.md" - ] -} diff --git a/.publish/plugin/README.md b/.publish/plugin/README.md deleted file mode 100644 index 80c96a43..00000000 --- a/.publish/plugin/README.md +++ /dev/null @@ -1,60 +0,0 @@ -# @wrnexus/plugin - -## Least-privilege package permissions - -Package manifests declare every framework capability they register: - -```json -{ - "wrnexus": { - "permissions": ["routes", "migrations"], - "routes": [{ "kind": "api", "path": "/api/example", "entry": "./route.ts" }] - } -} -``` - -Applications can enable fail-closed grants: - -```ts -export default { - pluginPermissions: { - enforce: true, - grants: { "example-plugin": ["routes"] }, - }, -}; -``` - -Discovery rejects used-but-undeclared capabilities with -`WRN-PLUGIN-PERMISSION-UNDECLARED` and ungranted capabilities with -`WRN-PLUGIN-PERMISSION-DENIED`. Permissions cover components, browser runtime, -assets, styles, routes, middleware, migrations, config, transforms, -diagnostics/tooling, and server/build hooks. - -## Compatibility matrices - -Manifests can add `compatibility: { bunMin: "1.3.0", os: ["linux", -"darwin"] }` alongside `runtimes` and `requires`. Use -`testPluginCompatibility(manifest, targets)` in a package test to exercise the -complete support matrix. Runtime discovery enforces the same Bun minimum, OS, -runtime, and capability declarations used by the test kit. - -Deterministic WRNexusJS plugin contracts for configuration, AST/code transforms, -diagnostics, development servers, production builds, and DevToolbar extensions. - -Use `definePlugin()` and declare `enforce`, `before`, or `after` when ordering matters. -Duplicate names and dependency cycles are rejected. - -## Complete lifecycle and contributions - -Plugins may implement `setup`, `configure`, `configResolved`, `transformAst`, -`transformCode`, `diagnostics`, `routes`, `configureServer`, `buildStart`, -`buildEnd`, `render`, `deploy`, `shutdown`, and `hmrUpdate`. The runner preserves -resolved plugin order for every hook and executes `setup` exactly once. - -In addition to components, routes, middleware, assets, styles, runtimes, and -migrations, plugins can contribute `directives`, `cliCommands`, -`virtualModules`, `deploymentAdapters`, `configSchemas`, `documentation`, and -`typeDefinitions`. Names are collision checked. Configuration schemas run after -configuration resolution, CLI commands are callable as normal `wrnexus` -commands, directives participate in AST transformation, and production builds -materialize virtual modules and invoke matching contributed adapters. diff --git a/.publish/plugin/package.json b/.publish/plugin/package.json deleted file mode 100644 index 0aa05a84..00000000 --- a/.publish/plugin/package.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "name": "@wrnexus/plugin", - "version": "0.8.8", - "type": "module", - "description": "@wrnexus/plugin — part of the WrNexus framework.", - "license": "MIT", - "repository": { - "type": "git", - "url": "https://git.workroot.in/WorkRoot/WRNexusJS.git", - "directory": "packages/plugin" - }, - "homepage": "https://wrnexusjs.dev/packages/plugin", - "bugs": { - "url": "https://git.workroot.in/WorkRoot/WRNexusJS/issues" - }, - "keywords": [ - "wrnexus", - "bun", - "typescript", - "plugin" - ], - "sideEffects": false, - "main": "./dist/index.js", - "module": "./dist/index.js", - "types": "./dist/index.d.ts", - "engines": { - "bun": ">=1.3.0" - }, - "publishConfig": { - "registry": "https://registry.npmjs.org/", - "access": "restricted" - }, - "exports": { - ".": { - "types": "./dist/index.d.ts", - "import": "./dist/index.js" - }, - "./types": { - "types": "./dist/types.d.ts", - "import": "./dist/types.js" - }, - "./manifest": { - "types": "./dist/manifest.d.ts", - "import": "./dist/manifest.js" - }, - "./discovery": { - "types": "./dist/discovery.d.ts", - "import": "./dist/discovery.js" - } - }, - "dependencies": { - "@wrnexus/syntax": "^0.8.8" - }, - "files": [ - "dist", - "README.md" - ] -} diff --git a/.publish/pubsub/package.json b/.publish/pubsub/package.json index b5967a03..1354a450 100644 --- a/.publish/pubsub/package.json +++ b/.publish/pubsub/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/pubsub", - "version": "0.8.8", + "version": "0.8.9", "type": "module", "description": "@wrnexus/pubsub — part of the WrNexus framework.", "license": "MIT", @@ -45,8 +45,8 @@ } }, "dependencies": { - "@wrnexus/core": "^0.8.8", - "@wrnexus/rpc": "^0.8.8" + "@wrnexus/core": "^0.8.9", + "@wrnexus/rpc": "^0.8.9" }, "files": [ "dist", diff --git a/.publish/queue/README.md b/.publish/queue/README.md deleted file mode 100644 index e2917849..00000000 --- a/.publish/queue/README.md +++ /dev/null @@ -1,211 +0,0 @@ -# @wrnexus/queue - -> A background job queue with delays, retries + exponential backoff, recurring jobs, and concurrent workers. - -Part of the **WrNexus** framework — an SSR-first, Bun-native full-stack web framework. - -## Overview - -`@wrnexus/queue` is a server-side in-process job queue. You register named -workers, enqueue jobs (optionally delayed or recurring), and let the queue poll -and run them on a timer — with per-job retry limits and doubling backoff between -attempts. The default store lives in memory; the design allows a pluggable driver -to back it with Redis/SQL for durability across restarts. Reach for it when you -need to defer work (emails, webhooks, cleanup) off the request path without a -heavyweight external broker. Tests can drive it deterministically via `drain()`. - -## Installation - -```bash -bun add @wrnexus/queue -``` - -> Private package — the machine must be authenticated to the `wrnexus` npm org -> (a read token in `~/.npmrc`). Requires **Bun** (Node is not supported). - -## API - -The package exports a single factory plus its supporting types. - -### `createQueue(options?): Queue` - -Creates a new queue instance. - -```ts -function createQueue(options?: QueueOptions): Queue; -``` - -#### `QueueOptions` - -| Option | Type | Default | Description | -| ------------- | ------------------------------------ | ---------- | -------------------------------------------------------- | -| `maxAttempts` | `number` | `3` | Default max attempts per job before it is dead-lettered. | -| `backoffMs` | `number` | `1000` | Base retry backoff in ms; doubles per attempt. | -| `pollMs` | `number` | `250` | Poll interval used once `start()` is called (ms). | -| `onFailed` | `(job: Job, error: unknown) => void` | — | Called when a job exhausts its attempts. | -| `concurrency` | `number` | unlimited | Maximum jobs claimed by one `drain()` call. | -| `capacity` | `number` | unlimited | Maximum queued plus active jobs before adds reject. | -| `now` | `() => number` | `Date.now` | Clock injection for deterministic tests. | - -### `Queue` - -The object returned by `createQueue`. - -| Method | Signature | Description | -| ---------- | -------------------------------------------------------------- | -------------------------------------------------------------- | -| `add` | `add(name, data: T, options?: AddOptions): Promise>` | Enqueue a job under a worker name. Returns the created job. | -| `process` | `process(name, handler: JobHandler): void` | Register the worker that runs jobs of the given name. | -| `drain` | `drain(now?: number): Promise` | Run every job whose `runAt ≤ now`, once. Returns how many ran. | -| `start` | `start(): void` | Begin polling every `pollMs`. No-op if already started. | -| `stop` | `stop(): void` | Stop the poll timer. | -| `shutdown` | `shutdown({ force? }): Promise` | Stop accepting jobs and await active work; force aborts it. | -| `size` | `size(): number` | Number of jobs currently queued. | -| `get/list` | `get(id)` / `list(name?)` | Inspect defensive copies of pending jobs. | -| `cancel` | `cancel(id): boolean` | Remove queued work or abort an active handler. | -| `failed` | `failed(): Job[]` | Inspect exhausted jobs in the dead-letter collection. | -| `retry` | `retry(id): Promise` | Reset and requeue a dead-lettered job. | - -#### `AddOptions` - -| Option | Type | Description | -| ---------------- | -------- | -------------------------------------------------------------------------- | -| `delayMs` | `number` | Delay before the job becomes runnable (ms). | -| `maxAttempts` | `number` | Max attempts before dead-lettering. Defaults to the queue's `maxAttempts`. | -| `repeat` | `number` | Re-enqueue this job this many ms after each successful run (recurring). | -| `priority` | `number` | Higher values are selected first among due jobs. | -| `idempotencyKey` | `string` | Return the matching pending job instead of enqueueing a duplicate. | - -#### `JobHandler` - -```ts -type JobHandler = ( - job: Job, - context: { signal: AbortSignal }, -) => void | Promise; -``` - -#### `Job` - -```ts -interface Job { - id: string; // e.g. "job_1" - name: string; - data: T; - attempts: number; - maxAttempts: number; - runAt: number; // epoch ms; job runs when now ≥ runAt - repeat?: number; // if set, re-enqueue this many ms after each success -} -``` - -## Usage - -Register workers, enqueue jobs, then start the poller: - -```ts -import { createQueue } from "@wrnexus/queue"; - -const queue = createQueue({ maxAttempts: 3, backoffMs: 1000 }); - -// Register a worker for the "email" job name. -queue.process<{ to: string }>("email", async (job) => { - await send(job.data.to); -}); - -// Enqueue a delayed job with up to 3 attempts. -await queue.add("email", { to: "a@b.com" }, { delayMs: 5000, maxAttempts: 3 }); - -queue.start(); // begin polling; queue.stop() to halt -``` - -Use `context.signal` in network/database calls so forced shutdown and active -cancellation finish promptly. For process termination, prefer -`await queue.shutdown()`; use `{ force: true }` only after your grace period. - -### Durable queue - -`createDurableQueue({ store })` retains jobs until their handler succeeds and -supports atomic leases when a driver implements `QueueStore.claim`. It exposes -the same cancellation/shutdown behavior plus `list`, `failed`, and `retry`. -The included `memoryQueueStore()` is useful for tests; production Redis/SQL -drivers should make `claim()` atomic to prevent two workers executing one job. - -### Recurring jobs - -Pass `repeat` to re-enqueue a job a fixed interval after each successful run: - -```ts -queue.process("heartbeat", async () => ping()); -await queue.add("heartbeat", {}, { repeat: 60_000 }); // runs ~every minute -``` - -### Handling permanent failures - -When a job's `attempts` reaches `maxAttempts`, it is dropped and `onFailed` -fires instead of retrying: - -```ts -const queue = createQueue({ - onFailed: (job, error) => { - console.error(`job ${job.id} (${job.name}) gave up`, error); - }, -}); -``` - -### Deterministic testing - -Instead of `start()`, inject a clock and drive the queue with `drain()`: - -```ts -let clock = 0; -const queue = createQueue({ now: () => clock }); - -queue.process("task", async () => { - /* ... */ -}); -await queue.add("task", {}, { delayMs: 5000 }); - -clock = 5000; -const ran = await queue.drain(); // => 1 -``` - -### Durable workflows and approvals - -`createWorkflowEngine(store)` executes dependency-ordered steps and persists every transition, -result, progress update, failure, cancellation, and approval record. Approval steps pause safely -and can resume after a process restart because the snapshot lives in the supplied `WorkflowStore`. - -```ts -const workflow = defineDurableWorkflow({ - name: "publish-report", - steps: [ - { name: "build", run: buildReport }, - { name: "approve", dependsOn: ["build"], approval: true, run: (report) => report }, - { name: "publish", dependsOn: ["approve"], run: publishReport }, - ], -}); - -const run = await engine.start(workflow, input); -await engine.approve(workflow, run.id, "approve", currentUser.id); -``` - -Use `memoryWorkflowStore()` for tests. Production stores implement the small `get`, `put`, and -`list` contract using the same transactional database or durable service as the application. - -## Retry & backoff behavior - -- On a thrown handler error, the job is retried while `attempts < maxAttempts`. -- The next `runAt` is set to `now + backoffMs * 2^(attempts - 1)` (exponential - backoff): with `backoffMs: 1000` the delays are 1s, 2s, 4s, … -- A job whose worker name has no registered handler stays queued until one is - registered (it is not counted as runnable by `drain`). -- `drain` is re-entrant-safe: overlapping calls are skipped while one is running. - -## Requirements / Notes - -- **Bun-only** runtime (Node is not supported), consistent with the rest of the - WrNexus framework. The queue itself relies only on standard timers - (`setInterval`/`clearInterval`) and has no runtime dependencies. -- The default store is in-process, so queued jobs do not survive a restart; a - pluggable driver is intended for backing it with Redis/SQL for durability. -- Works alongside `@wrnexus/core` for offloading work from the request path. diff --git a/.publish/queue/package.json b/.publish/queue/package.json deleted file mode 100644 index 08b4acdd..00000000 --- a/.publish/queue/package.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "name": "@wrnexus/queue", - "version": "0.8.8", - "type": "module", - "description": "@wrnexus/queue — part of the WrNexus framework.", - "license": "MIT", - "repository": { - "type": "git", - "url": "https://git.workroot.in/WorkRoot/WRNexusJS.git", - "directory": "packages/queue" - }, - "homepage": "https://wrnexusjs.dev/packages/queue", - "bugs": { - "url": "https://git.workroot.in/WorkRoot/WRNexusJS/issues" - }, - "keywords": [ - "wrnexus", - "bun", - "typescript", - "queue" - ], - "sideEffects": false, - "main": "./dist/index.js", - "module": "./dist/index.js", - "types": "./dist/index.d.ts", - "engines": { - "bun": ">=1.3.0" - }, - "publishConfig": { - "registry": "https://registry.npmjs.org/", - "access": "restricted" - }, - "exports": { - ".": { - "types": "./dist/index.d.ts", - "import": "./dist/index.js" - } - }, - "dependencies": { - "@wrnexus/core": "^0.8.8", - "@wrnexus/rpc": "^0.8.8" - }, - "files": [ - "dist", - "README.md" - ] -} diff --git a/.publish/reactive/README.md b/.publish/reactive/README.md deleted file mode 100644 index 2b17e421..00000000 --- a/.publish/reactive/README.md +++ /dev/null @@ -1,99 +0,0 @@ -# @wrnexus/reactive - -> Tiny, type-safe reactive primitives (signals) with zero dependencies. - -Part of the **WrNexus** framework — an SSR-first, Bun-native full-stack web framework. - -## Overview - -`@wrnexus/reactive` is the seed of WrNexus's reactivity layer: a minimal `signal` -primitive that holds a value, notifies subscribers when it changes, and hands back -an unsubscribe function. It is deliberately small and framework-agnostic — it powers -nothing on its own, but is shaped so client islands (and later the `.wrn` compiler's -`state` blocks) can build reactive bindings on top of it. Reach for it when you need -observable state without pulling in a full reactivity library. - -## Installation - -```bash -bun add @wrnexus/reactive -``` - -> Private package — the machine must be authenticated to the `wrnexus` npm org -> (a read token in `~/.npmrc`). Requires **Bun** (Node is not supported). - -## API - -The package has a single entry point (`.`) exporting one function and three types. - -### `signal(initial: T): Signal` - -Creates a reactive signal seeded with `initial`. Returns a `Signal`: - -| Member | Signature | Description | -| ----------- | ---------------------------------- | -------------------------------------------------------------------------------------------------------- | -| `get` | `(): T` | Read the current value. | -| `set` | `(next: T): void` | Write a new value. Subscribers run **only when the value actually changes** (compared with `Object.is`). | -| `update` | `(fn: (current: T) => T): void` | Apply a function to the current value; equivalent to `set(fn(get()))`. | -| `subscribe` | `(fn: Subscriber): Unsubscribe` | Register a subscriber; returns a function that removes it. | - -### Types - -```ts -type Subscriber = (value: T) => void; -type Unsubscribe = () => void; - -interface Signal { - get(): T; - set(next: T): void; - update(fn: (current: T) => T): void; - subscribe(fn: Subscriber): Unsubscribe; -} -``` - -Notes on semantics: - -- **No-op updates are skipped.** `set` compares the incoming value to the current - one with `Object.is`; identical values do not notify subscribers. -- **Safe unsubscribe during notification.** Subscribers are iterated over a copy of - the subscriber set, so a subscriber may call its own (or another's) unsubscribe - while a notification is in flight. - -## Usage - -```ts -import { signal } from "@wrnexus/reactive"; - -const count = signal(0); - -count.get(); // 0 - -// Subscribe; the returned function unsubscribes. -const off = count.subscribe((value) => { - console.log("count is now", value); -}); - -count.set(1); // logs: count is now 1 -count.set(1); // no-op — value unchanged, no notification -count.update((n) => n + 1); // logs: count is now 2 - -off(); // stop listening -count.set(3); // nothing logged -``` - -Typed signals infer `T` from the initial value, or can be annotated explicitly: - -```ts -import { signal, type Signal } from "@wrnexus/reactive"; - -const user: Signal<{ name: string } | null> = signal(null); -user.set({ name: "Ada" }); -``` - -## Requirements / Notes - -- **Bun-only.** Distributed as TypeScript source (`main`/`exports` point at - `src/index.ts`); consume it under Bun, which runs `.ts` directly. -- **Zero dependencies.** The only runtime API used is the standard `Object.is`. -- Foundational primitive for WrNexus client islands and the forthcoming `.wrn` - compiler `state` blocks. diff --git a/.publish/reactive/package.json b/.publish/reactive/package.json deleted file mode 100644 index 9a058be4..00000000 --- a/.publish/reactive/package.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "name": "@wrnexus/reactive", - "version": "0.8.8", - "type": "module", - "description": "@wrnexus/reactive — part of the WrNexus framework.", - "license": "MIT", - "repository": { - "type": "git", - "url": "https://git.workroot.in/WorkRoot/WRNexusJS.git", - "directory": "packages/reactive" - }, - "homepage": "https://wrnexusjs.dev/packages/reactive", - "bugs": { - "url": "https://git.workroot.in/WorkRoot/WRNexusJS/issues" - }, - "keywords": [ - "wrnexus", - "bun", - "typescript", - "reactive" - ], - "sideEffects": false, - "main": "./dist/index.js", - "module": "./dist/index.js", - "types": "./dist/index.d.ts", - "engines": { - "bun": ">=1.3.0" - }, - "publishConfig": { - "registry": "https://registry.npmjs.org/", - "access": "restricted" - }, - "exports": { - ".": { - "types": "./dist/index.d.ts", - "import": "./dist/index.js" - } - }, - "files": [ - "dist", - "README.md" - ] -} diff --git a/.publish/router/package.json b/.publish/router/package.json index b3ec1ba9..1502276d 100644 --- a/.publish/router/package.json +++ b/.publish/router/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/router", - "version": "0.8.8", + "version": "0.8.9", "type": "module", "description": "@wrnexus/router — part of the WrNexus framework.", "license": "MIT", @@ -37,8 +37,8 @@ } }, "dependencies": { - "@wrnexus/compiler": "^0.8.8", - "@wrnexus/core": "^0.8.8" + "@wrnexus/compiler": "^0.8.9", + "@wrnexus/core": "^0.8.9" }, "files": [ "dist", diff --git a/.publish/ssr/README.md b/.publish/ssr/README.md index ba11ab53..1cb04405 100644 --- a/.publish/ssr/README.md +++ b/.publish/ssr/README.md @@ -89,7 +89,7 @@ const html = renderDocument({ }, url: new URL("https://acme.example/about"), body: "

About Us

", - scripts: ["/_wire/runtime.js", "/_wire/islands/about.js"], + scripts: ["/_wrn/runtime.js", "/_wrn/islands/about.js"], htmlAttrs: ' data-theme="dark"', }); diff --git a/.publish/ssr/package.json b/.publish/ssr/package.json index ab735ec4..9bb52f8c 100644 --- a/.publish/ssr/package.json +++ b/.publish/ssr/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/ssr", - "version": "0.8.8", + "version": "0.8.9", "type": "module", "description": "@wrnexus/ssr — part of the WrNexus framework.", "license": "MIT", @@ -45,7 +45,7 @@ } }, "dependencies": { - "@wrnexus/core": "^0.8.8", + "@wrnexus/core": "^0.8.9", "@wrnexus/store": "^0.8.8", "@wrnexus/security": "^0.8.8" }, diff --git a/.publish/styles/README.md b/.publish/styles/README.md index bb7709ab..9803057a 100644 --- a/.publish/styles/README.md +++ b/.publish/styles/README.md @@ -21,7 +21,7 @@ other arrays intentionally replace earlier values. Cycles and missing/invalid entries fail with stable `WRN-CONFIG-LAYER-*` diagnostics. `wrnexus config --explain` lists every resolved layer source. -> Global CSS bundling, the `--wire-*` design-token theme system, and the `wrnexus.config.ts` app-config loader for WrNexus apps. +> Global CSS bundling, the `--wrn-*` design-token theme system, and the `wrnexus.config.ts` app-config loader for WrNexus apps. Part of the **WrNexus** framework — an SSR-first, Bun-native full-stack web framework. @@ -30,7 +30,7 @@ Part of the **WrNexus** framework — an SSR-first, Bun-native full-stack web fr This package owns three server-side concerns that shape every page a WrNexus app renders: 1. **Global stylesheet pipeline** — finds `app/styles/global.css` (or aggregates `app/styles/*.css`), bundles it with Bun's CSS bundler (which resolves `@import`, including from `node_modules`), and produces one stylesheet that is ``ed into every page's ``. Because it is a plain global sheet, it styles server-rendered markup and hydrated client islands identically. A custom `process` hook lets you swap in Tailwind / PostCSS / Sass. -2. **Theme system** — design tokens exposed as CSS custom properties (`--wire-`), with built-in `light`/`dark` sets, deep-merged user overrides, an SSR `` render (no flash), and a tiny client runtime to toggle/persist the choice. +2. **Theme system** — design tokens exposed as CSS custom properties (`--wrn-`), with built-in `light`/`dark` sets, deep-merged user overrides, an SSR `` render (no flash), and a tiny client runtime to toggle/persist the choice. 3. **App config** — loads `wrnexus.config.ts` (the `AppConfig` type), applies named profile overrides, and loads the `.env` cascade. It runs server-side / at build time. Reach for it when configuring an app, defining themes, or customising how global CSS is produced. @@ -111,7 +111,7 @@ interface StyleProcessContext { | Export | Type / Signature | Purpose | | -------------------- | -------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | | `DEFAULT_THEMES` | `Record` | Built-in `light` and `dark` token maps. | -| `THEME_COOKIE` | `"wire-theme"` | Cookie the resolved theme is read from / persisted to. | +| `THEME_COOKIE` | `"wrn-theme"` | Cookie the resolved theme is read from / persisted to. | | `THEME_CSS_HREF` | `"/__wrnexus/theme.css"` | URL the generated theme stylesheet is served at. | | `THEME_JS_HREF` | `"/__wrnexus/theme.js"` | URL the client theme runtime is served at. | | `resolveThemeConfig` | `(config?: ThemeConfig) => ResolvedTheme` | Deep-merge the user's `theme` config over the defaults; pick the default theme (config's `default` if valid, else `dark`, else the first). | @@ -119,7 +119,7 @@ interface StyleProcessContext { | `renderThemeCss` | `(theme: ResolvedTheme) => string` | Generate the theme stylesheet: a `:root{…}` default plus one `[data-theme=""]{…}` block per theme. | | `renderThemeRuntime` | `(theme: ResolvedTheme) => string` | Generate the client runtime (see below). | -Tokens are emitted as `--wire-` custom properties, **except** the reserved key `color-scheme`, which is emitted as the native `color-scheme` CSS property so form controls and scrollbars match the theme. +Tokens are emitted as `--wrn-` custom properties, **except** the reserved key `color-scheme`, which is emitted as the native `color-scheme` CSS property so form controls and scrollbars match the theme. `ThemeConfig` / `ThemeTokens` / `ResolvedTheme`: @@ -164,7 +164,7 @@ theme: { } ``` -The client runtime (`renderThemeRuntime`) exposes `window.wireTheme` with `{ get, set, toggle, bind, themes }`, wires up any `[data-wire-theme-toggle]` and `[data-wire-theme-set]` elements on load, and persists the choice to the `wire-theme` cookie (`max-age` 1 year, `samesite=lax`). `toggle()` cycles through the configured theme names in order. +The client runtime (`renderThemeRuntime`) exposes `window.wrnTheme` with `{ get, set, toggle, bind, themes }`, connects up any `[data-wrn-theme-toggle]` and `[data-wrn-theme-set]` elements on load, and persists the choice to the `wrn-theme` cookie (`max-age` 1 year, `samesite=lax`). `toggle()` cycles through the configured theme names in order. ## Usage @@ -252,17 +252,17 @@ In templates, consume tokens via the custom properties: ```css .card { - background: var(--wire-color-surface); - color: var(--wire-color-text); - border: 1px solid var(--wire-color-border); - border-radius: var(--wire-radius); - box-shadow: var(--wire-shadow-1); + background: var(--wrn-color-surface); + color: var(--wrn-color-text); + border: 1px solid var(--wrn-color-border); + border-radius: var(--wrn-radius); + box-shadow: var(--wrn-shadow-1); } ``` ```html - - + + ``` ## Requirements / Notes @@ -270,4 +270,4 @@ In templates, consume tokens via the custom properties: - **Bun-only.** `bundleCss` uses `Bun.build`'s CSS bundler for `@import` resolution, nesting, and minification. Node is not supported. - Config and env loading use `node:fs` / `node:path` / `node:url` and read from `process.env`. - Peer package: `@wrnexus/core` supplies the `SeoConfig` and `SecurityConfig` types referenced by `AppConfig`. -- The bundled global stylesheet, the theme stylesheet (`THEME_CSS_HREF`), and the theme runtime (`THEME_JS_HREF`) are wired into pages by the framework's server; this package only produces their contents. +- The bundled global stylesheet, the theme stylesheet (`THEME_CSS_HREF`), and the theme runtime (`THEME_JS_HREF`) are connected into pages by the framework's server; this package only produces their contents. diff --git a/.publish/styles/package.json b/.publish/styles/package.json index c7edfa0a..baf53f76 100644 --- a/.publish/styles/package.json +++ b/.publish/styles/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/styles", - "version": "0.8.8", + "version": "0.8.10", "type": "module", "description": "@wrnexus/styles — part of the WrNexus framework.", "license": "MIT", @@ -37,8 +37,8 @@ } }, "dependencies": { - "@wrnexus/uploader": "^0.8.8", - "@wrnexus/core": "^0.8.8", + "@wrnexus/uploader": "^0.8.9", + "@wrnexus/core": "^0.8.9", "@wrnexus/plugin": "^0.8.8" }, "files": [ diff --git a/.publish/syntax/README.md b/.publish/syntax/README.md deleted file mode 100644 index 7039cc05..00000000 --- a/.publish/syntax/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# @wrnexus/syntax - -Canonical WRN lexer, parser, AST, language metadata, source positions, and stable -diagnostics. Framework tooling should import this package instead of implementing a -separate `.wrn` parser. - -See `docs/WRN-LANGUAGE-SPEC-1.0.md` in the WRNexusJS repository. diff --git a/.publish/syntax/package.json b/.publish/syntax/package.json deleted file mode 100644 index 225fffd1..00000000 --- a/.publish/syntax/package.json +++ /dev/null @@ -1,67 +0,0 @@ -{ - "name": "@wrnexus/syntax", - "version": "0.8.8", - "type": "module", - "description": "@wrnexus/syntax — part of the WrNexus framework.", - "license": "MIT", - "repository": { - "type": "git", - "url": "https://git.workroot.in/WorkRoot/WRNexusJS.git", - "directory": "packages/syntax" - }, - "homepage": "https://wrnexusjs.dev/packages/syntax", - "bugs": { - "url": "https://git.workroot.in/WorkRoot/WRNexusJS/issues" - }, - "keywords": [ - "wrnexus", - "bun", - "typescript", - "syntax" - ], - "sideEffects": false, - "main": "./dist/index.js", - "module": "./dist/index.js", - "types": "./dist/index.d.ts", - "engines": { - "bun": ">=1.3.0" - }, - "publishConfig": { - "registry": "https://registry.npmjs.org/", - "access": "restricted" - }, - "exports": { - ".": { - "types": "./dist/index.d.ts", - "import": "./dist/index.js" - }, - "./parser": { - "types": "./dist/parser.d.ts", - "import": "./dist/parser.js" - }, - "./tokenizer": { - "types": "./dist/tokenizer.d.ts", - "import": "./dist/tokenizer.js" - }, - "./types": { - "types": "./dist/types.d.ts", - "import": "./dist/types.js" - }, - "./diagnostics": { - "types": "./dist/diagnostics.d.ts", - "import": "./dist/diagnostics.js" - }, - "./spec": { - "types": "./dist/spec.d.ts", - "import": "./dist/spec.js" - }, - "./formatter": { - "types": "./dist/formatter.d.ts", - "import": "./dist/formatter.js" - } - }, - "files": [ - "dist", - "README.md" - ] -} diff --git a/.publish/test/package.json b/.publish/test/package.json index 60270cec..fd6bf089 100644 --- a/.publish/test/package.json +++ b/.publish/test/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/test", - "version": "0.8.8", + "version": "0.8.9", "type": "module", "description": "@wrnexus/test — part of the WrNexus framework.", "license": "MIT", diff --git a/.publish/tracking/README.md b/.publish/tracking/README.md deleted file mode 100644 index 5f08ef94..00000000 --- a/.publish/tracking/README.md +++ /dev/null @@ -1,128 +0,0 @@ -# @wrnexus/tracking - -> Error tracking for WrNexus apps: capture exceptions manually or via middleware and fan them out to pluggable sinks. - -Part of the **WrNexus** framework — an SSR-first, Bun-native full-stack web framework. - -## Overview - -`@wrnexus/tracking` is a small, server-side error-capture layer. You create a -tracker with one or more **sinks**, then feed it errors — either manually with -`tracker.capture(err, context)` or automatically by mounting `tracker.middleware()` -in your request pipeline. A `consoleSink` is included; forwarding to Sentry, -Datadog, or any other backend is just a matter of writing a tiny sink. Reach for -it when you want a single, sink-agnostic place to route application errors. Sinks -run best-effort — a throwing sink never breaks the request. - -## Installation - -```bash -bun add @wrnexus/tracking -``` - -> Private package — the machine must be authenticated to the `wrnexus` npm org -> (a read token in `~/.npmrc`). Requires **Bun** (Node is not supported). - -## API - -### `createTracker(options?): Tracker` - -Creates a tracker. `TrackerOptions`: - -| Option | Type | Description | -| ------------ | ------------------------------------------- | --------------------------------------------------------------------------- | -| `sinks` | `ErrorSink[]` | Initial sinks to fan events out to. Defaults to `[]`. | -| `now` | `() => number` | Clock used for `event.timestamp` (epoch ms). Defaults to `Date.now`. | -| `beforeSend` | `(event: ErrorEvent) => ErrorEvent \| null` | Scrub/enrich an event before it reaches any sink. Return `null` to drop it. | - -The returned `Tracker`: - -| Member | Signature | Description | -| ------------ | ---------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `capture` | `(error: unknown, context?: Record) => Promise` | Normalizes any thrown value into an `Error`, builds an `ErrorEvent`, runs `beforeSend`, then dispatches to all sinks. Non-`Error` values are wrapped in an `Error` named `NonError`. | -| `addSink` | `(sink: ErrorSink) => void` | Registers an additional sink at runtime. | -| `middleware` | `() => Middleware` | Returns a WrNexus `Middleware` that captures any error thrown downstream, then re-throws it so the framework's error handler still produces the response. | - -The middleware attaches this context to captured events: - -```ts -{ method: ctx.req.method, path: ctx.url.pathname, requestId: ctx.locals.requestId } -``` - -### `consoleSink: ErrorSink` - -A built-in sink that logs a compact one-line message via `console.error`, e.g. -`[error] TypeError: cannot read x {"userId":42}`. - -### Types - -```ts -interface ErrorEvent { - error: Error; - context: Record; // request info, user id, tags… - timestamp: number; // epoch ms -} - -interface ErrorSink { - name?: string; - capture(event: ErrorEvent): void | Promise; -} -``` - -## Usage - -Manual capture: - -```ts -import { createTracker, consoleSink } from "@wrnexus/tracking"; - -const tracker = createTracker({ sinks: [consoleSink] }); - -try { - await doWork(); -} catch (err) { - await tracker.capture(err, { userId: 42, op: "doWork" }); - throw err; -} -``` - -As request middleware: - -```ts -import { createTracker, consoleSink } from "@wrnexus/tracking"; - -const tracker = createTracker({ sinks: [consoleSink] }); - -app.use(tracker.middleware()); // captures + re-throws downstream errors -``` - -A custom sink with `beforeSend` scrubbing: - -```ts -import { createTracker, type ErrorSink } from "@wrnexus/tracking"; - -const sentrySink: ErrorSink = { - name: "sentry", - async capture(event) { - await Sentry.captureException(event.error, { extra: event.context }); - }, -}; - -const tracker = createTracker({ - sinks: [sentrySink], - beforeSend(event) { - delete event.context.password; // scrub secrets - return event; // return null to drop the event entirely - }, -}); - -tracker.addSink(anotherSink); // add more sinks later -``` - -## Requirements / Notes - -- Runs on **Bun** only (Node is not supported). -- Peer package: [`@wrnexus/core`](../core) — the `Context` and `Middleware` types - used by `tracker.middleware()` come from there. -- Sink dispatch is fire-and-forget-safe: all sinks run via `Promise.all`, and a - sink that throws is swallowed so it can never break the app. diff --git a/.publish/tracking/package.json b/.publish/tracking/package.json deleted file mode 100644 index f2ab3286..00000000 --- a/.publish/tracking/package.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "name": "@wrnexus/tracking", - "version": "0.8.8", - "type": "module", - "description": "@wrnexus/tracking — part of the WrNexus framework.", - "license": "MIT", - "repository": { - "type": "git", - "url": "https://git.workroot.in/WorkRoot/WRNexusJS.git", - "directory": "packages/tracking" - }, - "homepage": "https://wrnexusjs.dev/packages/tracking", - "bugs": { - "url": "https://git.workroot.in/WorkRoot/WRNexusJS/issues" - }, - "keywords": [ - "wrnexus", - "bun", - "typescript", - "tracking" - ], - "sideEffects": false, - "main": "./dist/index.js", - "module": "./dist/index.js", - "types": "./dist/index.d.ts", - "engines": { - "bun": ">=1.3.0" - }, - "publishConfig": { - "registry": "https://registry.npmjs.org/", - "access": "restricted" - }, - "exports": { - ".": { - "types": "./dist/index.d.ts", - "import": "./dist/index.js" - } - }, - "files": [ - "dist", - "README.md" - ] -} diff --git a/.publish/ui/README.md b/.publish/ui/README.md index 5ddddced..12712863 100644 --- a/.publish/ui/README.md +++ b/.publish/ui/README.md @@ -1,6 +1,6 @@ # @wrnexus/ui -> First-party Wire UI component library — a set of themeable `.wrn` components plus a single tokenized stylesheet. +> First-party WrNexus UI component library — a set of themeable `.wrn` components plus a single tokenized stylesheet. Part of the **WrNexus** framework — an SSR-first, Bun-native full-stack web framework. @@ -11,7 +11,7 @@ controls, and feedback UI) together with one themeable stylesheet, `ui.css`. The components are **auto-discovered** by the framework router — you don't import them in code. Once the package's component directory is on the router's scan path, you mount any component in a page with `data-component=""`. Every visual is -driven by `var(--wire-*)` theme tokens, so components restyle instantly when the +driven by `var(--wrn-*)` theme tokens, so components restyle instantly when the theme changes. The tiny JS surface (`src/index.ts`) exists only so the toolchain (CLI build + dev server) can locate the component directory and stylesheet. @@ -30,7 +30,7 @@ bun add @wrnexus/ui > (a read token in `~/.npmrc`). Requires **Bun** (Node is not supported). In practice you rarely install this directly: `@wrnexus/cli` and -`@wrnexus/dev-server` already depend on it and wire it into the router for you +`@wrnexus/dev-server` already depend on it and wrn it into the router for you (see [Auto-discovery](#auto-discovery)). ## Components @@ -54,19 +54,19 @@ element), and most render their body from either a named prop or the default slo ### Core / feedback -| Name | Purpose | Key props | -| -------------- | ---------------------------------------------------- | ----------------------------------------------------------------------------------------------- | -| `button` | Button | `label`, `variant` (`default`\|`primary`\|`danger`\|`ghost`), `size` (`sm`\|`md`\|`lg`), `type` | -| `input` | Text input | see source | -| `textarea` | Multi-line input | see source | -| `checkbox` | Checkbox | see source | -| `badge` | Small status badge | `label`, `variant` | -| `alert` | Callout box | `variant` (`info`\|`success`\|`danger`\|`warning`), `title`, `message` | -| `card` | Padded, bordered surface | `class` | -| `avatar` | User avatar | see source | -| `spinner` | Loading indicator | see source | -| `disclosure` | Expandable details/summary | see source | -| `theme-toggle` | Theme switch button (binds `data-wire-theme-toggle`) | `label` | +| Name | Purpose | Key props | +| -------------- | --------------------------------------------------- | ----------------------------------------------------------------------------------------------- | +| `button` | Button | `label`, `variant` (`default`\|`primary`\|`danger`\|`ghost`), `size` (`sm`\|`md`\|`lg`), `type` | +| `input` | Text input | see source | +| `textarea` | Multi-line input | see source | +| `checkbox` | Checkbox | see source | +| `badge` | Small status badge | `label`, `variant` | +| `alert` | Callout box | `variant` (`info`\|`success`\|`danger`\|`warning`), `title`, `message` | +| `card` | Padded, bordered surface | `class` | +| `avatar` | User avatar | see source | +| `spinner` | Loading indicator | see source | +| `disclosure` | Expandable details/summary | see source | +| `theme-toggle` | Theme switch button (binds `data-wrn-theme-toggle`) | `label` | ### Additional controls & data display @@ -100,7 +100,7 @@ are `.wrn` files rendered server-side. | ------------------ | -------------------------- | ------------------------------------------------------------------------------------------ | | `uiComponentsDir` | `() => string` | Absolute path to the `.wrn` component directory (feed to `buildRouter`'s `componentDirs`). | | `uiCssPath` | `() => string` | Absolute path to `ui.css`. | -| `uiCss` | `() => string` | The `ui.css` file contents (all `.wire-*` classes, themed via tokens). | +| `uiCss` | `() => string` | The `ui.css` file contents (all `.wrn-*` classes, themed via tokens). | | `uiComponentNames` | `() => string[]` | Sorted list of declared built-in component names. | | `uiComponentPath` | `(name: string) => string` | Absolute source path for a declared component name or case-insensitive alias. | @@ -126,9 +126,9 @@ primitives. Applications can still use Tailwind independently in their own source files. The shared stylesheet gives all component boundaries consistent, GPU-friendly -entry and interaction motion. Override `--wire-motion-fast`, -`--wire-motion-base`, `--wire-motion-slow`, `--wire-ease-standard`, or -`--wire-ease-emphasized` to tune it. Hover lift is limited to precise pointing +entry and interaction motion. Override `--wrn-motion-fast`, +`--wrn-motion-base`, `--wrn-motion-slow`, `--wrn-ease-standard`, or +`--wrn-ease-emphasized` to tune it. Hover lift is limited to precise pointing devices and `prefers-reduced-motion` is honored automatically. ### Using the selected theme in application UI @@ -139,21 +139,21 @@ pages and custom `.wrn` components can use the same contract: ```css .account-card { - background: var(--wire-color-surface); - color: var(--wire-color-text); - border: 1px solid var(--wire-color-border); + background: var(--wrn-color-surface); + color: var(--wrn-color-text); + border: 1px solid var(--wrn-color-border); } .account-card__action { - background: var(--wire-color-primary); - color: var(--wire-color-primary-contrast); + background: var(--wrn-color-primary); + color: var(--wrn-color-primary-contrast); } ``` -Stable no-spacing helper classes are also available: `wire-bg-page`, -`wire-bg-surface`, `wire-bg-surface-2`, `wire-bg-primary`, `wire-bg-secondary`, -`wire-text`, `wire-text-muted`, `wire-text-primary`, `wire-text-success`, -`wire-text-warning`, `wire-text-danger`, and `wire-border`. +Stable no-spacing helper classes are also available: `wrn-bg-page`, +`wrn-bg-surface`, `wrn-bg-surface-2`, `wrn-bg-primary`, `wrn-bg-secondary`, +`wrn-text`, `wrn-text-muted`, `wrn-text-primary`, `wrn-text-success`, +`wrn-text-warning`, `wrn-text-danger`, and `wrn-border`. Tailwind-authored custom markup can continue using the palette families already used by packaged components. `indigo-*` and `violet-*` resolve to primary, @@ -169,7 +169,7 @@ The router scans extra `componentDirs` (in addition to the app's own `app/components`) and keys components by name. Library dirs are scanned **first** and `app/components` **last**, so an app component of the same name shadows the library's. The CLI build (`@wrnexus/cli`) and dev server (`@wrnexus/dev-server`) -both wire the UI directory in for you: +both wrn the UI directory in for you: ```ts import { buildRouter } from "@wrnexus/router"; @@ -195,11 +195,11 @@ attributes (other than `data-component`) become string props: Ways to customize the components, in increasing order of power: -1. **Theme tokens** — override CSS custom properties such as `--wire-color-primary`, - `--wire-color-surface`, `--wire-radius-sm`, etc. Every component style resolves - through `var(--wire-*)`, so changing a token restyles everything instantly +1. **Theme tokens** — override CSS custom properties such as `--wrn-color-primary`, + `--wrn-color-surface`, `--wrn-radius-sm`, etc. Every component style resolves + through `var(--wrn-*)`, so changing a token restyles everything instantly (including across theme switches). -2. **App CSS** — redefine a `.wire-*` class in your own stylesheet, which is loaded +2. **App CSS** — redefine a `.wrn-*` class in your own stylesheet, which is loaded after `ui.css` and therefore wins. 3. **`class` prop** — pass a `class` prop to a component; it is appended to the component's root element, letting you add per-instance classes without touching @@ -217,4 +217,4 @@ Ways to customize the components, in increasing order of power: [`@wrnexus/dev-server`](../dev-server) / built by [`@wrnexus/cli`](../cli). - Depends on [`@wrnexus/core`](../core) (`dependencies`). - `theme-toggle` relies on the framework's theme runtime, which binds the - `data-wire-theme-toggle` attribute — no per-component JS is required. + `data-wrn-theme-toggle` attribute — no per-component JS is required. diff --git a/.publish/ui/components/Accordion.wrn b/.publish/ui/components/Accordion.wrn index ceb9ed1f..84426c6e 100644 --- a/.publish/ui/components/Accordion.wrn +++ b/.publish/ui/components/Accordion.wrn @@ -1,5 +1,3 @@ -import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn" - component Accordion { outputs { change(payload: { value?: string | number | boolean | null; values?: Array; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null) @@ -105,15 +103,15 @@ component Accordion { data-variant="{variant}" data-indicator="{indicator}" data-multiple="{allowsMultiple() ? 'true' : 'false'}" - class="wire-component wire-component--color-{color} wire-component--size-{size} wire-next--accordion wire-next--accordion-{variant} {bordered ? 'wire-next--accordion-bordered' : ''} {separated ? 'wire-next--accordion-separated' : ''} {flush ? 'wire-next--accordion-flush' : ''} {disabled ? 'wire-next--disabled' : ''} {class}" + class="wrn-component wrn-component--color-{color} wrn-component--size-{size} wrn-next--accordion wrn-next--accordion-{variant} {bordered ? 'wrn-next--accordion-bordered' : ''} {separated ? 'wrn-next--accordion-separated' : ''} {flush ? 'wrn-next--accordion-flush' : ''} {disabled ? 'wrn-next--disabled' : ''} {class}" > {#each items as item, index}
-

+

-
+

{child.content}

@@ -206,27 +204,26 @@ component Accordion {
{/each} -
-
+
{counterText()}
-
+
{#if tags && query && !allOptions().some((option) => option.label.toLowerCase() === query.toLowerCase())} - + {/if} @@ -455,68 +453,136 @@ component AdvancedSelect { {#if helpText && !validationMessage}{helpText}{/if} {validationMessage}
{#each visibleMembers() as item} {#if item.src} {item.tooltip || item.name} @@ -104,11 +102,11 @@ component AvatarGroup { {#if hiddenMembers().length > 0}
-
{#if quoteMark} - + {/if} -
+
{#if quote}

{quote}

{:else} @@ -42,17 +40,17 @@ component Blockquote {
{#if citation || citationTitle || avatarSrc} -
+
{#if avatarSrc} {avatarAlt} {/if} - + {#if citation} {#if citationUrl} {citation} @@ -65,63 +63,62 @@ component Blockquote {
{/if}
-