{"_id":"@adunblock/server-tag-express","_rev":"4-e763b068c3bc4938790ef415737d7286","name":"@adunblock/server-tag-express","dist-tags":{"latest":"1.0.3"},"versions":{"1.0.0":{"name":"@adunblock/server-tag-express","version":"1.0.0","keywords":["express","middleware","server-tag","script-injection","ssr","typescript"],"author":{"name":"AdUnblock"},"license":"MIT","_id":"@adunblock/server-tag-express@1.0.0","maintainers":[{"name":"billadunblock","email":"bill@ad-unblock.com"}],"homepage":"https://github.com/adunblock/server-tag#readme","bugs":{"url":"https://github.com/adunblock/server-tag/issues"},"dist":{"shasum":"abbfa774938a299cc715d296ab59b87de9d731de","tarball":"https://registry.npmjs.org/@adunblock/server-tag-express/-/server-tag-express-1.0.0.tgz","fileCount":7,"integrity":"sha512-clxe5Cu9OOmgnTPhFfPX7+QBtDQDg64glnJFGTp3fPkAwmzgt8X7Hz9lmvA9J2Wq10Iu2pyCovo5rWHEJg7esA==","signatures":[{"sig":"MEYCIQC+BOefJDxWfp2uybr0jAZOTTRcIDZAtm3m/iOE3dS0wwIhAN29hMC4uBH23gBqJq9i/yBFsfIilheG8K3vqntiy4zO","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":71724},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","module":"dist/index.mjs","engines":{"node":">=16.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.js"}},"gitHead":"cd6cfd2abdc080ef0208f699d7cab9a76c7364d7","scripts":{"dev":"cd test-app && npm run dev","test":"cd test-app && npm test","build":"vite build && npm run build:types","clean":"rm -rf dist","watch":"vite build --watch","type-check":"tsc --noEmit","build:types":"tsc --emitDeclarationOnly --outDir dist","prepublishOnly":"npm run clean && npm run build"},"_npmUser":{"name":"billadunblock","actor":{"name":"billadunblock","type":"user","email":"bill@ad-unblock.com"},"email":"bill@ad-unblock.com"},"repository":{"url":"git+https://github.com/adunblock/server-tag.git","type":"git","directory":"nodejs/express"},"_npmVersion":"11.0.0","description":"Express middleware for server-side script loading and injection","directories":{},"_nodeVersion":"22.11.0","dependencies":{"node-fetch":"^3.3.2"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^5.0.0","express":"^4.18.2","typescript":"^5.3.0","@types/node":"^20.10.0","@types/express":"^4.17.21","vite-plugin-dts":"^3.6.0"},"peerDependencies":{"express":"^4.18.0 || ^5.0.0"},"_npmOperationalInternal":{"tmp":"tmp/server-tag-express_1.0.0_1751334744087_0.7899569827883315","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"@adunblock/server-tag-express","version":"1.0.1","keywords":["express","middleware","server-tag","script-injection","ssr","typescript"],"author":{"name":"AdUnblock"},"license":"MIT","_id":"@adunblock/server-tag-express@1.0.1","maintainers":[{"name":"billadunblock","email":"bill@ad-unblock.com"}],"homepage":"https://github.com/adunblock/adunblock-server-tag#readme","bugs":{"url":"https://github.com/adunblock/adunblock-server-tag/issues"},"dist":{"shasum":"9d5e4381eb17ecb58ef3dbee9a9696fbe78054ef","tarball":"https://registry.npmjs.org/@adunblock/server-tag-express/-/server-tag-express-1.0.1.tgz","fileCount":7,"integrity":"sha512-MSLA2iKPCHl8jCZJNssVUCjyQnM431JysEWVD58jPnPK6BJtBqgD4vb7JbiIa2lP2WYJa3kJoXnt+61zvgRIKw==","signatures":[{"sig":"MEUCIG6bJBT2bycdm53FHYnn9FZ9+FY45+D3I5dQpYSFd5JLAiEA6/o90ENB95RcMtBLemAjHWSaAV80XCh/ZGUD1UmPLdg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":71734},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","module":"dist/index.mjs","engines":{"node":">=16.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.js"}},"gitHead":"e45bcbb8124d294566ec7fdf92a37df98c9c192f","scripts":{"dev":"cd test-app && npm run dev","test":"cd test-app && npm test","build":"vite build && npm run build:types","clean":"rm -rf dist","watch":"vite build --watch","type-check":"tsc --noEmit","build:types":"tsc --emitDeclarationOnly --outDir dist","prepublishOnly":"npm run clean && npm run build"},"_npmUser":{"name":"billadunblock","email":"bill@ad-unblock.com"},"repository":{"url":"git+https://github.com/adunblock/adunblock-server-tag.git","type":"git","directory":"nodejs/express"},"_npmVersion":"10.9.2","description":"Express middleware for server-side script loading and injection","directories":{},"_nodeVersion":"22.17.1","dependencies":{"node-fetch":"^3.3.2"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^5.0.0","express":"^4.18.2","typescript":"^5.3.0","@types/node":"^20.10.0","@types/express":"^4.17.21","vite-plugin-dts":"^3.6.0"},"peerDependencies":{"express":"^4.18.0 || ^5.0.0"},"_npmOperationalInternal":{"tmp":"tmp/server-tag-express_1.0.1_1755805148981_0.4877999034341778","host":"s3://npm-registry-packages-npm-production"}},"1.0.2":{"name":"@adunblock/server-tag-express","version":"1.0.2","keywords":["express","middleware","server-tag","script-injection","ssr","typescript"],"author":{"name":"AdUnblock"},"license":"MIT","_id":"@adunblock/server-tag-express@1.0.2","maintainers":[{"name":"billadunblock","email":"bill@ad-unblock.com"}],"homepage":"https://github.com/adunblock/adunblock-server-tag#readme","bugs":{"url":"https://github.com/adunblock/adunblock-server-tag/issues"},"dist":{"shasum":"fd9b034c648693cbd6892b8795c1c6fc5798952b","tarball":"https://registry.npmjs.org/@adunblock/server-tag-express/-/server-tag-express-1.0.2.tgz","fileCount":7,"integrity":"sha512-Fk3kF9kC8ZxhM6e4eo1u0hvVqMmrXBJguieX3Hn4cvcHYmfuUAd5WXcc4E5Ktz+YNFCJN0auM//ypE78A+82pw==","signatures":[{"sig":"MEQCIA1AYX7jMzvKq3UeKqMi+aiYu4trvRSLeFyZ5/PSgTcLAiACG3UtZzDNvOFAjeWG8pCJaqb7E4SGUB8iGeaWBMHubQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":71013},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","module":"dist/index.mjs","engines":{"node":">=16.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.js"}},"gitHead":"bce08b4440c3a051bc49e3dbee52873a0ebf790e","scripts":{"dev":"cd test-app && npm run dev","test":"cd test-app && npm test","build":"vite build && npm run build:types","clean":"rm -rf dist","watch":"vite build --watch","type-check":"tsc --noEmit","build:types":"tsc --emitDeclarationOnly --outDir dist","prepublishOnly":"npm run clean && npm run build"},"_npmUser":{"name":"billadunblock","email":"bill@ad-unblock.com"},"repository":{"url":"git+https://github.com/adunblock/adunblock-server-tag.git","type":"git","directory":"nodejs/express"},"_npmVersion":"10.9.2","description":"Express middleware for server-side script loading and injection","directories":{},"_nodeVersion":"22.17.1","dependencies":{"node-fetch":"^3.3.2"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^5.0.0","express":"^4.18.2","typescript":"^5.3.0","@types/node":"^20.10.0","@types/express":"^4.17.21","vite-plugin-dts":"^3.6.0"},"peerDependencies":{"express":"^4.18.0 || ^5.0.0"},"_npmOperationalInternal":{"tmp":"tmp/server-tag-express_1.0.2_1763235920931_0.9565896699784648","host":"s3://npm-registry-packages-npm-production"}},"1.0.3":{"name":"@adunblock/server-tag-express","version":"1.0.3","description":"Express middleware for server-side script loading and injection","main":"dist/index.js","module":"dist/index.mjs","types":"dist/index.d.ts","type":"module","keywords":["express","middleware","server-tag","script-injection","ssr","typescript"],"scripts":{"dev":"cd test-app && npm run dev","build":"vite build && npm run build:types","build:types":"tsc --emitDeclarationOnly --outDir dist","watch":"vite build --watch","test":"cd test-app && npm test","type-check":"tsc --noEmit","clean":"rm -rf dist","prepublishOnly":"npm run clean && npm run build"},"dependencies":{"node-fetch":"^3.3.2"},"peerDependencies":{"express":"^4.18.0 || ^5.0.0"},"devDependencies":{"@types/express":"^4.17.21","@types/node":"^20.10.0","express":"^4.18.2","typescript":"^5.3.0","vite":"^5.0.0","vite-plugin-dts":"^3.6.0"},"engines":{"node":">=16.0.0"},"license":"MIT","author":{"name":"AdUnblock"},"repository":{"type":"git","url":"git+https://github.com/adunblock/adunblock-server-tag.git","directory":"nodejs/express"},"exports":{".":{"import":"./dist/index.mjs","require":"./dist/index.js","types":"./dist/index.d.ts"}},"_id":"@adunblock/server-tag-express@1.0.3","gitHead":"d6edac9ccb68d4067d52754540ba247e27dc6774","bugs":{"url":"https://github.com/adunblock/adunblock-server-tag/issues"},"homepage":"https://github.com/adunblock/adunblock-server-tag#readme","_nodeVersion":"22.17.1","_npmVersion":"10.9.2","dist":{"integrity":"sha512-0Us3inl/FvRl3Oiv81QO61S0nVQlhhLcX6OQizINRCioRmDpEJ+v68njiVbBANe9kKQBD9XMnLiURYTRFZ8ndw==","shasum":"f7570e26a08597cc437b45bb8d989d7b207d1d1d","tarball":"https://registry.npmjs.org/@adunblock/server-tag-express/-/server-tag-express-1.0.3.tgz","fileCount":7,"unpackedSize":71083,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIAZ2cj1AhIxzE8+iBg3B1xFJ+JUWBlsHii/s8ZGQdvprAiEA5O+sTgVOZiOppmKqYAhaO+9r6H6/inPW+Hs3XPD8sdg="}]},"_npmUser":{"name":"billadunblock","email":"bill@ad-unblock.com"},"directories":{},"maintainers":[{"name":"billadunblock","email":"bill@ad-unblock.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/server-tag-express_1.0.3_1776446941791_0.8807249606888843"},"_hasShrinkwrap":false}},"time":{"created":"2025-07-01T01:52:23.950Z","modified":"2026-04-17T17:29:02.091Z","1.0.0":"2025-07-01T01:52:24.260Z","1.0.1":"2025-08-21T19:39:09.183Z","1.0.2":"2025-11-15T19:45:21.145Z","1.0.3":"2026-04-17T17:29:01.943Z"},"bugs":{"url":"https://github.com/adunblock/adunblock-server-tag/issues"},"author":{"name":"AdUnblock"},"license":"MIT","homepage":"https://github.com/adunblock/adunblock-server-tag#readme","keywords":["express","middleware","server-tag","script-injection","ssr","typescript"],"repository":{"type":"git","url":"git+https://github.com/adunblock/adunblock-server-tag.git","directory":"nodejs/express"},"description":"Express middleware for server-side script loading and injection","maintainers":[{"name":"billadunblock","email":"bill@ad-unblock.com"}],"readme":"# @adunblock/server-tag-express\n\nA powerful Express middleware for server-side script loading and injection. Load external scripts from remote JSON endpoints with caching, security, and flexible integration options. Built with TypeScript for full type safety.\n\n## Features\n\n- 🚀 **Server-Side Script Loading**: Fetch and inject scripts on the server, not the client\n- ⚡ **Smart Caching**: Configurable cache intervals with automatic expiration\n- 🛡️ **Security**: URL validation prevents malicious script injection\n- 🎨 **Flexible Integration**: Multiple injection methods (auto, manual, templates)\n- 🔧 **Error Handling**: Graceful fallbacks and custom error handlers\n- 📊 **Template Support**: Works with EJS, Handlebars, Pug, and more\n- 🎪 **Easy Setup**: Simple middleware integration with any Express app\n- 💎 **TypeScript**: Full type safety with comprehensive type definitions\n- ⚡ **Modern Build**: Vite-powered build system with ES modules and CommonJS support\n\n## Installation\n\n```bash\nnpm install @adunblock/server-tag-express\n```\n\n## Quick Start\n\n```typescript\nimport express from 'express';\nimport { serverTagMiddleware } from '@adunblock/server-tag-express';\n\nconst app = express();\n\n// Add ServerTag middleware with TypeScript support\napp.use(serverTagMiddleware({\n  remoteUrl: 'https://public.adunblocker.com/api/vendor_scripts'\n}));\n\n// Your routes will now have access to script data\napp.get('/', (req, res) => {\n  // Scripts available in res.locals.serverTagScripts (fully typed!)\n  res.send(`\n    <html>\n      <head>\n        ${res.locals.serverTagHtml}\n      </head>\n      <body>\n        <h1>Hello World!</h1>\n      </body>\n    </html>\n  `);\n});\n\napp.listen(3000);\n```\n\n## TypeScript Support\n\nThis package is written in TypeScript and provides comprehensive type definitions:\n\n```typescript\nimport type { \n  ServerTagMiddlewareOptions,\n  ScriptAttributes,\n  ServerTagConfig \n} from '@adunblock/server-tag-express';\n\nconst config: ServerTagMiddlewareOptions = {\n  remoteUrl: 'https://public.adunblocker.com/api/vendor_scripts',\n  cacheInterval: 300,\n  scriptAttributes: {\n    async: true,\n    defer: false\n  }\n};\n\napp.use(serverTagMiddleware(config));\n```\n\n## Expected Remote Response Format\n\nYour remote URL should return JSON in this format (array directly):\n\n```json\n[\n  \"https://example.com/script1.js\",\n  \"https://example.com/script2.js\"\n]\n```\n\nThe default endpoint is `https://public.adunblocker.com/api/vendor_scripts`.\n\n> **Note**: For backward compatibility, the package also supports the legacy format `{\"js\": [...]}` but the new format (array directly) is preferred.\n\n## Integration Methods\n\n### 1. Template Integration (Recommended)\n\nUse with any template engine (EJS, Handlebars, Pug, etc.):\n\n```html\n<!-- EJS Example -->\n<head>\n  <title>My App</title>\n  <%- serverTagHtml %>\n</head>\n\n<!-- Handlebars Example -->\n<head>\n  <title>My App</title>\n  {{{serverTagHtml}}}\n</head>\n\n<!-- Pug Example -->\nhead\n  title My App\n  != serverTagHtml\n```\n\n### 2. Auto Injection\n\nAutomatically inject scripts into HTML responses:\n\n```typescript\napp.use(serverTagMiddleware({\n  remoteUrl: 'https://public.adunblocker.com/api/vendor_scripts',\n  injectIntoHtml: true,\n  injectPosition: '</head>', // Where to inject\n  scriptAttributes: { async: true } // Script tag attributes\n}));\n```\n\n### 3. Manual Injection\n\nProgrammatically inject scripts into HTML strings:\n\n```typescript\nimport { injectScripts } from '@adunblock/server-tag-express';\n\napp.get('/page', (req, res) => {\n  let html = '<html><head></head><body>Content</body></html>';\n  \n  // Inject scripts manually (fully typed)\n  html = injectScripts(html, res.locals.serverTagScripts || []);\n  \n  res.send(html);\n});\n```\n\n### 4. Direct Access\n\nAccess script data directly for custom implementations:\n\n```typescript\napp.get('/custom', (req, res) => {\n  const scripts: string[] = res.locals.serverTagScripts || [];\n  \n  // Custom script rendering with type safety\n  const scriptTags = scripts.map(url => \n    `<script src=\"${url}\" async></script>`\n  ).join('\\n');\n  \n  res.send(`<html><head>${scriptTags}</head>...</html>`);\n});\n```\n\n## Configuration Options\n\n| Option | Type | Default | Description |\n|--------|------|---------|-------------|\n| `remoteUrl` | `string` | **Required** | URL to fetch script configuration from |\n| `cacheInterval` | `number` | `300` | Cache duration in seconds |\n| `injectIntoHtml` | `boolean` | `true` | Auto-inject scripts into HTML responses |\n| `injectPosition` | `string` | `'</head>'` | Where to inject scripts in HTML |\n| `scriptAttributes` | `ScriptAttributes` | `{async: true}` | Additional attributes applied to every generated `<script>` tag (e.g. `data-code`) |\n| `onError` | `function` | `undefined` | Custom error handler function |\n| `shouldInject` | `function` | `() => true` | Conditional injection logic |\n\n### Advanced Configuration\n\n```typescript\nimport type { ServerTagMiddlewareOptions } from '@adunblock/server-tag-express';\n\nconst config: ServerTagMiddlewareOptions = {\n  remoteUrl: 'https://public.adunblocker.com/api/vendor_scripts',\n  cacheInterval: 600, // 10 minutes\n  injectIntoHtml: true,\n  injectPosition: '</head>',\n  scriptAttributes: {\n    defer: true,\n    'data-source': 'server-tag',\n    'data-code': 'abc123'\n  },\n  shouldInject: (req, res) => {\n    // Don't inject scripts on API routes\n    return !req.path.startsWith('/api/');\n  },\n  onError: (error, req, res) => {\n    console.error('ServerTag error:', error.message);\n    // Optional: send error to monitoring service\n  }\n};\n\napp.use(serverTagMiddleware(config));\n```\n\n## Type Definitions\n\n### Core Types\n\n```typescript\ninterface ServerTagConfig {\n  cacheInterval?: number;\n  timeout?: number;\n  retries?: number;\n  retryDelay?: number;\n}\n\ninterface ScriptAttributes {\n  async?: boolean;\n  defer?: boolean;\n  type?: string;\n  [key: string]: string | boolean | number | undefined;\n}\n\ninterface ServerTagMiddlewareOptions extends ServerTagConfig {\n  remoteUrl: string;\n  injectIntoHtml?: boolean;\n  injectPosition?: string;\n  scriptAttributes?: ScriptAttributes;\n  onError?: (error: Error, req: Request, res: Response) => void;\n  shouldInject?: (req: Request, res: Response) => boolean;\n}\n```\n\n### Error Types\n\n```typescript\nclass ServerTagError extends Error {\n  constructor(message: string, code?: string, statusCode?: number);\n}\n\nclass NetworkError extends ServerTagError {\n  constructor(message: string, url: string);\n}\n\nclass ValidationError extends ServerTagError {\n  constructor(message: string);\n}\n\nclass TimeoutError extends ServerTagError {\n  constructor(message: string);\n}\n```\n\n## Route-Specific Configuration\n\nApply ServerTag to specific routes with different configurations:\n\n```typescript\n// Global middleware\napp.use(serverTagMiddleware({\n  remoteUrl: 'https://public.adunblocker.com/api/vendor_scripts'\n}));\n\n// Route-specific middleware\napp.get('/special', \n  serverTagMiddleware({\n    remoteUrl: 'https://public.adunblocker.com/api/vendor_scripts',\n    cacheInterval: 60, // 1 minute cache\n    scriptAttributes: { defer: true }\n  }),\n  (req, res) => {\n    res.render('special-page');\n  }\n);\n```\n\n## Template Engine Examples\n\n### EJS\n\n```html\n<!DOCTYPE html>\n<html>\n<head>\n  <title><%= title %></title>\n  <%- serverTagHtml %>\n</head>\n<body>\n  <h1><%= title %></h1>\n</body>\n</html>\n```\n\n### Handlebars\n\n```html\n<!DOCTYPE html>\n<html>\n<head>\n  <title>{{title}}</title>\n  {{{serverTagHtml}}}\n</head>\n<body>\n  <h1>{{title}}</h1>\n</body>\n</html>\n```\n\n### Pug\n\n```pug\ndoctype html\nhtml\n  head\n    title= title\n    != serverTagHtml\n```\n\n## API Reference\n\n### `serverTagMiddleware(options)`\n\nMain middleware function that adds ServerTag functionality to Express apps.\n\n**Parameters:**\n- `options` (ServerTagMiddlewareOptions): Configuration options\n\n**Returns:** Express middleware function\n\n### `injectScripts(html, scripts, attributes, position)`\n\nHelper function to manually inject scripts into HTML strings.\n\n**Parameters:**\n- `html` (string): HTML string to modify\n- `scripts` (string[]): Array of script URLs\n- `attributes` (ScriptAttributes): Script tag attributes\n- `position` (string): Where to inject (default: `'</head>'`)\n\n**Returns:** Modified HTML string\n\n### `renderServerTags(res, attributes)`\n\nTemplate helper function for rendering script tags.\n\n**Parameters:**\n- `res` (Response): Express response object\n- `attributes` (ScriptAttributes): Script tag attributes\n\n**Returns:** HTML string with script tags\n\n### `getScripts(remoteUrl, config)`\n\nLow-level function to fetch scripts from remote URL.\n\n**Parameters:**\n- `remoteUrl` (string): URL to fetch from\n- `config` (ServerTagConfig): Configuration options\n\n**Returns:** Promise resolving to `{js: string[]}`\n\n### `generateScriptTags(scriptUrls, attributes)`\n\nGenerate HTML script tags from URLs.\n\n**Parameters:**\n- `scriptUrls` (string[]): Array of script URLs\n- `attributes` (ScriptAttributes): Script tag attributes\n\n**Returns:** HTML string with script tags\n\n## Error Handling\n\nServerTag includes robust error handling with custom error types:\n\n```typescript\nimport { \n  ServerTagError, \n  NetworkError, \n  ValidationError, \n  TimeoutError \n} from '@adunblock/server-tag-express';\n\napp.use(serverTagMiddleware({\n  remoteUrl: 'https://public.adunblocker.com/api/vendor_scripts',\n  onError: (error, req, res) => {\n    if (error instanceof NetworkError) {\n      console.error('Network error:', error.message, 'URL:', error.url);\n    } else if (error instanceof ValidationError) {\n      console.error('Validation error:', error.message);\n    } else if (error instanceof TimeoutError) {\n      console.error('Timeout error:', error.message);\n    }\n    \n    // Scripts will be empty array on error\n    console.log('Scripts loaded:', res.locals.serverTagScripts?.length || 0);\n  }\n}));\n```\n\n## Development\n\n### Building from Source\n\n```bash\n# Install dependencies\nnpm install\n\n# Build the package\nnpm run build\n\n# Watch for changes\nnpm run watch\n\n# Type checking\nnpm run type-check\n```\n\n### Running the Test App\n\n```bash\n# Start the TypeScript test app\nnpm run dev\n\n# Visit http://localhost:3001\n```\n\n### Project Structure\n\n```\nnodejs/express/\n├── src/\n│   ├── index.ts         # Main exports\n│   ├── middleware.ts    # Express middleware\n│   ├── ServerTag.ts     # Core functionality\n│   └── types.ts         # TypeScript types\n├── test-app/           # Demo application\n│   ├── server.ts       # TypeScript Express server\n│   ├── views/          # EJS templates\n│   └── package.json    # Test app dependencies\n├── dist/               # Built package files\n├── tsconfig.json       # TypeScript config\n├── vite.config.ts      # Vite build config\n└── README.md           # This file\n```\n\n## Testing\n\nThe test application demonstrates all integration patterns with TypeScript:\n\n- **Template Integration**: EJS templates with helper functions\n- **Auto Injection**: Automatic script injection\n- **Manual Injection**: Programmatic script injection\n- **API Access**: JSON endpoints for script data\n- **Type Safety**: Full TypeScript integration\n\n## Browser Compatibility\n\nServerTag works with all browsers supported by the generated script tags. The middleware itself runs on Node.js 16+.\n\n## Contributing\n\nContributions are welcome! Please feel free to submit issues and pull requests.\n\n## License\n\nMIT License - see LICENSE file for details.\n\n## Related Packages\n\n- [`@adunblock/server-tag-nextjs`](../nextjs/) - Next.js integration\n- [`@adunblock/server-tag-react-router`](../react-router/) - React Router integration ","readmeFilename":"README.md"}