@euconform/core 1.0.0 → 1.2.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 (34) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +13 -0
  3. package/README.md +109 -0
  4. package/dist/index.d.ts +2 -2
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +1 -1
  7. package/dist/inference/ollama-client.d.ts +1 -0
  8. package/dist/inference/ollama-client.d.ts.map +1 -1
  9. package/dist/inference/ollama-client.js +15 -5
  10. package/dist/legal-checks/annex-iv-report.d.ts +3 -0
  11. package/dist/legal-checks/annex-iv-report.d.ts.map +1 -1
  12. package/dist/legal-checks/annex-iv-report.js +1 -0
  13. package/dist/legal-checks/deadline-timeline.d.ts +57 -0
  14. package/dist/legal-checks/deadline-timeline.d.ts.map +1 -0
  15. package/dist/legal-checks/deadline-timeline.js +265 -0
  16. package/dist/legal-checks/gap-analysis.d.ts +48 -0
  17. package/dist/legal-checks/gap-analysis.d.ts.map +1 -0
  18. package/dist/legal-checks/gap-analysis.js +397 -0
  19. package/dist/legal-checks/gpai-classifier.d.ts +49 -0
  20. package/dist/legal-checks/gpai-classifier.d.ts.map +1 -0
  21. package/dist/legal-checks/gpai-classifier.js +197 -0
  22. package/dist/legal-checks/index.d.ts +6 -0
  23. package/dist/legal-checks/index.d.ts.map +1 -1
  24. package/dist/legal-checks/index.js +3 -0
  25. package/package.json +20 -1
  26. package/src/index.ts +25 -0
  27. package/src/inference/ollama-client.ts +21 -5
  28. package/src/legal-checks/annex-iv-report.ts +4 -0
  29. package/src/legal-checks/deadline-timeline.ts +313 -0
  30. package/src/legal-checks/gap-analysis.ts +454 -0
  31. package/src/legal-checks/gpai-classifier.ts +281 -0
  32. package/src/legal-checks/index.ts +33 -0
  33. package/tests/gpai-classifier.test.ts +125 -0
  34. package/tests/gpai-timeline.test.ts +55 -0
