@sweberdev/witness 0.0.0-stage → 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 +21 -2
- package/dist/ai-sdk.cjs +921 -0
- package/dist/ai-sdk.cjs.map +1 -0
- package/dist/ai-sdk.d.cts +95 -0
- package/dist/ai-sdk.d.ts +95 -0
- package/dist/ai-sdk.js +150 -0
- package/dist/ai-sdk.js.map +1 -0
- package/dist/bin.cjs +944 -0
- package/dist/bin.cjs.map +1 -0
- package/dist/bin.d.cts +1 -0
- package/dist/bin.d.ts +1 -0
- package/dist/bin.js +12 -0
- package/dist/bin.js.map +1 -0
- package/dist/chunk-APXU3MTX.js +958 -0
- package/dist/chunk-APXU3MTX.js.map +1 -0
- package/dist/chunk-DHX7SNWE.js +310 -0
- package/dist/chunk-DHX7SNWE.js.map +1 -0
- package/dist/chunk-L65R2SM5.js +228 -0
- package/dist/chunk-L65R2SM5.js.map +1 -0
- package/dist/chunk-UBRK4CTM.js +156 -0
- package/dist/chunk-UBRK4CTM.js.map +1 -0
- package/dist/chunk-XJOQ5IUT.js +110 -0
- package/dist/chunk-XJOQ5IUT.js.map +1 -0
- package/dist/cli.cjs +964 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/cli.d.cts +7 -0
- package/dist/cli.d.ts +7 -0
- package/dist/cli.js +9 -0
- package/dist/cli.js.map +1 -0
- package/dist/disclosure-5a3UIVQZ.d.cts +70 -0
- package/dist/disclosure-C7Y_uY73.d.ts +70 -0
- package/dist/elements-auto.cjs +642 -0
- package/dist/elements-auto.cjs.map +1 -0
- package/dist/elements-auto.d.cts +3 -0
- package/dist/elements-auto.d.ts +3 -0
- package/dist/elements-auto.js +16 -0
- package/dist/elements-auto.js.map +1 -0
- package/dist/elements.cjs +639 -0
- package/dist/elements.cjs.map +1 -0
- package/dist/elements.d.cts +47 -0
- package/dist/elements.d.ts +47 -0
- package/dist/elements.js +13 -0
- package/dist/elements.js.map +1 -0
- package/dist/image-B7YvdITx.d.ts +49 -0
- package/dist/image-BNrEO-hj.d.cts +49 -0
- package/dist/index.cjs +1344 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +124 -0
- package/dist/index.d.ts +124 -0
- package/dist/index.js +77 -0
- package/dist/index.js.map +1 -0
- package/dist/types-WWHzrNlc.d.cts +90 -0
- package/dist/types-WWHzrNlc.d.ts +90 -0
- package/package.json +123 -3
- package/src/ai-sdk.ts +252 -0
- package/src/bin.ts +6 -0
- package/src/cli.ts +160 -0
- package/src/disclosure.ts +172 -0
- package/src/elements-auto.ts +5 -0
- package/src/elements.ts +356 -0
- package/src/i18n.ts +259 -0
- package/src/image.ts +552 -0
- package/src/index.ts +66 -0
- package/src/inflate.ts +189 -0
- package/src/marking.ts +237 -0
- package/src/types.ts +115 -0
- package/src/watermark.ts +165 -0
- package/styles.css +103 -0
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
// src/i18n.ts
|
|
2
|
+
var en = {
|
|
3
|
+
kinds: {
|
|
4
|
+
chatbot: {
|
|
5
|
+
label: "AI assistant",
|
|
6
|
+
title: "You are chatting with an AI",
|
|
7
|
+
body: "This assistant is an AI system, not a person. Its answers are generated automatically and can be wrong."
|
|
8
|
+
},
|
|
9
|
+
generated: {
|
|
10
|
+
label: "AI-generated",
|
|
11
|
+
title: "AI-generated content",
|
|
12
|
+
body: "This content was generated by an AI system."
|
|
13
|
+
},
|
|
14
|
+
edited: {
|
|
15
|
+
label: "AI-edited",
|
|
16
|
+
title: "Edited with AI",
|
|
17
|
+
body: "This content was edited or altered with an AI system."
|
|
18
|
+
},
|
|
19
|
+
deepfake: {
|
|
20
|
+
label: "Artificially generated",
|
|
21
|
+
title: "Artificially generated or manipulated",
|
|
22
|
+
body: "This content was generated or manipulated with AI. It may show people, places or events differently from how they really were."
|
|
23
|
+
},
|
|
24
|
+
"emotion-recognition": {
|
|
25
|
+
label: "Emotion recognition",
|
|
26
|
+
title: "Emotion recognition is active",
|
|
27
|
+
body: "This service uses an AI system that recognises emotions or intentions from your data."
|
|
28
|
+
},
|
|
29
|
+
"biometric-categorisation": {
|
|
30
|
+
label: "Biometric categorisation",
|
|
31
|
+
title: "Biometric categorisation is active",
|
|
32
|
+
body: "This service uses an AI system that assigns people to categories based on biometric data."
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
ui: {
|
|
36
|
+
acknowledge: "Got it",
|
|
37
|
+
details: "About this label",
|
|
38
|
+
generatedWith: "Generated with {generator}",
|
|
39
|
+
createdOn: "Created on {date}",
|
|
40
|
+
humanReviewed: "Reviewed by a person",
|
|
41
|
+
moreInfo: "How we use AI",
|
|
42
|
+
close: "Close"
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
var de = {
|
|
46
|
+
kinds: {
|
|
47
|
+
chatbot: {
|
|
48
|
+
label: "KI-Assistent",
|
|
49
|
+
title: "Sie chatten mit einer KI",
|
|
50
|
+
body: "Dieser Assistent ist ein KI-System und kein Mensch. Seine Antworten werden automatisch erzeugt und k\xF6nnen falsch sein."
|
|
51
|
+
},
|
|
52
|
+
generated: {
|
|
53
|
+
label: "KI-generiert",
|
|
54
|
+
title: "KI-generierter Inhalt",
|
|
55
|
+
body: "Dieser Inhalt wurde von einem KI-System erzeugt."
|
|
56
|
+
},
|
|
57
|
+
edited: {
|
|
58
|
+
label: "Mit KI bearbeitet",
|
|
59
|
+
title: "Mit KI bearbeitet",
|
|
60
|
+
body: "Dieser Inhalt wurde mit einem KI-System bearbeitet oder ver\xE4ndert."
|
|
61
|
+
},
|
|
62
|
+
deepfake: {
|
|
63
|
+
label: "K\xFCnstlich erzeugt",
|
|
64
|
+
title: "K\xFCnstlich erzeugt oder ver\xE4ndert",
|
|
65
|
+
body: "Dieser Inhalt wurde mit KI erzeugt oder ver\xE4ndert. Er kann Personen, Orte oder Ereignisse anders zeigen, als sie wirklich waren."
|
|
66
|
+
},
|
|
67
|
+
"emotion-recognition": {
|
|
68
|
+
label: "Emotionserkennung",
|
|
69
|
+
title: "Emotionserkennung ist aktiv",
|
|
70
|
+
body: "Dieser Dienst setzt ein KI-System ein, das aus Ihren Daten Emotionen oder Absichten erkennt."
|
|
71
|
+
},
|
|
72
|
+
"biometric-categorisation": {
|
|
73
|
+
label: "Biometrische Kategorisierung",
|
|
74
|
+
title: "Biometrische Kategorisierung ist aktiv",
|
|
75
|
+
body: "Dieser Dienst setzt ein KI-System ein, das Personen anhand biometrischer Daten Kategorien zuordnet."
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
ui: {
|
|
79
|
+
acknowledge: "Verstanden",
|
|
80
|
+
details: "\xDCber diese Kennzeichnung",
|
|
81
|
+
generatedWith: "Erzeugt mit {generator}",
|
|
82
|
+
createdOn: "Erstellt am {date}",
|
|
83
|
+
humanReviewed: "Von einem Menschen gepr\xFCft",
|
|
84
|
+
moreInfo: "Wie wir KI einsetzen",
|
|
85
|
+
close: "Schliessen"
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
var fr = {
|
|
89
|
+
kinds: {
|
|
90
|
+
chatbot: {
|
|
91
|
+
label: "Assistant IA",
|
|
92
|
+
title: "Vous discutez avec une IA",
|
|
93
|
+
body: "Cet assistant est un syst\xE8me d\u2019IA, pas une personne. Ses r\xE9ponses sont g\xE9n\xE9r\xE9es automatiquement et peuvent \xEAtre erron\xE9es."
|
|
94
|
+
},
|
|
95
|
+
generated: {
|
|
96
|
+
label: "G\xE9n\xE9r\xE9 par IA",
|
|
97
|
+
title: "Contenu g\xE9n\xE9r\xE9 par IA",
|
|
98
|
+
body: "Ce contenu a \xE9t\xE9 g\xE9n\xE9r\xE9 par un syst\xE8me d\u2019IA."
|
|
99
|
+
},
|
|
100
|
+
edited: {
|
|
101
|
+
label: "Modifi\xE9 par IA",
|
|
102
|
+
title: "Modifi\xE9 avec l\u2019IA",
|
|
103
|
+
body: "Ce contenu a \xE9t\xE9 modifi\xE9 ou alt\xE9r\xE9 \xE0 l\u2019aide d\u2019un syst\xE8me d\u2019IA."
|
|
104
|
+
},
|
|
105
|
+
deepfake: {
|
|
106
|
+
label: "G\xE9n\xE9r\xE9 artificiellement",
|
|
107
|
+
title: "G\xE9n\xE9r\xE9 ou manipul\xE9 artificiellement",
|
|
108
|
+
body: "Ce contenu a \xE9t\xE9 g\xE9n\xE9r\xE9 ou manipul\xE9 avec l\u2019IA. Il peut montrer des personnes, des lieux ou des \xE9v\xE9nements autrement qu\u2019ils n\u2019ont r\xE9ellement \xE9t\xE9."
|
|
109
|
+
},
|
|
110
|
+
"emotion-recognition": {
|
|
111
|
+
label: "Reconnaissance des \xE9motions",
|
|
112
|
+
title: "La reconnaissance des \xE9motions est active",
|
|
113
|
+
body: "Ce service utilise un syst\xE8me d\u2019IA qui reconna\xEEt des \xE9motions ou des intentions \xE0 partir de vos donn\xE9es."
|
|
114
|
+
},
|
|
115
|
+
"biometric-categorisation": {
|
|
116
|
+
label: "Cat\xE9gorisation biom\xE9trique",
|
|
117
|
+
title: "La cat\xE9gorisation biom\xE9trique est active",
|
|
118
|
+
body: "Ce service utilise un syst\xE8me d\u2019IA qui classe les personnes dans des cat\xE9gories \xE0 partir de donn\xE9es biom\xE9triques."
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
ui: {
|
|
122
|
+
acknowledge: "Compris",
|
|
123
|
+
details: "\xC0 propos de ce label",
|
|
124
|
+
generatedWith: "G\xE9n\xE9r\xE9 avec {generator}",
|
|
125
|
+
createdOn: "Cr\xE9\xE9 le {date}",
|
|
126
|
+
humanReviewed: "V\xE9rifi\xE9 par une personne",
|
|
127
|
+
moreInfo: "Comment nous utilisons l\u2019IA",
|
|
128
|
+
close: "Fermer"
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
var it = {
|
|
132
|
+
kinds: {
|
|
133
|
+
chatbot: {
|
|
134
|
+
label: "Assistente IA",
|
|
135
|
+
title: "Stai parlando con un\u2019IA",
|
|
136
|
+
body: "Questo assistente \xE8 un sistema di IA, non una persona. Le sue risposte sono generate automaticamente e possono essere sbagliate."
|
|
137
|
+
},
|
|
138
|
+
generated: {
|
|
139
|
+
label: "Generato con IA",
|
|
140
|
+
title: "Contenuto generato con IA",
|
|
141
|
+
body: "Questo contenuto \xE8 stato generato da un sistema di IA."
|
|
142
|
+
},
|
|
143
|
+
edited: {
|
|
144
|
+
label: "Modificato con IA",
|
|
145
|
+
title: "Modificato con l\u2019IA",
|
|
146
|
+
body: "Questo contenuto \xE8 stato modificato o alterato con un sistema di IA."
|
|
147
|
+
},
|
|
148
|
+
deepfake: {
|
|
149
|
+
label: "Generato artificialmente",
|
|
150
|
+
title: "Generato o manipolato artificialmente",
|
|
151
|
+
body: "Questo contenuto \xE8 stato generato o manipolato con l\u2019IA. Pu\xF2 mostrare persone, luoghi o eventi in modo diverso da come sono stati realmente."
|
|
152
|
+
},
|
|
153
|
+
"emotion-recognition": {
|
|
154
|
+
label: "Riconoscimento delle emozioni",
|
|
155
|
+
title: "Il riconoscimento delle emozioni \xE8 attivo",
|
|
156
|
+
body: "Questo servizio utilizza un sistema di IA che riconosce emozioni o intenzioni a partire dai tuoi dati."
|
|
157
|
+
},
|
|
158
|
+
"biometric-categorisation": {
|
|
159
|
+
label: "Categorizzazione biometrica",
|
|
160
|
+
title: "La categorizzazione biometrica \xE8 attiva",
|
|
161
|
+
body: "Questo servizio utilizza un sistema di IA che assegna le persone a categorie in base a dati biometrici."
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
ui: {
|
|
165
|
+
acknowledge: "Ho capito",
|
|
166
|
+
details: "Informazioni su questa etichetta",
|
|
167
|
+
generatedWith: "Generato con {generator}",
|
|
168
|
+
createdOn: "Creato il {date}",
|
|
169
|
+
humanReviewed: "Verificato da una persona",
|
|
170
|
+
moreInfo: "Come usiamo l\u2019IA",
|
|
171
|
+
close: "Chiudi"
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
var registry = /* @__PURE__ */ new Map([
|
|
175
|
+
["en", en],
|
|
176
|
+
["de", de],
|
|
177
|
+
["fr", fr],
|
|
178
|
+
["it", it]
|
|
179
|
+
]);
|
|
180
|
+
var BUILT_IN_LOCALES = ["en", "de", "fr", "it"];
|
|
181
|
+
function registerLocale(code, messages) {
|
|
182
|
+
const key = code.toLowerCase();
|
|
183
|
+
const base = registry.get(key) ?? registry.get(key.split("-")[0] ?? "") ?? en;
|
|
184
|
+
registry.set(key, mergeMessages(base, messages));
|
|
185
|
+
}
|
|
186
|
+
function registeredLocales() {
|
|
187
|
+
return [...registry.keys()];
|
|
188
|
+
}
|
|
189
|
+
function resolveLocale(input) {
|
|
190
|
+
const tags = input == null ? [] : typeof input === "string" ? [input] : input;
|
|
191
|
+
for (const tag of tags) {
|
|
192
|
+
const lower = tag.toLowerCase();
|
|
193
|
+
if (registry.has(lower)) return lower;
|
|
194
|
+
const language = lower.split("-")[0] ?? "";
|
|
195
|
+
if (registry.has(language)) return language;
|
|
196
|
+
}
|
|
197
|
+
return "en";
|
|
198
|
+
}
|
|
199
|
+
function getMessages(locale, override) {
|
|
200
|
+
const messages = registry.get(resolveLocale(locale)) ?? en;
|
|
201
|
+
return override ? mergeMessages(messages, override) : messages;
|
|
202
|
+
}
|
|
203
|
+
function disclosureText(kind, locale, override) {
|
|
204
|
+
return getMessages(locale, override).kinds[kind];
|
|
205
|
+
}
|
|
206
|
+
function format(template, values) {
|
|
207
|
+
return template.replace(/\{(\w+)\}/g, (match, name) => values[name] ?? match);
|
|
208
|
+
}
|
|
209
|
+
function mergeMessages(base, override) {
|
|
210
|
+
const kinds = { ...base.kinds };
|
|
211
|
+
for (const [kind, text] of Object.entries(override.kinds ?? {})) {
|
|
212
|
+
const key = kind;
|
|
213
|
+
kinds[key] = { ...base.kinds[key], ...text };
|
|
214
|
+
}
|
|
215
|
+
const ui = { ...base.ui, ...override.ui ?? {} };
|
|
216
|
+
return { kinds, ui };
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export {
|
|
220
|
+
BUILT_IN_LOCALES,
|
|
221
|
+
registerLocale,
|
|
222
|
+
registeredLocales,
|
|
223
|
+
resolveLocale,
|
|
224
|
+
getMessages,
|
|
225
|
+
disclosureText,
|
|
226
|
+
format
|
|
227
|
+
};
|
|
228
|
+
//# sourceMappingURL=chunk-L65R2SM5.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/i18n.ts"],"sourcesContent":["import type {\n DisclosureKind,\n DisclosureText,\n LocaleMessages,\n LocaleOverride,\n UiText,\n} from \"./types.js\";\n\nconst en: LocaleMessages = {\n kinds: {\n chatbot: {\n label: \"AI assistant\",\n title: \"You are chatting with an AI\",\n body: \"This assistant is an AI system, not a person. Its answers are generated automatically and can be wrong.\",\n },\n generated: {\n label: \"AI-generated\",\n title: \"AI-generated content\",\n body: \"This content was generated by an AI system.\",\n },\n edited: {\n label: \"AI-edited\",\n title: \"Edited with AI\",\n body: \"This content was edited or altered with an AI system.\",\n },\n deepfake: {\n label: \"Artificially generated\",\n title: \"Artificially generated or manipulated\",\n body: \"This content was generated or manipulated with AI. It may show people, places or events differently from how they really were.\",\n },\n \"emotion-recognition\": {\n label: \"Emotion recognition\",\n title: \"Emotion recognition is active\",\n body: \"This service uses an AI system that recognises emotions or intentions from your data.\",\n },\n \"biometric-categorisation\": {\n label: \"Biometric categorisation\",\n title: \"Biometric categorisation is active\",\n body: \"This service uses an AI system that assigns people to categories based on biometric data.\",\n },\n },\n ui: {\n acknowledge: \"Got it\",\n details: \"About this label\",\n generatedWith: \"Generated with {generator}\",\n createdOn: \"Created on {date}\",\n humanReviewed: \"Reviewed by a person\",\n moreInfo: \"How we use AI\",\n close: \"Close\",\n },\n};\n\nconst de: LocaleMessages = {\n kinds: {\n chatbot: {\n label: \"KI-Assistent\",\n title: \"Sie chatten mit einer KI\",\n body: \"Dieser Assistent ist ein KI-System und kein Mensch. Seine Antworten werden automatisch erzeugt und können falsch sein.\",\n },\n generated: {\n label: \"KI-generiert\",\n title: \"KI-generierter Inhalt\",\n body: \"Dieser Inhalt wurde von einem KI-System erzeugt.\",\n },\n edited: {\n label: \"Mit KI bearbeitet\",\n title: \"Mit KI bearbeitet\",\n body: \"Dieser Inhalt wurde mit einem KI-System bearbeitet oder verändert.\",\n },\n deepfake: {\n label: \"Künstlich erzeugt\",\n title: \"Künstlich erzeugt oder verändert\",\n body: \"Dieser Inhalt wurde mit KI erzeugt oder verändert. Er kann Personen, Orte oder Ereignisse anders zeigen, als sie wirklich waren.\",\n },\n \"emotion-recognition\": {\n label: \"Emotionserkennung\",\n title: \"Emotionserkennung ist aktiv\",\n body: \"Dieser Dienst setzt ein KI-System ein, das aus Ihren Daten Emotionen oder Absichten erkennt.\",\n },\n \"biometric-categorisation\": {\n label: \"Biometrische Kategorisierung\",\n title: \"Biometrische Kategorisierung ist aktiv\",\n body: \"Dieser Dienst setzt ein KI-System ein, das Personen anhand biometrischer Daten Kategorien zuordnet.\",\n },\n },\n ui: {\n acknowledge: \"Verstanden\",\n details: \"Über diese Kennzeichnung\",\n generatedWith: \"Erzeugt mit {generator}\",\n createdOn: \"Erstellt am {date}\",\n humanReviewed: \"Von einem Menschen geprüft\",\n moreInfo: \"Wie wir KI einsetzen\",\n close: \"Schliessen\",\n },\n};\n\nconst fr: LocaleMessages = {\n kinds: {\n chatbot: {\n label: \"Assistant IA\",\n title: \"Vous discutez avec une IA\",\n body: \"Cet assistant est un système d’IA, pas une personne. Ses réponses sont générées automatiquement et peuvent être erronées.\",\n },\n generated: {\n label: \"Généré par IA\",\n title: \"Contenu généré par IA\",\n body: \"Ce contenu a été généré par un système d’IA.\",\n },\n edited: {\n label: \"Modifié par IA\",\n title: \"Modifié avec l’IA\",\n body: \"Ce contenu a été modifié ou altéré à l’aide d’un système d’IA.\",\n },\n deepfake: {\n label: \"Généré artificiellement\",\n title: \"Généré ou manipulé artificiellement\",\n body: \"Ce contenu a été généré ou manipulé avec l’IA. Il peut montrer des personnes, des lieux ou des événements autrement qu’ils n’ont réellement été.\",\n },\n \"emotion-recognition\": {\n label: \"Reconnaissance des émotions\",\n title: \"La reconnaissance des émotions est active\",\n body: \"Ce service utilise un système d’IA qui reconnaît des émotions ou des intentions à partir de vos données.\",\n },\n \"biometric-categorisation\": {\n label: \"Catégorisation biométrique\",\n title: \"La catégorisation biométrique est active\",\n body: \"Ce service utilise un système d’IA qui classe les personnes dans des catégories à partir de données biométriques.\",\n },\n },\n ui: {\n acknowledge: \"Compris\",\n details: \"À propos de ce label\",\n generatedWith: \"Généré avec {generator}\",\n createdOn: \"Créé le {date}\",\n humanReviewed: \"Vérifié par une personne\",\n moreInfo: \"Comment nous utilisons l’IA\",\n close: \"Fermer\",\n },\n};\n\nconst it: LocaleMessages = {\n kinds: {\n chatbot: {\n label: \"Assistente IA\",\n title: \"Stai parlando con un’IA\",\n body: \"Questo assistente è un sistema di IA, non una persona. Le sue risposte sono generate automaticamente e possono essere sbagliate.\",\n },\n generated: {\n label: \"Generato con IA\",\n title: \"Contenuto generato con IA\",\n body: \"Questo contenuto è stato generato da un sistema di IA.\",\n },\n edited: {\n label: \"Modificato con IA\",\n title: \"Modificato con l’IA\",\n body: \"Questo contenuto è stato modificato o alterato con un sistema di IA.\",\n },\n deepfake: {\n label: \"Generato artificialmente\",\n title: \"Generato o manipolato artificialmente\",\n body: \"Questo contenuto è stato generato o manipolato con l’IA. Può mostrare persone, luoghi o eventi in modo diverso da come sono stati realmente.\",\n },\n \"emotion-recognition\": {\n label: \"Riconoscimento delle emozioni\",\n title: \"Il riconoscimento delle emozioni è attivo\",\n body: \"Questo servizio utilizza un sistema di IA che riconosce emozioni o intenzioni a partire dai tuoi dati.\",\n },\n \"biometric-categorisation\": {\n label: \"Categorizzazione biometrica\",\n title: \"La categorizzazione biometrica è attiva\",\n body: \"Questo servizio utilizza un sistema di IA che assegna le persone a categorie in base a dati biometrici.\",\n },\n },\n ui: {\n acknowledge: \"Ho capito\",\n details: \"Informazioni su questa etichetta\",\n generatedWith: \"Generato con {generator}\",\n createdOn: \"Creato il {date}\",\n humanReviewed: \"Verificato da una persona\",\n moreInfo: \"Come usiamo l’IA\",\n close: \"Chiudi\",\n },\n};\n\nconst registry = new Map<string, LocaleMessages>([\n [\"en\", en],\n [\"de\", de],\n [\"fr\", fr],\n [\"it\", it],\n]);\n\n/** The locales bundled with the free package. */\nexport const BUILT_IN_LOCALES = [\"en\", \"de\", \"fr\", \"it\"] as const;\n\n/**\n * Adds or replaces a locale, e.g. one of the 24 EU languages from Witness Pro.\n * A partial override is merged over the existing locale of that code, or over English.\n */\nexport function registerLocale(code: string, messages: LocaleMessages | LocaleOverride): void {\n const key = code.toLowerCase();\n const base = registry.get(key) ?? registry.get(key.split(\"-\")[0] ?? \"\") ?? en;\n registry.set(key, mergeMessages(base, messages));\n}\n\n/** Codes of all registered locales. */\nexport function registeredLocales(): string[] {\n return [...registry.keys()];\n}\n\n/**\n * Picks the best registered locale for a tag such as \"de-CH\" or a list of tags such as\n * `navigator.languages`. Falls back to English.\n */\nexport function resolveLocale(input?: string | readonly string[] | null): string {\n const tags = input == null ? [] : typeof input === \"string\" ? [input] : input;\n for (const tag of tags) {\n const lower = tag.toLowerCase();\n if (registry.has(lower)) return lower;\n const language = lower.split(\"-\")[0] ?? \"\";\n if (registry.has(language)) return language;\n }\n return \"en\";\n}\n\n/** All messages of a locale, with an optional override merged in. */\nexport function getMessages(\n locale?: string | readonly string[] | null,\n override?: LocaleOverride,\n): LocaleMessages {\n const messages = registry.get(resolveLocale(locale)) ?? en;\n return override ? mergeMessages(messages, override) : messages;\n}\n\n/** Wording for one disclosure kind. */\nexport function disclosureText(\n kind: DisclosureKind,\n locale?: string | readonly string[] | null,\n override?: LocaleOverride,\n): DisclosureText {\n return getMessages(locale, override).kinds[kind];\n}\n\n/** Replaces `{name}` placeholders. Unknown placeholders stay as they are. */\nexport function format(template: string, values: Record<string, string | undefined>): string {\n return template.replace(/\\{(\\w+)\\}/g, (match, name: string) => values[name] ?? match);\n}\n\nfunction mergeMessages(\n base: LocaleMessages,\n override: LocaleMessages | LocaleOverride,\n): LocaleMessages {\n const kinds = { ...base.kinds };\n for (const [kind, text] of Object.entries(override.kinds ?? {})) {\n const key = kind as DisclosureKind;\n kinds[key] = { ...base.kinds[key], ...(text as Partial<DisclosureText>) };\n }\n const ui: UiText = { ...base.ui, ...(override.ui ?? {}) };\n return { kinds, ui };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import {
|
|
2
|
+
detectImageFormat,
|
|
3
|
+
iptcSourceType,
|
|
4
|
+
markImage,
|
|
5
|
+
readImageMarking,
|
|
6
|
+
readTextWatermark
|
|
7
|
+
} from "./chunk-APXU3MTX.js";
|
|
8
|
+
|
|
9
|
+
// src/cli.ts
|
|
10
|
+
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
11
|
+
import { basename, extname, join } from "path";
|
|
12
|
+
import { parseArgs } from "util";
|
|
13
|
+
var HELP = `witness: AI Act Article 50 marking for images and text
|
|
14
|
+
|
|
15
|
+
Usage
|
|
16
|
+
witness mark <files...> (--out <dir> | --in-place) [options]
|
|
17
|
+
witness inspect <files...> [--json] [--require]
|
|
18
|
+
|
|
19
|
+
mark options
|
|
20
|
+
--generator <name> AI system, e.g. "gpt-image-2"
|
|
21
|
+
--generator-version <v> its version
|
|
22
|
+
--kind <kind> generated (default), edited or deepfake
|
|
23
|
+
--provider <name> your company or product
|
|
24
|
+
--url <url> page that explains how you use AI
|
|
25
|
+
--description <text> free text written to dc:description
|
|
26
|
+
--overwrite-c2pa also rewrite files that carry a C2PA manifest
|
|
27
|
+
|
|
28
|
+
inspect options
|
|
29
|
+
--json one JSON object per file
|
|
30
|
+
--require exit 1 if a file carries no AI marking
|
|
31
|
+
|
|
32
|
+
Images: PNG, JPEG, WebP (XMP with IPTC Digital Source Type).
|
|
33
|
+
Text files are checked for the Witness text watermark.`;
|
|
34
|
+
var KINDS = ["generated", "edited", "deepfake"];
|
|
35
|
+
async function runCli(argv, io = defaultIo) {
|
|
36
|
+
const [command, ...rest] = argv;
|
|
37
|
+
if (!command || command === "--help" || command === "-h" || command === "help") {
|
|
38
|
+
io.out(HELP);
|
|
39
|
+
return command ? 0 : 1;
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
if (command === "mark") return await mark(rest, io);
|
|
43
|
+
if (command === "inspect") return await inspect(rest, io);
|
|
44
|
+
io.err(`Unknown command "${command}". Run "witness --help".`);
|
|
45
|
+
return 1;
|
|
46
|
+
} catch (error) {
|
|
47
|
+
io.err(error instanceof Error ? error.message : String(error));
|
|
48
|
+
return 1;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
async function mark(argv, io) {
|
|
52
|
+
const { values, positionals } = parseArgs({
|
|
53
|
+
args: argv,
|
|
54
|
+
allowPositionals: true,
|
|
55
|
+
options: {
|
|
56
|
+
out: { type: "string" },
|
|
57
|
+
"in-place": { type: "boolean" },
|
|
58
|
+
generator: { type: "string" },
|
|
59
|
+
"generator-version": { type: "string" },
|
|
60
|
+
kind: { type: "string" },
|
|
61
|
+
provider: { type: "string" },
|
|
62
|
+
url: { type: "string" },
|
|
63
|
+
description: { type: "string" },
|
|
64
|
+
"overwrite-c2pa": { type: "boolean" }
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
if (positionals.length === 0) throw new Error("witness mark: no files given.");
|
|
68
|
+
if (!values.out && !values["in-place"]) {
|
|
69
|
+
throw new Error("witness mark: pass --out <dir> or --in-place.");
|
|
70
|
+
}
|
|
71
|
+
const kind = values.kind ?? "generated";
|
|
72
|
+
if (!KINDS.includes(kind))
|
|
73
|
+
throw new Error(`witness mark: --kind must be one of ${KINDS.join(", ")}.`);
|
|
74
|
+
const input = { kind };
|
|
75
|
+
if (values.generator) input.generator = values.generator;
|
|
76
|
+
if (values["generator-version"]) input.generatorVersion = values["generator-version"];
|
|
77
|
+
if (values.provider) input.provider = values.provider;
|
|
78
|
+
if (values.url) input.disclosureUrl = values.url;
|
|
79
|
+
if (values.description) input.description = values.description;
|
|
80
|
+
if (values.out) await mkdir(values.out, { recursive: true });
|
|
81
|
+
let failed = 0;
|
|
82
|
+
for (const file of positionals) {
|
|
83
|
+
const bytes = new Uint8Array(await readFile(file));
|
|
84
|
+
const result = markImage(bytes, input, {
|
|
85
|
+
c2pa: values["overwrite-c2pa"] ? "overwrite" : "skip"
|
|
86
|
+
});
|
|
87
|
+
if (result.status === "unsupported") {
|
|
88
|
+
io.err(`${file}: not a PNG, JPEG or WebP file, skipped`);
|
|
89
|
+
failed++;
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
if (result.status === "skipped-c2pa") {
|
|
93
|
+
io.err(`${file}: carries a C2PA manifest, left unchanged (use --overwrite-c2pa to force)`);
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
const target = values.out ? join(values.out, basename(file)) : file;
|
|
97
|
+
await writeFile(target, result.bytes);
|
|
98
|
+
io.out(`${file}: marked (${result.format}) -> ${target}`);
|
|
99
|
+
}
|
|
100
|
+
return failed > 0 ? 1 : 0;
|
|
101
|
+
}
|
|
102
|
+
async function inspect(argv, io) {
|
|
103
|
+
const { values, positionals } = parseArgs({
|
|
104
|
+
args: argv,
|
|
105
|
+
allowPositionals: true,
|
|
106
|
+
options: { json: { type: "boolean" }, require: { type: "boolean" } }
|
|
107
|
+
});
|
|
108
|
+
if (positionals.length === 0) throw new Error("witness inspect: no files given.");
|
|
109
|
+
let unmarked = 0;
|
|
110
|
+
for (const file of positionals) {
|
|
111
|
+
const bytes = new Uint8Array(await readFile(file));
|
|
112
|
+
const format = detectImageFormat(bytes);
|
|
113
|
+
if (format) {
|
|
114
|
+
const info = readImageMarking(bytes);
|
|
115
|
+
const marked = info.aiGenerated || info.c2pa;
|
|
116
|
+
if (!marked) unmarked++;
|
|
117
|
+
if (values.json) {
|
|
118
|
+
const { xmp: _xmp, ...rest } = info;
|
|
119
|
+
io.out(JSON.stringify({ file, ...rest }));
|
|
120
|
+
} else {
|
|
121
|
+
const parts = [
|
|
122
|
+
`${file}: ${format}`,
|
|
123
|
+
info.sourceType ? `source type ${iptcSourceType(info.sourceType)}` : "no IPTC source type",
|
|
124
|
+
info.generator ? `generator ${info.generator}` : null,
|
|
125
|
+
info.c2pa ? "C2PA manifest present (not verified)" : null,
|
|
126
|
+
marked ? "AI-marked" : "NOT AI-marked"
|
|
127
|
+
];
|
|
128
|
+
io.out(parts.filter(Boolean).join(", "));
|
|
129
|
+
}
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
const ext = extname(file).toLowerCase();
|
|
133
|
+
const text = new TextDecoder().decode(bytes);
|
|
134
|
+
const watermark = readTextWatermark(text);
|
|
135
|
+
if (!watermark) unmarked++;
|
|
136
|
+
if (values.json) {
|
|
137
|
+
io.out(JSON.stringify({ file, format: ext.slice(1) || "text", watermark }));
|
|
138
|
+
} else {
|
|
139
|
+
io.out(
|
|
140
|
+
watermark ? `${file}: text watermark found${watermark.generator ? `, generator ${watermark.generator}` : ""}${watermark.createdAt ? `, created ${watermark.createdAt}` : ""}` : `${file}: no Witness text watermark`
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return values.require && unmarked > 0 ? 1 : 0;
|
|
145
|
+
}
|
|
146
|
+
var defaultIo = {
|
|
147
|
+
out: (line) => process.stdout.write(`${line}
|
|
148
|
+
`),
|
|
149
|
+
err: (line) => process.stderr.write(`${line}
|
|
150
|
+
`)
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
export {
|
|
154
|
+
runCli
|
|
155
|
+
};
|
|
156
|
+
//# sourceMappingURL=chunk-UBRK4CTM.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts"],"sourcesContent":["import { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { basename, extname, join } from \"node:path\";\nimport { parseArgs } from \"node:util\";\nimport { detectImageFormat, markImage, readImageMarking } from \"./image.js\";\nimport { iptcSourceType } from \"./marking.js\";\nimport type { ContentKind, MarkingInput } from \"./types.js\";\nimport { readTextWatermark } from \"./watermark.js\";\n\nexport interface CliIo {\n out: (line: string) => void;\n err: (line: string) => void;\n}\n\nconst HELP = `witness: AI Act Article 50 marking for images and text\n\nUsage\n witness mark <files...> (--out <dir> | --in-place) [options]\n witness inspect <files...> [--json] [--require]\n\nmark options\n --generator <name> AI system, e.g. \"gpt-image-2\"\n --generator-version <v> its version\n --kind <kind> generated (default), edited or deepfake\n --provider <name> your company or product\n --url <url> page that explains how you use AI\n --description <text> free text written to dc:description\n --overwrite-c2pa also rewrite files that carry a C2PA manifest\n\ninspect options\n --json one JSON object per file\n --require exit 1 if a file carries no AI marking\n\nImages: PNG, JPEG, WebP (XMP with IPTC Digital Source Type).\nText files are checked for the Witness text watermark.`;\n\nconst KINDS: ContentKind[] = [\"generated\", \"edited\", \"deepfake\"];\n\nexport async function runCli(argv: string[], io: CliIo = defaultIo): Promise<number> {\n const [command, ...rest] = argv;\n if (!command || command === \"--help\" || command === \"-h\" || command === \"help\") {\n io.out(HELP);\n return command ? 0 : 1;\n }\n try {\n if (command === \"mark\") return await mark(rest, io);\n if (command === \"inspect\") return await inspect(rest, io);\n io.err(`Unknown command \"${command}\". Run \"witness --help\".`);\n return 1;\n } catch (error) {\n io.err(error instanceof Error ? error.message : String(error));\n return 1;\n }\n}\n\nasync function mark(argv: string[], io: CliIo): Promise<number> {\n const { values, positionals } = parseArgs({\n args: argv,\n allowPositionals: true,\n options: {\n out: { type: \"string\" },\n \"in-place\": { type: \"boolean\" },\n generator: { type: \"string\" },\n \"generator-version\": { type: \"string\" },\n kind: { type: \"string\" },\n provider: { type: \"string\" },\n url: { type: \"string\" },\n description: { type: \"string\" },\n \"overwrite-c2pa\": { type: \"boolean\" },\n },\n });\n if (positionals.length === 0) throw new Error(\"witness mark: no files given.\");\n if (!values.out && !values[\"in-place\"]) {\n throw new Error(\"witness mark: pass --out <dir> or --in-place.\");\n }\n const kind = (values.kind ?? \"generated\") as ContentKind;\n if (!KINDS.includes(kind))\n throw new Error(`witness mark: --kind must be one of ${KINDS.join(\", \")}.`);\n const input: MarkingInput = { kind };\n if (values.generator) input.generator = values.generator;\n if (values[\"generator-version\"]) input.generatorVersion = values[\"generator-version\"];\n if (values.provider) input.provider = values.provider;\n if (values.url) input.disclosureUrl = values.url;\n if (values.description) input.description = values.description;\n if (values.out) await mkdir(values.out, { recursive: true });\n\n let failed = 0;\n for (const file of positionals) {\n const bytes = new Uint8Array(await readFile(file));\n const result = markImage(bytes, input, {\n c2pa: values[\"overwrite-c2pa\"] ? \"overwrite\" : \"skip\",\n });\n if (result.status === \"unsupported\") {\n io.err(`${file}: not a PNG, JPEG or WebP file, skipped`);\n failed++;\n continue;\n }\n if (result.status === \"skipped-c2pa\") {\n io.err(`${file}: carries a C2PA manifest, left unchanged (use --overwrite-c2pa to force)`);\n continue;\n }\n const target = values.out ? join(values.out, basename(file)) : file;\n await writeFile(target, result.bytes);\n io.out(`${file}: marked (${result.format}) -> ${target}`);\n }\n return failed > 0 ? 1 : 0;\n}\n\nasync function inspect(argv: string[], io: CliIo): Promise<number> {\n const { values, positionals } = parseArgs({\n args: argv,\n allowPositionals: true,\n options: { json: { type: \"boolean\" }, require: { type: \"boolean\" } },\n });\n if (positionals.length === 0) throw new Error(\"witness inspect: no files given.\");\n let unmarked = 0;\n for (const file of positionals) {\n const bytes = new Uint8Array(await readFile(file));\n const format = detectImageFormat(bytes);\n if (format) {\n const info = readImageMarking(bytes);\n const marked = info.aiGenerated || info.c2pa;\n if (!marked) unmarked++;\n if (values.json) {\n const { xmp: _xmp, ...rest } = info;\n io.out(JSON.stringify({ file, ...rest }));\n } else {\n const parts = [\n `${file}: ${format}`,\n info.sourceType\n ? `source type ${iptcSourceType(info.sourceType)}`\n : \"no IPTC source type\",\n info.generator ? `generator ${info.generator}` : null,\n info.c2pa ? \"C2PA manifest present (not verified)\" : null,\n marked ? \"AI-marked\" : \"NOT AI-marked\",\n ];\n io.out(parts.filter(Boolean).join(\", \"));\n }\n continue;\n }\n const ext = extname(file).toLowerCase();\n const text = new TextDecoder().decode(bytes);\n const watermark = readTextWatermark(text);\n if (!watermark) unmarked++;\n if (values.json) {\n io.out(JSON.stringify({ file, format: ext.slice(1) || \"text\", watermark }));\n } else {\n io.out(\n watermark\n ? `${file}: text watermark found${watermark.generator ? `, generator ${watermark.generator}` : \"\"}${watermark.createdAt ? `, created ${watermark.createdAt}` : \"\"}`\n : `${file}: no Witness text watermark`,\n );\n }\n }\n return values.require && unmarked > 0 ? 1 : 0;\n}\n\nconst defaultIo: CliIo = {\n out: (line) => process.stdout.write(`${line}\\n`),\n err: (line) => process.stderr.write(`${line}\\n`),\n};\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
// src/disclosure.ts
|
|
2
|
+
var PREFIX = "witness:ack:";
|
|
3
|
+
function createDisclosure(options = {}) {
|
|
4
|
+
const kind = options.kind ?? "chatbot";
|
|
5
|
+
const id = options.id ?? kind;
|
|
6
|
+
const version = options.version ?? "1";
|
|
7
|
+
const locale = options.locale ?? "en";
|
|
8
|
+
const now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
9
|
+
const storage = resolveStorage(options.storage);
|
|
10
|
+
const key = `${PREFIX}${id}`;
|
|
11
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
12
|
+
let shown = false;
|
|
13
|
+
const read = () => {
|
|
14
|
+
try {
|
|
15
|
+
const raw = storage.getItem(key);
|
|
16
|
+
if (!raw) return null;
|
|
17
|
+
const parsed = JSON.parse(raw);
|
|
18
|
+
if (typeof parsed.version !== "string" || typeof parsed.at !== "string") return null;
|
|
19
|
+
return { version: parsed.version, at: parsed.at };
|
|
20
|
+
} catch {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
const emit = (type, at) => {
|
|
25
|
+
options.onEvent?.({ type, id, kind, version, locale, at });
|
|
26
|
+
};
|
|
27
|
+
const notify = () => {
|
|
28
|
+
for (const listener of listeners) listener();
|
|
29
|
+
};
|
|
30
|
+
return {
|
|
31
|
+
id,
|
|
32
|
+
kind,
|
|
33
|
+
version,
|
|
34
|
+
needsAcknowledgement: () => read()?.version !== version,
|
|
35
|
+
acknowledgedAt: () => {
|
|
36
|
+
const entry = read();
|
|
37
|
+
return entry?.version === version ? entry.at : null;
|
|
38
|
+
},
|
|
39
|
+
markShown() {
|
|
40
|
+
if (shown) return;
|
|
41
|
+
shown = true;
|
|
42
|
+
emit("shown", now().toISOString());
|
|
43
|
+
},
|
|
44
|
+
acknowledge() {
|
|
45
|
+
const at = now().toISOString();
|
|
46
|
+
try {
|
|
47
|
+
storage.setItem(key, JSON.stringify({ version, at }));
|
|
48
|
+
} catch {
|
|
49
|
+
}
|
|
50
|
+
emit("acknowledged", at);
|
|
51
|
+
notify();
|
|
52
|
+
},
|
|
53
|
+
reset() {
|
|
54
|
+
try {
|
|
55
|
+
storage.removeItem(key);
|
|
56
|
+
} catch {
|
|
57
|
+
}
|
|
58
|
+
notify();
|
|
59
|
+
},
|
|
60
|
+
subscribe(listener) {
|
|
61
|
+
listeners.add(listener);
|
|
62
|
+
return () => listeners.delete(listener);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
function resolveStorage(option) {
|
|
67
|
+
if (option && typeof option === "object") return option;
|
|
68
|
+
if (option !== "memory") {
|
|
69
|
+
try {
|
|
70
|
+
const candidate = option === "session" ? globalThis.sessionStorage : globalThis.localStorage;
|
|
71
|
+
if (candidate) {
|
|
72
|
+
const probe = `${PREFIX}probe`;
|
|
73
|
+
candidate.setItem(probe, "1");
|
|
74
|
+
candidate.removeItem(probe);
|
|
75
|
+
return candidate;
|
|
76
|
+
}
|
|
77
|
+
} catch {
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return memoryStorage();
|
|
81
|
+
}
|
|
82
|
+
function memoryStorage() {
|
|
83
|
+
const map = /* @__PURE__ */ new Map();
|
|
84
|
+
return {
|
|
85
|
+
getItem: (key) => map.get(key) ?? null,
|
|
86
|
+
setItem: (key, value) => {
|
|
87
|
+
map.set(key, value);
|
|
88
|
+
},
|
|
89
|
+
removeItem: (key) => {
|
|
90
|
+
map.delete(key);
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// src/types.ts
|
|
96
|
+
var DISCLOSURE_KINDS = [
|
|
97
|
+
"chatbot",
|
|
98
|
+
"generated",
|
|
99
|
+
"edited",
|
|
100
|
+
"deepfake",
|
|
101
|
+
"emotion-recognition",
|
|
102
|
+
"biometric-categorisation"
|
|
103
|
+
];
|
|
104
|
+
|
|
105
|
+
export {
|
|
106
|
+
createDisclosure,
|
|
107
|
+
memoryStorage,
|
|
108
|
+
DISCLOSURE_KINDS
|
|
109
|
+
};
|
|
110
|
+
//# sourceMappingURL=chunk-XJOQ5IUT.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/disclosure.ts","../src/types.ts"],"sourcesContent":["import type { DisclosureKind } from \"./types.js\";\n\n/** Minimal key-value storage, e.g. `localStorage`. */\nexport interface DisclosureStorage {\n getItem(key: string): string | null;\n setItem(key: string, value: string): void;\n removeItem(key: string): void;\n}\n\nexport interface DisclosureEvent {\n type: \"shown\" | \"acknowledged\";\n /** Your id for this disclosure, e.g. \"support-chat\". */\n id: string;\n kind: DisclosureKind;\n /** Bump the version when the wording changes, so people see the new text once. */\n version: string;\n locale: string;\n /** ISO 8601 timestamp. */\n at: string;\n}\n\nexport interface DisclosureOptions {\n /** Your id for this disclosure. Default: the kind. */\n id?: string;\n /** Default `chatbot`. */\n kind?: DisclosureKind;\n /** Default `1`. */\n version?: string;\n /** Locale shown, recorded in events. Default `en`. */\n locale?: string;\n /**\n * Where the acknowledgement is remembered: `local` (default), `session`, `memory` or your\n * own storage. Falls back to memory when browser storage is unavailable.\n */\n storage?: \"local\" | \"session\" | \"memory\" | DisclosureStorage;\n /**\n * Called when the notice is shown or acknowledged. Send these to your log (for example\n * Logarithm) to keep evidence of what people were told and when.\n */\n onEvent?: (event: DisclosureEvent) => void;\n /** Clock for tests. */\n now?: () => Date;\n}\n\nexport interface Disclosure {\n readonly id: string;\n readonly kind: DisclosureKind;\n readonly version: string;\n /** True until this version was acknowledged. */\n needsAcknowledgement(): boolean;\n /** When this version was acknowledged, or `null`. */\n acknowledgedAt(): string | null;\n /** Records that the notice was rendered. Emits a `shown` event once per instance. */\n markShown(): void;\n /** Records the acknowledgement and emits an `acknowledged` event. */\n acknowledge(): void;\n /** Forgets the acknowledgement. */\n reset(): void;\n /** Called whenever the acknowledgement changes. Returns an unsubscribe function. */\n subscribe(listener: () => void): () => void;\n}\n\nconst PREFIX = \"witness:ack:\";\n\n/**\n * Tracks whether a person has seen and acknowledged an AI disclosure.\n *\n * Article 50(5) asks for the information \"at the latest at the time of the first interaction\n * or exposure\". Keep a compact label visible after the acknowledgement: the acknowledgement\n * only collapses the notice, it does not make it disappear.\n */\nexport function createDisclosure(options: DisclosureOptions = {}): Disclosure {\n const kind = options.kind ?? \"chatbot\";\n const id = options.id ?? kind;\n const version = options.version ?? \"1\";\n const locale = options.locale ?? \"en\";\n const now = options.now ?? (() => new Date());\n const storage = resolveStorage(options.storage);\n const key = `${PREFIX}${id}`;\n const listeners = new Set<() => void>();\n let shown = false;\n\n const read = (): { version: string; at: string } | null => {\n try {\n const raw = storage.getItem(key);\n if (!raw) return null;\n const parsed = JSON.parse(raw) as { version?: unknown; at?: unknown };\n if (typeof parsed.version !== \"string\" || typeof parsed.at !== \"string\") return null;\n return { version: parsed.version, at: parsed.at };\n } catch {\n return null;\n }\n };\n\n const emit = (type: DisclosureEvent[\"type\"], at: string) => {\n options.onEvent?.({ type, id, kind, version, locale, at });\n };\n\n const notify = () => {\n for (const listener of listeners) listener();\n };\n\n return {\n id,\n kind,\n version,\n needsAcknowledgement: () => read()?.version !== version,\n acknowledgedAt: () => {\n const entry = read();\n return entry?.version === version ? entry.at : null;\n },\n markShown() {\n if (shown) return;\n shown = true;\n emit(\"shown\", now().toISOString());\n },\n acknowledge() {\n const at = now().toISOString();\n try {\n storage.setItem(key, JSON.stringify({ version, at }));\n } catch {\n // Storage full or blocked: the acknowledgement lasts for this page only.\n }\n emit(\"acknowledged\", at);\n notify();\n },\n reset() {\n try {\n storage.removeItem(key);\n } catch {\n // ignore\n }\n notify();\n },\n subscribe(listener) {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n };\n}\n\nfunction resolveStorage(option: DisclosureOptions[\"storage\"]): DisclosureStorage {\n if (option && typeof option === \"object\") return option;\n if (option !== \"memory\") {\n try {\n const candidate = option === \"session\" ? globalThis.sessionStorage : globalThis.localStorage;\n if (candidate) {\n const probe = `${PREFIX}probe`;\n candidate.setItem(probe, \"1\");\n candidate.removeItem(probe);\n return candidate;\n }\n } catch {\n // fall through to memory\n }\n }\n return memoryStorage();\n}\n\n/** In-memory storage, e.g. for server rendering or tests. */\nexport function memoryStorage(): DisclosureStorage {\n const map = new Map<string, string>();\n return {\n getItem: (key) => map.get(key) ?? null,\n setItem: (key, value) => {\n map.set(key, value);\n },\n removeItem: (key) => {\n map.delete(key);\n },\n };\n}\n","/**\n * The transparency situations Article 50 of the EU AI Act describes.\n *\n * - `chatbot`: a person interacts directly with an AI system (Art. 50(1)).\n * - `generated`: image, audio, video or text produced by an AI system (Art. 50(2) and 50(4)).\n * - `edited`: existing content altered with an AI system (Art. 50(2) and 50(4)).\n * - `deepfake`: generated or manipulated content that resembles real people, objects,\n * places or events and could falsely appear authentic (Art. 50(4)).\n * - `emotion-recognition` and `biometric-categorisation`: people are exposed to such a\n * system (Art. 50(3)).\n */\nexport type DisclosureKind =\n | \"chatbot\"\n | \"generated\"\n | \"edited\"\n | \"deepfake\"\n | \"emotion-recognition\"\n | \"biometric-categorisation\";\n\nexport const DISCLOSURE_KINDS: readonly DisclosureKind[] = [\n \"chatbot\",\n \"generated\",\n \"edited\",\n \"deepfake\",\n \"emotion-recognition\",\n \"biometric-categorisation\",\n];\n\n/** The kinds that describe a piece of content and can be written into a marking. */\nexport type ContentKind = \"generated\" | \"edited\" | \"deepfake\";\n\n/**\n * IPTC Digital Source Type values relevant to AI. See\n * https://cv.iptc.org/newscodes/digitalsourcetype/\n */\nexport type SourceType =\n | \"trainedAlgorithmicMedia\"\n | \"compositeWithTrainedAlgorithmicMedia\"\n | \"algorithmicallyEnhanced\"\n | \"algorithmicMedia\"\n | \"compositeSynthetic\";\n\n/** Wording for one disclosure kind in one language. */\nexport interface DisclosureText {\n /** Two or three words for a badge, e.g. \"AI-generated\". */\n label: string;\n /** One line for the heading of a notice. */\n title: string;\n /** One or two plain sentences that explain the label. */\n body: string;\n}\n\n/** Interface strings shared by the notice and label components. */\nexport interface UiText {\n /** Button that collapses the chatbot notice. */\n acknowledge: string;\n /** Accessible name of the button that opens label details. */\n details: string;\n /** \"Generated with {generator}\". `{generator}` is replaced. */\n generatedWith: string;\n /** \"Created on {date}\". `{date}` is replaced. */\n createdOn: string;\n /** Shown when a person reviewed the content and took editorial responsibility. */\n humanReviewed: string;\n /** Link text to a page with more information. */\n moreInfo: string;\n /** Closes the details popover. */\n close: string;\n}\n\nexport interface LocaleMessages {\n kinds: Record<DisclosureKind, DisclosureText>;\n ui: UiText;\n}\n\n/** A partial override of a locale, e.g. to switch German to the informal \"du\". */\nexport interface LocaleOverride {\n kinds?: Partial<Record<DisclosureKind, Partial<DisclosureText>>>;\n ui?: Partial<UiText>;\n}\n\n/** What a marking says about a piece of content. Input for {@link createMarking}. */\nexport interface MarkingInput {\n /** Defaults to `generated`. */\n kind?: ContentKind;\n /** Defaults from `kind`: `trainedAlgorithmicMedia`, or the composite type for `edited`. */\n sourceType?: SourceType;\n /** Name of the AI system or model, e.g. \"gpt-image-2\" or \"Claude\". */\n generator?: string;\n /** Version of the AI system, if known. */\n generatorVersion?: string;\n /** Who provides or operates the AI system, e.g. your company. */\n provider?: string;\n /** ISO 8601 timestamp. Defaults to now. */\n createdAt?: string | Date;\n /** A page that explains how and why you use AI. */\n disclosureUrl?: string;\n /** A person reviewed the content and holds editorial responsibility (Art. 50(4), text). */\n humanReviewed?: boolean;\n /** Free text, e.g. \"Image generated from the prompt by the newsroom\". */\n description?: string;\n}\n\n/** A normalised marking with all defaults applied. */\nexport interface Marking {\n kind: ContentKind;\n sourceType: SourceType;\n generator?: string;\n generatorVersion?: string;\n provider?: string;\n createdAt: string;\n disclosureUrl?: string;\n humanReviewed: boolean;\n description?: string;\n}\n"],"mappings":";AA8DA,IAAM,SAAS;AASR,SAAS,iBAAiB,UAA6B,CAAC,GAAe;AAC5E,QAAM,OAAO,QAAQ,QAAQ;AAC7B,QAAM,KAAK,QAAQ,MAAM;AACzB,QAAM,UAAU,QAAQ,WAAW;AACnC,QAAM,SAAS,QAAQ,UAAU;AACjC,QAAM,MAAM,QAAQ,QAAQ,MAAM,oBAAI,KAAK;AAC3C,QAAM,UAAU,eAAe,QAAQ,OAAO;AAC9C,QAAM,MAAM,GAAG,MAAM,GAAG,EAAE;AAC1B,QAAM,YAAY,oBAAI,IAAgB;AACtC,MAAI,QAAQ;AAEZ,QAAM,OAAO,MAA8C;AACzD,QAAI;AACF,YAAM,MAAM,QAAQ,QAAQ,GAAG;AAC/B,UAAI,CAAC,IAAK,QAAO;AACjB,YAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,UAAI,OAAO,OAAO,YAAY,YAAY,OAAO,OAAO,OAAO,SAAU,QAAO;AAChF,aAAO,EAAE,SAAS,OAAO,SAAS,IAAI,OAAO,GAAG;AAAA,IAClD,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAEA,QAAM,OAAO,CAAC,MAA+B,OAAe;AAC1D,YAAQ,UAAU,EAAE,MAAM,IAAI,MAAM,SAAS,QAAQ,GAAG,CAAC;AAAA,EAC3D;AAEA,QAAM,SAAS,MAAM;AACnB,eAAW,YAAY,UAAW,UAAS;AAAA,EAC7C;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,sBAAsB,MAAM,KAAK,GAAG,YAAY;AAAA,IAChD,gBAAgB,MAAM;AACpB,YAAM,QAAQ,KAAK;AACnB,aAAO,OAAO,YAAY,UAAU,MAAM,KAAK;AAAA,IACjD;AAAA,IACA,YAAY;AACV,UAAI,MAAO;AACX,cAAQ;AACR,WAAK,SAAS,IAAI,EAAE,YAAY,CAAC;AAAA,IACnC;AAAA,IACA,cAAc;AACZ,YAAM,KAAK,IAAI,EAAE,YAAY;AAC7B,UAAI;AACF,gBAAQ,QAAQ,KAAK,KAAK,UAAU,EAAE,SAAS,GAAG,CAAC,CAAC;AAAA,MACtD,QAAQ;AAAA,MAER;AACA,WAAK,gBAAgB,EAAE;AACvB,aAAO;AAAA,IACT;AAAA,IACA,QAAQ;AACN,UAAI;AACF,gBAAQ,WAAW,GAAG;AAAA,MACxB,QAAQ;AAAA,MAER;AACA,aAAO;AAAA,IACT;AAAA,IACA,UAAU,UAAU;AAClB,gBAAU,IAAI,QAAQ;AACtB,aAAO,MAAM,UAAU,OAAO,QAAQ;AAAA,IACxC;AAAA,EACF;AACF;AAEA,SAAS,eAAe,QAAyD;AAC/E,MAAI,UAAU,OAAO,WAAW,SAAU,QAAO;AACjD,MAAI,WAAW,UAAU;AACvB,QAAI;AACF,YAAM,YAAY,WAAW,YAAY,WAAW,iBAAiB,WAAW;AAChF,UAAI,WAAW;AACb,cAAM,QAAQ,GAAG,MAAM;AACvB,kBAAU,QAAQ,OAAO,GAAG;AAC5B,kBAAU,WAAW,KAAK;AAC1B,eAAO;AAAA,MACT;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AACA,SAAO,cAAc;AACvB;AAGO,SAAS,gBAAmC;AACjD,QAAM,MAAM,oBAAI,IAAoB;AACpC,SAAO;AAAA,IACL,SAAS,CAAC,QAAQ,IAAI,IAAI,GAAG,KAAK;AAAA,IAClC,SAAS,CAAC,KAAK,UAAU;AACvB,UAAI,IAAI,KAAK,KAAK;AAAA,IACpB;AAAA,IACA,YAAY,CAAC,QAAQ;AACnB,UAAI,OAAO,GAAG;AAAA,IAChB;AAAA,EACF;AACF;;;ACxJO,IAAM,mBAA8C;AAAA,EACzD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;","names":[]}
|