@blamejs/core 0.7.106 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/CHANGELOG.md +19 -1
  2. package/NOTICE +17 -1
  3. package/README.md +4 -3
  4. package/index.js +16 -0
  5. package/lib/asyncapi-bindings.js +160 -0
  6. package/lib/asyncapi-traits.js +143 -0
  7. package/lib/asyncapi.js +531 -0
  8. package/lib/audit.js +6 -0
  9. package/lib/auth/acr-vocabulary.js +265 -0
  10. package/lib/auth/auth-time-tracker.js +111 -0
  11. package/lib/auth/elevation-grant.js +306 -0
  12. package/lib/auth/sd-jwt-vc-disclosure.js +95 -0
  13. package/lib/auth/sd-jwt-vc-holder.js +203 -0
  14. package/lib/auth/sd-jwt-vc-issuer.js +197 -0
  15. package/lib/auth/sd-jwt-vc.js +526 -0
  16. package/lib/auth/step-up-policy.js +335 -0
  17. package/lib/auth/step-up.js +445 -0
  18. package/lib/compliance-ai-act-logging.js +186 -0
  19. package/lib/compliance-ai-act-prohibited.js +205 -0
  20. package/lib/compliance-ai-act-risk.js +189 -0
  21. package/lib/compliance-ai-act-transparency.js +200 -0
  22. package/lib/compliance-ai-act.js +558 -0
  23. package/lib/compliance.js +2 -0
  24. package/lib/crypto.js +32 -0
  25. package/lib/flag-cache.js +136 -0
  26. package/lib/flag-evaluation-context.js +135 -0
  27. package/lib/flag-providers.js +279 -0
  28. package/lib/flag-targeting.js +210 -0
  29. package/lib/flag.js +284 -0
  30. package/lib/inbox.js +367 -0
  31. package/lib/mail-arc-sign.js +372 -0
  32. package/lib/mail-auth.js +2 -0
  33. package/lib/middleware/ai-act-disclosure.js +166 -0
  34. package/lib/middleware/asyncapi-serve.js +136 -0
  35. package/lib/middleware/flag-context.js +76 -0
  36. package/lib/middleware/index.js +15 -0
  37. package/lib/middleware/openapi-serve.js +143 -0
  38. package/lib/middleware/require-step-up.js +186 -0
  39. package/lib/openapi-paths-builder.js +248 -0
  40. package/lib/openapi-schema-walk.js +192 -0
  41. package/lib/openapi-security.js +169 -0
  42. package/lib/openapi-yaml.js +154 -0
  43. package/lib/openapi.js +443 -0
  44. package/lib/pqc-software.js +195 -0
  45. package/lib/vault/index.js +3 -0
  46. package/lib/vault-aad.js +259 -0
  47. package/lib/vendor/MANIFEST.json +29 -0
  48. package/lib/vendor/noble-post-quantum.cjs +18 -0
  49. package/lib/ws-client.js +829 -0
  50. package/package.json +1 -1
  51. package/sbom.cyclonedx.json +6 -6
