From 8b728a3e5d7e74dd970ebd7eb1c1d2e1ab3434d3 Mon Sep 17 00:00:00 2001 From: Ajay Ghanwat Date: Sat, 25 Jul 2026 13:38:18 +0530 Subject: [PATCH] New Captcha Package added --- AUDIO-PLAYBACK-FIX.md | 51 + CAPTCHA-FIX8-README.md | 40 + CAPTCHA-FIXED7-INSTRUCTIONS.md | 51 + MANIFEST.md | 38 + README.md | 1551 +---------------- RUNTIME-COMPATIBILITY.md | 7 + VALIDATION.md | 27 + bun.lock | 109 +- docs/IMAGE-STYLES.md | 47 + docs/image-style-gallery.png | Bin 0 -> 141355 bytes examples/captcha-showcase/README.md | 17 + .../app/api/captcha/[...path].ts | 10 + examples/captcha-showcase/app/api/contact.ts | 39 + .../captcha-showcase/app/api/page-grant.ts | 34 + .../app/components/Captcha.wrn | 320 ++++ examples/captcha-showcase/app/lib/captcha.ts | 17 + .../app/middleware/captcha-page-gate.ts | 21 + .../captcha-showcase/app/pages/captcha.wrn | 37 + examples/captcha-showcase/app/pages/index.wrn | 633 +++++++ .../captcha-showcase/app/pages/protected.wrn | 17 + .../captcha-showcase/app/pages/styles.wrn | 96 + examples/captcha-showcase/app/routes.gen.ts | 58 + .../captcha-showcase/app/schemas/contact.ts | 27 + .../captcha-showcase/app/styles/global.css | 7 + examples/captcha-showcase/package.json | 25 + .../public/assets/wrnexus/captcha.js | 878 ++++++++++ .../captcha-showcase/test/showcase.test.ts | 211 +++ examples/captcha-showcase/tsconfig.json | 5 + examples/captcha-showcase/wrnexus.config.ts | 23 + .../app/pages/advanced-forms.wrn | 4 +- .../component-showcase/app/pages/base.wrn | 2 +- .../app/pages/components/button.wrn | 121 +- .../app/pages/components/input-number.wrn | 271 ++- .../component-showcase/app/pages/index.wrn | 4 +- .../scripts/generate-showcase.mjs | 209 ++- integration/README.md | 19 + package.json | 3 +- packages/captcha/CHANGELOG.md | 46 + packages/captcha/README.md | 324 ++++ packages/captcha/SECURITY.md | 33 + packages/captcha/assets/audio/README.md | 5 + packages/captcha/assets/audio/en/0.wav | Bin 0 -> 44028 bytes packages/captcha/assets/audio/en/1.wav | Bin 0 -> 41338 bytes packages/captcha/assets/audio/en/2.wav | Bin 0 -> 39074 bytes packages/captcha/assets/audio/en/3.wav | Bin 0 -> 39878 bytes packages/captcha/assets/audio/en/4.wav | Bin 0 -> 42602 bytes packages/captcha/assets/audio/en/5.wav | Bin 0 -> 46398 bytes packages/captcha/assets/audio/en/6.wav | Bin 0 -> 44108 bytes packages/captcha/assets/audio/en/7.wav | Bin 0 -> 45706 bytes packages/captcha/assets/audio/en/8.wav | Bin 0 -> 37290 bytes packages/captcha/assets/audio/en/9.wav | Bin 0 -> 45482 bytes packages/captcha/assets/audio/en/a.wav | Bin 0 -> 36956 bytes packages/captcha/assets/audio/en/b.wav | Bin 0 -> 37806 bytes packages/captcha/assets/audio/en/by.wav | Bin 0 -> 41052 bytes packages/captcha/assets/audio/en/c.wav | Bin 0 -> 38228 bytes packages/captcha/assets/audio/en/d.wav | Bin 0 -> 35906 bytes packages/captcha/assets/audio/en/divided.wav | Bin 0 -> 52826 bytes packages/captcha/assets/audio/en/e.wav | Bin 0 -> 33678 bytes packages/captcha/assets/audio/en/eight.wav | Bin 0 -> 37290 bytes packages/captcha/assets/audio/en/f.wav | Bin 0 -> 37156 bytes packages/captcha/assets/audio/en/five.wav | Bin 0 -> 46398 bytes packages/captcha/assets/audio/en/four.wav | Bin 0 -> 42602 bytes packages/captcha/assets/audio/en/g.wav | Bin 0 -> 36804 bytes packages/captcha/assets/audio/en/h.wav | Bin 0 -> 39588 bytes packages/captcha/assets/audio/en/i.wav | Bin 0 -> 37404 bytes packages/captcha/assets/audio/en/is.wav | Bin 0 -> 38304 bytes packages/captcha/assets/audio/en/j.wav | Bin 0 -> 39526 bytes packages/captcha/assets/audio/en/k.wav | Bin 0 -> 41520 bytes packages/captcha/assets/audio/en/l.wav | Bin 0 -> 36844 bytes packages/captcha/assets/audio/en/m.wav | Bin 0 -> 38928 bytes packages/captcha/assets/audio/en/minus.wav | Bin 0 -> 45838 bytes packages/captcha/assets/audio/en/n.wav | Bin 0 -> 38476 bytes packages/captcha/assets/audio/en/nine.wav | Bin 0 -> 45482 bytes packages/captcha/assets/audio/en/o.wav | Bin 0 -> 36844 bytes packages/captcha/assets/audio/en/one.wav | Bin 0 -> 41338 bytes packages/captcha/assets/audio/en/p.wav | Bin 0 -> 37550 bytes packages/captcha/assets/audio/en/plus.wav | Bin 0 -> 42816 bytes packages/captcha/assets/audio/en/q.wav | Bin 0 -> 42936 bytes packages/captcha/assets/audio/en/r.wav | Bin 0 -> 38994 bytes packages/captcha/assets/audio/en/s.wav | Bin 0 -> 37098 bytes packages/captcha/assets/audio/en/seven.wav | Bin 0 -> 45706 bytes packages/captcha/assets/audio/en/six.wav | Bin 0 -> 44108 bytes packages/captcha/assets/audio/en/t.wav | Bin 0 -> 37944 bytes packages/captcha/assets/audio/en/three.wav | Bin 0 -> 39878 bytes packages/captcha/assets/audio/en/times.wav | Bin 0 -> 50368 bytes packages/captcha/assets/audio/en/two.wav | Bin 0 -> 39074 bytes packages/captcha/assets/audio/en/u.wav | Bin 0 -> 38718 bytes packages/captcha/assets/audio/en/v.wav | Bin 0 -> 41022 bytes packages/captcha/assets/audio/en/w.wav | Bin 0 -> 46058 bytes packages/captcha/assets/audio/en/what.wav | Bin 0 -> 38532 bytes packages/captcha/assets/audio/en/x.wav | Bin 0 -> 41148 bytes packages/captcha/assets/audio/en/y.wav | Bin 0 -> 41836 bytes packages/captcha/assets/audio/en/z.wav | Bin 0 -> 40062 bytes packages/captcha/assets/audio/en/zero.wav | Bin 0 -> 44028 bytes packages/captcha/assets/client/captcha.js | 878 ++++++++++ packages/captcha/components/Captcha.wrn | 320 ++++ packages/captcha/package.json | 53 + packages/captcha/src/audio/index.ts | 1 + packages/captcha/src/audio/renderer.ts | 239 +++ packages/captcha/src/challenges/bitmap.ts | 45 + .../captcha/src/challenges/calculation.ts | 60 + packages/captcha/src/challenges/image.ts | 130 ++ packages/captcha/src/challenges/index.ts | 31 + packages/captcha/src/challenges/invisible.ts | 38 + packages/captcha/src/challenges/png.ts | 240 +++ packages/captcha/src/challenges/styles.ts | 131 ++ packages/captcha/src/challenges/text.ts | 55 + packages/captcha/src/challenges/visual.ts | 690 ++++++++ packages/captcha/src/client/index.ts | 36 + packages/captcha/src/crypto.ts | 55 + packages/captcha/src/engine.ts | 400 +++++ packages/captcha/src/http.ts | 194 +++ packages/captcha/src/index.ts | 14 + packages/captcha/src/middleware.ts | 129 ++ packages/captcha/src/normalize.ts | 25 + packages/captcha/src/plugin.ts | 124 ++ packages/captcha/src/policy.ts | 63 + packages/captcha/src/providers/custom.ts | 8 + packages/captcha/src/providers/hcaptcha.ts | 24 + packages/captcha/src/providers/index.ts | 7 + packages/captcha/src/providers/managed.ts | 69 + packages/captcha/src/providers/recaptcha.ts | 23 + packages/captcha/src/providers/self-hosted.ts | 32 + packages/captcha/src/providers/siteverify.ts | 147 ++ packages/captcha/src/providers/turnstile.ts | 23 + packages/captcha/src/server/index.ts | 12 + packages/captcha/src/stores/memory.ts | 107 ++ packages/captcha/src/stores/redis.ts | 182 ++ packages/captcha/src/stores/sqlite.ts | 170 ++ packages/captcha/src/types.ts | 339 ++++ packages/captcha/src/validation.ts | 51 + packages/captcha/test/audio.test.ts | 24 + packages/captcha/test/client-runtime.test.ts | 49 + packages/captcha/test/component.test.ts | 38 + packages/captcha/test/engine.test.ts | 203 +++ packages/captcha/test/http.test.ts | 29 + packages/captcha/test/middleware.test.ts | 89 + packages/captcha/test/policy.test.ts | 20 + packages/captcha/test/providers.test.ts | 44 + packages/captcha/test/visual-styles.test.ts | 112 ++ packages/captcha/tsconfig.json | 7 + packages/ui/COMPONENTS.md | 17 +- packages/ui/component-reference.json | 699 +++++++- packages/ui/components/Captcha.wrn | 320 ++++ packages/ui/components/InputNumber.wrn | 639 ++++++- scripts/install-captcha.mjs | 82 + services/managed-captcha/README.md | 66 + services/managed-captcha/docs/openapi.yaml | 176 ++ .../migrations/001_initial.sql | 26 + services/managed-captcha/package.json | 19 + services/managed-captcha/src/index.ts | 14 + services/managed-captcha/src/service.ts | 241 +++ services/managed-captcha/src/store.ts | 17 + services/managed-captcha/src/types.ts | 45 + services/managed-captcha/test/service.test.ts | 74 + services/managed-captcha/tsconfig.json | 5 + tsconfig.json | 368 +++- 157 files changed, 12092 insertions(+), 1913 deletions(-) create mode 100644 AUDIO-PLAYBACK-FIX.md create mode 100644 CAPTCHA-FIX8-README.md create mode 100644 CAPTCHA-FIXED7-INSTRUCTIONS.md create mode 100644 MANIFEST.md create mode 100644 RUNTIME-COMPATIBILITY.md create mode 100644 VALIDATION.md create mode 100644 docs/IMAGE-STYLES.md create mode 100644 docs/image-style-gallery.png create mode 100644 examples/captcha-showcase/README.md create mode 100644 examples/captcha-showcase/app/api/captcha/[...path].ts create mode 100644 examples/captcha-showcase/app/api/contact.ts create mode 100644 examples/captcha-showcase/app/api/page-grant.ts create mode 100644 examples/captcha-showcase/app/components/Captcha.wrn create mode 100644 examples/captcha-showcase/app/lib/captcha.ts create mode 100644 examples/captcha-showcase/app/middleware/captcha-page-gate.ts create mode 100644 examples/captcha-showcase/app/pages/captcha.wrn create mode 100644 examples/captcha-showcase/app/pages/index.wrn create mode 100644 examples/captcha-showcase/app/pages/protected.wrn create mode 100644 examples/captcha-showcase/app/pages/styles.wrn create mode 100644 examples/captcha-showcase/app/routes.gen.ts create mode 100644 examples/captcha-showcase/app/schemas/contact.ts create mode 100644 examples/captcha-showcase/app/styles/global.css create mode 100644 examples/captcha-showcase/package.json create mode 100644 examples/captcha-showcase/public/assets/wrnexus/captcha.js create mode 100644 examples/captcha-showcase/test/showcase.test.ts create mode 100644 examples/captcha-showcase/tsconfig.json create mode 100644 examples/captcha-showcase/wrnexus.config.ts create mode 100644 integration/README.md create mode 100644 packages/captcha/CHANGELOG.md create mode 100644 packages/captcha/README.md create mode 100644 packages/captcha/SECURITY.md create mode 100644 packages/captcha/assets/audio/README.md create mode 100644 packages/captcha/assets/audio/en/0.wav create mode 100644 packages/captcha/assets/audio/en/1.wav create mode 100644 packages/captcha/assets/audio/en/2.wav create mode 100644 packages/captcha/assets/audio/en/3.wav create mode 100644 packages/captcha/assets/audio/en/4.wav create mode 100644 packages/captcha/assets/audio/en/5.wav create mode 100644 packages/captcha/assets/audio/en/6.wav create mode 100644 packages/captcha/assets/audio/en/7.wav create mode 100644 packages/captcha/assets/audio/en/8.wav create mode 100644 packages/captcha/assets/audio/en/9.wav create mode 100644 packages/captcha/assets/audio/en/a.wav create mode 100644 packages/captcha/assets/audio/en/b.wav create mode 100644 packages/captcha/assets/audio/en/by.wav create mode 100644 packages/captcha/assets/audio/en/c.wav create mode 100644 packages/captcha/assets/audio/en/d.wav create mode 100644 packages/captcha/assets/audio/en/divided.wav create mode 100644 packages/captcha/assets/audio/en/e.wav create mode 100644 packages/captcha/assets/audio/en/eight.wav create mode 100644 packages/captcha/assets/audio/en/f.wav create mode 100644 packages/captcha/assets/audio/en/five.wav create mode 100644 packages/captcha/assets/audio/en/four.wav create mode 100644 packages/captcha/assets/audio/en/g.wav create mode 100644 packages/captcha/assets/audio/en/h.wav create mode 100644 packages/captcha/assets/audio/en/i.wav create mode 100644 packages/captcha/assets/audio/en/is.wav create mode 100644 packages/captcha/assets/audio/en/j.wav create mode 100644 packages/captcha/assets/audio/en/k.wav create mode 100644 packages/captcha/assets/audio/en/l.wav create mode 100644 packages/captcha/assets/audio/en/m.wav create mode 100644 packages/captcha/assets/audio/en/minus.wav create mode 100644 packages/captcha/assets/audio/en/n.wav create mode 100644 packages/captcha/assets/audio/en/nine.wav create mode 100644 packages/captcha/assets/audio/en/o.wav create mode 100644 packages/captcha/assets/audio/en/one.wav create mode 100644 packages/captcha/assets/audio/en/p.wav create mode 100644 packages/captcha/assets/audio/en/plus.wav create mode 100644 packages/captcha/assets/audio/en/q.wav create mode 100644 packages/captcha/assets/audio/en/r.wav create mode 100644 packages/captcha/assets/audio/en/s.wav create mode 100644 packages/captcha/assets/audio/en/seven.wav create mode 100644 packages/captcha/assets/audio/en/six.wav create mode 100644 packages/captcha/assets/audio/en/t.wav create mode 100644 packages/captcha/assets/audio/en/three.wav create mode 100644 packages/captcha/assets/audio/en/times.wav create mode 100644 packages/captcha/assets/audio/en/two.wav create mode 100644 packages/captcha/assets/audio/en/u.wav create mode 100644 packages/captcha/assets/audio/en/v.wav create mode 100644 packages/captcha/assets/audio/en/w.wav create mode 100644 packages/captcha/assets/audio/en/what.wav create mode 100644 packages/captcha/assets/audio/en/x.wav create mode 100644 packages/captcha/assets/audio/en/y.wav create mode 100644 packages/captcha/assets/audio/en/z.wav create mode 100644 packages/captcha/assets/audio/en/zero.wav create mode 100644 packages/captcha/assets/client/captcha.js create mode 100644 packages/captcha/components/Captcha.wrn create mode 100644 packages/captcha/package.json create mode 100644 packages/captcha/src/audio/index.ts create mode 100644 packages/captcha/src/audio/renderer.ts create mode 100644 packages/captcha/src/challenges/bitmap.ts create mode 100644 packages/captcha/src/challenges/calculation.ts create mode 100644 packages/captcha/src/challenges/image.ts create mode 100644 packages/captcha/src/challenges/index.ts create mode 100644 packages/captcha/src/challenges/invisible.ts create mode 100644 packages/captcha/src/challenges/png.ts create mode 100644 packages/captcha/src/challenges/styles.ts create mode 100644 packages/captcha/src/challenges/text.ts create mode 100644 packages/captcha/src/challenges/visual.ts create mode 100644 packages/captcha/src/client/index.ts create mode 100644 packages/captcha/src/crypto.ts create mode 100644 packages/captcha/src/engine.ts create mode 100644 packages/captcha/src/http.ts create mode 100644 packages/captcha/src/index.ts create mode 100644 packages/captcha/src/middleware.ts create mode 100644 packages/captcha/src/normalize.ts create mode 100644 packages/captcha/src/plugin.ts create mode 100644 packages/captcha/src/policy.ts create mode 100644 packages/captcha/src/providers/custom.ts create mode 100644 packages/captcha/src/providers/hcaptcha.ts create mode 100644 packages/captcha/src/providers/index.ts create mode 100644 packages/captcha/src/providers/managed.ts create mode 100644 packages/captcha/src/providers/recaptcha.ts create mode 100644 packages/captcha/src/providers/self-hosted.ts create mode 100644 packages/captcha/src/providers/siteverify.ts create mode 100644 packages/captcha/src/providers/turnstile.ts create mode 100644 packages/captcha/src/server/index.ts create mode 100644 packages/captcha/src/stores/memory.ts create mode 100644 packages/captcha/src/stores/redis.ts create mode 100644 packages/captcha/src/stores/sqlite.ts create mode 100644 packages/captcha/src/types.ts create mode 100644 packages/captcha/src/validation.ts create mode 100644 packages/captcha/test/audio.test.ts create mode 100644 packages/captcha/test/client-runtime.test.ts create mode 100644 packages/captcha/test/component.test.ts create mode 100644 packages/captcha/test/engine.test.ts create mode 100644 packages/captcha/test/http.test.ts create mode 100644 packages/captcha/test/middleware.test.ts create mode 100644 packages/captcha/test/policy.test.ts create mode 100644 packages/captcha/test/providers.test.ts create mode 100644 packages/captcha/test/visual-styles.test.ts create mode 100644 packages/captcha/tsconfig.json create mode 100644 packages/ui/components/Captcha.wrn create mode 100644 scripts/install-captcha.mjs create mode 100644 services/managed-captcha/README.md create mode 100644 services/managed-captcha/docs/openapi.yaml create mode 100644 services/managed-captcha/migrations/001_initial.sql create mode 100644 services/managed-captcha/package.json create mode 100644 services/managed-captcha/src/index.ts create mode 100644 services/managed-captcha/src/service.ts create mode 100644 services/managed-captcha/src/store.ts create mode 100644 services/managed-captcha/src/types.ts create mode 100644 services/managed-captcha/test/service.test.ts create mode 100644 services/managed-captcha/tsconfig.json diff --git a/AUDIO-PLAYBACK-FIX.md b/AUDIO-PLAYBACK-FIX.md new file mode 100644 index 00000000..d13f3d7f --- /dev/null +++ b/AUDIO-PLAYBACK-FIX.md @@ -0,0 +1,51 @@ +# WRNexusJS CAPTCHA audio playback fix + +This patch fixes audio CAPTCHA playback in production and bundled server builds. + +## Root cause + +`AssetAudioRenderer` originally located WAV files only from `import.meta.url`. After +WRNexusJS bundles the server into `dist/server.js`, that URL points to the app build +folder instead of `packages/captcha`, so `/api/captcha/audio/...` fails while trying +to read the WAV clips. + +## Changes + +- Resolves audio assets from the installed `@wrnexus/captcha/audio` package entry. +- Supports `WRNEXUS_CAPTCHA_AUDIO_DIR` for custom deployments. +- Searches workspace and `node_modules` layouts as safe fallbacks. +- Keeps the browser `Audio` instance alive while it plays. +- Stops old audio when a new challenge is loaded. +- Returns a clear HTTP 503 and server log if audio rendering fails. +- Adds a real WAV rendering test. + +## Apply + +Extract this ZIP into the WRNexusJS repository root and replace the included files. +Then run: + +```bash +bun run typecheck +bun test packages/captcha +bun run --cwd examples/captcha-showcase check +``` + +Restart the server. If running the production build, rebuild first: + +```bash +bun run --cwd examples/captcha-showcase build +bun examples/captcha-showcase/dist/server.js +``` + +## Verify + +1. Open the browser Network panel. +2. Click **Listen**. +3. The `/api/captcha/audio/?key=...` request must return HTTP 200. +4. Its `content-type` must be `audio/wav`. + +For a nonstandard package layout, set an absolute path before starting the server: + +```powershell +$env:WRNEXUS_CAPTCHA_AUDIO_DIR="E:\WireJS\packages\captcha\assets\audio" +``` diff --git a/CAPTCHA-FIX8-README.md b/CAPTCHA-FIX8-README.md new file mode 100644 index 00000000..00172b5e --- /dev/null +++ b/CAPTCHA-FIX8-README.md @@ -0,0 +1,40 @@ +# WRNexusJS CAPTCHA Fixed 8 + +Extract this ZIP directly into the WRNexusJS repository root and allow the included files to replace the current CAPTCHA files. + +## Added + +- 18 concrete generated CAPTCHA image styles +- `imageStyle="random"` +- `allowedStyles` and `excludedStyles` +- `randomizeStyle` +- Resolved style metadata and browser event details +- Easy, normal, hard, controlled-pool, and full renderer showcase at `/styles` +- Tailwind Iconify/Lucide showcase dependencies +- Faster CRC32 PNG encoding +- Safe floating-point line rendering for web, darts, distortion, wave, and scribble effects +- Tests for every renderer and style-pool rule + +## Supported styles + +`classic`, `collision`, `snow`, `corrosion`, `spiderweb`, `cross-shadow`, `split`, `split2`, `cut`, `darts`, `distortion`, `stitch`, `striped`, `wave`, `grid-noise`, `scribble`, `pixel`, `broken-lines`, and `random`. + +## After extraction + +```bash +bun install +bun run scripts/generate-ui-component-reference.mjs +bun run typecheck +bun test packages/captcha +bun test services/managed-captcha +bun run --cwd examples/captcha-showcase check +bun run --cwd examples/captcha-showcase dev +``` + +Open: + +```text +http://localhost:3000/styles +``` + +Use a hard browser refresh after restarting the development server. diff --git a/CAPTCHA-FIXED7-INSTRUCTIONS.md b/CAPTCHA-FIXED7-INSTRUCTIONS.md new file mode 100644 index 00000000..37d58bac --- /dev/null +++ b/CAPTCHA-FIXED7-INSTRUCTIONS.md @@ -0,0 +1,51 @@ +# WRNexusJS CAPTCHA Fixed 7 + +Extract this patch directly into the WRNexusJS repository root and allow files to be replaced. + +## Added + +- `size="compact"`, `size="normal"`, and `size="big"` +- Backward-compatible `small`/`sm`, `large`/`lg`, and `compact="true"` aliases +- `showListen="true|false"` for the Listen button +- Existing `showAudio="true|false"` remains the broader audio-alternative switch +- Self-hosted `type="not-robot"` checkbox challenge +- Showcase examples and tests for all new behavior +- Managed API/OpenAPI support for `not-robot` + +## Usage + +```wrn + +``` + +```wrn + +``` + +The not-robot checkbox is server verified. It uses a one-time challenge, minimum completion time, honeypot, expiry, attempt limits, action binding, optional hostname/session/IP binding, and a single-use response token. It is a low-friction mode; high-risk traffic should escalate to a visual challenge or an external provider. + +## Regenerate and test + +```bash +bun run scripts/generate-ui-component-reference.mjs +bun run typecheck +bun test packages/captcha +bun run --cwd examples/captcha-showcase check +bun run --cwd examples/captcha-showcase dev +``` + +Hard-refresh the browser after restarting the development server. diff --git a/MANIFEST.md b/MANIFEST.md new file mode 100644 index 00000000..5515daec --- /dev/null +++ b/MANIFEST.md @@ -0,0 +1,38 @@ +# Bundle manifest + +## Package + +- Engine, challenge generators, optimized PNG renderer, audio renderer +- Memory, SQLite-compatible, and Redis-compatible stores +- Self-hosted, managed, Turnstile, reCAPTCHA, hCaptcha, and custom providers +- HTTP handlers, guards, page gates, adaptive policy, validation integration +- Plugin and DevToolbar audit panel +- Tailwind-only `Captcha.wrn` with compact/normal/big sizing, listen visibility, not-robot mode, 25–75 disturbance control, and generated image renderer props +- English WAV audio assets +- Unit tests for all 18 PNG renderers, style resolution, security behavior, and documentation + +## Example + +- All self-hosted challenge types, including the not-robot checkbox +- Audio and invisible examples +- Shared `@wrnexus/validation` contact schema with client and API validation +- Protected form API using `captchaGuard` +- Working once-per-session protected-page grant and safe return redirect +- Compact, normal, and big component examples +- Listen-visible and listen-hidden examples +- Easy, normal, and hard image-disturbance examples +- Dedicated `/styles` gallery with 18 concrete renderers, random selection, allowed pools, and excluded pools +- External-provider placeholders + +## Managed service + +- Project keys, allowed hostnames, quotas, key rotation +- Public create/solve and secret-authenticated verification routes +- Memory reference store, SQL migration, OpenAPI specification + +## Integration + +- Repeat-safe installer +- TypeScript path aliases +- Current component-discovery compatibility copy +- Generated 18-style preview gallery and renderer guidance diff --git a/README.md b/README.md index f82f6bee..228ab710 100644 --- a/README.md +++ b/README.md @@ -1,1536 +1,39 @@ -# WrNexus +# WRNexusJS CAPTCHA complete bundle -> **WRNexusJS 0.3 architecture:** see [the language specification](docs/WRN-LANGUAGE-SPEC-1.0.md), -> [architecture guide](docs/ARCHITECTURE-0.3.md), [upgrade guide](docs/UPGRADE-0.3.md), and -> [40-point implementation matrix](docs/40-POINT-IMPLEMENTATION-0.3.md). Validate a release with -> the [one-by-one test checklist](docs/TEST-CHECKLIST-0.3.md) and [audit record](docs/AUDIT-0.3.md). -> An **SSR-first** full-stack web framework MVP with **server-rendered, reactive -> components**. Built in TypeScript, **Bun-first**, Node-friendly where possible. +This bundle targets WRNexusJS `main-80` / package version `0.3.6` and contains: -It gives you file-based pages and API routes, middleware, realtime WebSocket -routes, opt-in client hydration, and the custom `.wrn` language/compiler through -one shared syntax and runtime model. +- `packages/captcha`: publishable `@wrnexus/captcha` package +- `packages/captcha/components/Captcha.wrn`: Tailwind-only UI component +- `examples/captcha-showcase`: runnable challenge, disturbance, validated-form, and protected-page showcase +- `services/managed-captcha`: managed API starter with projects, keys, hostname restrictions, quotas, and rotation +- `scripts/install-captcha.mjs`: repeat-safe repository installer +- `integration`: integration notes and generated path changes +- `docs/IMAGE-STYLES.md`: renderer catalog with a generated preview gallery -> πŸ“˜ **[The Complete Guide](docs/GUIDE.md)** β€” one document covering every package, -> the whole `.wrn` language, all attributes/config properties, and a step-by-step -> path from `create` to a full app (pages, APIs, DB, auth, realtime, deploy). - ---- - -## Why SSR-first with reactive components? - -- **Fast, robust default.** Pages render to HTML on the server, so users get - content immediately and pages work even before (or without) JavaScript. -- **Ship JS only where needed.** A page is plain HTML until it mounts a component - with `data-component=""`. Components render on the server too; only their - reactive state and forms run on the browser β€” the rest ships zero JavaScript. -- **Clean separation.** Server rendering (`@wrnexus/ssr`) never touches the DOM or - client runtime; client hydration (`@wrnexus/csr`) never runs on the server. They - meet only at one seam: a single runtime, `/__wrnexus/reactive.js`. - ---- - -## Installation - -Requires [Bun](https://bun.sh) β‰₯ 1.3.0. +## Install into a WRNexusJS checkout ```bash -bun install -bun run dev # runs examples/basic-app at http://localhost:3000 +bun scripts/install-captcha.mjs /path/to/WRNexusJS \ + --with-showcase \ + --with-managed-service \ + --check ``` -## CLI commands +The installer copies the package, places `Captcha.wrn` in `packages/ui/components` for current component discovery, adds TypeScript aliases, optionally installs the example and managed service, and can run validation commands. -```bash -wrnexus dev [app-dir] [--port=3000] # start the dev server (HMR + regenerates typed queries) -wrnexus build [app-dir] # build the production server bundle + assets -wrnexus generate mobile # scaffold a Capacitor iOS/Android shell -wrnexus mobile add @capacitor/camera # install a native plugin + sync projects -wrnexus create # scaffold a new app -wrnexus generate # scaffold a page | component | api | schema (alias: g) -wrnexus eject # copy a Wire UI component into app/components -wrnexus test [app-dir] [--watch] # run the app's tests (bun test, `test` profile) -wrnexus profiles [app-dir] # list config profiles (dev/prod/uat/…) + their env files +Read `packages/captcha/README.md`, `packages/captcha/SECURITY.md`, and `services/managed-captcha/README.md` before production deployment. -# Database -wrnexus db migrate # apply pending migrations -wrnexus db rollback # revert the last migration -wrnexus db status # list applied / pending migrations -wrnexus db new [--from-models] # scaffold a migration (optionally from TS models) -wrnexus db generate # (re)generate typed queries from app/db/queries/*.sql -wrnexus db seed # run app/db/seed.ts (re-runnable dev data) -wrnexus db studio [table] # list tables + row counts, or dump a table's rows -``` - -### Mobile and PWA - -The same `view` HTML renders in browsers and in the Capacitor app. Add device -directives directly to normal elements. Camera directives use the native camera -in Capacitor, phone camera capture in supported mobile browsers, and a file -picker in desktop browsers. The markup and responsive theme remain shared. - -Cross-platform capabilities can also be declared directly in `.wrn` markup: - -```html - - -

