Files
worldmonitor/package.json
Elie Habib 9a0b0ccef8 fix(geo): tokenization-based keyword matching to prevent false positives (#503)
* fix(geo): tokenization-based keyword matching to prevent false positives

Replace String.includes() with tokenization-based Set.has() matching
across the geo-tagging pipeline. Prevents false positives like "assad"
matching inside "ambassador" and "hts" matching inside "rights".

- Add src/utils/keyword-match.ts as single source of truth
- Decompose possessives/hyphens ("Assad's" → includes "assad")
- Support multi-word phrase matching ("white house" as contiguous)
- Remove false-positive-prone DC keywords ('house', 'us ')
- Update 9 consumer files across geo-hub, map, CII, and asset systems
- Add 44 tests covering false positives, true positives, edge cases

Co-authored-by: karim <mirakijka@gmail.com>
Fixes #324

* fix(geo): add inflection suffix matching + fix test imports

Address code review feedback:

P1a: Add suffix-aware matching for plurals and demonyms so existing
keyword lists don't regress (houthi→houthis, ukraine→ukrainian,
iran→iranian, israel→israeli, russia→russian, taiwan→taiwanese).
Uses curated suffix list + e-dropping rule to avoid false positives.

P1b: Expand conflictTopics arrays in DeckGLMap and Map with demonym
forms so "Iranian senate..." correctly registers as conflict topic.

P2: Replace inline test functions with real module import via tsx.
Tests now exercise the production keyword-match.ts directly.

* fix: wire geo-keyword tests into test:data command

The .mts test file wasn't covered by `node --test tests/*.test.mjs`.
Add `npx tsx --test tests/*.test.mts` so test:data runs both suites.

* fix: cross-platform test:data + pin tsx in devDependencies

- Use tsx as test runner for both .mjs and .mts (single invocation)
- Removes ; separator which breaks on Windows cmd.exe
- Add tsx to devDependencies so it works in offline/CI environments

* fix(geo): multi-word demonym matching + short-keyword suffix guard

- Add wordMatches() for suffix-aware phrase matching so "South Korean"
  matches keyword "south korea" and "North Korean" matches "north korea"
- Add MIN_SUFFIX_KEYWORD_LEN=4 guard so short keywords like "ai", "us",
  "hts" only do exact-match (prevents "ais"→"ai", "uses"→"us" false positives)
- Add 5 new tests covering both fixes (58 total, all passing)

* fix(geo): support plural demonyms in keyword matching

Add compound suffixes (ians, eans, ans, ns, is) to handle plural
demonym forms like "Iranians"→"iran", "Ukrainians"→"ukraine",
"Russians"→"russia", "Israelis"→"israel". Adds 5 new tests (63 total).

---------

Co-authored-by: karim <mirakijka@gmail.com>
2026-02-28 10:58:53 +04:00

102 lines
5.6 KiB
JSON