@@ -0,0 +1,205 @@
1
+ "use strict";
2
+ /**
3
+ * EU AI Act Article 5 — prohibited AI practices.
4
+ *
5
+ * Per Regulation (EU) 2024/1689 Art. 5, certain AI practices are
6
+ * unconditionally prohibited in the EU market. The practices fall
7
+ * into eight categories listed below; each entry carries:
8
+ *
9
+ * - id — short canonical identifier (used by classify())
10
+ * - article — the sub-article of Art. 5 (a-h)
11
+ * - title — operator-readable title
12
+ * - description — paraphrase of the prohibited practice
13
+ * - examples — non-exhaustive list of system shapes that fall in
14
+ *
15
+ * The catalog is operator-readable but NOT operator-extensible — Art. 5
16
+ * is set by EU regulation; operators don't add private "prohibited
17
+ * practices". For private "we don't allow this" rules see
18
+ * b.compliance.aiAct.local-policy (separate primitive).
19
+ *
20
+ * Effective date: 2026-02-02 per Art. 113(a). Operators with EU users
21
+ * MUST classify each AI system against this catalog before deployment.
22
+ */
23
+
24
+ var PROHIBITED_PRACTICES = Object.freeze([
25
+ Object.freeze({
26
+ id: "subliminal-manipulation",
27
+ article: "Art. 5(1)(a)",
28
+ title: "Subliminal techniques beyond a person's consciousness",
29
+ description: "AI systems that deploy subliminal, purposefully manipulative, or deceptive techniques with the objective or effect of materially distorting a person's behaviour by appreciably impairing their ability to make an informed decision, thereby causing or likely to cause significant harm.",
30
+ examples: Object.freeze([
31
+ "Hidden audio cues in marketing audio that bypass conscious perception",
32
+ "UX patterns engineered with eye-tracking to push specific decisions",
33
+ "Generative content that subliminally embeds product imagery",
34
+ ]),
35
+ effectiveDate: "2026-02-02",
36
+ }),
37
+ Object.freeze({
38
+ id: "exploit-vulnerabilities",
39
+ article: "Art. 5(1)(b)",
40
+ title: "Exploiting vulnerabilities of specific groups",
41
+ description: "AI systems that exploit vulnerabilities of a specific group of persons (due to age, disability, or specific social or economic situation) with the objective or effect of materially distorting their behaviour, thereby causing or likely to cause significant harm.",
42
+ examples: Object.freeze([
43
+ "Recommender systems targeting children with addictive content patterns",
44
+ "Predatory lending offers tuned to financial-distress signals",
45
+ "Manipulative chatbots aimed at elderly users with dementia signals",
46
+ ]),
47
+ effectiveDate: "2026-02-02",
48
+ }),
49
+ Object.freeze({
50
+ id: "social-scoring",
51
+ article: "Art. 5(1)(c)",
52
+ title: "Social scoring by public authorities",
53
+ description: "AI systems for the evaluation or classification of natural persons over a certain period of time based on their social behaviour or known, inferred or predicted personal or personality characteristics, leading to detrimental or unfavourable treatment that is unjustified or disproportionate.",
54
+ examples: Object.freeze([
55
+ "General-purpose social-credit ranking by a state agency",
56
+ "Cross-context behavior scoring used to deny unrelated services",
57
+ ]),
58
+ effectiveDate: "2026-02-02",
59
+ }),
60
+ Object.freeze({
61
+ id: "predictive-policing-individual",
62
+ article: "Art. 5(1)(d)",
63
+ title: "Predictive policing solely on profiling",
64
+ description: "AI systems for making risk assessments of natural persons in order to assess or predict the risk of a natural person committing a criminal offence, based solely on the profiling of a natural person or on assessing their personality traits and characteristics.",
65
+ examples: Object.freeze([
66
+ "Recidivism scoring using only demographic + arrest-history features",
67
+ "Heatmap-driven 'pre-crime' targeting of named individuals",
68
+ ]),
69
+ effectiveDate: "2026-02-02",
70
+ }),
71
+ Object.freeze({
72
+ id: "untargeted-facial-scraping",
73
+ article: "Art. 5(1)(e)",
74
+ title: "Untargeted scraping for facial-recognition databases",
75
+ description: "AI systems that create or expand facial-recognition databases through the untargeted scraping of facial images from the internet or CCTV footage.",
76
+ examples: Object.freeze([
77
+ "Bulk-collecting public profile photos to populate a face-search index",
78
+ "CCTV-derived enrolment of unconsenting bystanders",
79
+ ]),
80
+ effectiveDate: "2026-02-02",
81
+ }),
82
+ Object.freeze({
83
+ id: "emotion-inference-workplace-edu",
84
+ article: "Art. 5(1)(f)",
85
+ title: "Emotion inference in workplace and education",
86
+ description: "AI systems to infer emotions of a natural person in the areas of workplace and education institutions, except where the use of the AI system is intended to be put in place or into the market for medical or safety reasons.",
87
+ examples: Object.freeze([
88
+ "Camera-based 'engagement' scoring of students during remote class",
89
+ "Sentiment analysis of employee video calls for performance review",
90
+ ]),
91
+ effectiveDate: "2026-02-02",
92
+ }),
93
+ Object.freeze({
94
+ id: "biometric-categorisation-sensitive",
95
+ article: "Art. 5(1)(g)",
96
+ title: "Biometric categorisation by sensitive attributes",
97
+ description: "AI systems of biometric categorisation that categorise individually natural persons based on their biometric data to deduce or infer their race, political opinions, trade union membership, religious or philosophical beliefs, sex life or sexual orientation.",
98
+ examples: Object.freeze([
99
+ "Face-derived political-opinion or religion classifiers",
100
+ "Voice-derived sexual-orientation inference",
101
+ ]),
102
+ effectiveDate: "2026-02-02",
103
+ }),
104
+ Object.freeze({
105
+ id: "real-time-remote-biometric-id",
106
+ article: "Art. 5(1)(h)",
107
+ title: "Real-time remote biometric identification in public",
108
+ description: "AI systems for real-time remote biometric identification in publicly accessible spaces for law-enforcement purposes — except for narrowly-defined exceptions (search for missing persons, prevention of imminent threat, suspect of serious crime listed in Annex II).",
109
+ examples: Object.freeze([
110
+ "Mass facial-recognition gates at festivals or stations",
111
+ "Real-time crowd-scanning ID systems without imminent-threat trigger",
112
+ ]),
113
+ effectiveDate: "2026-02-02",
114
+ }),
115
+ ]);
116
+
117
+ function listPractices() {
118
+ return PROHIBITED_PRACTICES.slice();
119
+ }
120
+
121
+ function getPractice(id) {
122
+ for (var i = 0; i < PROHIBITED_PRACTICES.length; i += 1) {
123
+ if (PROHIBITED_PRACTICES[i].id === id) return PROHIBITED_PRACTICES[i];
124
+ }
125
+ return null;
126
+ }
127
+
128
+ function listIds() {
129
+ return PROHIBITED_PRACTICES.map(function (p) { return p.id; });
130
+ }
131
+
132
+ // Classify a system description against the catalog. Returns the array
133
+ // of practice IDs it appears to fall under, based on operator-supplied
134
+ // signals. The classifier is intentionally conservative — it errs on
135
+ // the side of flagging a system as potentially-prohibited; legal
136
+ // review is required before deployment regardless of the result.
137
+
138
+ function classify(systemDescription) {
139
+ if (!systemDescription || typeof systemDescription !== "object") {
140
+ return [];
141
+ }
142
+ var hits = [];
143
+ // (a) subliminal manipulation
144
+ if (systemDescription.usesSubliminalCues === true ||
145
+ systemDescription.intent === "subliminal-influence") {
146
+ hits.push("subliminal-manipulation");
147
+ }
148
+ // (b) exploit vulnerabilities
149
+ if (systemDescription.targetsVulnerableGroup === true) {
150
+ hits.push("exploit-vulnerabilities");
151
+ }
152
+ // (c) social scoring
153
+ if (systemDescription.purpose === "social-scoring" &&
154
+ systemDescription.deployerType === "public-authority") {
155
+ hits.push("social-scoring");
156
+ }
157
+ // (d) predictive policing on profiling alone
158
+ if (systemDescription.purpose === "predictive-policing" &&
159
+ systemDescription.usesProfileOnly === true) {
160
+ hits.push("predictive-policing-individual");
161
+ }
162
+ // (e) untargeted facial-recognition database build
163
+ if (systemDescription.builds === "facial-recognition-db" &&
164
+ systemDescription.scrapesUntargeted === true) {
165
+ hits.push("untargeted-facial-scraping");
166
+ }
167
+ // (f) emotion inference in workplace / education
168
+ if (systemDescription.infersEmotion === true &&
169
+ (systemDescription.deployContext === "workplace" ||
170
+ systemDescription.deployContext === "education")) {
171
+ if (systemDescription.purpose !== "medical" &&
172
+ systemDescription.purpose !== "safety") {
173
+ hits.push("emotion-inference-workplace-edu");
174
+ }
175
+ }
176
+ // (g) biometric categorisation of sensitive attributes
177
+ if (systemDescription.biometricCategorisation === true &&
178
+ Array.isArray(systemDescription.inferredAttributes)) {
179
+ var sensitive = ["race", "political-opinion", "trade-union",
180
+ "religion", "philosophy", "sex-life", "sexual-orientation"];
181
+ for (var i = 0; i < systemDescription.inferredAttributes.length; i += 1) {
182
+ if (sensitive.indexOf(systemDescription.inferredAttributes[i]) !== -1) {
183
+ hits.push("biometric-categorisation-sensitive");
184
+ break;
185
+ }
186
+ }
187
+ }
188
+ // (h) real-time remote biometric ID for law enforcement
189
+ if (systemDescription.remoteBiometricId === "real-time" &&
190
+ systemDescription.deployContext === "law-enforcement-public-space" &&
191
+ systemDescription.exemption !== "missing-person" &&
192
+ systemDescription.exemption !== "imminent-threat" &&
193
+ systemDescription.exemption !== "annex-ii-suspect") {
194
+ hits.push("real-time-remote-biometric-id");
195
+ }
196
+ return hits;
197
+ }
198
+
199
+ module.exports = {
200
+ PROHIBITED_PRACTICES: PROHIBITED_PRACTICES,
201
+ listPractices: listPractices,
202
+ listIds: listIds,
203
+ getPractice: getPractice,
204
+ classify: classify,
205
+ };
@@ -0,0 +1,189 @@
1
+ "use strict";
2
+ /**
3
+ * EU AI Act Article 6 + Annex III — high-risk AI system classification.
4
+ *
5
+ * Per Regulation (EU) 2024/1689 Art. 6, an AI system is "high-risk"
6
+ * when:
7
+ *
8
+ * (a) it is intended to be used as a safety component of a product
9
+ * covered by the Union harmonisation legislation listed in Annex I,
10
+ * OR the AI system is itself such a product, AND the product is
11
+ * required to undergo third-party conformity assessment; OR
12
+ *
13
+ * (b) it falls into one of the eight Annex III use-case categories.
14
+ *
15
+ * The classifier returns the tier and the matched Annex-III row(s) so
16
+ * operators can produce the Annex IV technical documentation required
17
+ * by Art. 11 + 18.
18
+ *
19
+ * Tiers:
20
+ * "prohibited" → falls under Article 5
21
+ * "high-risk" → falls under Article 6 / Annex III
22
+ * "limited-risk" → falls under Article 50 (transparency obligations only)
23
+ * "minimal-risk" → no specific obligations beyond general principles
24
+ * "general-purpose" → general-purpose AI model (GPAI) — Article 53
25
+ */
26
+
27
+ var ANNEX_III_USE_CASES = Object.freeze([
28
+ Object.freeze({
29
+ id: "biometric-id-categorisation",
30
+ annexRow: "Annex III §1",
31
+ title: "Biometric identification and categorisation",
32
+ description: "Remote biometric identification systems (excluding the prohibited real-time-public ones), biometric categorisation systems, emotion-recognition systems.",
33
+ obligations: Object.freeze(["risk-management-art-9", "data-governance-art-10",
34
+ "technical-documentation-art-11", "logging-art-12",
35
+ "transparency-art-13", "human-oversight-art-14",
36
+ "accuracy-robustness-art-15"]),
37
+ }),
38
+ Object.freeze({
39
+ id: "critical-infrastructure",
40
+ annexRow: "Annex III §2",
41
+ title: "Critical infrastructure",
42
+ description: "AI systems intended to be used as safety components in the management and operation of critical digital infrastructure, road traffic, or in the supply of water, gas, heating or electricity.",
43
+ obligations: Object.freeze(["risk-management-art-9", "data-governance-art-10",
44
+ "technical-documentation-art-11", "logging-art-12",
45
+ "transparency-art-13", "human-oversight-art-14",
46
+ "accuracy-robustness-art-15"]),
47
+ }),
48
+ Object.freeze({
49
+ id: "education-vocational",
50
+ annexRow: "Annex III §3",
51
+ title: "Education and vocational training",
52
+ description: "AI systems for determining access, admission or assignment to educational and vocational training institutions; for evaluating learning outcomes; for assessing the appropriate level of education; for monitoring and detecting prohibited behaviour during tests.",
53
+ obligations: Object.freeze(["risk-management-art-9", "data-governance-art-10",
54
+ "technical-documentation-art-11", "logging-art-12",
55
+ "transparency-art-13", "human-oversight-art-14",
56
+ "accuracy-robustness-art-15"]),
57
+ }),
58
+ Object.freeze({
59
+ id: "employment-workers-mgmt",
60
+ annexRow: "Annex III §4",
61
+ title: "Employment, workers management, and access to self-employment",
62
+ description: "AI systems for recruitment / selection, advertising vacancies, screening or filtering applications, evaluating candidates; for promotion / termination decisions, task allocation based on individual behaviour or personal traits, for monitoring and evaluating performance and behaviour.",
63
+ obligations: Object.freeze(["risk-management-art-9", "data-governance-art-10",
64
+ "technical-documentation-art-11", "logging-art-12",
65
+ "transparency-art-13", "human-oversight-art-14",
66
+ "accuracy-robustness-art-15"]),
67
+ }),
68
+ Object.freeze({
69
+ id: "essential-services",
70
+ annexRow: "Annex III §5",
71
+ title: "Access to essential private and public services",
72
+ description: "AI systems used by public authorities to evaluate eligibility for essential public assistance benefits and services; for credit-worthiness scoring of natural persons; for risk assessment and pricing of life and health insurance; for emergency-response triage.",
73
+ obligations: Object.freeze(["risk-management-art-9", "data-governance-art-10",
74
+ "technical-documentation-art-11", "logging-art-12",
75
+ "transparency-art-13", "human-oversight-art-14",
76
+ "accuracy-robustness-art-15"]),
77
+ }),
78
+ Object.freeze({
79
+ id: "law-enforcement",
80
+ annexRow: "Annex III §6",
81
+ title: "Law enforcement",
82
+ description: "AI systems used by law enforcement authorities for risk assessment of natural persons (excluding the prohibited profiling-only ones), polygraphs / similar tools, evaluating reliability of evidence, profiling for detection / investigation of criminal offences.",
83
+ obligations: Object.freeze(["risk-management-art-9", "data-governance-art-10",
84
+ "technical-documentation-art-11", "logging-art-12",
85
+ "transparency-art-13", "human-oversight-art-14",
86
+ "accuracy-robustness-art-15", "fundamental-rights-impact-assessment-art-27"]),
87
+ }),
88
+ Object.freeze({
89
+ id: "migration-asylum-border",
90
+ annexRow: "Annex III §7",
91
+ title: "Migration, asylum and border control management",
92
+ description: "AI systems used by competent public authorities for assessing risks (security, irregular migration, health) posed by a natural person intending to enter / having entered the EU; for examining applications for asylum / visa / residence permits; for detecting / recognising / identifying natural persons in the context of migration.",
93
+ obligations: Object.freeze(["risk-management-art-9", "data-governance-art-10",
94
+ "technical-documentation-art-11", "logging-art-12",
95
+ "transparency-art-13", "human-oversight-art-14",
96
+ "accuracy-robustness-art-15", "fundamental-rights-impact-assessment-art-27"]),
97
+ }),
98
+ Object.freeze({
99
+ id: "judicial-democratic-process",
100
+ annexRow: "Annex III §8",
101
+ title: "Administration of justice and democratic processes",
102
+ description: "AI systems intended to assist judicial authorities in researching / interpreting facts and the law and applying the law; AI systems used to influence the outcome of an election or referendum or voting behaviour.",
103
+ obligations: Object.freeze(["risk-management-art-9", "data-governance-art-10",
104
+ "technical-documentation-art-11", "logging-art-12",
105
+ "transparency-art-13", "human-oversight-art-14",
106
+ "accuracy-robustness-art-15"]),
107
+ }),
108
+ ]);
109
+
110
+ function listAnnexIII() {
111
+ return ANNEX_III_USE_CASES.slice();
112
+ }
113
+
114
+ function getAnnexIII(id) {
115
+ for (var i = 0; i < ANNEX_III_USE_CASES.length; i += 1) {
116
+ if (ANNEX_III_USE_CASES[i].id === id) return ANNEX_III_USE_CASES[i];
117
+ }
118
+ return null;
119
+ }
120
+
121
+ // Operator-side helper to determine the matching Annex III row from a
122
+ // purpose vocabulary the framework recognises.
123
+ function classifyAnnexIII(purpose) {
124
+ if (typeof purpose !== "string") return [];
125
+ var hits = [];
126
+ // Per-row heuristics — operators with a private vocabulary use
127
+ // .isHighRisk and pass annexId directly.
128
+ if (purpose === "biometric-id" || purpose === "biometric-category" ||
129
+ purpose === "emotion-recognition") {
130
+ hits.push("biometric-id-categorisation");
131
+ }
132
+ if (purpose === "critical-infrastructure-control" ||
133
+ purpose === "traffic-control" || purpose === "energy-grid") {
134
+ hits.push("critical-infrastructure");
135
+ }
136
+ if (purpose === "school-admissions" || purpose === "exam-grading" ||
137
+ purpose === "exam-proctoring") {
138
+ hits.push("education-vocational");
139
+ }
140
+ if (purpose === "candidate-screening" || purpose === "performance-evaluation" ||
141
+ purpose === "promotion-decision" || purpose === "termination-decision") {
142
+ hits.push("employment-workers-mgmt");
143
+ }
144
+ if (purpose === "credit-scoring" || purpose === "insurance-pricing" ||
145
+ purpose === "benefits-eligibility" || purpose === "emergency-triage") {
146
+ hits.push("essential-services");
147
+ }
148
+ if (purpose === "evidence-evaluation" || purpose === "law-enforcement-risk-assessment" ||
149
+ purpose === "polygraph") {
150
+ hits.push("law-enforcement");
151
+ }
152
+ if (purpose === "asylum-application-screening" || purpose === "visa-screening" ||
153
+ purpose === "border-biometric-id") {
154
+ hits.push("migration-asylum-border");
155
+ }
156
+ if (purpose === "judicial-research-assistant" || purpose === "election-influence") {
157
+ hits.push("judicial-democratic-process");
158
+ }
159
+ return hits;
160
+ }
161
+
162
+ function isHighRisk(opts) {
163
+ if (!opts || typeof opts !== "object") return false;
164
+ if (opts.tier === "high-risk") return true;
165
+ if (opts.purpose) {
166
+ var matches = classifyAnnexIII(opts.purpose);
167
+ if (matches.length > 0) return true;
168
+ }
169
+ if (opts.safetyComponentForRegulatedProduct === true &&
170
+ opts.requiresThirdPartyConformity === true) {
171
+ return true;
172
+ }
173
+ return false;
174
+ }
175
+
176
+ function obligationsFor(annexId) {
177
+ var row = getAnnexIII(annexId);
178
+ if (!row) return [];
179
+ return row.obligations.slice();
180
+ }
181
+
182
+ module.exports = {
183
+ ANNEX_III_USE_CASES: ANNEX_III_USE_CASES,
184
+ listAnnexIII: listAnnexIII,
185
+ getAnnexIII: getAnnexIII,
186
+ classifyAnnexIII: classifyAnnexIII,
187
+ isHighRisk: isHighRisk,
188
+ obligationsFor: obligationsFor,
189
+ };
@@ -0,0 +1,200 @@
1
+ "use strict";
2
+ /**
3
+ * EU AI Act Article 50 — transparency obligations.
4
+ *
5
+ * Per Regulation (EU) 2024/1689 Art. 50, providers / deployers of
6
+ * certain AI systems must inform end-users that they are interacting
7
+ * with an AI system, OR that content was AI-generated, OR that AI
8
+ * detected emotions / biometric attributes. Effective 2026-08-02.
9
+ *
10
+ * Four obligations:
11
+ *
12
+ * §1 — AI systems intended to interact directly with natural
13
+ * persons MUST disclose to the user that they are interacting
14
+ * with AI (unless this is obvious from context).
15
+ *
16
+ * §2 — Providers of GPAI / generative AI systems generating synthetic
17
+ * audio / image / video / text MUST mark the output as
18
+ * artificially generated or manipulated, in a machine-readable
19
+ * format and detectable as such.
20
+ *
21
+ * §3 — Deployers of emotion-recognition or biometric categorisation
22
+ * systems MUST inform exposed natural persons.
23
+ *
24
+ * §4 — Deployers generating / manipulating image / audio / video
25
+ * constituting a deep fake MUST disclose that the content is
26
+ * artificially generated. Deployers generating / manipulating
27
+ * text published to inform on matters of public interest MUST
28
+ * disclose that the text is AI-generated, unless reviewed by a
29
+ * human editor with editorial responsibility.
30
+ *
31
+ * The framework provides:
32
+ *
33
+ * - banner({ kind }) → builder for the standard disclosure banner
34
+ * - watermark({ kind, ... }) → builder for content-marking tags
35
+ * - cspMetaTag({ ... }) → meta tag pair for HTML pages
36
+ * - jsonLdDisclosure({ ... }) → JSON-LD <script> for structured-data emit
37
+ * - C2pa-stub → operator-feeds-claims pattern for
38
+ * C2PA Content Credentials integration
39
+ */
40
+
41
+ var validateOpts = require("./validate-opts");
42
+ var { ComplianceError } = require("./framework-error");
43
+
44
+ var BANNER_KINDS = Object.freeze([
45
+ "ai-interaction", // Art. 50(1)
46
+ "ai-generated-content", // Art. 50(2) / 50(4)
47
+ "emotion-recognition", // Art. 50(3)
48
+ "biometric-categorisation", // Art. 50(3)
49
+ "deep-fake", // Art. 50(4)
50
+ "ai-text-public-interest", // Art. 50(4) text variant
51
+ ]);
52
+
53
+ var BANNER_TEXT_EN = Object.freeze({
54
+ "ai-interaction": "You are interacting with an AI system.",
55
+ "ai-generated-content": "This content was generated or modified by AI.",
56
+ "emotion-recognition": "This system uses AI to recognise emotional state.",
57
+ "biometric-categorisation": "This system uses AI biometric categorisation.",
58
+ "deep-fake": "This media was generated or modified by AI (deep-fake).",
59
+ "ai-text-public-interest": "This text was generated by AI on a matter of public interest.",
60
+ });
61
+
62
+ function banner(opts) {
63
+ opts = opts || {};
64
+ validateOpts(opts, [
65
+ "kind", "lang", "deployerName", "controllerContact",
66
+ "linkPolicy", "linkContact",
67
+ ], "compliance.aiAct.transparency.banner");
68
+ if (BANNER_KINDS.indexOf(opts.kind) === -1) {
69
+ throw new ComplianceError("compliance-ai-act/bad-banner-kind",
70
+ "transparency.banner: kind must be one of " + BANNER_KINDS.join(", ") +
71
+ " — got " + JSON.stringify(opts.kind));
72
+ }
73
+ var lang = (typeof opts.lang === "string" && opts.lang.length > 0) ? opts.lang : "en";
74
+ var text = BANNER_TEXT_EN[opts.kind];
75
+ // Operators with a non-en lang supply their own translation via the
76
+ // returned object — the framework only ships en defaults for now.
77
+ var out = {
78
+ kind: opts.kind,
79
+ lang: lang,
80
+ text: text,
81
+ article: _articleFor(opts.kind),
82
+ };
83
+ if (typeof opts.deployerName === "string" && opts.deployerName.length > 0) {
84
+ out.deployerName = opts.deployerName;
85
+ }
86
+ if (typeof opts.controllerContact === "string" && opts.controllerContact.length > 0) {
87
+ out.controllerContact = opts.controllerContact;
88
+ }
89
+ if (typeof opts.linkPolicy === "string" && opts.linkPolicy.length > 0) {
90
+ out.linkPolicy = opts.linkPolicy;
91
+ }
92
+ if (typeof opts.linkContact === "string" && opts.linkContact.length > 0) {
93
+ out.linkContact = opts.linkContact;
94
+ }
95
+ return out;
96
+ }
97
+
98
+ function _articleFor(kind) {
99
+ switch (kind) {
100
+ case "ai-interaction": return "Art. 50(1)";
101
+ case "ai-generated-content": return "Art. 50(2)";
102
+ case "emotion-recognition": return "Art. 50(3)";
103
+ case "biometric-categorisation": return "Art. 50(3)";
104
+ case "deep-fake": return "Art. 50(4)";
105
+ case "ai-text-public-interest": return "Art. 50(4)";
106
+ default: return null;
107
+ }
108
+ }
109
+
110
+ function htmlBanner(opts) {
111
+ var b = banner(opts);
112
+ var attrs =
113
+ 'role="status" data-blamejs-aiAct="' + b.article +
114
+ '" data-blamejs-kind="' + b.kind +
115
+ '" lang="' + b.lang + '"';
116
+ return '<div ' + attrs + '>' + _escapeHtml(b.text) + '</div>';
117
+ }
118
+
119
+ function _escapeHtml(s) {
120
+ if (typeof s !== "string") return "";
121
+ return s.replace(/&/g, "&amp;")
122
+ .replace(/</g, "&lt;")
123
+ .replace(/>/g, "&gt;")
124
+ .replace(/"/g, "&quot;");
125
+ }
126
+
127
+ // Watermark builder for Art. 50(2). Operators integrate with C2PA /
128
+ // SynthID / SoundID / hidden-watermark schemes; the framework emits a
129
+ // machine-readable manifest fragment that downstream tooling can attach
130
+ // or hash into the asset.
131
+ function watermark(opts) {
132
+ opts = opts || {};
133
+ validateOpts(opts, [
134
+ "mediaKind", "modelId", "modelVersion", "createdAt", "promptHash",
135
+ "manipulation", "deployerName", "encoding",
136
+ ], "compliance.aiAct.transparency.watermark");
137
+ if (typeof opts.mediaKind !== "string" ||
138
+ ["image", "audio", "video", "text"].indexOf(opts.mediaKind) === -1) {
139
+ throw new ComplianceError("compliance-ai-act/bad-watermark",
140
+ "transparency.watermark: mediaKind must be one of image/audio/video/text — got " +
141
+ JSON.stringify(opts.mediaKind));
142
+ }
143
+ validateOpts.requireNonEmptyString(opts.modelId,
144
+ "transparency.watermark: modelId", ComplianceError, "compliance-ai-act/bad-watermark");
145
+ var manifest = {
146
+ "@context": "https://blamejs.com/schemas/ai-act-watermark/v1",
147
+ aiActArticle: "Art. 50(2)",
148
+ mediaKind: opts.mediaKind,
149
+ modelId: opts.modelId,
150
+ modelVersion: opts.modelVersion || null,
151
+ createdAt: opts.createdAt || new Date().toISOString(),
152
+ promptHash: opts.promptHash || null,
153
+ manipulation: opts.manipulation === true ? true : false,
154
+ deployerName: opts.deployerName || null,
155
+ encoding: opts.encoding || "json",
156
+ };
157
+ return manifest;
158
+ }
159
+
160
+ function jsonLdDisclosure(opts) {
161
+ var w = watermark(opts);
162
+ var script = '<script type="application/ld+json" ' +
163
+ 'data-blamejs-aiAct="Art. 50(2)">' +
164
+ JSON.stringify(w) + '</script>';
165
+ return script;
166
+ }
167
+
168
+ // CSP-meta-tag pair for HTML pages — the disclosure-policy URI plus
169
+ // the AI-Act-Notice header are emitted as <meta> equivalents so static
170
+ // renderers can include them.
171
+ function metaTags(opts) {
172
+ opts = opts || {};
173
+ validateOpts(opts, [
174
+ "kind", "policyUri", "deployerName",
175
+ ], "compliance.aiAct.transparency.metaTags");
176
+ if (BANNER_KINDS.indexOf(opts.kind) === -1) {
177
+ throw new ComplianceError("compliance-ai-act/bad-banner-kind",
178
+ "transparency.metaTags: kind must be one of " + BANNER_KINDS.join(", "));
179
+ }
180
+ var out = [];
181
+ out.push('<meta name="ai-act-notice" content="' + _escapeHtml(opts.kind) + '">');
182
+ out.push('<meta name="ai-act-article" content="' + _escapeHtml(_articleFor(opts.kind)) + '">');
183
+ if (typeof opts.policyUri === "string" && opts.policyUri.length > 0) {
184
+ out.push('<link rel="ai-act-policy" href="' + _escapeHtml(opts.policyUri) + '">');
185
+ }
186
+ if (typeof opts.deployerName === "string" && opts.deployerName.length > 0) {
187
+ out.push('<meta name="ai-act-deployer" content="' + _escapeHtml(opts.deployerName) + '">');
188
+ }
189
+ return out.join("\n");
190
+ }
191
+
192
+ module.exports = {
193
+ BANNER_KINDS: BANNER_KINDS,
194
+ BANNER_TEXT_EN: BANNER_TEXT_EN,
195
+ banner: banner,
196
+ htmlBanner: htmlBanner,
197
+ watermark: watermark,
198
+ jsonLdDisclosure: jsonLdDisclosure,
199
+ metaTags: metaTags,
200
+ };