{"_id":"@chattoo/rlb-chattoo-widget","_rev":"5-4287956336ab5f7f1a7b26d768f1bc13","name":"@chattoo/rlb-chattoo-widget","dist-tags":{"latest":"1.0.5"},"versions":{"1.0.1":{"name":"@chattoo/rlb-chattoo-widget","version":"1.0.1","keywords":["chat","widget","web-component","lit","custom-element","chattoo"],"license":"UNLICENSED","_id":"@chattoo/rlb-chattoo-widget@1.0.1","maintainers":[{"name":"riolo.giuseppe","email":"riolo.giuseppe@gmail.com"}],"homepage":"https://github.com/open-riolabs/rlb-chattoo-widget#readme","bugs":{"url":"https://github.com/open-riolabs/rlb-chattoo-widget/issues"},"dist":{"shasum":"e55d989e75c63b1cc8e2bee7969b1d966d938034","tarball":"https://registry.npmjs.org/@chattoo/rlb-chattoo-widget/-/rlb-chattoo-widget-1.0.1.tgz","fileCount":24,"integrity":"sha512-OAsZ2OMGcZCalF+PsAS3ewRH+nes75XBAqWdHwv3V7/JY6khMUAtIBj8yX5SPMhowX/qCVDGtzXPoC37GSCmTw==","signatures":[{"sig":"MEUCIA+LtD9Bt3oJ3u5q6RYYqL2RsN57iKwuBsBH9w3vcIRlAiEAjz956DVbYYcFWmGRqBNqXEyRsglu2ZIMqsjORCohnxk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":254326},"main":"./index.js","type":"module","types":"./types/index.d.ts","module":"./index.js","exports":{".":{"types":"./types/index.d.ts","import":"./index.js"},"./standalone":"./chat-widget.js","./package.json":"./package.json"},"gitHead":"746db24b38a2c2e16e93049714ba01bf751f06cd","scripts":{"dev":"vite","mock":"node mock/server.mjs","test":"vitest run","build":"npm run build:standalone && npm run build:package && npm run build:types","dev:all":"node mock/server.mjs & vite","preview":"vite preview","typecheck":"tsc --noEmit","test:watch":"vitest","build:types":"tsc -p tsconfig.build.json","build:package":"vite build --mode package","build:standalone":"vite build"},"_npmUser":{"name":"riolo.giuseppe","email":"riolo.giuseppe@gmail.com"},"repository":{"url":"git+https://github.com/open-riolabs/rlb-chattoo-widget.git","type":"git"},"_npmVersion":"10.9.8","description":"Embeddable chat widget: one script tag on a site, or an npm package in an app","directories":{},"sideEffects":["./index.js","./chat-widget.js"],"_nodeVersion":"22.23.2","dependencies":{"lit":"^3.2.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"ws":"^8.18.0","vite":"^6.0.7","vitest":"^3.0.5","typescript":"^5.9.3","@types/node":"^25.5.2"},"_npmOperationalInternal":{"tmp":"tmp/rlb-chattoo-widget_1.0.1_1787144439334_0.11932511707143445","host":"s3://npm-registry-packages-npm-production"}},"1.0.2":{"name":"@chattoo/rlb-chattoo-widget","version":"1.0.2","keywords":["chat","widget","web-component","lit","custom-element","chattoo"],"license":"UNLICENSED","_id":"@chattoo/rlb-chattoo-widget@1.0.2","maintainers":[{"name":"riolo.giuseppe","email":"riolo.giuseppe@gmail.com"}],"homepage":"https://github.com/open-riolabs/rlb-chattoo-widget#readme","bugs":{"url":"https://github.com/open-riolabs/rlb-chattoo-widget/issues"},"dist":{"shasum":"e5e68e7ed87582de03636329c3b0beb62444c01c","tarball":"https://registry.npmjs.org/@chattoo/rlb-chattoo-widget/-/rlb-chattoo-widget-1.0.2.tgz","fileCount":2,"integrity":"sha512-vadN7xJuVg6iavOoJEr4qLEdSagn0QkovLNTrE0uxP+2qbj14KcKoz8D5b1wzePdWmqrvh+Gs8O3deVetiL5vg==","signatures":[{"sig":"MEYCIQD64igLdvPoLrRDH6ymNkw/JS4lmcc7rP+h+0yABkK/qgIhAP1Xq2/Z/9e+P5BGoRg1LxrWazUVXRMJBG6FkY8VcsDZ","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":11590},"main":"./index.js","type":"module","types":"./types/index.d.ts","module":"./index.js","exports":{".":{"types":"./types/index.d.ts","import":"./index.js"},"./standalone":"./chat-widget.js","./package.json":"./package.json"},"gitHead":"fb44df9daffc3ae61a2a4b6cbfc35751b2726c20","scripts":{"dev":"vite","mock":"node mock/server.mjs","test":"vitest run","build":"npm run build:standalone && npm run build:package && npm run build:types && npm run check:dist","dev:all":"node mock/server.mjs & vite","preview":"vite preview","typecheck":"tsc --noEmit","check:dist":"node scripts/check-artifacts.mjs","test:watch":"vitest","build:types":"tsc -p tsconfig.build.json","build:package":"vite build --mode package","build:standalone":"vite build"},"_npmUser":{"name":"riolo.giuseppe","email":"riolo.giuseppe@gmail.com"},"repository":{"url":"git+https://github.com/open-riolabs/rlb-chattoo-widget.git","type":"git"},"_npmVersion":"10.9.8","description":"Embeddable chat widget: one script tag on a site, or an npm package in an app","directories":{},"sideEffects":true,"_nodeVersion":"22.23.2","dependencies":{"lit":"^3.2.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"ws":"^8.18.0","vite":"^6.0.7","vitest":"^3.0.5","typescript":"^5.9.3","@types/node":"^25.5.2"},"_npmOperationalInternal":{"tmp":"tmp/rlb-chattoo-widget_1.0.2_1787263557759_0.10185730509044633","host":"s3://npm-registry-packages-npm-production"}},"1.0.3":{"name":"@chattoo/rlb-chattoo-widget","version":"1.0.3","keywords":["chat","widget","web-component","lit","custom-element","chattoo"],"license":"UNLICENSED","_id":"@chattoo/rlb-chattoo-widget@1.0.3","maintainers":[{"name":"riolo.giuseppe","email":"riolo.giuseppe@gmail.com"}],"homepage":"https://github.com/open-riolabs/rlb-chattoo-widget#readme","bugs":{"url":"https://github.com/open-riolabs/rlb-chattoo-widget/issues"},"dist":{"shasum":"8a65292b33220f9e82ef5d26268443951b36d00a","tarball":"https://registry.npmjs.org/@chattoo/rlb-chattoo-widget/-/rlb-chattoo-widget-1.0.3.tgz","fileCount":24,"integrity":"sha512-DbdTX26O7fVxiC6LVV0rfKJk61SRZRZM/VfEGAvp3LuOzunCUNym8z1GEQf1b/vZQMLjUMqbzYLoKb4IQbH9PA==","signatures":[{"sig":"MEQCIA6itqDMUgUZtCeZY4AU2pzx1xibyGcJj9JCivWgexWMAiB71C10Jsd9F64rlPSdOPWyLfA720WrB1YHJtp0gh1EoA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":323328},"main":"./index.js","type":"module","types":"./types/index.d.ts","module":"./index.js","exports":{".":{"types":"./types/index.d.ts","import":"./index.js"},"./standalone":"./chat-widget.js","./package.json":"./package.json"},"gitHead":"aaecaa3f91c0ece5e93c0607097f0d8f537ff497","scripts":{"dev":"vite","mock":"node mock/server.mjs","test":"vitest run","build":"npm run build:standalone && npm run build:package && npm run build:types && npm run check:dist","dev:all":"node mock/server.mjs & vite","preview":"vite preview","typecheck":"tsc --noEmit","check:dist":"node scripts/check-artifacts.mjs","test:watch":"vitest","build:types":"tsc -p tsconfig.build.json","build:package":"vite build --mode package","build:standalone":"vite build"},"_npmUser":{"name":"riolo.giuseppe","email":"riolo.giuseppe@gmail.com"},"repository":{"url":"git+https://github.com/open-riolabs/rlb-chattoo-widget.git","type":"git"},"_npmVersion":"10.9.8","description":"Embeddable chat widget: one script tag on a site, or an npm package in an app","directories":{},"sideEffects":true,"_nodeVersion":"22.23.2","dependencies":{"lit":"^3.2.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"ws":"^8.18.0","vite":"^6.0.7","vitest":"^3.0.5","typescript":"^5.9.3","@types/node":"^25.5.2"},"_npmOperationalInternal":{"tmp":"tmp/rlb-chattoo-widget_1.0.3_1787263928199_0.6144158560751081","host":"s3://npm-registry-packages-npm-production"}},"1.0.4":{"name":"@chattoo/rlb-chattoo-widget","version":"1.0.4","keywords":["chat","widget","web-component","lit","custom-element","chattoo"],"license":"UNLICENSED","_id":"@chattoo/rlb-chattoo-widget@1.0.4","maintainers":[{"name":"riolo.giuseppe","email":"riolo.giuseppe@gmail.com"}],"homepage":"https://github.com/open-riolabs/rlb-chattoo-widget#readme","bugs":{"url":"https://github.com/open-riolabs/rlb-chattoo-widget/issues"},"dist":{"shasum":"e8663201dc8bd77d30b93a5c5c8c9e776037fd6e","tarball":"https://registry.npmjs.org/@chattoo/rlb-chattoo-widget/-/rlb-chattoo-widget-1.0.4.tgz","fileCount":24,"integrity":"sha512-ERv5BNuU+AfD2Jqpo8UEh9O7DXQERv9pVJH5NYLgq3oH0OpanrH24DxV5J/BBhw4TDZN7cATWe0eG6DG/gTaLA==","signatures":[{"sig":"MEUCIQDJnvhTwkaw7vJRchYi6y+e5bnPFuchRPGwFioKTDsD3AIgNvvkfbRyHRiwJEy0m0UaeKROFkZeM71rKiu0wYvVS9A=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIBBzA6QwsJqXwtQj4nC5EqgtN6uqRrzQ9LvcPNqKnSW1AiEAsNrpPh5PSlDM4CqCz5GjXc50M+ag1MIMdLGpwPoB+pM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":325570},"main":"./index.js","type":"module","types":"./types/index.d.ts","module":"./index.js","exports":{".":{"types":"./types/index.d.ts","import":"./index.js"},"./standalone":"./chat-widget.js","./package.json":"./package.json"},"gitHead":"5213dc82b74bac60a997c79953ae91017acf7a58","scripts":{"dev":"vite","mock":"node mock/server.mjs","test":"vitest run","build":"npm run build:standalone && npm run build:package && npm run build:types && npm run check:dist","dev:all":"node mock/server.mjs & vite","preview":"vite preview","typecheck":"tsc --noEmit","check:dist":"node scripts/check-artifacts.mjs","test:watch":"vitest","build:types":"tsc -p tsconfig.build.json","build:package":"vite build --mode package","build:standalone":"vite build"},"_npmUser":{"name":"riolo.giuseppe","email":"riolo.giuseppe@gmail.com"},"repository":{"url":"git+https://github.com/open-riolabs/rlb-chattoo-widget.git","type":"git"},"_npmVersion":"10.9.8","description":"Embeddable chat widget: one script tag on a site, or an npm package in an app","directories":{},"sideEffects":true,"_nodeVersion":"22.23.2","dependencies":{"lit":"^3.2.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"ws":"^8.18.0","vite":"^6.0.7","vitest":"^3.0.5","typescript":"^5.9.3","@types/node":"^25.5.2"},"_npmOperationalInternal":{"tmp":"tmp/rlb-chattoo-widget_1.0.4_1789036074277_0.8951885246767433","host":"s3://npm-registry-packages-npm-production"}},"1.0.5":{"_id":"@chattoo/rlb-chattoo-widget@1.0.5","bugs":{"url":"https://github.com/open-riolabs/rlb-chattoo-widget/issues"},"dist":{"shasum":"cf348b6837360250e3bcf0aa4e79d37c6f6bc875","tarball":"https://registry.npmjs.org/@chattoo/rlb-chattoo-widget/-/rlb-chattoo-widget-1.0.5.tgz","fileCount":24,"integrity":"sha512-+/69mWsjAQU71HH9VXtcXxTEhodeZwGWZzhOYGVyWWStDqAQ2daO9HmGgI1j2edN1V8KIiG5ed9nuUgky4dAqw==","signatures":[{"sig":"MEYCIQDREHU81aRvWRVI6nl9HE8n4W8k+A2PV07IfJCrwtyWCwIhAIA8fQ8k+STXF3yvpa3mUJn7bonsr8mjaOyyzYVuM+Px","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQD5cUdsNwKBny4b9+ngdX4M2238z+YkoICcjfy/ySi1GgIgeiuiNJNm4jMd5g+ckYwmwLjri7Z2ebqfP9E2q+/Pyzo="}],"unpackedSize":327172},"main":"./index.js","name":"@chattoo/rlb-chattoo-widget","type":"module","types":"./types/index.d.ts","module":"./index.js","exports":{".":{"types":"./types/index.d.ts","import":"./index.js"},"./standalone":"./chat-widget.js","./package.json":"./package.json"},"gitHead":"3635ad33e25faecbd43a57384973f600d2316f29","license":"UNLICENSED","scripts":{"dev":"vite","mock":"node mock/server.mjs","test":"vitest run","build":"npm run build:standalone && npm run build:package && npm run build:types && npm run check:dist","dev:all":"node mock/server.mjs & vite","preview":"vite preview","typecheck":"tsc --noEmit","check:dist":"node scripts/check-artifacts.mjs","test:watch":"vitest","build:types":"tsc -p tsconfig.build.json","build:package":"vite build --mode package","build:standalone":"vite build"},"version":"1.0.5","_npmUser":{"name":"riolo.giuseppe","email":"riolo.giuseppe@gmail.com"},"homepage":"https://github.com/open-riolabs/rlb-chattoo-widget#readme","keywords":["chat","widget","web-component","lit","custom-element","chattoo"],"repository":{"url":"git+https://github.com/open-riolabs/rlb-chattoo-widget.git","type":"git"},"_npmVersion":"10.9.8","description":"Embeddable chat widget: one script tag on a site, or an npm package in an app","directories":{},"maintainers":[{"name":"riolo.giuseppe","email":"riolo.giuseppe@gmail.com"}],"sideEffects":true,"_nodeVersion":"22.23.2","dependencies":{"lit":"^3.2.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"ws":"^8.18.0","vite":"^6.0.7","vitest":"^3.0.5","typescript":"^5.9.3","@types/node":"^25.5.2"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/rlb-chattoo-widget_1.0.5_1789040377253_0.6180185494461319"}}},"time":{"created":"2026-08-19T13:00:39.178Z","modified":"2026-09-10T11:39:37.532Z","1.0.1":"2026-08-19T13:00:39.476Z","1.0.2":"2026-08-20T22:05:57.919Z","1.0.3":"2026-08-20T22:12:08.427Z","1.0.4":"2026-09-10T10:27:54.374Z","1.0.5":"2026-09-10T11:39:37.358Z"},"bugs":{"url":"https://github.com/open-riolabs/rlb-chattoo-widget/issues"},"license":"UNLICENSED","homepage":"https://github.com/open-riolabs/rlb-chattoo-widget#readme","keywords":["chat","widget","web-component","lit","custom-element","chattoo"],"repository":{"url":"git+https://github.com/open-riolabs/rlb-chattoo-widget.git","type":"git"},"description":"Embeddable chat widget: one script tag on a site, or an npm package in an app","maintainers":[{"name":"riolo.giuseppe","email":"riolo.giuseppe@gmail.com"}],"readme":"# @chattoo/rlb-chattoo-widget\n\nAn embeddable chat widget. One `<script>` tag on a site you do not control, or an npm package in\nan application you do.\n\nIt is the browser half of the **web channel** of `rlb-chat-connector`: the visitor types here, the\nmessage travels to the connector, and the tenant's answer comes back over a WebSocket. Everything\nthe widget knows about the platform is three HTTP-ish endpoints — it has no idea a broker exists.\n\n- **Runtime**: [Lit](https://lit.dev) web components — a custom element, not a framework plugin\n- **Ships as**: a single self-contained IIFE (~40 KB, Lit included) *and* an ESM package with types\n- **Isolation**: shadow DOM. Host page CSS does not leak in, widget CSS does not leak out\n\n---\n\n## Install\n\n### As a package\n\n```bash\nnpm install @chattoo/rlb-chattoo-widget\n```\n\n```ts\nimport { mount } from '@chattoo/rlb-chattoo-widget';\n\nmount({ siteId: 'acme-site', endpoint: 'https://gateway.example.com' });\n```\n\nLit is a normal dependency and stays **external** in the ESM build, so an application that already\nuses Lit keeps one copy of it. Two copies would mean two custom element registries fighting over\nthe same tag names.\n\n### As a script tag\n\nFor sites where you cannot run a build — a CMS, a landing page, someone else's template:\n\n```html\n<script src=\"https://cdn.example.com/chat-widget.js\"\n        data-site-id=\"acme-site\"\n        data-endpoint=\"https://gateway.example.com\"\n        data-language=\"en\"\n        data-theme=\"dark\"></script>\n```\n\nThat is the whole integration. The script mounts itself from its own attributes: a site that only\nwants a chat bubble should not have to write JavaScript. The file is\n`node_modules/@chattoo/rlb-chattoo-widget/dist/chat-widget.js`, or\n`@chattoo/rlb-chattoo-widget/standalone` if your bundler resolves subpaths.\n\n`data-endpoint` is optional: without it the widget uses the origin the script was served from,\nwhich is right whenever the gateway also serves the file.\n\n---\n\n## Using it in Angular\n\nThe widget is a custom element, so Angular needs to be told not to treat `<rlb-chat>` as a\ncomponent of its own. That is one line, and it is the only Angular-specific step.\n\n**1. Allow custom elements** in the module (or the standalone component) that renders it:\n\n```ts\nimport { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';\n\n@NgModule({\n  declarations: [AppComponent],\n  schemas: [CUSTOM_ELEMENTS_SCHEMA],   // without this: \"rlb-chat is not a known element\"\n})\nexport class AppModule {}\n```\n\nFor a standalone component, put `CUSTOM_ELEMENTS_SCHEMA` in its own `schemas` array instead.\n\n**2. Register the element** once, at startup. Importing the package defines `<rlb-chat>` and\ninstalls `window.rlbChat`:\n\n```ts\n// main.ts\nimport '@chattoo/rlb-chattoo-widget';\n```\n\n**3a. Let it float** over the whole application — the usual case. Mount it once and forget it:\n\n```ts\nimport { Component, OnInit } from '@angular/core';\nimport { mount } from '@chattoo/rlb-chattoo-widget';\n\n@Component({ selector: 'app-root', template: '<router-outlet />' })\nexport class AppComponent implements OnInit {\n  ngOnInit(): void {\n    mount({\n      siteId: 'acme-site',\n      endpoint: 'https://gateway.example.com',\n      language: 'en',\n    });\n  }\n}\n```\n\n**3b. Or place it in a template**, when you want it inside a panel instead of floating:\n\n```html\n<rlb-chat site-id=\"acme-site\"\n          endpoint=\"https://gateway.example.com\"\n          language=\"en\"\n          theme=\"dark\"\n          inline></rlb-chat>\n```\n\nAttribute names are dash-cased (`site-id`, `auto-open`, `no-attachments`); property names on the\nelement are camelCase. Angular's `[property]` binding writes the property directly, which is what\nyou want for anything that is not a string:\n\n```html\n<rlb-chat [siteId]=\"siteId\" [endpoint]=\"endpoint\" [strings]=\"customStrings\"></rlb-chat>\n```\n\n`strings` is an object and **must** be bound as a property: an attribute would stringify it.\n\n### Two things that bite in Angular specifically\n\n**Server-side rendering.** The package touches `window` and `document` when it loads, so importing\nit at module scope breaks an SSR build. Import it inside `ngOnInit`, or guard it:\n\n```ts\nif (typeof window !== 'undefined') {\n  const { mount } = await import('@chattoo/rlb-chattoo-widget');\n  mount({ siteId, endpoint });\n}\n```\n\n**Zone.js and events.** The widget's events cross the shadow boundary (`composed: true`), so\nAngular's `(rlb-send)` binding catches them normally. What Angular does *not* see is state changing\ninside the widget: it is a web component, not a signal. If you need to know what the visitor sent,\nlisten for the event rather than reading properties.\n\n---\n\n## Configuration\n\nEverything below is both an attribute and a property. `mount()` takes the same set as an options\nobject, plus `target` and `inline`.\n\n| attribute | property | default | |\n|---|---|---|---|\n| `site-id` | `siteId` | — | **required.** Identifies the channel: it is the `externalRef` of the web channel on the tenant |\n| `endpoint` | `endpoint` | script origin | where the gateway lives |\n| `language` | `language` | browser language | `en` and `it` ship in the bundle; anything else falls back to `en` unless you pass `strings` |\n| `theme` | `theme` | follows the OS | `light` or `dark` |\n| `position` | `position` | `right` | which corner the bubble sits in |\n| `inline` | `inline` | `false` | render in place instead of floating, for a panel or a page section |\n| `auto-open` | `autoOpen` | `false` | open the panel on load instead of waiting for a click |\n| `no-attachments` | `noAttachments` | `false` | hide the attachment button |\n| `captcha-mode` | `captchaMode` | `invisible` | passed through to the captcha bundle |\n| `captcha-script` | `captchaScript` | derived from `endpoint` | override where the captcha bundle is fetched from |\n| — | `strings` | — | partial overrides merged over the bundled strings. Property only |\n\n`data-strings` on the script tag carries the same thing as JSON. Malformed JSON is ignored rather\nthan fatal: a typo in an attribute must not cost the site its chat.\n\n## Events\n\nBoth bubble and cross the shadow boundary, so an ordinary listener on an ancestor catches them.\n\n| event | `detail` | |\n|---|---|---|\n| `rlb-send` | `ChatContent` | the visitor sent something |\n| `rlb-choice` | `ChoiceOption` | the visitor tapped a button or a list row |\n\n```ts\ndocument.addEventListener('rlb-send', (e) => analytics.track('chat_message', e.detail));\n```\n\n## Theming\n\nSet CSS custom properties on the element, or on `:root` for the floating case. They are read\nthrough the shadow boundary, which is the one thing that does cross it by design:\n\n```css\nrlb-chat {\n  --rlb-accent: #7c3aed;\n  --rlb-accent-contrast: #ffffff;\n  --rlb-radius: 8px;\n  --rlb-font: \"Inter\", system-ui, sans-serif;\n}\n```\n\nThe full set: `--rlb-accent`, `--rlb-accent-contrast`, `--rlb-surface`, `--rlb-surface-muted`,\n`--rlb-text`, `--rlb-text-muted`, `--rlb-border`, `--rlb-bubble-agent`, `--rlb-bubble-visitor`,\n`--rlb-bubble-visitor-text`, `--rlb-radius`, `--rlb-shadow`, `--rlb-font`, `--rlb-z`.\n\nDark mode overrides the surface and text tokens and leaves the accent alone, so a brand colour set\nonce works in both.\n\n---\n\n## What the widget expects on the other side\n\nThree endpoints under `endpoint`, exposed in production by `rlb-gateway`:\n\n| | |\n|---|---|\n| `POST /chat-connector/web/session` | opens a session, returns a `sessionToken` and a `sessionId` |\n| `POST /chat-connector/web/messages` | sends one message |\n| `WS /ws` | replies, typing indicators and delivery states come back here |\n\nThe socket carries the session token as a **WebSocket subprotocol** rather than a query parameter,\nso it never lands in a proxy access log. It then expects\n`{action: 'subscribe', topic: 'chatWeb', select: {sessionId}}`, and a malformed frame is dropped\nwithout an answer.\n\n`mock/server.mjs` implements all three plus a fake captcha, so the widget can be exercised end to\nend with no gateway, no broker and no database:\n\n```bash\nnpm run dev:all      # mock server + vite, then open the printed URL\n```\n\n---\n\n## What it does, and what it refuses to do\n\nIt renders text, images, video, audio, documents, buttons and list pickers, shows typing and\ndelivery states, and remembers the session across a page reload.\n\nIt does **not** persist history: reopening after the session expires starts an empty conversation.\nThe transcript belongs to the platform, not to a script on someone else's page.\n\nThe contract in `src/contract.ts` is a **subset** of the connector's: only the content types the\nweb channel can actually render. It is duplicated on purpose rather than imported — this package\nmust not depend on the connector to build.\n\n## A known limit: every visitor in one bucket\n\nThe captcha bundle does **not** send a `clientId`, so upstream every visitor lands in the single\n`anonymous` bucket: one abuser burns the whole site's request budget and raises the proof-of-work\ndifficulty for every honest browser.\n\nThis is not a widget defect and cannot be fixed inside the widget. The fix is either the host site\nproxying `/captcha*` same-origin and injecting the visitor's real IP, or `rlb-gateway` injecting\nthe caller's IP on the captcha routes.\n\n`/captcha/validate` must **never** be exposed same-origin: it burns the token, and anyone could\nconsume another visitor's verification.\n\n---\n\n## Working on it\n\n```bash\nnpm install\nnpm test             # vitest — 18 tests\nnpm run dev:all      # mock backend + dev server\nnpm run build        # standalone IIFE, ESM package, and type declarations\n```\n\n`npm run build` produces three things in `dist/`:\n\n| | |\n|---|---|\n| `chat-widget.js` | the standalone IIFE, Lit bundled in — the script tag |\n| `index.js` | ESM with Lit left external — the npm package |\n| `types/` | declarations, generated from the same sources |\n","readmeFilename":"README.md"}