@@ -0,0 +1,313 @@
1
+ /**
2
+ * EU AI Act Compliance Deadline Timeline
3
+ * Based on Regulation (EU) 2024/1689, Article 113
4
+ *
5
+ * Non-legal-advice: These dates are extracted from the official regulation text
6
+ * and publicly available EU Commission sources. Always verify with legal counsel.
7
+ */
8
+
9
+ import type { RiskLevel } from "../types";
10
+
11
+ export type DeadlineStatus = "past" | "imminent" | "upcoming" | "future";
12
+
13
+ export type AffectedAudience =
14
+ | "all"
15
+ | "prohibited"
16
+ | "gpai"
17
+ | "high-annex-iii"
18
+ | "high-annex-i"
19
+ | "limited"
20
+ | "minimal";
21
+
22
+ export interface DeadlineObligation {
23
+ titleKey: string;
24
+ articleRef: string;
25
+ audiences: AffectedAudience[];
26
+ maxPenalty?: string;
27
+ proposed?: boolean;
28
+ }
29
+
30
+ export interface DeadlinePhase {
31
+ id: string;
32
+ isoDate: string;
33
+ chapterRef: string;
34
+ titleKey: string;
35
+ subtitleKey: string;
36
+ obligations: DeadlineObligation[];
37
+ status?: DeadlineStatus;
38
+ proposed?: boolean;
39
+ }
40
+
41
+ const MS_PER_DAY = 1000 * 60 * 60 * 24;
42
+
43
+ export function getDeadlineStatus(isoDate: string, nowMs = Date.now()): DeadlineStatus {
44
+ const diffDays = (new Date(isoDate).getTime() - nowMs) / MS_PER_DAY;
45
+ if (diffDays < 0) return "past";
46
+ if (diffDays <= 90) return "imminent";
47
+ if (diffDays <= 365) return "upcoming";
48
+ return "future";
49
+ }
50
+
51
+ export function getDaysUntilDeadline(isoDate: string, nowMs = Date.now()): number {
52
+ return Math.round((new Date(isoDate).getTime() - nowMs) / MS_PER_DAY);
53
+ }
54
+
55
+ /**
56
+ * EU AI Act Timeline Milestones – Art. 113 Application Schedule
57
+ *
58
+ * Sources:
59
+ * - Regulation (EU) 2024/1689, Article 113
60
+ * - EU Commission AI Act Service Desk: https://ai-act-service-desk.ec.europa.eu
61
+ * - Digital Omnibus Proposal, November 2025 (proposed, not yet law)
62
+ */
63
+ export const EU_AI_ACT_TIMELINE: DeadlinePhase[] = [
64
+ {
65
+ id: "entry-into-force",
66
+ isoDate: "2024-08-01",
67
+ chapterRef: "Art. 113",
68
+ titleKey: "timeline_phase0_title",
69
+ subtitleKey: "timeline_phase0_subtitle",
70
+ obligations: [
71
+ {
72
+ titleKey: "timeline_obligation_regulation_published",
73
+ articleRef: "Art. 113",
74
+ audiences: ["all"],
75
+ },
76
+ ],
77
+ },
78
+ {
79
+ id: "phase-1-prohibited",
80
+ isoDate: "2025-02-02",
81
+ chapterRef: "Art. 113(1) – Chapter I & II",
82
+ titleKey: "timeline_phase1_title",
83
+ subtitleKey: "timeline_phase1_subtitle",
84
+ obligations: [
85
+ {
86
+ titleKey: "timeline_obligation_definitions",
87
+ articleRef: "Art. 1–4",
88
+ audiences: ["all"],
89
+ },
90
+ {
91
+ titleKey: "timeline_obligation_prohibited",
92
+ articleRef: "Art. 5",
93
+ audiences: ["all", "prohibited"],
94
+ maxPenalty: "€35M / 7%",
95
+ },
96
+ {
97
+ titleKey: "timeline_obligation_ai_literacy",
98
+ articleRef: "Art. 4",
99
+ audiences: ["all"],
100
+ },
101
+ ],
102
+ },
103
+ {
104
+ id: "phase-2-gpai",
105
+ isoDate: "2025-08-02",
106
+ chapterRef: "Art. 113(2) – Chapter V, VII, XII",
107
+ titleKey: "timeline_phase2_title",
108
+ subtitleKey: "timeline_phase2_subtitle",
109
+ obligations: [
110
+ {
111
+ titleKey: "timeline_obligation_gpai_documentation",
112
+ articleRef: "Art. 53",
113
+ audiences: ["gpai"],
114
+ maxPenalty: "€15M / 3%",
115
+ },
116
+ {
117
+ titleKey: "timeline_obligation_gpai_copyright",
118
+ articleRef: "Art. 53(1)(c)",
119
+ audiences: ["gpai"],
120
+ },
121
+ {
122
+ titleKey: "timeline_obligation_gpai_systemic_risk",
123
+ articleRef: "Art. 55",
124
+ audiences: ["gpai"],
125
+ maxPenalty: "€15M / 3%",
126
+ },
127
+ {
128
+ titleKey: "timeline_obligation_notified_bodies",
129
+ articleRef: "Art. 28–39",
130
+ audiences: ["high-annex-iii", "high-annex-i"],
131
+ },
132
+ {
133
+ titleKey: "timeline_obligation_governance",
134
+ articleRef: "Art. 64–70",
135
+ audiences: ["all"],
136
+ },
137
+ {
138
+ titleKey: "timeline_obligation_penalties_framework",
139
+ articleRef: "Art. 99–100",
140
+ audiences: ["all"],
141
+ },
142
+ ],
143
+ },
144
+ {
145
+ id: "phase-3-high-risk-annex-iii",
146
+ isoDate: "2026-08-02",
147
+ chapterRef: "Art. 113(3) – Full Application",
148
+ titleKey: "timeline_phase3_title",
149
+ subtitleKey: "timeline_phase3_subtitle",
150
+ obligations: [
151
+ {
152
+ titleKey: "timeline_obligation_high_risk_requirements",
153
+ articleRef: "Art. 8–25",
154
+ audiences: ["high-annex-iii"],
155
+ maxPenalty: "€15M / 3%",
156
+ },
157
+ {
158
+ titleKey: "timeline_obligation_risk_management",
159
+ articleRef: "Art. 9",
160
+ audiences: ["high-annex-iii"],
161
+ },
162
+ {
163
+ titleKey: "timeline_obligation_data_governance",
164
+ articleRef: "Art. 10",
165
+ audiences: ["high-annex-iii"],
166
+ },
167
+ {
168
+ titleKey: "timeline_obligation_transparency_deployers",
169
+ articleRef: "Art. 13",
170
+ audiences: ["high-annex-iii"],
171
+ },
172
+ {
173
+ titleKey: "timeline_obligation_human_oversight",
174
+ articleRef: "Art. 14",
175
+ audiences: ["high-annex-iii"],
176
+ },
177
+ {
178
+ titleKey: "timeline_obligation_conformity_assessment",
179
+ articleRef: "Art. 43",
180
+ audiences: ["high-annex-iii"],
181
+ },
182
+ {
183
+ titleKey: "timeline_obligation_ce_marking",
184
+ articleRef: "Art. 48",
185
+ audiences: ["high-annex-iii"],
186
+ },
187
+ {
188
+ titleKey: "timeline_obligation_eu_database",
189
+ articleRef: "Art. 49",
190
+ audiences: ["high-annex-iii"],
191
+ },
192
+ {
193
+ titleKey: "timeline_obligation_limited_risk_transparency",
194
+ articleRef: "Art. 50",
195
+ audiences: ["limited"],
196
+ maxPenalty: "€15M / 3%",
197
+ },
198
+ {
199
+ titleKey: "timeline_obligation_post_market",
200
+ articleRef: "Art. 72–73",
201
+ audiences: ["high-annex-iii"],
202
+ },
203
+ ],
204
+ },
205
+ {
206
+ id: "phase-4-high-risk-annex-i",
207
+ isoDate: "2027-08-02",
208
+ chapterRef: "Art. 113(4) – Art. 6(1) + Annex I",
209
+ titleKey: "timeline_phase4_title",
210
+ subtitleKey: "timeline_phase4_subtitle",
211
+ obligations: [
212
+ {
213
+ titleKey: "timeline_obligation_annex_i_products",
214
+ articleRef: "Art. 6(1) + Annex I",
215
+ audiences: ["high-annex-i"],
216
+ maxPenalty: "€15M / 3%",
217
+ },
218
+ {
219
+ titleKey: "timeline_obligation_gpai_grace_period_ends",
220
+ articleRef: "Art. 113(5)",
221
+ audiences: ["gpai"],
222
+ },
223
+ ],
224
+ },
225
+ ];
226
+
227
+ /**
228
+ * Digital Omnibus proposed changes (November 2025).
229
+ * NOT YET LAW – under negotiation by EP and Council as of early 2026.
230
+ */
231
+ export const DIGITAL_OMNIBUS_PROPOSALS: DeadlinePhase[] = [
232
+ {
233
+ id: "omnibus-high-risk-annex-iii",
234
+ isoDate: "2027-12-02",
235
+ chapterRef: "Digital Omnibus (proposed)",
236
+ titleKey: "timeline_omnibus_phase3_title",
237
+ subtitleKey: "timeline_omnibus_phase3_subtitle",
238
+ proposed: true,
239
+ obligations: [
240
+ {
241
+ titleKey: "timeline_omnibus_high_risk_delayed",
242
+ articleRef: "Annex III (proposed)",
243
+ audiences: ["high-annex-iii"],
244
+ proposed: true,
245
+ },
246
+ ],
247
+ },
248
+ {
249
+ id: "omnibus-high-risk-annex-i",
250
+ isoDate: "2028-08-02",
251
+ chapterRef: "Digital Omnibus (proposed)",
252
+ titleKey: "timeline_omnibus_phase4_title",
253
+ subtitleKey: "timeline_omnibus_phase4_subtitle",
254
+ proposed: true,
255
+ obligations: [
256
+ {
257
+ titleKey: "timeline_omnibus_annex_i_delayed",
258
+ articleRef: "Annex I (proposed)",
259
+ audiences: ["high-annex-i"],
260
+ proposed: true,
261
+ },
262
+ ],
263
+ },
264
+ ];
265
+
266
+ export function getAudiencesForRiskLevel(level: RiskLevel): AffectedAudience[] {
267
+ switch (level) {
268
+ case "unacceptable":
269
+ return ["all", "prohibited"];
270
+ case "high":
271
+ return ["all", "high-annex-iii", "high-annex-i"];
272
+ case "limited":
273
+ return ["all", "limited"];
274
+ case "minimal":
275
+ return ["all", "minimal"];
276
+ }
277
+ }
278
+
279
+ export function getNextDeadline(): DeadlinePhase | null {
280
+ const today = new Date().toISOString().slice(0, 10);
281
+ const upcoming = EU_AI_ACT_TIMELINE.filter((p) => p.isoDate > today).sort((a, b) =>
282
+ a.isoDate.localeCompare(b.isoDate)
283
+ );
284
+ return upcoming[0] ?? null;
285
+ }
286
+
287
+ export function getTimelineForRiskLevel(
288
+ riskLevel: RiskLevel
289
+ ): (DeadlinePhase & { status: DeadlineStatus })[] {
290
+ const audiences = getAudiencesForRiskLevel(riskLevel);
291
+ return EU_AI_ACT_TIMELINE.filter((phase) =>
292
+ phase.obligations.some((o) => o.audiences.some((a) => audiences.includes(a)))
293
+ ).map((phase) => ({
294
+ ...phase,
295
+ status: getDeadlineStatus(phase.isoDate),
296
+ }));
297
+ }
298
+
299
+ /**
300
+ * Filter timeline phases by explicit audience list.
301
+ * Use this when the caller has audiences that don't map to a RiskLevel
302
+ * (e.g., GPAI providers need ["all", "gpai"]).
303
+ */
304
+ export function getTimelineForAudiences(
305
+ audiences: AffectedAudience[]
306
+ ): (DeadlinePhase & { status: DeadlineStatus })[] {
307
+ return EU_AI_ACT_TIMELINE.filter((phase) =>
308
+ phase.obligations.some((o) => o.audiences.some((a) => audiences.includes(a)))
309
+ ).map((phase) => ({
310
+ ...phase,
311
+ status: getDeadlineStatus(phase.isoDate),
312
+ }));
313
+ }