{"_id":"@arnelirobles/express-rnx","name":"@arnelirobles/express-rnx","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"@arnelirobles/express-rnx","version":"1.0.0","description":"Express middleware for rnxJS reactive components integration","type":"commonjs","main":"src/index.js","exports":{".":"./src/index.js"},"scripts":{"test":"jest","test:watch":"jest --watch","test:coverage":"jest --coverage"},"keywords":["express","rnxjs","reactive","components","middleware"],"author":{"name":"Arnel Irobles","email":"arnel@arnelirobles.com"},"license":"MPL-2.0","homepage":"https://github.com/BaryoDev/rnxjs","repository":{"type":"git","url":"git+https://github.com/BaryoDev/rnxjs.git","directory":"packages/express-rnx"},"bugs":{"url":"https://github.com/BaryoDev/rnxjs/issues"},"engines":{"node":">=12.0.0"},"devDependencies":{"express":"^4.18.0","jest":"^29.0.0"},"peerDependencies":{"express":">=4.0.0"},"_id":"@arnelirobles/express-rnx@1.0.0","gitHead":"18708492d977b4f308e03dcd2969d7ea2b1fe81f","_nodeVersion":"22.21.1","_npmVersion":"10.9.4","dist":{"integrity":"sha512-zcV6w581PZTZb6WMSIuZ/7b1nyYZLu7QGwCzPJmqct3lO3c6LUvLC6WcM0syp0CHym7J9BditL/1tPW+OniSYw==","shasum":"1ba5945f2db8ee13f549ed1d7e605e6de551bc3d","tarball":"https://registry.npmjs.org/@arnelirobles/express-rnx/-/express-rnx-1.0.0.tgz","fileCount":7,"unpackedSize":20553,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCbOVlALYVJXc78KzIG4pEYSR2a3MuiUA+aqX2BBFDOLAIhANTg72FG6T7XA7rLJjrzzEEJzIMBA0r/0olaXrd3FQwU"}]},"_npmUser":{"name":"arnelirobles","email":"arnelirobles@gmail.com"},"directories":{},"maintainers":[{"name":"arnelirobles","email":"arnelirobles@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/express-rnx_1.0.0_1766760032374_0.4844012544086336"},"_hasShrinkwrap":false}},"time":{"created":"2025-12-26T14:40:32.275Z","1.0.0":"2025-12-26T14:40:32.556Z","modified":"2025-12-26T14:40:32.860Z"},"maintainers":[{"name":"arnelirobles","email":"arnelirobles@gmail.com"}],"description":"Express middleware for rnxJS reactive components integration","homepage":"https://github.com/BaryoDev/rnxjs","keywords":["express","rnxjs","reactive","components","middleware"],"repository":{"type":"git","url":"git+https://github.com/BaryoDev/rnxjs.git","directory":"packages/express-rnx"},"author":{"name":"Arnel Irobles","email":"arnel@arnelirobles.com"},"bugs":{"url":"https://github.com/BaryoDev/rnxjs/issues"},"license":"MPL-2.0","readme":"# @arnelirobles/express-rnx\n\nExpress middleware and view helpers for integrating [rnxJS](https://github.com/BaryoDev/rnxjs) reactive components into Express applications.\n\n[![License: MPL-2.0](https://img.shields.io/badge/License-MPL%202.0-brightgreen.svg)](https://opensource.org/licenses/MPL-2.0)\n[![Node Version](https://img.shields.io/badge/node-%3E%3D12.0-green.svg)](https://nodejs.org/)\n\n## Overview\n\nexpress-rnx provides Express middleware that makes it easy to:\n\n- Include rnxJS library and stylesheets in your views\n- Create reactive state from server data\n- Render rnxJS components with template variables\n- Initialize rnxJS plugins (router, toast, storage)\n- Bind server data to reactive components\n\n## Installation\n\n### npm\n\n```bash\nnpm install @arnelirobles/express-rnx\n```\n\n### yarn\n\n```bash\nyarn add @arnelirobles/express-rnx\n```\n\n## Quick Start\n\n### 1. Setup Express Middleware\n\n```javascript\nconst express = require('express');\nconst rnxMiddleware = require('@arnelirobles/express-rnx');\n\nconst app = express();\n\n// Add rnx middleware\napp.use(rnxMiddleware({\n  cdn: true,\n  theme: 'bootstrap'\n}));\n\n// Set view engine\napp.set('view engine', 'ejs');\napp.set('views', './views');\n\n// Routes\napp.get('/', (req, res) => {\n  res.render('index');\n});\n\napp.listen(3000);\n```\n\n### 2. Use in EJS Templates\n\n```ejs\n<head>\n  <%- rnx.scripts() %>\n</head>\n\n<body>\n  <%\n    const userData = {\n      name: 'John Doe',\n      email: 'john@example.com'\n    };\n  %>\n\n  <%- rnx.state(userData, 'user') %>\n\n  <p>Welcome, <span data-bind=\"user.name\"></span>!</p>\n\n  <%- rnx.component('Button', { variant: 'primary', label: 'Save' }) %>\n</body>\n```\n\n### 3. Available Methods\n\n```javascript\n// In your Express route handlers\napp.get('/profile', (req, res) => {\n  const user = req.user; // Your user object\n\n  res.render('profile', {\n    user: user,\n    // res.locals.rnx helpers are automatically available\n  });\n});\n```\n\n## Middleware Configuration\n\nConfigure the middleware when initializing:\n\n```javascript\napp.use(rnxMiddleware({\n  // Include rnxJS scripts (default: true)\n  cdn: true,\n\n  // Theme variant (default: 'bootstrap')\n  // Options: 'bootstrap', 'm3', 'plugins', or null\n  theme: 'bootstrap',\n\n  // Storage configuration\n  storagePrefix: 'myapp_',\n\n  // Router configuration\n  routerMode: 'hash',\n\n  // Toast configuration\n  toastPosition: 'top-right',\n  toastDuration: 3000,\n  toastMax: 5\n}));\n```\n\n## View Helpers Reference\n\n### rnx.scripts()\n\nInclude rnxJS library and stylesheets.\n\n**Syntax:**\n```javascript\nrnx.scripts(cdn, theme)\n```\n\n**Parameters:**\n- `cdn` (boolean, default: true) - Use CDN for resources\n- `theme` (string, default: 'bootstrap') - Theme variant\n\n**Example:**\n```ejs\n<head>\n  <%- rnx.scripts(true, 'm3') %>\n</head>\n```\n\n### rnx.state()\n\nCreate reactive state from server data.\n\n**Syntax:**\n```javascript\nrnx.state(data, varName)\n```\n\n**Parameters:**\n- `data` - JavaScript object or array to serialize\n- `varName` (string, default: 'state') - Global state variable name\n\n**Example:**\n```ejs\n<%\n  const appData = {\n    user: {\n      name: req.user.name,\n      email: req.user.email,\n      role: req.user.role\n    },\n    notifications: notifications,\n    routes: {\n      home: '/',\n      profile: '/profile',\n      settings: '/settings'\n    }\n  };\n%>\n\n<%- rnx.state(appData, 'appState') %>\n\n<span data-bind=\"appState.user.name\"></span>\n<span data-bind=\"appState.user.email\"></span>\n```\n\n### rnx.component()\n\nRender rnxJS components.\n\n**Syntax:**\n```javascript\nrnx.component(name, props)\n```\n\n**Parameters:**\n- `name` (string) - Component name\n- `props` (object, optional) - Component properties\n\n**Example:**\n```ejs\n<%- rnx.component('Button', { variant: 'primary', label: 'Save' }) %>\n\n<%- rnx.component('Input', {\n  type: 'email',\n  placeholder: 'user@example.com',\n  data_bind: 'state.email'\n}) %>\n\n<%- rnx.component('DataTable', {\n  data: 'state.users',\n  columns: 'state.columns',\n  sortable: true,\n  pageable: true\n}) %>\n```\n\n### rnx.plugin()\n\nInitialize rnxJS plugins.\n\n**Syntax:**\n```javascript\nrnx.plugin(name, options)\n```\n\n**Parameters:**\n- `name` (string) - Plugin name ('router', 'toast', 'storage')\n- `options` (object, optional) - Plugin configuration\n\n**Example:**\n```ejs\n<%- rnx.plugin('router', { mode: 'hash', default_route: '/' }) %>\n\n<%- rnx.plugin('toast', {\n  position: 'top-right',\n  duration: 3000,\n  max_toasts: 5\n}) %>\n\n<%- rnx.plugin('storage', {\n  prefix: 'myapp_',\n  storage: 'localStorage'\n}) %>\n```\n\n### rnx.dataBind()\n\nCreate a data-bind attribute.\n\n**Syntax:**\n```javascript\nrnx.dataBind(path)\n```\n\n**Example:**\n```ejs\n<span <%- rnx.dataBind('user.name') %>></span>\n<!-- Output: <span data-bind=\"user.name\"></span> -->\n```\n\n### rnx.dataRule()\n\nCreate a validation rule attribute.\n\n**Syntax:**\n```javascript\nrnx.dataRule(rules)\n```\n\n**Example:**\n```ejs\n<input <%- rnx.dataRule('required|email') %> />\n<!-- Output: <input data-rule=\"required|email\" /> -->\n\n<input <%- rnx.dataRule(['required', 'email', 'max:100']) %> />\n```\n\n## Complete Example\n\n### app.js\n\n```javascript\nconst express = require('express');\nconst rnxMiddleware = require('@arnelirobles/express-rnx');\n\nconst app = express();\n\n// Middleware\napp.use(express.static('public'));\napp.use(rnxMiddleware({\n  cdn: true,\n  theme: 'bootstrap',\n  toastPosition: 'top-right',\n  toastDuration: 3000\n}));\n\napp.set('view engine', 'ejs');\napp.set('views', './views');\n\n// Mock user\nconst mockUser = {\n  id: 1,\n  name: 'John Doe',\n  email: 'john@example.com',\n  role: 'admin'\n};\n\nconst mockNotifications = [\n  { id: 1, title: 'Welcome', message: 'Welcome to the app!', type: 'info' },\n  { id: 2, title: 'Success', message: 'Your profile was updated', type: 'success' }\n];\n\n// Routes\napp.get('/', (req, res) => {\n  res.render('index', {\n    user: mockUser,\n    notifications: mockNotifications\n  });\n});\n\napp.get('/profile', (req, res) => {\n  res.render('profile', {\n    user: mockUser\n  });\n});\n\napp.get('/components', (req, res) => {\n  const users = [\n    { id: 1, name: 'Alice', email: 'alice@example.com', status: 'active' },\n    { id: 2, name: 'Bob', email: 'bob@example.com', status: 'inactive' },\n    { id: 3, name: 'Charlie', email: 'charlie@example.com', status: 'active' }\n  ];\n\n  const columns = [\n    { key: 'id', label: 'ID' },\n    { key: 'name', label: 'Name' },\n    { key: 'email', label: 'Email' },\n    { key: 'status', label: 'Status' }\n  ];\n\n  res.render('components', {\n    users: users,\n    columns: columns\n  });\n});\n\napp.listen(3000, () => {\n  console.log('Server running on http://localhost:3000');\n});\n```\n\n### views/layout.ejs\n\n```ejs\n<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"UTF-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <title>rnxJS Express Example</title>\n  <%- rnx.scripts(true, 'bootstrap') %>\n</head>\n<body>\n  <nav class=\"navbar navbar-expand-lg navbar-light bg-light\">\n    <div class=\"container-fluid\">\n      <a class=\"navbar-brand\" href=\"/\">My App</a>\n      <div class=\"collapse navbar-collapse\">\n        <ul class=\"navbar-nav ms-auto\">\n          <li class=\"nav-item\">\n            <a class=\"nav-link\" href=\"/\">Home</a>\n          </li>\n          <li class=\"nav-item\">\n            <a class=\"nav-link\" href=\"/profile\">Profile</a>\n          </li>\n          <li class=\"nav-item\">\n            <a class=\"nav-link\" href=\"/components\">Components</a>\n          </li>\n        </ul>\n      </div>\n    </div>\n  </nav>\n\n  <main class=\"container mt-5\">\n    <%- body %>\n  </main>\n\n  <%- rnx.plugin('toast', { position: 'top-right' }) %>\n</body>\n</html>\n```\n\n### views/index.ejs\n\n```ejs\n<%\n  const appState = {\n    user: user,\n    notifications: notifications,\n    page: 'home'\n  };\n%>\n\n<%- rnx.state(appState, 'appState') %>\n\n<h1>Welcome to rnxJS!</h1>\n<p>Hello, <span data-bind=\"appState.user.name\"></span>!</p>\n\n<div class=\"row mt-5\">\n  <div class=\"col-md-6\">\n    <h2>Recent Notifications</h2>\n    <% notifications.forEach(notif => { %>\n      <%- rnx.component('Card', {\n        title: notif.title,\n        content: notif.message,\n        type: notif.type\n      }) %>\n    <% }); %>\n  </div>\n\n  <div class=\"col-md-6\">\n    <h2>Actions</h2>\n    <%- rnx.component('Button', {\n      variant: 'primary',\n      label: 'View Profile',\n      onclick: 'window.location.href = \"/profile\"'\n    }) %>\n    <br><br>\n    <%- rnx.component('Button', {\n      variant: 'secondary',\n      label: 'View Components',\n      onclick: 'window.location.href = \"/components\"'\n    }) %>\n  </div>\n</div>\n```\n\n### views/profile.ejs\n\n```ejs\n<%\n  const profileState = {\n    user: user\n  };\n%>\n\n<%- rnx.state(profileState, 'profileState') %>\n<%- rnx.plugin('toast', { position: 'top-right' }) %>\n\n<h1>User Profile</h1>\n\n<div class=\"profile-card\">\n  <h2 data-bind=\"profileState.user.name\"></h2>\n  <p><strong>Email:</strong> <span data-bind=\"profileState.user.email\"></span></p>\n  <p><strong>Role:</strong> <span data-bind=\"profileState.user.role\"></span></p>\n\n  <%- rnx.component('Button', {\n    variant: 'primary',\n    label: 'Edit Profile',\n    onclick: 'window.rnx.toast.info(\"Opening edit dialog\")'\n  }) %>\n\n  <%- rnx.component('Button', {\n    variant: 'danger',\n    label: 'Logout',\n    onclick: 'window.rnx.toast.warning(\"Logging out...\")'\n  }) %>\n</div>\n```\n\n## Security Considerations\n\n### HTML Escaping\n\nAll component props and attributes are automatically HTML-escaped to prevent XSS:\n\n```ejs\n<%- rnx.component('Button', { label: userInput }) %>\n<!-- userInput is escaped automatically -->\n```\n\n### Data Binding Expressions\n\nString values starting with `state.`, `{`, or `[` are preserved for data binding:\n\n```ejs\n<%- rnx.component('Div', { content: 'state.message' }) %>\n<!-- Preserves as: content=\"state.message\" -->\n\n<%- rnx.component('Div', { title: 'Hello' }) %>\n<!-- Escapes and quotes: title=\"Hello\" -->\n```\n\n## Testing\n\nTests are included. Run with:\n\n```bash\nnpm test\n```\n\n## Contributing\n\nContributions are welcome! Please see [CONTRIBUTING.md](../../CONTRIBUTING.md) for guidelines.\n\n## License\n\nMPL-2.0 - See [LICENSE](../../LICENSE) for details.\n\n## Support\n\n- Documentation: [rnxJS Documentation](https://github.com/BaryoDev/rnxjs)\n- Issues: [GitHub Issues](https://github.com/BaryoDev/rnxjs/issues)\n- Discussions: [GitHub Discussions](https://github.com/BaryoDev/rnxjs/discussions)\n\n## Changelog\n\n### 1.0.0 (2024)\n\n- Initial release\n- Express middleware with res.locals.rnx helpers\n- View helpers: scripts, state, component, plugin\n- Attribute helpers: dataBind, dataRule\n- Bootstrap 5.3+ support\n- Plugin integration (router, toast, storage)\n- Full XSS prevention and HTML escaping\n- Support for EJS, Pug, and other template engines\n- Configuration options\n","readmeFilename":"README.md","_rev":"1-dc082e6c6e106c37a03406114182a85e"}