{
"name": "world-monitor",
"private": true,
"version": "2.5.20",
"license": "AGPL-3.0-only",
"type": "module",
"scripts": {
"lint:md": "markdownlint-cli2 '**/*.md' '!.agent/**' '!.agents/**' '!.claude/**' '!.factory/**' '!.windsurf/**' '!skills/**' '!docs/internal/**' '!docs/Docs_To_Review/**'",
"version:sync": "node scripts/sync-desktop-version.mjs",
"version:check": "node scripts/sync-desktop-version.mjs --check",
"dev": "vite",
"dev:tech": "cross-env VITE_VARIANT=tech vite",
"dev:finance": "cross-env VITE_VARIANT=finance vite",
"dev:happy": "cross-env VITE_VARIANT=happy vite",
"build": "tsc && vite build",
"build:sidecar-sebuf": "node scripts/build-sidecar-sebuf.mjs",
"build:desktop": "node scripts/build-sidecar-sebuf.mjs && tsc && vite build",
"build:full": "cross-env-shell VITE_VARIANT=full \"tsc && vite build\"",
"build:tech": "cross-env-shell VITE_VARIANT=tech \"tsc && vite build\"",
"build:finance": "cross-env-shell VITE_VARIANT=finance \"tsc && vite build\"",
"build:happy": "cross-env-shell VITE_VARIANT=happy \"tsc && vite build\"",
"typecheck": "tsc --noEmit",
"typecheck:api": "tsc --noEmit -p tsconfig.api.json",
"typecheck:all": "tsc --noEmit && tsc --noEmit -p tsconfig.api.json",
"tauri": "tauri",
"preview": "vite preview",
"test:e2e:full": "cross-env VITE_VARIANT=full playwright test",
"test:e2e:tech": "cross-env VITE_VARIANT=tech playwright test",
"test:e2e:finance": "cross-env VITE_VARIANT=finance playwright test",
"test:e2e:runtime": "cross-env VITE_VARIANT=full playwright test e2e/runtime-fetch.spec.ts",
"test:e2e": "npm run test:e2e:runtime && npm run test:e2e:full && npm run test:e2e:tech && npm run test:e2e:finance",
"test:data": "tsx --test tests/*.test.mjs tests/*.test.mts",
"test:feeds": "node scripts/validate-rss-feeds.mjs",
"test:sidecar": "node --test src-tauri/sidecar/local-api-server.test.mjs api/_cors.test.mjs api/youtube/embed.test.mjs api/cyber-threats.test.mjs api/usni-fleet.test.mjs scripts/ais-relay-rss.test.cjs api/loaders-xml-wms-regression.test.mjs",
"test:e2e:visual:full": "cross-env VITE_VARIANT=full playwright test -g \"matches golden screenshots per layer and zoom\"",
"test:e2e:visual:tech": "cross-env VITE_VARIANT=tech playwright test -g \"matches golden screenshots per layer and zoom\"",
"test:e2e:visual": "npm run test:e2e:visual:full && npm run test:e2e:visual:tech",
"test:e2e:visual:update:full": "cross-env VITE_VARIANT=full playwright test -g \"matches golden screenshots per layer and zoom\" --update-snapshots",
"test:e2e:visual:update:tech": "cross-env VITE_VARIANT=tech playwright test -g \"matches golden screenshots per layer and zoom\" --update-snapshots",
"test:e2e:visual:update": "npm run test:e2e:visual:update:full && npm run test:e2e:visual:update:tech",
"desktop:dev": "npm run version:sync && cross-env VITE_DESKTOP_RUNTIME=1 tauri dev -f devtools",
"desktop:build:full": "npm run version:sync && cross-env VITE_VARIANT=full VITE_DESKTOP_RUNTIME=1 tauri build",
"desktop:build:tech": "npm run version:sync && cross-env VITE_VARIANT=tech VITE_DESKTOP_RUNTIME=1 tauri build --config src-tauri/tauri.tech.conf.json",
"desktop:build:finance": "npm run version:sync && cross-env VITE_VARIANT=finance VITE_DESKTOP_RUNTIME=1 tauri build --config src-tauri/tauri.finance.conf.json",
"desktop:package:macos:full": "node scripts/desktop-package.mjs --os macos --variant full",
"desktop:package:macos:tech": "node scripts/desktop-package.mjs --os macos --variant tech",
"desktop:package:windows:full": "node scripts/desktop-package.mjs --os windows --variant full",
"desktop:package:windows:tech": "node scripts/desktop-package.mjs --os windows --variant tech",
"desktop:package:macos:full:sign": "node scripts/desktop-package.mjs --os macos --variant full --sign",
"desktop:package:macos:tech:sign": "node scripts/desktop-package.mjs --os macos --variant tech --sign",
"desktop:package:windows:full:sign": "node scripts/desktop-package.mjs --os windows --variant full --sign",
"desktop:package:windows:tech:sign": "node scripts/desktop-package.mjs --os windows --variant tech --sign",
"desktop:package": "node scripts/desktop-package.mjs"
},
"devDependencies": {
"@playwright/test": "^1.52.0",
"@tauri-apps/cli": "^2.10.0",
"@types/canvas-confetti": "^1.9.0",
"@types/d3": "^7.4.3",
"@types/maplibre-gl": "^1.13.2",
"@types/papaparse": "^5.5.2",
"@types/topojson-client": "^3.1.5",
"@types/topojson-specification": "^1.0.5",
"cross-env": "^10.1.0",
"esbuild": "^0.27.3",
"markdownlint-cli2": "^0.20.0",
"tsx": "^4.21.0",
"typescript": "^5.7.2",
"vite": "^6.0.7",
"vite-plugin-pwa": "^1.2.0",
"ws": "^8.19.0"
},
"dependencies": {
"@deck.gl/aggregation-layers": "^9.2.6",
"@deck.gl/core": "^9.2.6",
"@deck.gl/geo-layers": "^9.2.6",
"@deck.gl/layers": "^9.2.6",
"@deck.gl/mapbox": "^9.2.6",
"@sentry/browser": "^10.39.0",
"@upstash/ratelimit": "^2.0.8",
"@upstash/redis": "^1.36.1",
"@vercel/analytics": "^1.6.1",
"@xenova/transformers": "^2.17.2",
"canvas-confetti": "^1.9.4",
"convex": "^1.32.0",
"d3": "^7.9.0",
"deck.gl": "^9.2.6",
"fast-xml-parser": "^5.3.7",
"i18next": "^25.8.10",
"i18next-browser-languagedetector": "^8.2.1",
"maplibre-gl": "^5.16.0",
"onnxruntime-web": "^1.23.2",
"papaparse": "^5.5.3",
"posthog-js": "^1.352.0",
"topojson-client": "^3.1.0",
"youtubei.js": "^16.0.1"
},
"overrides": {
"fast-xml-parser": "^5.3.7"
}
}