@discloai/core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +218 -0
  3. package/dist/discloai.min.js +1 -0
  4. package/dist/index.d.mts +152 -0
  5. package/dist/index.d.ts +152 -0
  6. package/dist/index.js +795 -0
  7. package/dist/index.js.map +1 -0
  8. package/dist/index.mjs +764 -0
  9. package/dist/index.mjs.map +1 -0
  10. package/package.json +58 -0
  11. package/src/__tests__/audit.test.ts +117 -0
  12. package/src/__tests__/init.test.ts +49 -0
  13. package/src/__tests__/wcag.test.ts +260 -0
  14. package/src/audit.ts +155 -0
  15. package/src/components/AIContentLabel.ts +108 -0
  16. package/src/components/BiometricNotice.ts +82 -0
  17. package/src/components/ChatbotDisclosure.ts +188 -0
  18. package/src/components/DeepfakeLabel.ts +123 -0
  19. package/src/config.ts +191 -0
  20. package/src/i18n/bg.json +9 -0
  21. package/src/i18n/cs.json +9 -0
  22. package/src/i18n/da.json +9 -0
  23. package/src/i18n/de.json +9 -0
  24. package/src/i18n/el.json +9 -0
  25. package/src/i18n/en.json +9 -0
  26. package/src/i18n/es.json +9 -0
  27. package/src/i18n/et.json +9 -0
  28. package/src/i18n/fi.json +9 -0
  29. package/src/i18n/fr.json +9 -0
  30. package/src/i18n/ga.json +9 -0
  31. package/src/i18n/hr.json +9 -0
  32. package/src/i18n/hu.json +9 -0
  33. package/src/i18n/index.ts +145 -0
  34. package/src/i18n/it.json +9 -0
  35. package/src/i18n/lt.json +9 -0
  36. package/src/i18n/lv.json +9 -0
  37. package/src/i18n/mt.json +9 -0
  38. package/src/i18n/nl.json +9 -0
  39. package/src/i18n/pl.json +9 -0
  40. package/src/i18n/pt.json +9 -0
  41. package/src/i18n/ro.json +9 -0
  42. package/src/i18n/sk.json +9 -0
  43. package/src/i18n/sl.json +9 -0
  44. package/src/i18n/sv.json +9 -0
  45. package/src/index.ts +19 -0
  46. package/src/init.ts +56 -0
  47. package/src/vendors.ts +29 -0
  48. package/src/version.ts +1 -0
  49. package/src/wcag.ts +46 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 DiscloAI
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,218 @@
1
+ # @discloai/core
2
+
3
+ > EU AI Act Article 50 compliance SDK — automatic chatbot disclosures, AI content labels, deepfake labels, and biometric notices.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@discloai/core)](https://www.npmjs.com/package/@discloai/core)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
7
+ [![Bundle size](https://img.shields.io/badge/CDN%20bundle-<15KB%20gz-brightgreen)](https://cdn.discloai.com/discloai.min.js)
8
+
9
+ ---
10
+
11
+ ## Why DiscloAI?
12
+
13
+ The **EU AI Act Article 50** (enforcement from August 2026) requires anyone deploying AI systems in the EU to:
14
+
15
+ - **§1** — Tell users when they're interacting with an AI chatbot
16
+ - **§3** — Notify users when biometric data is processed
17
+ - **§4 ¶1** — Label deepfake / synthetic media
18
+ - **§4 ¶2** — Label AI-generated content
19
+
20
+ DiscloAI handles all four requirements in a single `<script>` tag. Non-compliance can result in fines up to **€15 million or 3% of worldwide annual turnover**.
21
+
22
+ ---
23
+
24
+ ## Quick start
25
+
26
+ ### Script tag (any site)
27
+
28
+ ```html
29
+ <script
30
+ src="https://cdn.discloai.com/discloai.min.js"
31
+ data-site-id="YOUR_SITE_ID"
32
+ defer>
33
+ </script>
34
+ ```
35
+
36
+ Get your Site ID from the [DiscloAI dashboard](https://app.discloai.com).
37
+
38
+ ### npm
39
+
40
+ ```bash
41
+ npm install @discloai/core
42
+ ```
43
+
44
+ ```ts
45
+ import { init } from '@discloai/core';
46
+
47
+ init({ siteId: 'YOUR_SITE_ID' });
48
+ ```
49
+
50
+ ### React
51
+
52
+ ```bash
53
+ npm install @discloai/react
54
+ ```
55
+
56
+ ```tsx
57
+ // app/layout.tsx or _app.tsx
58
+ import { DiscloAIProvider } from '@discloai/react';
59
+
60
+ export default function RootLayout({ children }) {
61
+ return (
62
+ <html>
63
+ <body>
64
+ <DiscloAIProvider siteId="YOUR_SITE_ID" />
65
+ {children}
66
+ </body>
67
+ </html>
68
+ );
69
+ }
70
+ ```
71
+
72
+ ### Next.js (App Router)
73
+
74
+ ```bash
75
+ npm install @discloai/nextjs
76
+ ```
77
+
78
+ ```tsx
79
+ // app/layout.tsx
80
+ import { DiscloAI } from '@discloai/nextjs';
81
+
82
+ export default function RootLayout({ children }) {
83
+ return (
84
+ <html>
85
+ <body>
86
+ <DiscloAI siteId="YOUR_SITE_ID" />
87
+ {children}
88
+ </body>
89
+ </html>
90
+ );
91
+ }
92
+ ```
93
+
94
+ ### WordPress
95
+
96
+ Install the [DiscloAI WordPress plugin](https://wordpress.org/plugins/discloai-disclosure/) and enter your Site ID in **Settings → DiscloAI**.
97
+
98
+ ---
99
+
100
+ ## Components
101
+
102
+ DiscloAI automatically detects and configures the right disclosure for each use case.
103
+
104
+ | Component | Article 50 | Trigger |
105
+ |---|---|---|
106
+ | `ChatbotDisclosure` | §1 | On chat widget open |
107
+ | `AIContentLabel` | §4 ¶2 | On page load if `[data-discloai-content]` present |
108
+ | `DeepfakeLabel` | §4 ¶1 | On page load if `[data-discloai-synthetic]` present |
109
+ | `BiometricNotice` | §3 | On page load if `[data-discloai-biometric]` present |
110
+
111
+ ### Manual control
112
+
113
+ ```ts
114
+ import { init } from '@discloai/core';
115
+
116
+ init({
117
+ siteId: 'YOUR_SITE_ID',
118
+ components: {
119
+ ChatbotDisclosure: { enabled: true, selector: '#my-chat-widget' },
120
+ AIContentLabel: { enabled: true },
121
+ DeepfakeLabel: { enabled: true },
122
+ BiometricNotice: { enabled: false },
123
+ },
124
+ });
125
+ ```
126
+
127
+ ---
128
+
129
+ ## Vendor presets
130
+
131
+ DiscloAI auto-detects common chat platforms. Supported: **Intercom**, **Crisp**, **Tidio**, **Zendesk**, **Drift**, **LiveChat**, **HubSpot**, **Freshdesk**.
132
+
133
+ ```ts
134
+ import { init, VENDOR_PRESETS } from '@discloai/core';
135
+
136
+ init({
137
+ siteId: 'YOUR_SITE_ID',
138
+ components: {
139
+ ChatbotDisclosure: VENDOR_PRESETS.intercom,
140
+ },
141
+ });
142
+ ```
143
+
144
+ ---
145
+
146
+ ## Exemptions
147
+
148
+ ```ts
149
+ init({
150
+ siteId: 'YOUR_SITE_ID',
151
+ components: {
152
+ ChatbotDisclosure: {
153
+ enabled: true,
154
+ obviousContext: false, // §1 — only exempts if human-staffed fallback exists
155
+ },
156
+ AIContentLabel: {
157
+ enabled: true,
158
+ editorialControl: false, // §4¶2 — only exempts if human substantially edited output
159
+ },
160
+ DeepfakeLabel: {
161
+ enabled: true,
162
+ artisticContext: false, // §4¶1 — only exempts clearly fictional/satirical content
163
+ },
164
+ },
165
+ });
166
+ ```
167
+
168
+ ---
169
+
170
+ ## Compliance audit log
171
+
172
+ All disclosure events are logged to the DiscloAI backend using `sendBeacon`. Logs include:
173
+ - Disclosure type + SDK version
174
+ - Anonymised page URL (SHA-256 hash)
175
+ - Anonymised session (SHA-256 hash)
176
+ - Server-generated timestamp
177
+
178
+ No PII is ever sent or stored.
179
+
180
+ ---
181
+
182
+ ## Performance
183
+
184
+ - CDN bundle: **< 15KB gzipped**
185
+ - Adds **< 100ms** to page TTI (Time to Interactive)
186
+ - Zero dependencies
187
+ - SSR-safe (no `window`/`document` at module import time)
188
+
189
+ ---
190
+
191
+ ## Internationalization
192
+
193
+ Disclosures are displayed in the user's browser language. Supports all 24 official EU languages:
194
+
195
+ `bg` `cs` `da` `de` `el` `en` `es` `et` `fi` `fr` `ga` `hr` `hu` `it` `lt` `lv` `mt` `nl` `pl` `pt` `ro` `sk` `sl` `sv`
196
+
197
+ ---
198
+
199
+ ## Security
200
+
201
+ - No `innerHTML` — all DOM manipulation uses `textContent` / `createElement`
202
+ - Audit events authenticated by Origin header (no tokens in the browser)
203
+ - Custom CSS sanitized — escape sequences and dangerous properties stripped
204
+ - WCAG 2.1 AA compliant — all disclosures have `role="dialog"`, `aria-modal`, focus management
205
+
206
+ ---
207
+
208
+ ## Dashboard
209
+
210
+ Manage sites, view compliance audit logs, and generate PDF compliance reports at **[app.discloai.com](https://app.discloai.com)**.
211
+
212
+ Plans: Starter (€49/mo), Professional (€129/mo), Agency (€349/mo).
213
+
214
+ ---
215
+
216
+ ## License
217
+
218
+ MIT © 2026 DiscloAI
@@ -0,0 +1 @@
1
+ "use strict";var DiscloAI=(()=>{var k=Object.defineProperty;var oe=Object.getOwnPropertyDescriptor;var ae=Object.getOwnPropertyNames;var ne=Object.prototype.hasOwnProperty;var se=(t,e)=>{for(var i in e)k(t,i,{get:e[i],enumerable:!0})},re=(t,e,i,a)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of ae(e))!ne.call(t,n)&&n!==i&&k(t,n,{get:()=>e[n],enumerable:!(a=oe(e,n))||a.enumerable});return t};var le=t=>re(k({},"__esModule",{value:!0}),t);var Ke={};se(Ke,{VENDOR_PRESETS:()=>g,init:()=>te,resolveVendorSelector:()=>ie,sanitizeCSS:()=>u,sendAuditEvent:()=>c});var ce={locale:"en"},de=[/url\s*\(/i,/@import/i,/expression\s*\(/i,/javascript:/i];function u(t){let e=t.replace(/\\[0-9a-fA-F]{1,6}\s?/g,"ESCAPED");for(let i of de)if(i.test(e))return console.warn("[DiscloAI] Custom CSS blocked: forbidden pattern detected"),"";return t}async function ue(t,e){let i=new AbortController,a=setTimeout(()=>i.abort(),2e3),n=`https://api.discloai.com/v1/config/${encodeURIComponent(t)}`,s=e??n,r=s.startsWith("http://localhost")||s.startsWith("http://127.0.0.1"),l=s.startsWith("https://")||r?s:n;try{let o=await fetch(l,{signal:i.signal});if(!o.ok)return null;let p=await o.json();return fe(p)}catch{return null}finally{clearTimeout(a)}}function fe(t){if(typeof t!="object"||t===null)return null;let e=t,i={};return typeof e.locale=="string"&&(i.locale=e.locale),typeof e.chatbotDisclosure=="object"&&e.chatbotDisclosure!==null&&(i.chatbotDisclosure=e.chatbotDisclosure),typeof e.aiContentLabel=="object"&&e.aiContentLabel!==null&&(i.aiContentLabel=e.aiContentLabel),typeof e.deepfakeLabel=="object"&&e.deepfakeLabel!==null&&(i.deepfakeLabel=e.deepfakeLabel),typeof e.biometricNotice=="object"&&e.biometricNotice!==null&&(i.biometricNotice=e.biometricNotice),i}async function v(t){let e=await ue(t.siteId,t.configEndpoint);return{siteId:t.siteId,locale:t.locale??e?.locale??ce.locale,chatbotDisclosure:t.chatbotDisclosure??e?.chatbotDisclosure,aiContentLabel:t.aiContentLabel??e?.aiContentLabel,deepfakeLabel:t.deepfakeLabel??e?.deepfakeLabel,biometricNotice:t.biometricNotice??e?.biometricNotice}}var y="https://app.discloai.com/api/v1/audit/event";async function x(t){let e=new TextEncoder().encode(t),i=await crypto.subtle.digest("SHA-256",e);return pe(i)}function pe(t){return Array.from(new Uint8Array(t)).map(e=>e.toString(16).padStart(2,"0")).join("")}async function c(t){try{let e=typeof window<"u"?window.__DISCLOAI_CONFIG__??{}:{},i=e.siteId??"",a=e.auditEndpoint??y,n=a.startsWith("http://localhost")||a.startsWith("http://127.0.0.1"),s=a.startsWith("https://")||n?a:y,r=new Date().toISOString(),l=typeof globalThis.location<"u"?globalThis.location.href:"",[o,p]=await Promise.all([x(l),x(t.sessionId)]),b=JSON.stringify({siteId:i,timestamp:r,pageUrlHash:o,sessionHash:p,disclosureType:t.disclosureType,componentVersion:t.componentVersion,rendered:t.rendered??!0}),m=new Blob([b],{type:"application/json"});typeof navigator<"u"&&typeof navigator.sendBeacon=="function"?navigator.sendBeacon(s,m):fetch(s,{method:"POST",body:m,keepalive:!0}).catch(()=>{})}catch{}}var E={"chatbot.disclosure.default":"You are talking to an AI assistant.","chatbot.disclosure.prominent":"This chat is powered by artificial intelligence.","content.label.default":"AI-generated content","content.label.artistic":"AI-assisted creative work","deepfake.label.default":"AI-generated image","deepfake.label.artistic":"AI-generated artistic content","biometric.notice.default":"This system processes biometric data. EU AI Act Article 50 \xA73 applies."};var S={"chatbot.disclosure.default":"Vous discutez avec un assistant IA.","chatbot.disclosure.prominent":"Cette conversation est aliment\xE9e par l'intelligence artificielle.","content.label.default":"Contenu g\xE9n\xE9r\xE9 par IA","content.label.artistic":"\u0152uvre cr\xE9ative assist\xE9e par IA","deepfake.label.default":"Image g\xE9n\xE9r\xE9e par IA","deepfake.label.artistic":"Contenu artistique g\xE9n\xE9r\xE9 par IA","biometric.notice.default":"Ce syst\xE8me traite des donn\xE9es biom\xE9triques. L'article 50 \xA73 de la loi europ\xE9enne sur l'IA s'applique."};var D={"chatbot.disclosure.default":"Sie sprechen mit einem KI-Assistenten.","chatbot.disclosure.prominent":"Dieser Chat wird durch k\xFCnstliche Intelligenz betrieben.","content.label.default":"KI-generierter Inhalt","content.label.artistic":"KI-unterst\xFCtztes kreatives Werk","deepfake.label.default":"KI-generiertes Bild","deepfake.label.artistic":"KI-generierter k\xFCnstlerischer Inhalt","biometric.notice.default":"Dieses System verarbeitet biometrische Daten. EU-KI-Gesetz Artikel 50 \xA73 gilt."};var L={"chatbot.disclosure.default":"Est\xE1s hablando con un asistente de IA.","chatbot.disclosure.prominent":"Este chat est\xE1 impulsado por inteligencia artificial.","content.label.default":"Contenido generado por IA","content.label.artistic":"Obra creativa asistida por IA","deepfake.label.default":"Imagen generada por IA","deepfake.label.artistic":"Contenido art\xEDstico generado por IA","biometric.notice.default":"Este sistema procesa datos biom\xE9tricos. Se aplica el art\xEDculo 50 \xA73 de la Ley de IA de la UE."};var z={"chatbot.disclosure.default":"\u0420\u0430\u0437\u0433\u043E\u0432\u0430\u0440\u044F\u0442\u0435 \u0441 AI \u0430\u0441\u0438\u0441\u0442\u0435\u043D\u0442.","chatbot.disclosure.prominent":"\u0422\u043E\u0437\u0438 \u0447\u0430\u0442 \u0435 \u0437\u0430\u0445\u0440\u0430\u043D\u0432\u0430\u043D \u043E\u0442 \u0438\u0437\u043A\u0443\u0441\u0442\u0432\u0435\u043D \u0438\u043D\u0442\u0435\u043B\u0435\u043A\u0442.","content.label.default":"\u0421\u044A\u0434\u044A\u0440\u0436\u0430\u043D\u0438\u0435, \u0433\u0435\u043D\u0435\u0440\u0438\u0440\u0430\u043D\u043E \u043E\u0442 AI","content.label.artistic":"\u0422\u0432\u043E\u0440\u0447\u0435\u0441\u043A\u0430 \u0440\u0430\u0431\u043E\u0442\u0430 \u0441 \u043F\u043E\u043C\u043E\u0449\u0442\u0430 \u043D\u0430 AI","deepfake.label.default":"AI-\u0433\u0435\u043D\u0435\u0440\u0438\u0440\u0430\u043D\u043E \u0438\u0437\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u0435","deepfake.label.artistic":"AI-\u0433\u0435\u043D\u0435\u0440\u0438\u0440\u0430\u043D\u043E \u0445\u0443\u0434\u043E\u0436\u0435\u0441\u0442\u0432\u0435\u043D\u043E \u0441\u044A\u0434\u044A\u0440\u0436\u0430\u043D\u0438\u0435","biometric.notice.default":"\u0422\u0430\u0437\u0438 \u0441\u0438\u0441\u0442\u0435\u043C\u0430 \u043E\u0431\u0440\u0430\u0431\u043E\u0442\u0432\u0430 \u0431\u0438\u043E\u043C\u0435\u0442\u0440\u0438\u0447\u043D\u0438 \u0434\u0430\u043D\u043D\u0438. \u041F\u0440\u0438\u043B\u0430\u0433\u0430 \u0441\u0435 \u0427\u043B\u0435\u043D 50 \xA73 \u043E\u0442 \u0417\u0430\u043A\u043E\u043D\u0430 \u0437\u0430 AI \u043D\u0430 \u0415\u0421."};var w={"chatbot.disclosure.default":"Razgovarate s AI asistentom.","chatbot.disclosure.prominent":"Ovaj chat pokre\u0107e umjetna inteligencija.","content.label.default":"Sadr\u017Eaj generiran AI-jem","content.label.artistic":"Kreativni rad uz pomo\u0107 AI-ja","deepfake.label.default":"AI-generirana slika","deepfake.label.artistic":"AI-generirani umjetni\u010Dki sadr\u017Eaj","biometric.notice.default":"Ovaj sustav obra\u0111uje biometrijske podatke. Primjenjuje se \u010Clanak 50 \xA73 Zakona EU o AI."};var j={"chatbot.disclosure.default":"Mluv\xEDte s AI asistentem.","chatbot.disclosure.prominent":"Tento chat je poh\xE1n\u011Bn um\u011Blou inteligenc\xED.","content.label.default":"Obsah generovan\xFD AI","content.label.artistic":"Kreativn\xED pr\xE1ce s pomoc\xED AI","deepfake.label.default":"Obr\xE1zek generovan\xFD AI","deepfake.label.artistic":"Um\u011Bleck\xFD obsah generovan\xFD AI","biometric.notice.default":"Tento syst\xE9m zpracov\xE1v\xE1 biometrick\xE9 \xFAdaje. Plat\xED \u010Dl. 50 \xA73 z\xE1kona EU o AI."};var T={"chatbot.disclosure.default":"Du taler med en AI-assistent.","chatbot.disclosure.prominent":"Denne chat drives af kunstig intelligens.","content.label.default":"AI-genereret indhold","content.label.artistic":"AI-assisteret kreativt arbejde","deepfake.label.default":"AI-genereret billede","deepfake.label.artistic":"AI-genereret kunstnerisk indhold","biometric.notice.default":"Dette system behandler biometriske data. EU AI Act artikel 50 \xA73 finder anvendelse."};var N={"chatbot.disclosure.default":"\u039C\u03B9\u03BB\u03AC\u03C4\u03B5 \u03BC\u03B5 \u03AD\u03BD\u03B1\u03BD \u03B2\u03BF\u03B7\u03B8\u03CC AI.","chatbot.disclosure.prominent":"\u0391\u03C5\u03C4\u03AE \u03B7 \u03C3\u03C5\u03BD\u03BF\u03BC\u03B9\u03BB\u03AF\u03B1 \u03C4\u03C1\u03BF\u03C6\u03BF\u03B4\u03BF\u03C4\u03B5\u03AF\u03C4\u03B1\u03B9 \u03B1\u03C0\u03CC \u03C4\u03B5\u03C7\u03BD\u03B7\u03C4\u03AE \u03BD\u03BF\u03B7\u03BC\u03BF\u03C3\u03CD\u03BD\u03B7.","content.label.default":"\u03A0\u03B5\u03C1\u03B9\u03B5\u03C7\u03CC\u03BC\u03B5\u03BD\u03BF \u03C0\u03BF\u03C5 \u03B4\u03B7\u03BC\u03B9\u03BF\u03C5\u03C1\u03B3\u03AE\u03B8\u03B7\u03BA\u03B5 \u03B1\u03C0\u03CC AI","content.label.artistic":"\u0394\u03B7\u03BC\u03B9\u03BF\u03C5\u03C1\u03B3\u03B9\u03BA\u03AE \u03B5\u03C1\u03B3\u03B1\u03C3\u03AF\u03B1 \u03BC\u03B5 \u03B2\u03BF\u03AE\u03B8\u03B5\u03B9\u03B1 AI","deepfake.label.default":"\u0395\u03B9\u03BA\u03CC\u03BD\u03B1 \u03C0\u03BF\u03C5 \u03B4\u03B7\u03BC\u03B9\u03BF\u03C5\u03C1\u03B3\u03AE\u03B8\u03B7\u03BA\u03B5 \u03B1\u03C0\u03CC AI","deepfake.label.artistic":"\u039A\u03B1\u03BB\u03BB\u03B9\u03C4\u03B5\u03C7\u03BD\u03B9\u03BA\u03CC \u03C0\u03B5\u03C1\u03B9\u03B5\u03C7\u03CC\u03BC\u03B5\u03BD\u03BF \u03C0\u03BF\u03C5 \u03B4\u03B7\u03BC\u03B9\u03BF\u03C5\u03C1\u03B3\u03AE\u03B8\u03B7\u03BA\u03B5 \u03B1\u03C0\u03CC AI","biometric.notice.default":"\u0391\u03C5\u03C4\u03CC \u03C4\u03BF \u03C3\u03CD\u03C3\u03C4\u03B7\u03BC\u03B1 \u03B5\u03C0\u03B5\u03BE\u03B5\u03C1\u03B3\u03AC\u03B6\u03B5\u03C4\u03B1\u03B9 \u03B2\u03B9\u03BF\u03BC\u03B5\u03C4\u03C1\u03B9\u03BA\u03AC \u03B4\u03B5\u03B4\u03BF\u03BC\u03AD\u03BD\u03B1. \u0399\u03C3\u03C7\u03CD\u03B5\u03B9 \u0386\u03C1\u03B8\u03C1\u03BF 50 \xA73 \u039D\u03CC\u03BC\u03BF\u03C5 \u0395\u0395 \u03B3\u03B9\u03B1 AI."};var O={"chatbot.disclosure.default":"R\xE4\xE4gite AI-assistendiga.","chatbot.disclosure.prominent":"Seda vestlust k\xE4itab tehisintellekt.","content.label.default":"AI loodud sisu","content.label.artistic":"AI-abistatud loominguline t\xF6\xF6","deepfake.label.default":"AI loodud pilt","deepfake.label.artistic":"AI loodud kunstiline sisu","biometric.notice.default":"See s\xFCsteem t\xF6\xF6tleb biomeetrilisi andmeid. Kohaldub EL AI seaduse artikkel 50 \xA73."};var P={"chatbot.disclosure.default":"Puhut AI-assistentin kanssa.","chatbot.disclosure.prominent":"T\xE4m\xE4 chat toimii teko\xE4lyn avulla.","content.label.default":"Teko\xE4lyn tuottama sis\xE4lt\xF6","content.label.artistic":"Teko\xE4lyavusteinen luova ty\xF6","deepfake.label.default":"Teko\xE4lyn luoma kuva","deepfake.label.artistic":"Teko\xE4lyn luoma taiteellinen sis\xE4lt\xF6","biometric.notice.default":"T\xE4m\xE4 j\xE4rjestelm\xE4 k\xE4sittelee biometrisi\xE4 tietoja. EU:n teko\xE4lylain 50 artiklan 3 kohta soveltuu."};var M={"chatbot.disclosure.default":"T\xE1 t\xFA ag caint le c\xFAnt\xF3ir AI.","chatbot.disclosure.prominent":"T\xE1 an comhr\xE1 seo faoi chumhacht na hintleachta saorga.","content.label.default":"\xC1bhar arna ghini\xFAint ag AI","content.label.artistic":"Obair chruthaitheach le c\xFAnamh AI","deepfake.label.default":"\xCDomh\xE1 arna ghini\xFAint ag AI","deepfake.label.artistic":"\xC1bhar eala\xEDne arna ghini\xFAint ag AI","biometric.notice.default":"Pr\xF3ise\xE1lann an c\xF3ras seo sonra\xED bithfh\xE9ini\xFAlachta. T\xE1 Airteagal 50 \xA73 de Dhl\xED AI an AE infheidhme."};var U={"chatbot.disclosure.default":"AI-asszisztenssel besz\xE9l.","chatbot.disclosure.prominent":"Ez a cseveg\xE9s mesters\xE9ges intelligencia \xE1ltal m\u0171k\xF6dik.","content.label.default":"AI \xE1ltal gener\xE1lt tartalom","content.label.artistic":"AI-val t\xE1mogatott kreat\xEDv munka","deepfake.label.default":"AI \xE1ltal gener\xE1lt k\xE9p","deepfake.label.artistic":"AI \xE1ltal gener\xE1lt m\u0171v\xE9szeti tartalom","biometric.notice.default":"Ez a rendszer biometrikus adatokat dolgoz fel. Az EU MI-t\xF6rv\xE9ny 50. cikk 3. \xA7-a alkalmazand\xF3."};var V={"chatbot.disclosure.default":"Stai parlando con un assistente AI.","chatbot.disclosure.prominent":"Questa chat \xE8 alimentata dall'intelligenza artificiale.","content.label.default":"Contenuto generato dall'AI","content.label.artistic":"Lavoro creativo assistito dall'AI","deepfake.label.default":"Immagine generata dall'AI","deepfake.label.artistic":"Contenuto artistico generato dall'AI","biometric.notice.default":"Questo sistema elabora dati biometrici. Si applica l'Articolo 50 \xA73 della Legge AI dell'UE."};var R={"chatbot.disclosure.default":"J\u016Bs run\u0101jat ar AI asistentu.","chatbot.disclosure.prominent":"\u0160o t\u0113rz\u0113\u0161anu darbina m\u0101ksl\u012Bgais intelekts.","content.label.default":"AI \u0123ener\u0113ts saturs","content.label.artistic":"AI asist\u0113ts rado\u0161s darbs","deepfake.label.default":"AI \u0123ener\u0113ts att\u0113ls","deepfake.label.artistic":"AI \u0123ener\u0113ts m\u0101ksliniecisks saturs","biometric.notice.default":"\u0160\u012B sist\u0113ma apstr\u0101d\u0101 biometriskos datus. Piem\u0113rojams ES MI likuma 50. pants \xA73."};var _={"chatbot.disclosure.default":"J\u016Bs kalbat\u0117s su AI asistentu.","chatbot.disclosure.prominent":"\u0160is pokalbis vyksta dirbtinio intelekto pagalba.","content.label.default":"AI sugeneruotas turinys","content.label.artistic":"AI padedamas k\u016Brybinis darbas","deepfake.label.default":"AI sugeneruotas vaizdas","deepfake.label.artistic":"AI sugeneruotas meninis turinys","biometric.notice.default":"\u0160i sistema apdoroja biometrinius duomenis. Taikomas ES DI \u012Fstatymo 50 straipsnio 3 dalis."};var B={"chatbot.disclosure.default":"Qed titkellem ma' assistent AI.","chatbot.disclosure.prominent":"Dan il-chat huwa m\u0127addem mill-intelli\u0121enza artifi\u010Bjali.","content.label.default":"Kontenut i\u0121\u0121enerat minn AI","content.label.artistic":"Xog\u0127ol kreattiv assistit minn AI","deepfake.label.default":"Imma\u0121ni i\u0121\u0121enerata minn AI","deepfake.label.artistic":"Kontenut artistiku i\u0121\u0121enerat minn AI","biometric.notice.default":"Dan is-sistema tippro\u010Bessa data bijometrika. Japplika l-Artikolu 50 \xA73 tal-Li\u0121i AI tal-UE."};var K={"chatbot.disclosure.default":"U praat met een AI-assistent.","chatbot.disclosure.prominent":"Deze chat wordt aangedreven door kunstmatige intelligentie.","content.label.default":"AI-gegenereerde inhoud","content.label.artistic":"AI-ondersteund creatief werk","deepfake.label.default":"AI-gegenereerde afbeelding","deepfake.label.artistic":"AI-gegenereerde artistieke inhoud","biometric.notice.default":"Dit systeem verwerkt biometrische gegevens. EU AI-wet artikel 50 \xA73 is van toepassing."};var W={"chatbot.disclosure.default":"Rozmawiasz z asystentem AI.","chatbot.disclosure.prominent":"Ten czat jest obs\u0142ugiwany przez sztuczn\u0105 inteligencj\u0119.","content.label.default":"Tre\u015B\u0107 wygenerowana przez AI","content.label.artistic":"Praca tw\xF3rcza wspierana przez AI","deepfake.label.default":"Obraz wygenerowany przez AI","deepfake.label.artistic":"Artystyczne tre\u015Bci wygenerowane przez AI","biometric.notice.default":"Ten system przetwarza dane biometryczne. Stosuje si\u0119 art. 50 \xA73 Ustawy UE o AI."};var H={"chatbot.disclosure.default":"Voc\xEA est\xE1 falando com um assistente de IA.","chatbot.disclosure.prominent":"Este chat \xE9 alimentado por intelig\xEAncia artificial.","content.label.default":"Conte\xFAdo gerado por IA","content.label.artistic":"Trabalho criativo assistido por IA","deepfake.label.default":"Imagem gerada por IA","deepfake.label.artistic":"Conte\xFAdo art\xEDstico gerado por IA","biometric.notice.default":"Este sistema processa dados biom\xE9tricos. Aplica-se o Artigo 50 \xA73 da Lei de IA da UE."};var q={"chatbot.disclosure.default":"Vorbi\u021Bi cu un asistent AI.","chatbot.disclosure.prominent":"Acest chat este alimentat de inteligen\u021Ba artificial\u0103.","content.label.default":"Con\u021Binut generat de AI","content.label.artistic":"Lucrare creativ\u0103 asistat\u0103 de AI","deepfake.label.default":"Imagine generat\u0103 de AI","deepfake.label.artistic":"Con\u021Binut artistic generat de AI","biometric.notice.default":"Acest sistem proceseaz\u0103 date biometrice. Se aplic\u0103 Articolul 50 \xA73 din Legea AI a UE."};var F={"chatbot.disclosure.default":"Hovor\xEDte s AI asistentom.","chatbot.disclosure.prominent":"Tento chat je poh\xE1\u0148an\xFD umelou inteligenciou.","content.label.default":"Obsah generovan\xFD AI","content.label.artistic":"Kreat\xEDvna pr\xE1ca s pomocou AI","deepfake.label.default":"Obr\xE1zok generovan\xFD AI","deepfake.label.artistic":"Umeleck\xFD obsah generovan\xFD AI","biometric.notice.default":"Tento syst\xE9m sprac\xFAva biometrick\xE9 \xFAdaje. Plat\xED \u010Dl. 50 \xA73 z\xE1kona E\xDA o AI."};var G={"chatbot.disclosure.default":"Pogovarjate se z AI pomo\u010Dnikom.","chatbot.disclosure.prominent":"Ta klepet poganja umetna inteligenca.","content.label.default":"Vsebina, ustvarjena z AI","content.label.artistic":"Ustvarjalno delo s pomo\u010Djo AI","deepfake.label.default":"Slika, ustvarjena z AI","deepfake.label.artistic":"Umetni\u0161ka vsebina, ustvarjena z AI","biometric.notice.default":"Ta sistem obdeluje biometri\u010Dne podatke. Velja \u010Clen 50 \xA73 Zakona EU o AI."};var J={"chatbot.disclosure.default":"Du pratar med en AI-assistent.","chatbot.disclosure.prominent":"Denna chatt drivs av artificiell intelligens.","content.label.default":"AI-genererat inneh\xE5ll","content.label.artistic":"AI-assisterat kreativt arbete","deepfake.label.default":"AI-genererad bild","deepfake.label.artistic":"AI-genererat konstn\xE4rligt inneh\xE5ll","biometric.notice.default":"Detta system behandlar biometriska uppgifter. EU AI-lagens artikel 50 \xA73 \xE4r till\xE4mplig."};var C=["bg","hr","cs","da","de","el","en","es","et","fi","fr","ga","hu","it","lv","lt","mt","nl","pl","pt","ro","sk","sl","sv"],Q={bg:z,hr:w,cs:j,da:T,de:D,el:N,en:E,es:L,et:O,fi:P,fr:S,ga:M,hu:U,it:V,lv:R,lt:_,mt:B,nl:K,pl:W,pt:H,ro:q,sk:F,sl:G,sv:J};function Z(){if(typeof navigator>"u")return"en";let t=navigator.language;if(C.includes(t))return t;let e=t.split("-")[0];return e!==void 0&&C.includes(e)?e:"en"}function d(t,e){let i=e??Z(),n=Q[i][t];if(n!==void 0)return n;let s=Q.en[t];return s!==void 0?s:t}var f="0.1.0";var Ve='[data-discloai-label="ai-content"] { position: relative; display: inline-block; background: #e0e7ff; color: #3730a3; font-size: 0.75rem; padding: 2px 6px; border-radius: 3px; font-family: sans-serif; white-space: nowrap; }';function $(t,e,i){try{if(typeof document>"u")return;if(e.exemptions?.editorialControl===!0&&typeof e.exemptions?.editorialResponsibility=="string"){c({disclosureType:"AIContentLabel",componentVersion:f,sessionId:t,rendered:!1});return}let a=e.selector??'[data-discloai-content="true"]',n=Array.from(document.querySelectorAll(a)),s=e.variant==="artistic"?d("content.label.artistic"):d("content.label.default");for(let l of n){if(l.hasAttribute("data-discloai-labeled"))continue;let o=document.createElement("span");o.textContent=s,o.setAttribute("data-discloai-label","ai-content"),o.setAttribute("role","img"),o.setAttribute("aria-label",s),l.parentNode&&l.parentNode.insertBefore(o,l),l.setAttribute("data-discloai-labeled","true")}let r=document.createElement("style");if(r.textContent=Ve,i&&r.setAttribute("nonce",i),document.head.appendChild(r),e.customCSS){let l=u(e.customCSS);if(l){let o=document.createElement("style");o.textContent=l,i&&o.setAttribute("nonce",i),document.head.appendChild(o)}}c({disclosureType:"AIContentLabel",componentVersion:f,sessionId:t,rendered:!0})}catch{}}function Re(t,e,i,a){if(!t.parentNode)return;let n=document.createElement("div");n.style.cssText="position: relative; display: inline-block;",t.parentNode.insertBefore(n,t),n.appendChild(t);let s=document.createElement("div");s.textContent=e,s.setAttribute("role","img"),s.setAttribute("aria-label",e),s.style.cssText="position: absolute; top: "+i+"; left: "+a+"; z-index: 2147483647; background: rgba(0,0,0,0.7); color: #fff; font-size: 0.75rem; padding: 2px 8px; border-radius: 3px; pointer-events: none;",n.appendChild(s)}function X(t,e,i){try{if(typeof document>"u")return;let n=e.exemptions?.artisticContext===!0||e.variant==="artistic"?d("deepfake.label.artistic"):d("deepfake.label.default"),s;e.selector?s=Array.from(document.querySelectorAll(e.selector)).filter(r=>r.tagName==="VIDEO"||r.tagName==="IMG"):s=[...Array.from(document.querySelectorAll("video")),...Array.from(document.querySelectorAll("img"))];for(let r of s){if(r.hasAttribute("data-discloai-labeled"))continue;let o=r.tagName==="VIDEO"?"8px":"4px";Re(r,n,o,o),r.setAttribute("data-discloai-labeled","true")}if(e.customCSS){let r=u(e.customCSS);if(r){let l=document.createElement("style");l.textContent=r,i&&l.setAttribute("nonce",i),document.head.appendChild(l)}}c({disclosureType:"DeepfakeLabel",componentVersion:f,sessionId:t,rendered:!0})}catch{}}var _e={intercom:"#intercom-container",crisp:"#crisp-chatbox",tidio:"#tidio-chat",zendesk:"#launcher",drift:"#drift-frame-controller",livechat:"#chat-widget-container"};function Y(t,e,i){try{if(typeof document>"u")return;let a=`discloai:${t}:ChatbotDisclosure:seen`;try{if(typeof sessionStorage<"u"&&sessionStorage.getItem(a)==="true")return}catch{}if(e.exemptions?.obviousContext===!0){console.info("[DiscloAI] ChatbotDisclosure: obviousContext exemption applied"),c({disclosureType:"ChatbotDisclosure",componentVersion:f,sessionId:t,rendered:!1});return}let n=()=>{let o=document.createElement("div");o.setAttribute("role","alert"),o.setAttribute("aria-live","polite"),o.style.cssText="position: fixed; bottom: 80px; right: 24px; z-index: 2147483647; background: #fff; border: 1px solid #c7d2fe; border-radius: 8px; padding: 12px 16px; max-width: 320px; box-shadow: 0 4px 16px rgba(0,0,0,0.12); font-family: sans-serif;";let p=e.variant==="prominent"?d("chatbot.disclosure.prominent"):d("chatbot.disclosure.default"),b=document.createElement("p");b.textContent=p,o.appendChild(b);let m=()=>{o.parentNode&&o.parentNode.removeChild(o);try{typeof sessionStorage<"u"&&sessionStorage.setItem(a,"true")}catch{}},h=document.createElement("button");if(h.textContent="\u2715",h.addEventListener("click",m),o.appendChild(h),e.customCSS){let A=u(e.customCSS);if(A){let I=document.createElement("style");I.textContent=A,i&&I.setAttribute("nonce",i),document.head.appendChild(I)}}document.body.appendChild(o),setTimeout(m,8e3),c({disclosureType:"ChatbotDisclosure",componentVersion:f,sessionId:t,rendered:!0})};if((e.triggerEvent??"on-load")==="on-load"){n();return}let r="";if(e.vendor?r=_e[e.vendor]??"":e.selector&&(r=e.selector),!r){n();return}let l=new MutationObserver(()=>{let o=document.querySelector(r);if(o){let p=window.getComputedStyle(o);p.display!=="none"&&p.visibility!=="hidden"&&(l.disconnect(),n())}});l.observe(document.body,{childList:!0,subtree:!0,attributes:!0})}catch{}}function ee(t,e,i){try{if(typeof document>"u")return;let a=document.createElement("div");a.setAttribute("role","alert"),a.setAttribute("aria-live","assertive"),a.style.cssText="position: fixed; top: 0; left: 0; right: 0; z-index: 2147483647; background: #fef3c7; border-bottom: 2px solid #f59e0b; padding: 12px 24px; font-family: sans-serif; font-size: 0.875rem; text-align: center;";let n=d("biometric.notice.default"),s=document.createElement("span");s.textContent=n,a.appendChild(s);let r=document.createElement("button");if(r.textContent="\u2715",r.addEventListener("click",()=>{a.parentNode&&a.parentNode.removeChild(a)}),a.appendChild(r),e.customCSS){let l=u(e.customCSS);if(l){let o=document.createElement("style");o.textContent=l,i&&o.setAttribute("nonce",i),document.head.appendChild(o)}}document.body.appendChild(a),c({disclosureType:"BiometricNotice",componentVersion:f,sessionId:t,rendered:!0})}catch{}}function te(t){if(!t?.siteId){console.warn("[DiscloAI] siteId is required");return}let e=t.cspNonce;v(t).then(i=>{Be(i,e)})}function Be(t,e){let{siteId:i}=t;t.aiContentLabel?.enabled!==!1&&$(i,t.aiContentLabel??{},e),t.deepfakeLabel?.enabled!==!1&&X(i,t.deepfakeLabel??{},e),t.chatbotDisclosure?.enabled!==!1&&Y(i,t.chatbotDisclosure??{},e),t.biometricNotice?.enabled!==!1&&ee(i,t.biometricNotice??{},e)}var g={intercom:"#intercom-frame, .intercom-launcher",crisp:".crisp-client, #crisp-chatbox",tidio:"#tidio-chat, #tidio-chat-iframe",zendesk:"#launcher, .zEWidget-launcher",drift:"#drift-widget, .drift-widget-welcome",livechat:"#chat-widget-container, .livechat-widget"};function ie(t){return t in g?g[t]:null}return le(Ke);})();
@@ -0,0 +1,152 @@
1
+ interface ChatbotDisclosureConfig {
2
+ enabled?: boolean;
3
+ /** CSS selector for the target chatbot widget element */
4
+ selector?: string;
5
+ /** One of the 6 built-in vendor presets */
6
+ vendor?: "intercom" | "crisp" | "tidio" | "zendesk" | "drift" | "livechat";
7
+ /** When to show the disclosure: on page load or when the chat widget opens */
8
+ triggerEvent?: "on-load" | "on-open";
9
+ /** Style variant */
10
+ variant?: "minimal" | "prominent" | "custom";
11
+ /** Custom CSS string — sanitized before injection; url(), @import, expression(), javascript: are blocked */
12
+ customCSS?: string;
13
+ exemptions?: {
14
+ /** Suppress disclosure when AI use is already obvious from context */
15
+ obviousContext?: boolean;
16
+ /** Suppress for editorial/creative AI use */
17
+ editorialControl?: boolean;
18
+ };
19
+ }
20
+
21
+ interface AIContentLabelConfig {
22
+ enabled?: boolean;
23
+ variant?: "default" | "artistic";
24
+ /** CSS selector for elements to label */
25
+ selector?: string;
26
+ /** Custom CSS string — sanitized before injection */
27
+ customCSS?: string;
28
+ exemptions?: {
29
+ /** Both editorialControl AND editorialResponsibility must be present to suppress rendering */
30
+ editorialControl?: boolean;
31
+ editorialResponsibility?: string;
32
+ };
33
+ }
34
+
35
+ interface DeepfakeLabelConfig {
36
+ enabled?: boolean;
37
+ variant?: "default" | "artistic";
38
+ /** CSS selector for synthetic media elements to label */
39
+ selector?: string;
40
+ /** Custom CSS string — sanitized before injection */
41
+ customCSS?: string;
42
+ exemptions?: {
43
+ /** Uses artistic label text but still renders — does NOT suppress disclosure */
44
+ artisticContext?: boolean;
45
+ };
46
+ }
47
+
48
+ interface BiometricNoticeConfig {
49
+ enabled?: boolean;
50
+ /** CSS selector for the element that triggers the biometric system */
51
+ selector?: string;
52
+ /** Custom CSS string — sanitized before injection */
53
+ customCSS?: string;
54
+ }
55
+
56
+ interface DiscloAIInitOptions {
57
+ /** Public site identifier. Not a secret — safe to log and include in requests. */
58
+ siteId: string;
59
+ /** CSP nonce to apply to all injected <style> elements. */
60
+ cspNonce?: string;
61
+ /** BCP-47 locale override. Falls back to navigator.language then 'en'. */
62
+ locale?: string;
63
+ /** Override config fetch URL for local dev. Only https:// or http://localhost accepted. */
64
+ configEndpoint?: string;
65
+ chatbotDisclosure?: ChatbotDisclosureConfig;
66
+ aiContentLabel?: AIContentLabelConfig;
67
+ deepfakeLabel?: DeepfakeLabelConfig;
68
+ biometricNotice?: BiometricNoticeConfig;
69
+ }
70
+ interface DiscloAIConfig {
71
+ siteId: string;
72
+ locale: string;
73
+ chatbotDisclosure?: ChatbotDisclosureConfig;
74
+ aiContentLabel?: AIContentLabelConfig;
75
+ deepfakeLabel?: DeepfakeLabelConfig;
76
+ biometricNotice?: BiometricNoticeConfig;
77
+ }
78
+ /**
79
+ * Sanitize a custom CSS string.
80
+ * Returns an empty string and emits a warning if any forbidden pattern is found.
81
+ * Forbidden: url(...), @import, expression(...), javascript:
82
+ *
83
+ * M-1 fix: CSS hex escape sequences (e.g. \75rl() === url()) are normalized
84
+ * before pattern matching to prevent bypass via Unicode escapes.
85
+ */
86
+ declare function sanitizeCSS(css: string): string;
87
+
88
+ /**
89
+ * Initialise DiscloAI EU AI Act Article 50 disclosure widgets.
90
+ *
91
+ * Non-blocking contract:
92
+ * - init() itself is synchronous and returns immediately
93
+ * - Config resolution (remote fetch + merge) is fire-and-forget
94
+ * - Component injection is always deferred — never blocks DOMContentLoaded
95
+ *
96
+ * @param options - Init options including siteId and per-component config
97
+ */
98
+ declare function init(options?: Partial<DiscloAIInitOptions>): void;
99
+
100
+ /**
101
+ * Built-in chatbot vendor selector presets.
102
+ * Used by ChatbotDisclosure to locate the widget element automatically.
103
+ * When no selector matches, the component falls back to global banner mode.
104
+ */
105
+ declare const VENDOR_PRESETS: {
106
+ readonly intercom: "#intercom-frame, .intercom-launcher";
107
+ readonly crisp: ".crisp-client, #crisp-chatbox";
108
+ readonly tidio: "#tidio-chat, #tidio-chat-iframe";
109
+ readonly zendesk: "#launcher, .zEWidget-launcher";
110
+ readonly drift: "#drift-widget, .drift-widget-welcome";
111
+ readonly livechat: "#chat-widget-container, .livechat-widget";
112
+ };
113
+ type VendorPreset = keyof typeof VENDOR_PRESETS;
114
+ /**
115
+ * Resolve the CSS selector for a given vendor preset key.
116
+ * Returns null if the key is not a known preset.
117
+ *
118
+ * SECURITY: The returned selector string must never be passed to eval() or
119
+ * used in a way that allows script injection. Use only with querySelector().
120
+ */
121
+ declare function resolveVendorSelector(vendor: string): string | null;
122
+
123
+ interface AuditEventParams {
124
+ /** One of the four EU AI Act Article 50 disclosure types */
125
+ disclosureType: string;
126
+ /** Semver string of the component that fired the event */
127
+ componentVersion: string;
128
+ /** Caller-supplied session identifier — will be hashed before sending */
129
+ sessionId: string;
130
+ /** Whether the disclosure was rendered to the user (default: true) */
131
+ rendered?: boolean;
132
+ }
133
+ interface DiscloAIWindowConfig {
134
+ siteId?: string;
135
+ auditEndpoint?: string;
136
+ /** Override the config fetch URL — only http://localhost or https:// are accepted. */
137
+ configEndpoint?: string;
138
+ }
139
+ declare global {
140
+ interface Window {
141
+ __DISCLOAI_CONFIG__?: DiscloAIWindowConfig;
142
+ }
143
+ }
144
+ /**
145
+ * Fire-and-forget audit event.
146
+ *
147
+ * Uses navigator.sendBeacon (primary) or fetch({ keepalive: true }) (fallback).
148
+ * Never throws — any error is caught and silently swallowed.
149
+ */
150
+ declare function sendAuditEvent(params: AuditEventParams): Promise<void>;
151
+
152
+ export { type AIContentLabelConfig, type AuditEventParams, type BiometricNoticeConfig, type ChatbotDisclosureConfig, type DeepfakeLabelConfig, type DiscloAIConfig, type DiscloAIInitOptions, VENDOR_PRESETS, type VendorPreset, init, resolveVendorSelector, sanitizeCSS, sendAuditEvent };
@@ -0,0 +1,152 @@
1
+ interface ChatbotDisclosureConfig {
2
+ enabled?: boolean;
3
+ /** CSS selector for the target chatbot widget element */
4
+ selector?: string;
5
+ /** One of the 6 built-in vendor presets */
6
+ vendor?: "intercom" | "crisp" | "tidio" | "zendesk" | "drift" | "livechat";
7
+ /** When to show the disclosure: on page load or when the chat widget opens */
8
+ triggerEvent?: "on-load" | "on-open";
9
+ /** Style variant */
10
+ variant?: "minimal" | "prominent" | "custom";
11
+ /** Custom CSS string — sanitized before injection; url(), @import, expression(), javascript: are blocked */
12
+ customCSS?: string;
13
+ exemptions?: {
14
+ /** Suppress disclosure when AI use is already obvious from context */
15
+ obviousContext?: boolean;
16
+ /** Suppress for editorial/creative AI use */
17
+ editorialControl?: boolean;
18
+ };
19
+ }
20
+
21
+ interface AIContentLabelConfig {
22
+ enabled?: boolean;
23
+ variant?: "default" | "artistic";
24
+ /** CSS selector for elements to label */
25
+ selector?: string;
26
+ /** Custom CSS string — sanitized before injection */
27
+ customCSS?: string;
28
+ exemptions?: {
29
+ /** Both editorialControl AND editorialResponsibility must be present to suppress rendering */
30
+ editorialControl?: boolean;
31
+ editorialResponsibility?: string;
32
+ };
33
+ }
34
+
35
+ interface DeepfakeLabelConfig {
36
+ enabled?: boolean;
37
+ variant?: "default" | "artistic";
38
+ /** CSS selector for synthetic media elements to label */
39
+ selector?: string;
40
+ /** Custom CSS string — sanitized before injection */
41
+ customCSS?: string;
42
+ exemptions?: {
43
+ /** Uses artistic label text but still renders — does NOT suppress disclosure */
44
+ artisticContext?: boolean;
45
+ };
46
+ }
47
+
48
+ interface BiometricNoticeConfig {
49
+ enabled?: boolean;
50
+ /** CSS selector for the element that triggers the biometric system */
51
+ selector?: string;
52
+ /** Custom CSS string — sanitized before injection */
53
+ customCSS?: string;
54
+ }
55
+
56
+ interface DiscloAIInitOptions {
57
+ /** Public site identifier. Not a secret — safe to log and include in requests. */
58
+ siteId: string;
59
+ /** CSP nonce to apply to all injected <style> elements. */
60
+ cspNonce?: string;
61
+ /** BCP-47 locale override. Falls back to navigator.language then 'en'. */
62
+ locale?: string;
63
+ /** Override config fetch URL for local dev. Only https:// or http://localhost accepted. */
64
+ configEndpoint?: string;
65
+ chatbotDisclosure?: ChatbotDisclosureConfig;
66
+ aiContentLabel?: AIContentLabelConfig;
67
+ deepfakeLabel?: DeepfakeLabelConfig;
68
+ biometricNotice?: BiometricNoticeConfig;
69
+ }
70
+ interface DiscloAIConfig {
71
+ siteId: string;
72
+ locale: string;
73
+ chatbotDisclosure?: ChatbotDisclosureConfig;
74
+ aiContentLabel?: AIContentLabelConfig;
75
+ deepfakeLabel?: DeepfakeLabelConfig;
76
+ biometricNotice?: BiometricNoticeConfig;
77
+ }
78
+ /**
79
+ * Sanitize a custom CSS string.
80
+ * Returns an empty string and emits a warning if any forbidden pattern is found.
81
+ * Forbidden: url(...), @import, expression(...), javascript:
82
+ *
83
+ * M-1 fix: CSS hex escape sequences (e.g. \75rl() === url()) are normalized
84
+ * before pattern matching to prevent bypass via Unicode escapes.
85
+ */
86
+ declare function sanitizeCSS(css: string): string;
87
+
88
+ /**
89
+ * Initialise DiscloAI EU AI Act Article 50 disclosure widgets.
90
+ *
91
+ * Non-blocking contract:
92
+ * - init() itself is synchronous and returns immediately
93
+ * - Config resolution (remote fetch + merge) is fire-and-forget
94
+ * - Component injection is always deferred — never blocks DOMContentLoaded
95
+ *
96
+ * @param options - Init options including siteId and per-component config
97
+ */
98
+ declare function init(options?: Partial<DiscloAIInitOptions>): void;
99
+
100
+ /**
101
+ * Built-in chatbot vendor selector presets.
102
+ * Used by ChatbotDisclosure to locate the widget element automatically.
103
+ * When no selector matches, the component falls back to global banner mode.
104
+ */
105
+ declare const VENDOR_PRESETS: {
106
+ readonly intercom: "#intercom-frame, .intercom-launcher";
107
+ readonly crisp: ".crisp-client, #crisp-chatbox";
108
+ readonly tidio: "#tidio-chat, #tidio-chat-iframe";
109
+ readonly zendesk: "#launcher, .zEWidget-launcher";
110
+ readonly drift: "#drift-widget, .drift-widget-welcome";
111
+ readonly livechat: "#chat-widget-container, .livechat-widget";
112
+ };
113
+ type VendorPreset = keyof typeof VENDOR_PRESETS;
114
+ /**
115
+ * Resolve the CSS selector for a given vendor preset key.
116
+ * Returns null if the key is not a known preset.
117
+ *
118
+ * SECURITY: The returned selector string must never be passed to eval() or
119
+ * used in a way that allows script injection. Use only with querySelector().
120
+ */
121
+ declare function resolveVendorSelector(vendor: string): string | null;
122
+
123
+ interface AuditEventParams {
124
+ /** One of the four EU AI Act Article 50 disclosure types */
125
+ disclosureType: string;
126
+ /** Semver string of the component that fired the event */
127
+ componentVersion: string;
128
+ /** Caller-supplied session identifier — will be hashed before sending */
129
+ sessionId: string;
130
+ /** Whether the disclosure was rendered to the user (default: true) */
131
+ rendered?: boolean;
132
+ }
133
+ interface DiscloAIWindowConfig {
134
+ siteId?: string;
135
+ auditEndpoint?: string;
136
+ /** Override the config fetch URL — only http://localhost or https:// are accepted. */
137
+ configEndpoint?: string;
138
+ }
139
+ declare global {
140
+ interface Window {
141
+ __DISCLOAI_CONFIG__?: DiscloAIWindowConfig;
142
+ }
143
+ }
144
+ /**
145
+ * Fire-and-forget audit event.
146
+ *
147
+ * Uses navigator.sendBeacon (primary) or fetch({ keepalive: true }) (fallback).
148
+ * Never throws — any error is caught and silently swallowed.
149
+ */
150
+ declare function sendAuditEvent(params: AuditEventParams): Promise<void>;
151
+
152
+ export { type AIContentLabelConfig, type AuditEventParams, type BiometricNoticeConfig, type ChatbotDisclosureConfig, type DeepfakeLabelConfig, type DiscloAIConfig, type DiscloAIInitOptions, VENDOR_PRESETS, type VendorPreset, init, resolveVendorSelector, sanitizeCSS, sendAuditEvent };