@ansvar/eu-regulations-mcp 1.0.0 → 1.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/README.md +60 -22
- package/data/regulations.db +0 -0
- package/dist/database/sqlite-adapter.d.ts +2 -2
- package/dist/database/sqlite-adapter.d.ts.map +1 -1
- package/dist/database/sqlite-adapter.js.map +1 -1
- package/dist/http-server.js +27 -5
- package/dist/http-server.js.map +1 -1
- package/dist/index.js +27 -4
- package/dist/index.js.map +1 -1
- package/dist/tools/about.d.ts +40 -0
- package/dist/tools/about.d.ts.map +1 -0
- package/dist/tools/about.js +61 -0
- package/dist/tools/about.js.map +1 -0
- package/dist/tools/list.d.ts +7 -0
- package/dist/tools/list.d.ts.map +1 -1
- package/dist/tools/list.js +73 -8
- package/dist/tools/list.js.map +1 -1
- package/dist/tools/registry.d.ts +11 -1
- package/dist/tools/registry.d.ts.map +1 -1
- package/dist/tools/registry.js +56 -4
- package/dist/tools/registry.js.map +1 -1
- package/dist/worker.d.ts.map +1 -1
- package/dist/worker.js +17 -5
- package/dist/worker.js.map +1 -1
- package/package.json +6 -5
- package/scripts/add-cross-references.sql +0 -200
- package/scripts/analyze-survey-responses.ts +0 -285
- package/scripts/build-db.ts +0 -421
- package/scripts/bulk-reingest-all.ts +0 -331
- package/scripts/check-updates.ts +0 -294
- package/scripts/extract-eprivacy-recitals.ts +0 -98
- package/scripts/ingest-eurlex-browser.ts +0 -113
- package/scripts/ingest-eurlex.ts +0 -349
- package/scripts/ingest-unece.ts +0 -382
- package/scripts/migrate-postgres.ts +0 -445
- package/scripts/migrate-to-postgres.ts +0 -353
- package/scripts/reingest-all-with-recitals.sh +0 -81
- package/scripts/sync-versions.ts +0 -206
- package/scripts/test-cross-refs.js +0 -26
- package/scripts/test-postgres-adapter.ts +0 -146
- package/scripts/update-dora-rts-metadata.ts +0 -112
- package/src/database/postgres-adapter.ts +0 -84
- package/src/database/sqlite-adapter.ts +0 -44
- package/src/database/types.ts +0 -10
- package/src/http-server.ts +0 -149
- package/src/index.ts +0 -61
- package/src/middleware/rate-limit.ts +0 -104
- package/src/tools/applicability.ts +0 -167
- package/src/tools/article.ts +0 -81
- package/src/tools/compare.ts +0 -217
- package/src/tools/definitions.ts +0 -49
- package/src/tools/evidence.ts +0 -84
- package/src/tools/list.ts +0 -124
- package/src/tools/map.ts +0 -86
- package/src/tools/recital.ts +0 -60
- package/src/tools/registry.ts +0 -311
- package/src/tools/search.ts +0 -297
- package/src/worker.ts +0 -708
|
@@ -1,353 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env npx tsx
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Migrate regulations.db from SQLite to PostgreSQL.
|
|
5
|
-
*
|
|
6
|
-
* Usage:
|
|
7
|
-
* DATABASE_URL=postgresql://user:pass@localhost:5432/eu_regs npm run migrate:postgres
|
|
8
|
-
*
|
|
9
|
-
* This script:
|
|
10
|
-
* 1. Creates PostgreSQL schema
|
|
11
|
-
* 2. Reads from SQLite (data/regulations.db)
|
|
12
|
-
* 3. Writes to PostgreSQL using transactions
|
|
13
|
-
* 4. Verifies row counts match
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
import Database from 'better-sqlite3';
|
|
17
|
-
import pg from 'pg';
|
|
18
|
-
import { join, dirname } from 'path';
|
|
19
|
-
import { fileURLToPath } from 'url';
|
|
20
|
-
|
|
21
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
22
|
-
const __dirname = dirname(__filename);
|
|
23
|
-
|
|
24
|
-
const SQLITE_DB_PATH = join(__dirname, '..', 'data', 'regulations.db');
|
|
25
|
-
const PG_CONNECTION = process.env.DATABASE_URL;
|
|
26
|
-
|
|
27
|
-
if (!PG_CONNECTION) {
|
|
28
|
-
console.error('❌ DATABASE_URL environment variable is required');
|
|
29
|
-
console.error('Example: DATABASE_URL=postgresql://user:pass@localhost:5432/eu_regs');
|
|
30
|
-
process.exit(1);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const POSTGRES_SCHEMA = `
|
|
34
|
-
-- Core regulation metadata
|
|
35
|
-
CREATE TABLE IF NOT EXISTS regulations (
|
|
36
|
-
id TEXT PRIMARY KEY,
|
|
37
|
-
full_name TEXT NOT NULL,
|
|
38
|
-
celex_id TEXT NOT NULL,
|
|
39
|
-
effective_date DATE,
|
|
40
|
-
last_amended DATE,
|
|
41
|
-
eur_lex_url TEXT
|
|
42
|
-
);
|
|
43
|
-
|
|
44
|
-
-- Articles table
|
|
45
|
-
CREATE TABLE IF NOT EXISTS articles (
|
|
46
|
-
id SERIAL PRIMARY KEY,
|
|
47
|
-
regulation TEXT NOT NULL REFERENCES regulations(id),
|
|
48
|
-
article_number TEXT NOT NULL,
|
|
49
|
-
title TEXT,
|
|
50
|
-
text TEXT NOT NULL,
|
|
51
|
-
chapter TEXT,
|
|
52
|
-
recitals TEXT,
|
|
53
|
-
cross_references TEXT,
|
|
54
|
-
UNIQUE(regulation, article_number)
|
|
55
|
-
);
|
|
56
|
-
|
|
57
|
-
-- Full-text search indexes (PostgreSQL native)
|
|
58
|
-
CREATE INDEX IF NOT EXISTS articles_fts_idx ON articles
|
|
59
|
-
USING GIN (to_tsvector('english', COALESCE(title, '') || ' ' || text));
|
|
60
|
-
|
|
61
|
-
CREATE INDEX IF NOT EXISTS articles_regulation_idx ON articles(regulation);
|
|
62
|
-
|
|
63
|
-
-- Definitions
|
|
64
|
-
CREATE TABLE IF NOT EXISTS definitions (
|
|
65
|
-
id SERIAL PRIMARY KEY,
|
|
66
|
-
regulation TEXT NOT NULL REFERENCES regulations(id),
|
|
67
|
-
term TEXT NOT NULL,
|
|
68
|
-
definition TEXT NOT NULL,
|
|
69
|
-
article TEXT NOT NULL,
|
|
70
|
-
UNIQUE(regulation, term)
|
|
71
|
-
);
|
|
72
|
-
|
|
73
|
-
CREATE INDEX IF NOT EXISTS definitions_term_idx ON definitions(term);
|
|
74
|
-
|
|
75
|
-
-- Control mappings
|
|
76
|
-
CREATE TABLE IF NOT EXISTS control_mappings (
|
|
77
|
-
id SERIAL PRIMARY KEY,
|
|
78
|
-
framework TEXT NOT NULL DEFAULT 'ISO27001',
|
|
79
|
-
control_id TEXT NOT NULL,
|
|
80
|
-
control_name TEXT NOT NULL,
|
|
81
|
-
regulation TEXT NOT NULL REFERENCES regulations(id),
|
|
82
|
-
articles TEXT NOT NULL,
|
|
83
|
-
coverage TEXT CHECK(coverage IN ('full', 'partial', 'related')),
|
|
84
|
-
notes TEXT
|
|
85
|
-
);
|
|
86
|
-
|
|
87
|
-
CREATE INDEX IF NOT EXISTS control_mappings_framework_idx ON control_mappings(framework, control_id);
|
|
88
|
-
|
|
89
|
-
-- Applicability rules
|
|
90
|
-
CREATE TABLE IF NOT EXISTS applicability_rules (
|
|
91
|
-
id SERIAL PRIMARY KEY,
|
|
92
|
-
regulation TEXT NOT NULL REFERENCES regulations(id),
|
|
93
|
-
sector TEXT NOT NULL,
|
|
94
|
-
subsector TEXT,
|
|
95
|
-
applies BOOLEAN NOT NULL,
|
|
96
|
-
confidence TEXT CHECK(confidence IN ('definite', 'likely', 'possible')),
|
|
97
|
-
basis_article TEXT,
|
|
98
|
-
notes TEXT
|
|
99
|
-
);
|
|
100
|
-
|
|
101
|
-
CREATE INDEX IF NOT EXISTS applicability_sector_idx ON applicability_rules(sector);
|
|
102
|
-
|
|
103
|
-
-- Source registry for tracking data quality
|
|
104
|
-
CREATE TABLE IF NOT EXISTS source_registry (
|
|
105
|
-
regulation TEXT PRIMARY KEY REFERENCES regulations(id),
|
|
106
|
-
celex_id TEXT NOT NULL,
|
|
107
|
-
eur_lex_version TEXT,
|
|
108
|
-
last_fetched TIMESTAMPTZ,
|
|
109
|
-
articles_expected INTEGER,
|
|
110
|
-
articles_parsed INTEGER,
|
|
111
|
-
quality_status TEXT CHECK(quality_status IN ('complete', 'review', 'incomplete')),
|
|
112
|
-
notes TEXT
|
|
113
|
-
);
|
|
114
|
-
|
|
115
|
-
-- Recitals table
|
|
116
|
-
CREATE TABLE IF NOT EXISTS recitals (
|
|
117
|
-
id SERIAL PRIMARY KEY,
|
|
118
|
-
regulation TEXT NOT NULL REFERENCES regulations(id),
|
|
119
|
-
recital_number INTEGER NOT NULL,
|
|
120
|
-
text TEXT NOT NULL,
|
|
121
|
-
related_articles TEXT,
|
|
122
|
-
UNIQUE(regulation, recital_number)
|
|
123
|
-
);
|
|
124
|
-
|
|
125
|
-
-- Full-text search for recitals
|
|
126
|
-
CREATE INDEX IF NOT EXISTS recitals_fts_idx ON recitals
|
|
127
|
-
USING GIN (to_tsvector('english', text));
|
|
128
|
-
|
|
129
|
-
CREATE INDEX IF NOT EXISTS recitals_regulation_idx ON recitals(regulation);
|
|
130
|
-
`;
|
|
131
|
-
|
|
132
|
-
interface MigrationStats {
|
|
133
|
-
tableName: string;
|
|
134
|
-
sqliteCount: number;
|
|
135
|
-
postgresCount: number;
|
|
136
|
-
status: 'ok' | 'mismatch';
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
async function migrate() {
|
|
140
|
-
console.log('🚀 Starting SQLite → PostgreSQL migration\n');
|
|
141
|
-
|
|
142
|
-
// Open SQLite database (read-only)
|
|
143
|
-
console.log(`📂 Opening SQLite database: ${SQLITE_DB_PATH}`);
|
|
144
|
-
const sqlite = new Database(SQLITE_DB_PATH, { readonly: true });
|
|
145
|
-
|
|
146
|
-
// Connect to PostgreSQL
|
|
147
|
-
console.log(`🔌 Connecting to PostgreSQL: ${PG_CONNECTION.replace(/\/\/.*@/, '//***@')}`);
|
|
148
|
-
const pgPool = new pg.Pool({ connectionString: PG_CONNECTION });
|
|
149
|
-
const pgClient = await pgPool.connect();
|
|
150
|
-
|
|
151
|
-
try {
|
|
152
|
-
// Create PostgreSQL schema
|
|
153
|
-
console.log('\n📝 Creating PostgreSQL schema...');
|
|
154
|
-
await pgClient.query(POSTGRES_SCHEMA);
|
|
155
|
-
console.log('✅ Schema created');
|
|
156
|
-
|
|
157
|
-
// Start transaction
|
|
158
|
-
await pgClient.query('BEGIN');
|
|
159
|
-
|
|
160
|
-
const stats: MigrationStats[] = [];
|
|
161
|
-
|
|
162
|
-
// Migrate regulations
|
|
163
|
-
console.log('\n📋 Migrating regulations...');
|
|
164
|
-
const regulations = sqlite.prepare('SELECT * FROM regulations').all();
|
|
165
|
-
for (const reg of regulations) {
|
|
166
|
-
await pgClient.query(
|
|
167
|
-
`INSERT INTO regulations (id, full_name, celex_id, effective_date, last_amended, eur_lex_url)
|
|
168
|
-
VALUES ($1, $2, $3, $4, $5, $6)
|
|
169
|
-
ON CONFLICT (id) DO NOTHING`,
|
|
170
|
-
[reg.id, reg.full_name, reg.celex_id, reg.effective_date, reg.last_amended, reg.eur_lex_url]
|
|
171
|
-
);
|
|
172
|
-
}
|
|
173
|
-
const pgRegCount = await pgClient.query('SELECT COUNT(*) FROM regulations');
|
|
174
|
-
stats.push({
|
|
175
|
-
tableName: 'regulations',
|
|
176
|
-
sqliteCount: regulations.length,
|
|
177
|
-
postgresCount: parseInt(pgRegCount.rows[0].count),
|
|
178
|
-
status: regulations.length === parseInt(pgRegCount.rows[0].count) ? 'ok' : 'mismatch'
|
|
179
|
-
});
|
|
180
|
-
console.log(` SQLite: ${regulations.length} rows`);
|
|
181
|
-
console.log(` Postgres: ${pgRegCount.rows[0].count} rows`);
|
|
182
|
-
|
|
183
|
-
// Migrate articles
|
|
184
|
-
console.log('\n📄 Migrating articles...');
|
|
185
|
-
const articles = sqlite.prepare('SELECT * FROM articles').all();
|
|
186
|
-
for (const article of articles) {
|
|
187
|
-
await pgClient.query(
|
|
188
|
-
`INSERT INTO articles (regulation, article_number, title, text, chapter, recitals, cross_references)
|
|
189
|
-
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
|
190
|
-
ON CONFLICT (regulation, article_number) DO NOTHING`,
|
|
191
|
-
[article.regulation, article.article_number, article.title, article.text,
|
|
192
|
-
article.chapter, article.recitals, article.cross_references]
|
|
193
|
-
);
|
|
194
|
-
}
|
|
195
|
-
const pgArticleCount = await pgClient.query('SELECT COUNT(*) FROM articles');
|
|
196
|
-
stats.push({
|
|
197
|
-
tableName: 'articles',
|
|
198
|
-
sqliteCount: articles.length,
|
|
199
|
-
postgresCount: parseInt(pgArticleCount.rows[0].count),
|
|
200
|
-
status: articles.length === parseInt(pgArticleCount.rows[0].count) ? 'ok' : 'mismatch'
|
|
201
|
-
});
|
|
202
|
-
console.log(` SQLite: ${articles.length} rows`);
|
|
203
|
-
console.log(` Postgres: ${pgArticleCount.rows[0].count} rows`);
|
|
204
|
-
|
|
205
|
-
// Migrate recitals
|
|
206
|
-
console.log('\n📜 Migrating recitals...');
|
|
207
|
-
const recitals = sqlite.prepare('SELECT * FROM recitals').all();
|
|
208
|
-
for (const recital of recitals) {
|
|
209
|
-
await pgClient.query(
|
|
210
|
-
`INSERT INTO recitals (regulation, recital_number, text, related_articles)
|
|
211
|
-
VALUES ($1, $2, $3, $4)
|
|
212
|
-
ON CONFLICT (regulation, recital_number) DO NOTHING`,
|
|
213
|
-
[recital.regulation, recital.recital_number, recital.text, recital.related_articles]
|
|
214
|
-
);
|
|
215
|
-
}
|
|
216
|
-
const pgRecitalCount = await pgClient.query('SELECT COUNT(*) FROM recitals');
|
|
217
|
-
stats.push({
|
|
218
|
-
tableName: 'recitals',
|
|
219
|
-
sqliteCount: recitals.length,
|
|
220
|
-
postgresCount: parseInt(pgRecitalCount.rows[0].count),
|
|
221
|
-
status: recitals.length === parseInt(pgRecitalCount.rows[0].count) ? 'ok' : 'mismatch'
|
|
222
|
-
});
|
|
223
|
-
console.log(` SQLite: ${recitals.length} rows`);
|
|
224
|
-
console.log(` Postgres: ${pgRecitalCount.rows[0].count} rows`);
|
|
225
|
-
|
|
226
|
-
// Migrate definitions
|
|
227
|
-
console.log('\n📚 Migrating definitions...');
|
|
228
|
-
const definitions = sqlite.prepare('SELECT * FROM definitions').all();
|
|
229
|
-
for (const def of definitions) {
|
|
230
|
-
await pgClient.query(
|
|
231
|
-
`INSERT INTO definitions (regulation, term, definition, article)
|
|
232
|
-
VALUES ($1, $2, $3, $4)
|
|
233
|
-
ON CONFLICT (regulation, term) DO NOTHING`,
|
|
234
|
-
[def.regulation, def.term, def.definition, def.article]
|
|
235
|
-
);
|
|
236
|
-
}
|
|
237
|
-
const pgDefCount = await pgClient.query('SELECT COUNT(*) FROM definitions');
|
|
238
|
-
stats.push({
|
|
239
|
-
tableName: 'definitions',
|
|
240
|
-
sqliteCount: definitions.length,
|
|
241
|
-
postgresCount: parseInt(pgDefCount.rows[0].count),
|
|
242
|
-
status: definitions.length === parseInt(pgDefCount.rows[0].count) ? 'ok' : 'mismatch'
|
|
243
|
-
});
|
|
244
|
-
console.log(` SQLite: ${definitions.length} rows`);
|
|
245
|
-
console.log(` Postgres: ${pgDefCount.rows[0].count} rows`);
|
|
246
|
-
|
|
247
|
-
// Migrate control mappings
|
|
248
|
-
console.log('\n🛡️ Migrating control mappings...');
|
|
249
|
-
const mappings = sqlite.prepare('SELECT * FROM control_mappings').all();
|
|
250
|
-
for (const mapping of mappings) {
|
|
251
|
-
await pgClient.query(
|
|
252
|
-
`INSERT INTO control_mappings (framework, control_id, control_name, regulation, articles, coverage, notes)
|
|
253
|
-
VALUES ($1, $2, $3, $4, $5, $6, $7)`,
|
|
254
|
-
[mapping.framework, mapping.control_id, mapping.control_name, mapping.regulation,
|
|
255
|
-
mapping.articles, mapping.coverage, mapping.notes]
|
|
256
|
-
);
|
|
257
|
-
}
|
|
258
|
-
const pgMappingCount = await pgClient.query('SELECT COUNT(*) FROM control_mappings');
|
|
259
|
-
stats.push({
|
|
260
|
-
tableName: 'control_mappings',
|
|
261
|
-
sqliteCount: mappings.length,
|
|
262
|
-
postgresCount: parseInt(pgMappingCount.rows[0].count),
|
|
263
|
-
status: mappings.length === parseInt(pgMappingCount.rows[0].count) ? 'ok' : 'mismatch'
|
|
264
|
-
});
|
|
265
|
-
console.log(` SQLite: ${mappings.length} rows`);
|
|
266
|
-
console.log(` Postgres: ${pgMappingCount.rows[0].count} rows`);
|
|
267
|
-
|
|
268
|
-
// Migrate applicability rules
|
|
269
|
-
console.log('\n✅ Migrating applicability rules...');
|
|
270
|
-
const rules = sqlite.prepare('SELECT * FROM applicability_rules').all();
|
|
271
|
-
for (const rule of rules) {
|
|
272
|
-
await pgClient.query(
|
|
273
|
-
`INSERT INTO applicability_rules (regulation, sector, subsector, applies, confidence, basis_article, notes)
|
|
274
|
-
VALUES ($1, $2, $3, $4, $5, $6, $7)`,
|
|
275
|
-
[rule.regulation, rule.sector, rule.subsector, rule.applies === 1,
|
|
276
|
-
rule.confidence, rule.basis_article, rule.notes]
|
|
277
|
-
);
|
|
278
|
-
}
|
|
279
|
-
const pgRuleCount = await pgClient.query('SELECT COUNT(*) FROM applicability_rules');
|
|
280
|
-
stats.push({
|
|
281
|
-
tableName: 'applicability_rules',
|
|
282
|
-
sqliteCount: rules.length,
|
|
283
|
-
postgresCount: parseInt(pgRuleCount.rows[0].count),
|
|
284
|
-
status: rules.length === parseInt(pgRuleCount.rows[0].count) ? 'ok' : 'mismatch'
|
|
285
|
-
});
|
|
286
|
-
console.log(` SQLite: ${rules.length} rows`);
|
|
287
|
-
console.log(` Postgres: ${pgRuleCount.rows[0].count} rows`);
|
|
288
|
-
|
|
289
|
-
// Migrate source registry
|
|
290
|
-
console.log('\n📊 Migrating source registry...');
|
|
291
|
-
const registry = sqlite.prepare('SELECT * FROM source_registry').all();
|
|
292
|
-
for (const entry of registry) {
|
|
293
|
-
await pgClient.query(
|
|
294
|
-
`INSERT INTO source_registry (regulation, celex_id, eur_lex_version, last_fetched, articles_expected, articles_parsed, quality_status, notes)
|
|
295
|
-
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
|
296
|
-
ON CONFLICT (regulation) DO NOTHING`,
|
|
297
|
-
[entry.regulation, entry.celex_id, entry.eur_lex_version, entry.last_fetched,
|
|
298
|
-
entry.articles_expected, entry.articles_parsed, entry.quality_status, entry.notes]
|
|
299
|
-
);
|
|
300
|
-
}
|
|
301
|
-
const pgRegisCount = await pgClient.query('SELECT COUNT(*) FROM source_registry');
|
|
302
|
-
stats.push({
|
|
303
|
-
tableName: 'source_registry',
|
|
304
|
-
sqliteCount: registry.length,
|
|
305
|
-
postgresCount: parseInt(pgRegisCount.rows[0].count),
|
|
306
|
-
status: registry.length === parseInt(pgRegisCount.rows[0].count) ? 'ok' : 'mismatch'
|
|
307
|
-
});
|
|
308
|
-
console.log(` SQLite: ${registry.length} rows`);
|
|
309
|
-
console.log(` Postgres: ${pgRegisCount.rows[0].count} rows`);
|
|
310
|
-
|
|
311
|
-
// Commit transaction
|
|
312
|
-
await pgClient.query('COMMIT');
|
|
313
|
-
|
|
314
|
-
// Summary
|
|
315
|
-
console.log('\n' + '='.repeat(60));
|
|
316
|
-
console.log('📊 Migration Summary');
|
|
317
|
-
console.log('='.repeat(60));
|
|
318
|
-
|
|
319
|
-
let allOk = true;
|
|
320
|
-
for (const stat of stats) {
|
|
321
|
-
const emoji = stat.status === 'ok' ? '✅' : '❌';
|
|
322
|
-
console.log(`${emoji} ${stat.tableName.padEnd(20)} | SQLite: ${String(stat.sqliteCount).padStart(5)} | Postgres: ${String(stat.postgresCount).padStart(5)}`);
|
|
323
|
-
if (stat.status === 'mismatch') allOk = false;
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
console.log('='.repeat(60));
|
|
327
|
-
|
|
328
|
-
if (allOk) {
|
|
329
|
-
console.log('\n✅ Migration completed successfully!');
|
|
330
|
-
console.log('\n💡 Next steps:');
|
|
331
|
-
console.log(' 1. Update DATABASE_URL in your .env file');
|
|
332
|
-
console.log(' 2. Test queries with: npm run dev:api');
|
|
333
|
-
console.log(' 3. Keep SQLite database as backup');
|
|
334
|
-
} else {
|
|
335
|
-
console.log('\n⚠️ Migration completed with mismatches. Please review.');
|
|
336
|
-
process.exit(1);
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
} catch (error) {
|
|
340
|
-
await pgClient.query('ROLLBACK');
|
|
341
|
-
console.error('\n❌ Migration failed:', error);
|
|
342
|
-
throw error;
|
|
343
|
-
} finally {
|
|
344
|
-
sqlite.close();
|
|
345
|
-
pgClient.release();
|
|
346
|
-
await pgPool.end();
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
migrate().catch(err => {
|
|
351
|
-
console.error('Fatal error:', err);
|
|
352
|
-
process.exit(1);
|
|
353
|
-
});
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
|
|
3
|
-
# Re-ingest all 37 regulations to add recitals
|
|
4
|
-
# Run this script from the project root
|
|
5
|
-
|
|
6
|
-
echo "Re-ingesting all EU regulations with recitals..."
|
|
7
|
-
echo "This will take 5-10 minutes..."
|
|
8
|
-
echo ""
|
|
9
|
-
|
|
10
|
-
REGULATIONS=(
|
|
11
|
-
"32016R0679:gdpr"
|
|
12
|
-
"32022L2555:nis2"
|
|
13
|
-
"32022R2554:dora"
|
|
14
|
-
"32024R1689:ai-act"
|
|
15
|
-
"32024R2847:cra"
|
|
16
|
-
"32019R0881:cybersecurity-act"
|
|
17
|
-
"32025R0038:cyber_solidarity"
|
|
18
|
-
"02002L0058-20091219:eprivacy"
|
|
19
|
-
"32016L0680:led"
|
|
20
|
-
"32024R0482:eucc"
|
|
21
|
-
"02014R0910-20241018:eidas2"
|
|
22
|
-
"32023R2854:data-act"
|
|
23
|
-
"32022R2065:dsa"
|
|
24
|
-
"32022R1925:dma"
|
|
25
|
-
"32022R0868:dga"
|
|
26
|
-
"32018L1972:eecc"
|
|
27
|
-
"32025R0327:ehds"
|
|
28
|
-
"32017R0745:mdr"
|
|
29
|
-
"32017R0746:ivdr"
|
|
30
|
-
"32023R1114:mica"
|
|
31
|
-
"32015L2366:psd2"
|
|
32
|
-
"32014L0065:mifid2"
|
|
33
|
-
"32014R0600:mifir"
|
|
34
|
-
"32011L0061:aifmd"
|
|
35
|
-
"32019R2088:sfdr"
|
|
36
|
-
"32020R0852:eu_taxonomy"
|
|
37
|
-
"32023R0988:gpsr"
|
|
38
|
-
"32023R1230:machinery"
|
|
39
|
-
"32024L2853:pld"
|
|
40
|
-
"32014L0053:red"
|
|
41
|
-
"32022L2464:csrd"
|
|
42
|
-
"32024L1760:csddd"
|
|
43
|
-
"32023R0956:cbam"
|
|
44
|
-
"32023R1115:eudr"
|
|
45
|
-
"32022L2557:cer"
|
|
46
|
-
"42021X0387:un-r155"
|
|
47
|
-
"42021X0388:un-r156"
|
|
48
|
-
)
|
|
49
|
-
|
|
50
|
-
TOTAL=${#REGULATIONS[@]}
|
|
51
|
-
CURRENT=0
|
|
52
|
-
|
|
53
|
-
for reg in "${REGULATIONS[@]}"; do
|
|
54
|
-
CURRENT=$((CURRENT + 1))
|
|
55
|
-
IFS=':' read -r celex filename <<< "$reg"
|
|
56
|
-
|
|
57
|
-
echo "[$CURRENT/$TOTAL] Re-ingesting $filename (CELEX: $celex)"
|
|
58
|
-
|
|
59
|
-
if [[ "$celex" == 42021X* ]]; then
|
|
60
|
-
# UN/ECE regulations use different ingestion script
|
|
61
|
-
npx tsx scripts/ingest-unece.ts "$celex" "data/seed/${filename}.json"
|
|
62
|
-
else
|
|
63
|
-
# EU regulations use EUR-Lex ingestion
|
|
64
|
-
npx tsx scripts/ingest-eurlex.ts "$celex" "data/seed/${filename}.json"
|
|
65
|
-
fi
|
|
66
|
-
|
|
67
|
-
if [ $? -ne 0 ]; then
|
|
68
|
-
echo " ⚠️ Warning: Failed to ingest $filename"
|
|
69
|
-
fi
|
|
70
|
-
echo ""
|
|
71
|
-
done
|
|
72
|
-
|
|
73
|
-
echo "Re-building database..."
|
|
74
|
-
npm run build:db
|
|
75
|
-
|
|
76
|
-
echo ""
|
|
77
|
-
echo "✅ Done! Checking recital counts..."
|
|
78
|
-
sqlite3 data/regulations.db "SELECT regulation, COUNT(*) as recital_count FROM recitals GROUP BY regulation ORDER BY recital_count DESC LIMIT 10;"
|
|
79
|
-
|
|
80
|
-
echo ""
|
|
81
|
-
echo "Run 'npm test' to verify everything works."
|
package/scripts/sync-versions.ts
DELETED
|
@@ -1,206 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env tsx
|
|
2
|
-
/**
|
|
3
|
-
* Sync versions across all workspace packages and manifests
|
|
4
|
-
*
|
|
5
|
-
* Usage:
|
|
6
|
-
* npm run sync-versions
|
|
7
|
-
* npx tsx scripts/sync-versions.ts
|
|
8
|
-
*
|
|
9
|
-
* This script:
|
|
10
|
-
* 1. Reads version from root package.json (source of truth)
|
|
11
|
-
* 2. Updates all workspace package.json files
|
|
12
|
-
* 3. Updates Teams manifest version
|
|
13
|
-
* 4. Validates consistency
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
import { readFileSync, writeFileSync } from 'fs';
|
|
17
|
-
import { join, dirname } from 'path';
|
|
18
|
-
import { fileURLToPath } from 'url';
|
|
19
|
-
import { glob } from 'glob';
|
|
20
|
-
|
|
21
|
-
interface PackageJson {
|
|
22
|
-
name: string;
|
|
23
|
-
version: string;
|
|
24
|
-
[key: string]: any;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
interface TeamsManifest {
|
|
28
|
-
version: string;
|
|
29
|
-
[key: string]: any;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
interface ServerJson {
|
|
33
|
-
version: string;
|
|
34
|
-
packages?: Array<{
|
|
35
|
-
version: string;
|
|
36
|
-
[key: string]: any;
|
|
37
|
-
}>;
|
|
38
|
-
[key: string]: any;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
42
|
-
const __dirname = dirname(__filename);
|
|
43
|
-
const ROOT_DIR = join(__dirname, '..');
|
|
44
|
-
|
|
45
|
-
function readJson<T>(path: string): T {
|
|
46
|
-
const content = readFileSync(path, 'utf-8');
|
|
47
|
-
return JSON.parse(content);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function writeJson(path: string, data: any): void {
|
|
51
|
-
const content = JSON.stringify(data, null, 2) + '\n';
|
|
52
|
-
writeFileSync(path, content, 'utf-8');
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
async function main() {
|
|
56
|
-
console.log('🔄 Syncing versions across workspace...\n');
|
|
57
|
-
|
|
58
|
-
// 1. Read root version (source of truth)
|
|
59
|
-
const rootPkgPath = join(ROOT_DIR, 'package.json');
|
|
60
|
-
const rootPkg = readJson<PackageJson>(rootPkgPath);
|
|
61
|
-
const targetVersion = rootPkg.version;
|
|
62
|
-
|
|
63
|
-
console.log(`📦 Root version: ${targetVersion}`);
|
|
64
|
-
console.log(` Package: ${rootPkg.name}\n`);
|
|
65
|
-
|
|
66
|
-
// 2. Find all workspace packages
|
|
67
|
-
const workspacePackages = await glob('packages/*/package.json', { cwd: ROOT_DIR });
|
|
68
|
-
|
|
69
|
-
let updatedCount = 0;
|
|
70
|
-
let alreadyCurrentCount = 0;
|
|
71
|
-
|
|
72
|
-
console.log('📝 Updating workspace packages:');
|
|
73
|
-
|
|
74
|
-
for (const pkgPath of workspacePackages) {
|
|
75
|
-
const fullPath = join(ROOT_DIR, pkgPath);
|
|
76
|
-
const pkg = readJson<PackageJson>(fullPath);
|
|
77
|
-
|
|
78
|
-
if (pkg.version !== targetVersion) {
|
|
79
|
-
const oldVersion = pkg.version;
|
|
80
|
-
pkg.version = targetVersion;
|
|
81
|
-
writeJson(fullPath, pkg);
|
|
82
|
-
console.log(` ✅ ${pkg.name}: ${oldVersion} → ${targetVersion}`);
|
|
83
|
-
updatedCount++;
|
|
84
|
-
} else {
|
|
85
|
-
console.log(` ⏭️ ${pkg.name}: ${targetVersion} (already current)`);
|
|
86
|
-
alreadyCurrentCount++;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// 3. Update server.json (MCP registry metadata)
|
|
91
|
-
const serverJsonPath = join(ROOT_DIR, 'server.json');
|
|
92
|
-
const serverJson = readJson<ServerJson>(serverJsonPath);
|
|
93
|
-
|
|
94
|
-
console.log('\n🌐 Updating MCP server.json:');
|
|
95
|
-
|
|
96
|
-
let serverJsonUpdated = false;
|
|
97
|
-
if (serverJson.version !== targetVersion) {
|
|
98
|
-
const oldVersion = serverJson.version;
|
|
99
|
-
serverJson.version = targetVersion;
|
|
100
|
-
console.log(` ✅ server.json version: ${oldVersion} → ${targetVersion}`);
|
|
101
|
-
serverJsonUpdated = true;
|
|
102
|
-
} else {
|
|
103
|
-
console.log(` ⏭️ server.json version: ${targetVersion} (already current)`);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// Update package versions in server.json
|
|
107
|
-
if (serverJson.packages) {
|
|
108
|
-
for (const pkg of serverJson.packages) {
|
|
109
|
-
if (pkg.version !== targetVersion) {
|
|
110
|
-
const oldVersion = pkg.version;
|
|
111
|
-
pkg.version = targetVersion;
|
|
112
|
-
console.log(` ✅ package "${pkg.identifier || 'unknown'}": ${oldVersion} → ${targetVersion}`);
|
|
113
|
-
serverJsonUpdated = true;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
if (serverJsonUpdated) {
|
|
119
|
-
writeJson(serverJsonPath, serverJson);
|
|
120
|
-
updatedCount++;
|
|
121
|
-
} else {
|
|
122
|
-
alreadyCurrentCount++;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// 4. Update Teams manifest
|
|
126
|
-
const teamsManifestPath = join(ROOT_DIR, 'packages/teams-extension/manifest.json');
|
|
127
|
-
const teamsManifest = readJson<TeamsManifest>(teamsManifestPath);
|
|
128
|
-
|
|
129
|
-
console.log('\n📱 Updating Teams manifest:');
|
|
130
|
-
|
|
131
|
-
if (teamsManifest.version !== targetVersion) {
|
|
132
|
-
const oldVersion = teamsManifest.version;
|
|
133
|
-
teamsManifest.version = targetVersion;
|
|
134
|
-
writeJson(teamsManifestPath, teamsManifest);
|
|
135
|
-
console.log(` ✅ Teams manifest: ${oldVersion} → ${targetVersion}`);
|
|
136
|
-
updatedCount++;
|
|
137
|
-
} else {
|
|
138
|
-
console.log(` ⏭️ Teams manifest: ${targetVersion} (already current)`);
|
|
139
|
-
alreadyCurrentCount++;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// 5. Validation
|
|
143
|
-
console.log('\n🔍 Validating consistency...');
|
|
144
|
-
|
|
145
|
-
const allPackages = await glob('{package.json,packages/*/package.json}', { cwd: ROOT_DIR });
|
|
146
|
-
let inconsistencies = 0;
|
|
147
|
-
|
|
148
|
-
for (const pkgPath of allPackages) {
|
|
149
|
-
const fullPath = join(ROOT_DIR, pkgPath);
|
|
150
|
-
const pkg = readJson<PackageJson>(fullPath);
|
|
151
|
-
|
|
152
|
-
if (pkg.version !== targetVersion) {
|
|
153
|
-
console.log(` ❌ ${pkg.name}: ${pkg.version} (expected ${targetVersion})`);
|
|
154
|
-
inconsistencies++;
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// Re-read server.json for validation
|
|
159
|
-
const serverJsonValidation = readJson<ServerJson>(serverJsonPath);
|
|
160
|
-
if (serverJsonValidation.version !== targetVersion) {
|
|
161
|
-
console.log(` ❌ server.json: ${serverJsonValidation.version} (expected ${targetVersion})`);
|
|
162
|
-
inconsistencies++;
|
|
163
|
-
}
|
|
164
|
-
if (serverJsonValidation.packages) {
|
|
165
|
-
for (const pkg of serverJsonValidation.packages) {
|
|
166
|
-
if (pkg.version !== targetVersion) {
|
|
167
|
-
console.log(` ❌ server.json package "${pkg.identifier || 'unknown'}": ${pkg.version} (expected ${targetVersion})`);
|
|
168
|
-
inconsistencies++;
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
if (teamsManifest.version !== targetVersion) {
|
|
174
|
-
console.log(` ❌ Teams manifest: ${teamsManifest.version} (expected ${targetVersion})`);
|
|
175
|
-
inconsistencies++;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
// 6. Summary
|
|
179
|
-
console.log('\n' + '='.repeat(50));
|
|
180
|
-
console.log('📊 Summary:');
|
|
181
|
-
console.log(` Target version: ${targetVersion}`);
|
|
182
|
-
console.log(` Updated: ${updatedCount} files`);
|
|
183
|
-
console.log(` Already current: ${alreadyCurrentCount} files`);
|
|
184
|
-
console.log(` Inconsistencies: ${inconsistencies}`);
|
|
185
|
-
console.log('='.repeat(50));
|
|
186
|
-
|
|
187
|
-
if (inconsistencies > 0) {
|
|
188
|
-
console.error('\n❌ Version sync failed - inconsistencies found!');
|
|
189
|
-
process.exit(1);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
if (updatedCount > 0) {
|
|
193
|
-
console.log('\n✅ Version sync complete!');
|
|
194
|
-
console.log('\n💡 Next steps:');
|
|
195
|
-
console.log(' 1. Review changes: git diff');
|
|
196
|
-
console.log(' 2. Test builds: pnpm -r build && pnpm test');
|
|
197
|
-
console.log(' 3. Commit: git add . && git commit -m "chore: sync versions to ' + targetVersion + '"');
|
|
198
|
-
} else {
|
|
199
|
-
console.log('\n✅ All versions already synchronized!');
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
main().catch((error) => {
|
|
204
|
-
console.error('❌ Error:', error.message);
|
|
205
|
-
process.exit(1);
|
|
206
|
-
});
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import Database from 'better-sqlite3';
|
|
2
|
-
import { getArticle } from '../dist/tools/article.js';
|
|
3
|
-
|
|
4
|
-
const db = new Database('data/regulations.db', { readonly: true });
|
|
5
|
-
|
|
6
|
-
// Test DORA Article 17 cross-references
|
|
7
|
-
const dora17 = await getArticle(db, { regulation: 'DORA', article: '17' });
|
|
8
|
-
console.log('DORA Article 17:');
|
|
9
|
-
console.log(' Title:', dora17.title);
|
|
10
|
-
console.log(' Cross-refs:', dora17.cross_references);
|
|
11
|
-
console.log();
|
|
12
|
-
|
|
13
|
-
// Test NIS2 Article 23 cross-references
|
|
14
|
-
const nis23 = await getArticle(db, { regulation: 'NIS2', article: '23' });
|
|
15
|
-
console.log('NIS2 Article 23:');
|
|
16
|
-
console.log(' Title:', nis23.title);
|
|
17
|
-
console.log(' Cross-refs:', nis23.cross_references);
|
|
18
|
-
console.log();
|
|
19
|
-
|
|
20
|
-
// Test GDPR Article 33 cross-references
|
|
21
|
-
const gdpr33 = await getArticle(db, { regulation: 'GDPR', article: '33' });
|
|
22
|
-
console.log('GDPR Article 33:');
|
|
23
|
-
console.log(' Title:', gdpr33.title);
|
|
24
|
-
console.log(' Cross-refs:', gdpr33.cross_references);
|
|
25
|
-
|
|
26
|
-
db.close();
|