Browser help

- - -``` - -Use `wrnexus native list` to see built-in capabilities and -`wrnexus native add camera share` to install the packages required by the configured -Capacitor or Expo mode. Advanced browser code can use `native.run()` and register -custom adapters from `@wrnexus/native`. - -The declarative `data-native-browser` and `data-native-mobile` capability actions run -in browser and Capacitor/WebView pages. Fully native Expo compilation supports -`data-native-only` and `@mobile-*` event selection, but capability calls must currently -use the installed Expo module from native screen code; the compiler reports an -actionable error instead of silently dropping a `data-native-mobile` action. - -```wrn -page Home { - view { -
- Captured photo - - -
- } -} -``` - -Configure native and installable-web behavior in `wrnexus.config.ts`: - -```ts -const config: AppConfig = { - mobile: { - mode: "webview", // "webview" (Capacitor) or "native" (Expo/React Native) - enabled: true, - appId: "com.example.app", - appName: "Example", - serverUrl: "http://192.168.0.10:3000", - // Native mode uses this backend URL instead of rendering server HTML: - apiUrl: "https://api.example.com", - scheme: "example", - layout: "mobile", // optional app/layouts/mobile.wrn - icon: "resources/icon.png", - // Mode-specific advanced settings: - capacitor: { ios: { contentInset: "automatic" } }, - expo: { orientation: "portrait" }, - }, - pwa: { - enabled: true, - id: "/", - name: "Example", - shortName: "Example", - startUrl: "/", - scope: "/", - orientation: "any", - display: "standalone", - themeColor: "#6366f1", - backgroundColor: "#0f172a", - offlineUrl: "/", - cacheUrls: ["/", "/about"], - cacheName: "wrnexus-pwa-v1", // change to invalidate installed caches - }, -}; -``` - -PWA support is on by default: the framework serves `/site.webmanifest`, `/sw.js`, -injects install metadata, precaches configured offline URLs, and registers a -network-first service worker. Set `pwa.serviceWorker: false` to keep only the -manifest, or `pwa: false` to disable PWA support. Generated Capacitor shells include `error.html`, so -an unreachable server shows a retry screen instead of a blank WebView. - -Generate the configured mode with `wrnexus generate mobile`. You can override it -once with `--mode=webview` or `--mode=native`. WebView mode shares `.wrn` pages -through Capacitor. Native mode creates an Expo/React Native project with native -controls, file-based screens, and a typed backend helper; it shares WrNexus API, -upload, authentication, and realtime endpoints, but not HTML views. - -In native mode, `wrnexus mobile compile` converts `app/pages/**/*.wrn` into -matching Expo Router routes under `mobile/app/`. Portable tags are mapped to -React Native primitives (`div/main` β†’ `View`, text tags β†’ `Text`, `button` β†’ -`Pressable`, `input` β†’ `TextInput`, and `img` β†’ `Image`). State, interpolation, -events, `{#if}`, `{#each}`, class styles, and links are compiled to native JSX. -Unsupported DOM elements, inline CSS strings, SSR/data blocks, and browser APIs -produce compile errors instead of silently falling back to a WebView. - -From this repo the same commands are wired as root scripts: - -```bash -bun run dev # = wrnexus dev examples/basic-app -bun run build # = wrnexus build examples/basic-app -bun run create # = wrnexus create -``` - -Quality checks are wired at the repo root: - -```bash -bun run typecheck # TypeScript check -bun run lint # ESLint -bun run lint:fix # ESLint autofix -bun run format # Prettier write -bun run format:check # Prettier check -bun run check # typecheck + lint + format:check -``` - -Apps created with `wrnexus create ` include the same ESLint/Prettier -baseline for app code. - ---- - -## Project structure - -``` -myframework/ - packages/ - core/ # Context, Middleware types, security + error helpers - cli/ # `wrnexus` command (dev / build / create) - dev-server/ # Bun.serve HTTP + WebSocket pipeline - router/ # file-based router (scan + safe match) - ssr/ # server-side document rendering - csr/ # browser reactive runtime (hydrates components) - styles/ # global stylesheet pipeline + app config (CSS frameworks) - compiler/ # `.wrn` language compiler (lexer/parser/codegen) - reactive/ # tiny signal() implementation - examples/ - basic-app/ # reference app exercising every feature - app/ - pages/ # file-based pages -> /, /about - api/ # file-based API routes -> /api/* - middleware/ # global middleware (alphabetical order) - realtime/ # WebSocket routes -> /realtime/* - components/ # reactive components (.wrn, data-component="name") - styles/ # global CSS (global.css -> every page) - public/ # static assets -> /robots.txt, /images/logo.svg - wrnexus.config.ts # optional: SEO, head injection, CSS, security - package.json - tsconfig.json - README.md -``` - -The app folder is convention-based β€” drop a file in the right directory and it -becomes a route. Routes are resolved against a table scanned at startup; request -input is never turned into a file path. - ---- - -## Pages - -`app/pages/index.tsx` β†’ `/` - -```tsx -export const meta = { - title: "Home", - description: "Welcome to WrNexus", -}; - -export default function Home() { - return `

Hello from WrNexus

-
`; -} -``` - -`app/pages/about.tsx` β†’ `/about`. Dynamic segments use brackets: -`app/pages/users/[id].tsx` β†’ `/users/:id`, with `ctx.params.id` available. - -## SSR - -The server renders each page to a full HTML document. Page metadata is escaped -and placed in the ``; the body goes inside `#app`: - -```html - - - - - - Home - - - -
-

Hello from WrNexus

-
- -
-
- - - -``` - -The `
` mount was replaced by the component's -server-rendered HTML, and the reactive runtime is injected only because the page -now contains a `data-scope` (see "Per-page code-splitting" below). Everything in -a page module is **server-only** β€” it is never bundled to the browser. Errors -are readable in development and generic (no file paths) in production. - -## Components (server-rendered, hydrated on the browser) - -Components are `.wrn` files under `app/components`. They render on the **server** -(with their props applied) and are hydrated on the browser by the single generic -reactive runtime β€” they ship no JS of their own. Only forms and reactive state -run in the browser. - -`app/components/counter.wrn`: - -``` -component Counter { - props { - start = 0 // default's type drives coercion (number) - label = "Count" // (string) - } - state count = start - view { - - } -} -``` - -Mount it in any page and pass props as attributes β€” pass as many as you like: - -``` -
-``` - -Each attribute becomes a prop, coerced to the type of its declared default (so -`start="5"` arrives as the number `5`). Prop-driven text and attributes are -**baked server-side** (a static component ships zero JS); text that references -`state` stays reactive. Components (and layouts) can take children via `` β€” -including **named slots**: put `` in the component and fill it -from the mount with `
…
` (anything else fills the -default ``). Component names are validated before rendering. - -## Wire UI, theming & layouts - -**Wire UI** (`@wrnexus/ui`) ships common components β€” `button`, `input`, `select`, -`checkbox`, `radio`, `switch`, `textarea`, `badge`, `tag`, `alert`, `card`, -`avatar`, `spinner`, `skeleton`, `progress`, `tooltip`, `table`, `disclosure`, -`theme-toggle` β€” plus layout primitives `container`, `stack`, `hstack`, `grid`, -`divider`, `spacer`. They are auto-discovered, so you mount them straight away: - -``` -
-
-
-
-``` - -Override styles four ways, least β†’ most control: change a **theme token**, -redefine a **`.wire-*` class** in your CSS, pass a **`class` prop** (appended to -the root), or **`wrnexus eject `** to copy a component into `app/components` -and own it (an app component shadows the library one of the same name). - -**Theming.** Design tokens (`--wire-*`) come from `wrnexus.config.ts` `theme` (deep --merged over built-in light/dark), served at `/__wrnexus/theme.css`. The server -sets `` from the `wire-theme` cookie (no flash); a `theme-toggle` -component (or any `[data-wire-theme-toggle]` element) switches and persists it. -Choose a complete semantic palette with `theme: { palette: "violet" }`. Available -palettes are `blue`, `indigo`, `violet`, `emerald`, `cyan`, `rose`, `amber`, and -`slate`; a custom palette must provide every primary, secondary, info, success, -warning, danger, hover, and contrast color. - -**Page layouts.** Add named layouts under `app/layouts/` β€” e.g. `public.wrn`, -`dashboard.wrn`, `auth.wrn` β€” each a `component` with a `` where the -page body goes. A page picks one by name: - -``` -page Dashboard { - layout = "dashboard" - view {

Overview

... } -} -``` - -Pages with no `layout` fall back to a `default` layout if one exists; `layout = "none"` -opts out. Layouts can mount components (nav, theme-toggle) like any page. - -**Document layout.** A conventional `app/layouts/document.wrn` is applied outside -the selected page layout and may own ``, ``, ``, and `#app`. -The framework passes `cookies`, `theme`, `language`, `url`, and `pathname` as -SSR props, then safely merges page SEO, global head entries, styles, and scripts: - -```wrn -layout Document { - props { cookies = {} theme = "light" language = "en" } - view { - - -
- - } -} -``` - -Cookie-backed document attributes are present in the first server response, so -CSS can apply before paint instead of correcting the page after hydration. - -## API routes - -`app/api/hello.ts` β†’ `/api/hello`: - -```ts -export const GET = async () => Response.json({ message: "Hello API" }); -``` - -Method-specific handlers live in one file; unsupported methods return **405** -with an `Allow` header: - -```ts -export const POST = async (ctx) => { - const body = await ctx.req.json(); - return Response.json({ received: body }); -}; -``` - -## Validation (one schema, form + API) - -Define a schema once in `app/schemas/.ts` with the fluent builder: - -```ts -import { v } from "@wrnexus/validation"; -export default v.object({ - email: v.string().email(), - password: v.string().min(8, "Password must be at least 8 characters"), -}); -``` - -Use it on the **server** in an API route β€” bad input is rejected even if the -client is bypassed: - -```ts -import { parseBody } from "@wrnexus/validation"; -import login from "../schemas/login.ts"; -export const POST = async (ctx) => { - const r = await parseBody(login, ctx.req); - if (!r.ok) return r.response; // 400 { ok:false, errors } - return Response.json({ ok: true }); -}; -``` - -And on the **client** by naming it on a form β€” the framework injects the schema -descriptor + a tiny, eval-free validator that checks on submit/blur and writes -messages into `[data-error]` spans (no per-form JS, CSP-safe): - -``` -
- - - -
-``` - -Valid forms submit via `fetch` (JSON) and stay on the page: on success they -redirect (`data-redirect`) or reveal a `[data-success]` message; server-returned -field errors map back to the `[data-error]` spans. - -## i18n (pages + API) - -Put translations in `app/locales/.json` (nested keys, `{param}` placeholders): - -```json -// app/locales/es.json -{ "home": { "title": "Β‘Hola desde WrNexus!" }, "api": { "greeting": "Hola" } } -``` - -Translate in **views** with the `{t:key}` sugar (and `t:="key"` for -attributes), and in **API/handlers** with `ctx.t`: - -``` -

{t:home.title}

- -``` - -```ts -export const GET = (ctx) => Response.json({ message: ctx.t("api.greeting"), lang: ctx.lang }); -``` - -The active language is resolved per request from the `wire-lang` cookie β†’ the -browser's `Accept-Language` β†’ the config default (`wrnexus.config.ts` `i18n.default`), -and set on `` (no flash). A `[data-wire-lang-set="es"]` element (or -`