@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.
Files changed (58) hide show
  1. package/README.md +60 -22
  2. package/data/regulations.db +0 -0
  3. package/dist/database/sqlite-adapter.d.ts +2 -2
  4. package/dist/database/sqlite-adapter.d.ts.map +1 -1
  5. package/dist/database/sqlite-adapter.js.map +1 -1
  6. package/dist/http-server.js +27 -5
  7. package/dist/http-server.js.map +1 -1
  8. package/dist/index.js +27 -4
  9. package/dist/index.js.map +1 -1
  10. package/dist/tools/about.d.ts +40 -0
  11. package/dist/tools/about.d.ts.map +1 -0
  12. package/dist/tools/about.js +61 -0
  13. package/dist/tools/about.js.map +1 -0
  14. package/dist/tools/list.d.ts +7 -0
  15. package/dist/tools/list.d.ts.map +1 -1
  16. package/dist/tools/list.js +73 -8
  17. package/dist/tools/list.js.map +1 -1
  18. package/dist/tools/registry.d.ts +11 -1
  19. package/dist/tools/registry.d.ts.map +1 -1
  20. package/dist/tools/registry.js +56 -4
  21. package/dist/tools/registry.js.map +1 -1
  22. package/dist/worker.d.ts.map +1 -1
  23. package/dist/worker.js +17 -5
  24. package/dist/worker.js.map +1 -1
  25. package/package.json +6 -5
  26. package/scripts/add-cross-references.sql +0 -200
  27. package/scripts/analyze-survey-responses.ts +0 -285
  28. package/scripts/build-db.ts +0 -421
  29. package/scripts/bulk-reingest-all.ts +0 -331
  30. package/scripts/check-updates.ts +0 -294
  31. package/scripts/extract-eprivacy-recitals.ts +0 -98
  32. package/scripts/ingest-eurlex-browser.ts +0 -113
  33. package/scripts/ingest-eurlex.ts +0 -349
  34. package/scripts/ingest-unece.ts +0 -382
  35. package/scripts/migrate-postgres.ts +0 -445
  36. package/scripts/migrate-to-postgres.ts +0 -353
  37. package/scripts/reingest-all-with-recitals.sh +0 -81
  38. package/scripts/sync-versions.ts +0 -206
  39. package/scripts/test-cross-refs.js +0 -26
  40. package/scripts/test-postgres-adapter.ts +0 -146
  41. package/scripts/update-dora-rts-metadata.ts +0 -112
  42. package/src/database/postgres-adapter.ts +0 -84
  43. package/src/database/sqlite-adapter.ts +0 -44
  44. package/src/database/types.ts +0 -10
  45. package/src/http-server.ts +0 -149
  46. package/src/index.ts +0 -61
  47. package/src/middleware/rate-limit.ts +0 -104
  48. package/src/tools/applicability.ts +0 -167
  49. package/src/tools/article.ts +0 -81
  50. package/src/tools/compare.ts +0 -217
  51. package/src/tools/definitions.ts +0 -49
  52. package/src/tools/evidence.ts +0 -84
  53. package/src/tools/list.ts +0 -124
  54. package/src/tools/map.ts +0 -86
  55. package/src/tools/recital.ts +0 -60
  56. package/src/tools/registry.ts +0 -311
  57. package/src/tools/search.ts +0 -297
  58. 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."
@@ -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();