@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.
- package/LICENSE +21 -0
- package/README.md +218 -0
- package/dist/discloai.min.js +1 -0
- package/dist/index.d.mts +152 -0
- package/dist/index.d.ts +152 -0
- package/dist/index.js +795 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +764 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +58 -0
- package/src/__tests__/audit.test.ts +117 -0
- package/src/__tests__/init.test.ts +49 -0
- package/src/__tests__/wcag.test.ts +260 -0
- package/src/audit.ts +155 -0
- package/src/components/AIContentLabel.ts +108 -0
- package/src/components/BiometricNotice.ts +82 -0
- package/src/components/ChatbotDisclosure.ts +188 -0
- package/src/components/DeepfakeLabel.ts +123 -0
- package/src/config.ts +191 -0
- package/src/i18n/bg.json +9 -0
- package/src/i18n/cs.json +9 -0
- package/src/i18n/da.json +9 -0
- package/src/i18n/de.json +9 -0
- package/src/i18n/el.json +9 -0
- package/src/i18n/en.json +9 -0
- package/src/i18n/es.json +9 -0
- package/src/i18n/et.json +9 -0
- package/src/i18n/fi.json +9 -0
- package/src/i18n/fr.json +9 -0
- package/src/i18n/ga.json +9 -0
- package/src/i18n/hr.json +9 -0
- package/src/i18n/hu.json +9 -0
- package/src/i18n/index.ts +145 -0
- package/src/i18n/it.json +9 -0
- package/src/i18n/lt.json +9 -0
- package/src/i18n/lv.json +9 -0
- package/src/i18n/mt.json +9 -0
- package/src/i18n/nl.json +9 -0
- package/src/i18n/pl.json +9 -0
- package/src/i18n/pt.json +9 -0
- package/src/i18n/ro.json +9 -0
- package/src/i18n/sk.json +9 -0
- package/src/i18n/sl.json +9 -0
- package/src/i18n/sv.json +9 -0
- package/src/index.ts +19 -0
- package/src/init.ts +56 -0
- package/src/vendors.ts +29 -0
- package/src/version.ts +1 -0
- 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
|
+
[](https://www.npmjs.com/package/@discloai/core)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
[](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);})();
|
package/dist/index.d.mts
ADDED
|
@@ -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 };
|
package/dist/index.d.ts
ADDED
|
@@ -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 };
|