euparliamentmonitor 0.9.19 → 0.9.21
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 +2 -2
- package/package.json +4 -3
- package/scripts/aggregator/editorial-brief-resolver.d.ts +38 -0
- package/scripts/aggregator/editorial-brief-resolver.js +32 -0
- package/scripts/aggregator/generator/render-one.js +35 -0
- package/scripts/aggregator/html/localize-body.d.ts +32 -0
- package/scripts/aggregator/html/localize-body.js +69 -0
- package/scripts/aggregator/html/shell.d.ts +10 -0
- package/scripts/aggregator/html/shell.js +11 -1
- package/scripts/aggregator/markdown-renderer.d.ts +23 -24
- package/scripts/aggregator/markdown-renderer.js +39 -25
- package/scripts/aggregator/metadata/artifact-highlight.d.ts +15 -22
- package/scripts/aggregator/metadata/artifact-highlight.js +14 -230
- package/scripts/aggregator/metadata/artifact-walker.d.ts +34 -0
- package/scripts/aggregator/metadata/artifact-walker.js +177 -0
- package/scripts/aggregator/metadata/editorial-highlight.d.ts +15 -0
- package/scripts/aggregator/metadata/editorial-highlight.js +53 -0
- package/scripts/aggregator/metadata/priority-finding-highlight.js +7 -2
- package/scripts/aggregator/metadata/resolve-helpers.js +9 -3
- package/scripts/aggregator/metadata/text-utils.js +7 -0
- package/scripts/aggregator/metadata/translated-sibling.d.ts +23 -0
- package/scripts/aggregator/metadata/translated-sibling.js +39 -0
- package/scripts/aggregator/reader-guide/builder.js +3 -1
- package/scripts/aggregator/reader-guide/labels.d.ts +7 -0
- package/scripts/aggregator/reader-guide/labels.js +22 -0
- package/scripts/aggregator/reader-intelligence-guide.d.ts +1 -1
- package/scripts/aggregator/reader-intelligence-guide.js +1 -1
- package/scripts/aggregator/seo-entity-extractor.d.ts +45 -0
- package/scripts/aggregator/seo-entity-extractor.js +211 -0
- package/scripts/constants/articles/breaking-strings-central.d.ts +8 -0
- package/scripts/constants/articles/breaking-strings-central.js +105 -0
- package/scripts/constants/articles/breaking-strings-east.d.ts +8 -0
- package/scripts/constants/articles/breaking-strings-east.js +203 -0
- package/scripts/constants/articles/breaking-strings-nordic.d.ts +8 -0
- package/scripts/constants/articles/breaking-strings-nordic.js +252 -0
- package/scripts/constants/articles/breaking-strings-west.d.ts +8 -0
- package/scripts/constants/articles/breaking-strings-west.js +154 -0
- package/scripts/constants/articles/breaking.d.ts +0 -1
- package/scripts/constants/articles/breaking.js +9 -6
- package/scripts/constants/articles/dashboard/ar.d.ts +8 -0
- package/scripts/constants/articles/dashboard/ar.js +71 -0
- package/scripts/constants/articles/dashboard/da.d.ts +8 -0
- package/scripts/constants/articles/dashboard/da.js +71 -0
- package/scripts/constants/articles/dashboard/de.d.ts +8 -0
- package/scripts/constants/articles/dashboard/de.js +71 -0
- package/scripts/constants/articles/dashboard/en.d.ts +8 -0
- package/scripts/constants/articles/dashboard/en.js +71 -0
- package/scripts/constants/articles/dashboard/es.d.ts +8 -0
- package/scripts/constants/articles/dashboard/es.js +71 -0
- package/scripts/constants/articles/dashboard/fi.d.ts +8 -0
- package/scripts/constants/articles/dashboard/fi.js +71 -0
- package/scripts/constants/articles/dashboard/fr.d.ts +8 -0
- package/scripts/constants/articles/dashboard/fr.js +71 -0
- package/scripts/constants/articles/dashboard/he.d.ts +8 -0
- package/scripts/constants/articles/dashboard/he.js +71 -0
- package/scripts/constants/articles/dashboard/index.d.ts +7 -0
- package/scripts/constants/articles/dashboard/index.js +33 -0
- package/scripts/constants/articles/dashboard/ja.d.ts +8 -0
- package/scripts/constants/articles/dashboard/ja.js +71 -0
- package/scripts/constants/articles/dashboard/ko.d.ts +8 -0
- package/scripts/constants/articles/dashboard/ko.js +71 -0
- package/scripts/constants/articles/dashboard/nl.d.ts +8 -0
- package/scripts/constants/articles/dashboard/nl.js +71 -0
- package/scripts/constants/articles/dashboard/no.d.ts +8 -0
- package/scripts/constants/articles/dashboard/no.js +71 -0
- package/scripts/constants/articles/dashboard/sv.d.ts +8 -0
- package/scripts/constants/articles/dashboard/sv.js +71 -0
- package/scripts/constants/articles/dashboard/zh.d.ts +8 -0
- package/scripts/constants/articles/dashboard/zh.js +71 -0
- package/scripts/constants/articles/dashboard.d.ts +7 -2
- package/scripts/constants/articles/dashboard.js +4 -8
- package/scripts/constants/articles/deep-analysis/ar.d.ts +8 -0
- package/scripts/constants/articles/deep-analysis/ar.js +75 -0
- package/scripts/constants/articles/deep-analysis/da.d.ts +8 -0
- package/scripts/constants/articles/deep-analysis/da.js +75 -0
- package/scripts/constants/articles/deep-analysis/de.d.ts +8 -0
- package/scripts/constants/articles/deep-analysis/de.js +75 -0
- package/scripts/constants/articles/deep-analysis/en.d.ts +8 -0
- package/scripts/constants/articles/deep-analysis/en.js +75 -0
- package/scripts/constants/articles/deep-analysis/es.d.ts +8 -0
- package/scripts/constants/articles/deep-analysis/es.js +75 -0
- package/scripts/constants/articles/deep-analysis/fi.d.ts +8 -0
- package/scripts/constants/articles/deep-analysis/fi.js +75 -0
- package/scripts/constants/articles/deep-analysis/fr.d.ts +8 -0
- package/scripts/constants/articles/deep-analysis/fr.js +75 -0
- package/scripts/constants/articles/deep-analysis/he.d.ts +8 -0
- package/scripts/constants/articles/deep-analysis/he.js +75 -0
- package/scripts/constants/articles/deep-analysis/index.d.ts +7 -0
- package/scripts/constants/articles/deep-analysis/index.js +33 -0
- package/scripts/constants/articles/deep-analysis/ja.d.ts +8 -0
- package/scripts/constants/articles/deep-analysis/ja.js +75 -0
- package/scripts/constants/articles/deep-analysis/ko.d.ts +8 -0
- package/scripts/constants/articles/deep-analysis/ko.js +75 -0
- package/scripts/constants/articles/deep-analysis/nl.d.ts +8 -0
- package/scripts/constants/articles/deep-analysis/nl.js +75 -0
- package/scripts/constants/articles/deep-analysis/no.d.ts +8 -0
- package/scripts/constants/articles/deep-analysis/no.js +75 -0
- package/scripts/constants/articles/deep-analysis/sv.d.ts +8 -0
- package/scripts/constants/articles/deep-analysis/sv.js +75 -0
- package/scripts/constants/articles/deep-analysis/zh.d.ts +8 -0
- package/scripts/constants/articles/deep-analysis/zh.js +75 -0
- package/scripts/constants/articles/deep-analysis.d.ts +4 -3
- package/scripts/constants/articles/deep-analysis.js +3 -7
- package/scripts/constants/articles/localized-keywords-central.d.ts +8 -0
- package/scripts/constants/articles/localized-keywords-central.js +118 -0
- package/scripts/constants/articles/localized-keywords-nordic.d.ts +8 -0
- package/scripts/constants/articles/localized-keywords-nordic.js +303 -0
- package/scripts/constants/articles/localized-keywords.js +4 -2
- package/scripts/constants/articles/swot-builder-central.d.ts +8 -0
- package/scripts/constants/articles/swot-builder-central.js +90 -0
- package/scripts/constants/articles/swot-builder-nordic.d.ts +8 -0
- package/scripts/constants/articles/swot-builder-nordic.js +216 -0
- package/scripts/constants/articles/swot.js +4 -2
- package/scripts/constants/articles/week-ahead-eu.d.ts +12 -0
- package/scripts/constants/articles/week-ahead-eu.js +278 -0
- package/scripts/constants/articles/week-ahead-global.d.ts +12 -0
- package/scripts/constants/articles/week-ahead-global.js +278 -0
- package/scripts/constants/articles/week-ahead.d.ts +4 -7
- package/scripts/constants/articles/week-ahead.js +11 -535
- package/scripts/constants/world-bank/category-map-analysis.d.ts +9 -0
- package/scripts/constants/world-bank/category-map-analysis.js +204 -0
- package/scripts/constants/world-bank/category-map-legislative.d.ts +9 -0
- package/scripts/constants/world-bank/category-map-legislative.js +130 -0
- package/scripts/constants/world-bank/category-map-periodic.d.ts +9 -0
- package/scripts/constants/world-bank/category-map-periodic.js +176 -0
- package/scripts/constants/world-bank/category-map.d.ts +3 -26
- package/scripts/constants/world-bank/category-map.js +8 -501
- package/scripts/discover-untranslated-briefs.js +123 -4
- package/scripts/generators/news-indexes/per-language.js +21 -7
- package/scripts/generators/political-intelligence/html.js +39 -8
- package/scripts/generators/sitemap/html.js +25 -7
- package/scripts/mcp/ep/client.d.ts +0 -1
- package/scripts/mcp/ep/client.js +0 -65
- package/scripts/mcp/ep/error-classifier.d.ts +2 -2
- package/scripts/mcp/ep/error-classifier.js +2 -2
- package/scripts/mcp/ep/tools-list.d.ts +13 -0
- package/scripts/mcp/ep/tools-list.js +79 -0
- package/scripts/mcp/ep-mcp-client.d.ts +1 -0
- package/scripts/mcp/ep-mcp-client.js +1 -0
- package/scripts/mcp/imf/client.d.ts +3 -64
- package/scripts/mcp/imf/client.js +18 -207
- package/scripts/mcp/imf/http-transport.d.ts +92 -0
- package/scripts/mcp/imf/http-transport.js +232 -0
- package/scripts/mcp/transport/connection.d.ts +25 -53
- package/scripts/mcp/transport/connection.js +90 -250
- package/scripts/mcp/transport/process.d.ts +62 -0
- package/scripts/mcp/transport/process.js +147 -0
- package/scripts/mcp/transport/reconnect.d.ts +73 -0
- package/scripts/mcp/transport/reconnect.js +96 -0
- package/scripts/validate-brief-translations.js +122 -6
- package/scripts/constants/articles/breaking-strings-eu.d.ts +0 -7
- package/scripts/constants/articles/breaking-strings-global.d.ts +0 -7
- package/scripts/constants/articles/dashboard-builder-eu.d.ts +0 -7
- package/scripts/constants/articles/dashboard-builder-global.d.ts +0 -7
- package/scripts/constants/articles/deep-analysis-strings-eu.d.ts +0 -7
- package/scripts/constants/articles/deep-analysis-strings-global.d.ts +0 -7
- package/scripts/constants/articles/localized-keywords-eu.d.ts +0 -7
- package/scripts/constants/articles/swot-builder-eu.d.ts +0 -7
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2024-2026 Hack23 AB
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* @module MCP/transport/process
|
|
5
|
+
* @description Stdio process spawn / teardown helpers and JSON-RPC message routing
|
|
6
|
+
* for the MCPConnection stdio transport.
|
|
7
|
+
*
|
|
8
|
+
* Extracted from `connection.ts` to keep individual file sizes under 400 LOC.
|
|
9
|
+
* Operates on an explicit {@link SpawnContext} adapter rather than `this`.
|
|
10
|
+
*/
|
|
11
|
+
import { spawn } from 'child_process';
|
|
12
|
+
import { resolve, dirname } from 'path';
|
|
13
|
+
import { fileURLToPath } from 'url';
|
|
14
|
+
// ─── Binary path constants ────────────────────────────────────────────────────
|
|
15
|
+
/** npm binary name for the European Parliament MCP server */
|
|
16
|
+
export const BINARY_NAME = 'european-parliament-mcp-server';
|
|
17
|
+
/** Platform-specific binary filename (Windows uses .cmd shim) */
|
|
18
|
+
export const BINARY_FILE = process.platform === 'win32' ? `${BINARY_NAME}.cmd` : BINARY_NAME;
|
|
19
|
+
/** Default binary resolved from node_modules/.bin relative to this file's compiled location */
|
|
20
|
+
export const DEFAULT_SERVER_BINARY = resolve(dirname(fileURLToPath(import.meta.url)), `../../../node_modules/.bin/${BINARY_FILE}`);
|
|
21
|
+
/** Default request timeout in milliseconds — EU Parliament API responses commonly take 30-120+ seconds for large datasets */
|
|
22
|
+
export const DEFAULT_REQUEST_TIMEOUT_MS = 180_000;
|
|
23
|
+
/**
|
|
24
|
+
* Effective request timeout, configurable via `EP_REQUEST_TIMEOUT_MS` env var.
|
|
25
|
+
* This keeps the client-side timeout aligned with the MCP server timeout set
|
|
26
|
+
* in workflow configs and copilot-mcp.json.
|
|
27
|
+
*/
|
|
28
|
+
export const REQUEST_TIMEOUT_MS = (() => {
|
|
29
|
+
const envVal = process.env['EP_REQUEST_TIMEOUT_MS'];
|
|
30
|
+
if (envVal) {
|
|
31
|
+
const parsed = Number(envVal);
|
|
32
|
+
if (Number.isFinite(parsed) && parsed > 0)
|
|
33
|
+
return parsed;
|
|
34
|
+
}
|
|
35
|
+
return DEFAULT_REQUEST_TIMEOUT_MS;
|
|
36
|
+
})();
|
|
37
|
+
/** Connection startup delay in milliseconds */
|
|
38
|
+
export const CONNECTION_STARTUP_DELAY_MS = 500;
|
|
39
|
+
// ─── Exported helpers ─────────────────────────────────────────────────────────
|
|
40
|
+
/**
|
|
41
|
+
* Attempt a single connection via stdio (spawns server binary).
|
|
42
|
+
*
|
|
43
|
+
* @param ctx - Spawn context adapter from MCPConnection
|
|
44
|
+
*/
|
|
45
|
+
export async function attemptStdioConnection(ctx) {
|
|
46
|
+
try {
|
|
47
|
+
const isJavaScriptFile = ctx.serverPath.toLowerCase().endsWith('.js');
|
|
48
|
+
const command = isJavaScriptFile ? process.execPath : ctx.serverPath;
|
|
49
|
+
const args = isJavaScriptFile ? [ctx.serverPath] : [];
|
|
50
|
+
const childEnv = { ...process.env };
|
|
51
|
+
const envVal = childEnv['EP_REQUEST_TIMEOUT_MS'];
|
|
52
|
+
let effectiveTimeoutMs = ctx.requestTimeoutMs;
|
|
53
|
+
if (envVal !== undefined && envVal !== '') {
|
|
54
|
+
const parsed = Number(envVal);
|
|
55
|
+
if (Number.isFinite(parsed) && parsed > 0) {
|
|
56
|
+
effectiveTimeoutMs = parsed;
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
console.warn(`Invalid EP_REQUEST_TIMEOUT_MS value (non-finite or ≤0); falling back to ${ctx.requestTimeoutMs}ms`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
childEnv['EP_REQUEST_TIMEOUT_MS'] = String(effectiveTimeoutMs);
|
|
63
|
+
if (!isJavaScriptFile) {
|
|
64
|
+
args.push('--timeout', String(effectiveTimeoutMs));
|
|
65
|
+
}
|
|
66
|
+
const child = spawn(command, args, {
|
|
67
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
68
|
+
env: childEnv,
|
|
69
|
+
});
|
|
70
|
+
ctx.setProcess(child);
|
|
71
|
+
let buffer = '';
|
|
72
|
+
let startupError = null;
|
|
73
|
+
child.stdout?.on('data', (data) => {
|
|
74
|
+
buffer += data.toString();
|
|
75
|
+
const lines = buffer.split('\n');
|
|
76
|
+
buffer = lines.pop() ?? '';
|
|
77
|
+
for (const line of lines) {
|
|
78
|
+
if (line.trim()) {
|
|
79
|
+
ctx.onMessage(line);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
child.stderr?.on('data', (data) => {
|
|
84
|
+
const message = data.toString().trim();
|
|
85
|
+
if (message) {
|
|
86
|
+
console.error(`MCP Server: ${message}`);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
child.on('close', (code) => {
|
|
90
|
+
console.log(`MCP Server exited with code ${code}`);
|
|
91
|
+
ctx.setConnected(false);
|
|
92
|
+
ctx.rejectAllPending('MCP server connection closed');
|
|
93
|
+
});
|
|
94
|
+
child.on('error', (err) => {
|
|
95
|
+
startupError = err;
|
|
96
|
+
ctx.setConnected(false);
|
|
97
|
+
});
|
|
98
|
+
await new Promise((resolve) => setTimeout(resolve, CONNECTION_STARTUP_DELAY_MS));
|
|
99
|
+
if (startupError) {
|
|
100
|
+
throw startupError;
|
|
101
|
+
}
|
|
102
|
+
ctx.setConnected(true);
|
|
103
|
+
console.log(`✅ Connected to ${ctx.serverLabel}`);
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
107
|
+
console.error('❌ Failed to spawn MCP server:', message);
|
|
108
|
+
throw error;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Handle an incoming newline-delimited JSON-RPC message from the server.
|
|
113
|
+
* Routes responses to matching in-flight pending requests; logs notifications.
|
|
114
|
+
*
|
|
115
|
+
* @param line - Single JSON-RPC message line (no trailing newline)
|
|
116
|
+
* @param pending - In-flight request map maintained by {@link MCPConnection}
|
|
117
|
+
*/
|
|
118
|
+
export function handleIncomingMessage(line, pending) {
|
|
119
|
+
try {
|
|
120
|
+
const message = JSON.parse(line);
|
|
121
|
+
if (message.id !== null && message.id !== undefined && pending.has(message.id)) {
|
|
122
|
+
const req = pending.get(message.id);
|
|
123
|
+
if (req) {
|
|
124
|
+
pending.delete(message.id);
|
|
125
|
+
if (message.error) {
|
|
126
|
+
req.reject(new Error(message.error.message ?? 'MCP server error'));
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
req.resolve(message.result);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
pending.delete(message.id);
|
|
134
|
+
console.error(`MCP pending request ${String(message.id)} vanished before handling`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
else if ((message.id === null || message.id === undefined) && message.method) {
|
|
138
|
+
console.log(`MCP Notification: ${message.method}`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
143
|
+
console.error('Error parsing MCP message:', errorMessage);
|
|
144
|
+
console.error('Problematic line:', line);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=process.js.map
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module MCP/transport/reconnect
|
|
3
|
+
* @description Exponential back-off reconnect loop and tool-call retry logic
|
|
4
|
+
* for MCPConnection.
|
|
5
|
+
*
|
|
6
|
+
* Extracted from `connection.ts` to keep individual file sizes under 400 LOC.
|
|
7
|
+
* Operates on an explicit {@link ReconnectOps} adapter rather than `this`.
|
|
8
|
+
*/
|
|
9
|
+
import type { MCPToolResult } from '../../types/index.js';
|
|
10
|
+
/**
|
|
11
|
+
* Adapter passed by {@link MCPConnection} to reconnect/retry helpers.
|
|
12
|
+
* Exposes the handful of connection fields these helpers need through
|
|
13
|
+
* getter/setter callbacks so the fields remain on the connection class
|
|
14
|
+
* (preserving external observability via `getConnectionHealth()`).
|
|
15
|
+
*/
|
|
16
|
+
export interface ReconnectOps {
|
|
17
|
+
/** Maximum consecutive connection attempts before giving up */
|
|
18
|
+
readonly maxConnectionAttempts: number;
|
|
19
|
+
/** Base delay (ms) between retry attempts */
|
|
20
|
+
readonly connectionRetryDelay: number;
|
|
21
|
+
/** Human-readable label for log messages */
|
|
22
|
+
readonly serverLabel: string;
|
|
23
|
+
/** Call the underlying MCP tool (used by the retry loop) */
|
|
24
|
+
readonly callTool: (name: string, args: object) => Promise<MCPToolResult>;
|
|
25
|
+
/** Whether the transport is currently connected */
|
|
26
|
+
readonly isConnected: () => boolean;
|
|
27
|
+
/** Trigger a full reconnect cycle */
|
|
28
|
+
readonly connect: () => Promise<void>;
|
|
29
|
+
/** Mark the transport as (dis)connected */
|
|
30
|
+
readonly setConnected: (v: boolean) => void;
|
|
31
|
+
/** Current reconnect-attempt counter */
|
|
32
|
+
readonly getReconnectCount: () => number;
|
|
33
|
+
/** Update the reconnect-attempt counter */
|
|
34
|
+
readonly setReconnectCount: (n: number) => void;
|
|
35
|
+
/** Current in-flight reconnect promise (or null) */
|
|
36
|
+
readonly getReconnectingPromise: () => Promise<void> | null;
|
|
37
|
+
/** Persist or clear the in-flight reconnect promise */
|
|
38
|
+
readonly setReconnectingPromise: (p: Promise<void> | null) => void;
|
|
39
|
+
/** Current cumulative timeout counter */
|
|
40
|
+
readonly getTimeoutCount: () => number;
|
|
41
|
+
/** Update the cumulative timeout counter */
|
|
42
|
+
readonly setTimeoutCount: (n: number) => void;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Reconnect with exponential back-off. Concurrent callers await the same
|
|
46
|
+
* in-flight reconnect promise instead of spawning parallel attempts.
|
|
47
|
+
*
|
|
48
|
+
* @param ops - Reconnect operations adapter from MCPConnection
|
|
49
|
+
* @returns Promise that resolves when reconnection succeeds or all attempts are exhausted
|
|
50
|
+
*/
|
|
51
|
+
export declare function performReconnect(ops: ReconnectOps): Promise<void>;
|
|
52
|
+
/**
|
|
53
|
+
* Log a retry warning and, if disconnected, attempt to reconnect before waiting.
|
|
54
|
+
*
|
|
55
|
+
* @param lastError - The error from the failed attempt
|
|
56
|
+
* @param attempt - Zero-based current attempt index
|
|
57
|
+
* @param retries - Total retry count
|
|
58
|
+
* @param ops - Reconnect operations adapter
|
|
59
|
+
* @returns Promise that resolves after logging, optional reconnect, and inter-retry delay
|
|
60
|
+
*/
|
|
61
|
+
export declare function handleRetryAttempt(lastError: Error, attempt: number, retries: number, ops: ReconnectOps): Promise<void>;
|
|
62
|
+
/**
|
|
63
|
+
* Call an MCP tool with automatic retry on timeout or connection loss.
|
|
64
|
+
* Non-retriable errors are re-thrown immediately without consuming retry budget.
|
|
65
|
+
*
|
|
66
|
+
* @param name - Tool name
|
|
67
|
+
* @param args - Tool arguments (plain object, non-null, not an array)
|
|
68
|
+
* @param retries - Maximum number of retries (validated ≥ 0 by caller)
|
|
69
|
+
* @param ops - Reconnect operations adapter
|
|
70
|
+
* @returns Tool execution result
|
|
71
|
+
*/
|
|
72
|
+
export declare function runWithRetry(name: string, args: object, retries: number, ops: ReconnectOps): Promise<MCPToolResult>;
|
|
73
|
+
//# sourceMappingURL=reconnect.d.ts.map
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2024-2026 Hack23 AB
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { isRetriableError, RECONNECT_MAX_DELAY_MS } from './retry-policy.js';
|
|
4
|
+
// ─── Exported helpers ─────────────────────────────────────────────────────────
|
|
5
|
+
/**
|
|
6
|
+
* Reconnect with exponential back-off. Concurrent callers await the same
|
|
7
|
+
* in-flight reconnect promise instead of spawning parallel attempts.
|
|
8
|
+
*
|
|
9
|
+
* @param ops - Reconnect operations adapter from MCPConnection
|
|
10
|
+
* @returns Promise that resolves when reconnection succeeds or all attempts are exhausted
|
|
11
|
+
*/
|
|
12
|
+
export async function performReconnect(ops) {
|
|
13
|
+
const inflight = ops.getReconnectingPromise();
|
|
14
|
+
if (inflight !== null) {
|
|
15
|
+
return inflight;
|
|
16
|
+
}
|
|
17
|
+
ops.setReconnectCount(ops.getReconnectCount() + 1);
|
|
18
|
+
console.log(`🔄 Reconnecting to ${ops.serverLabel} (attempt ${ops.getReconnectCount()})...`);
|
|
19
|
+
const p = doReconnect(ops);
|
|
20
|
+
ops.setReconnectingPromise(p);
|
|
21
|
+
try {
|
|
22
|
+
await p;
|
|
23
|
+
}
|
|
24
|
+
finally {
|
|
25
|
+
ops.setReconnectingPromise(null);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Internal reconnect helper. Waits for an exponential back-off delay then
|
|
30
|
+
* delegates to `connect()`, which handles its own retry loop.
|
|
31
|
+
*
|
|
32
|
+
* @param ops - Reconnect operations adapter
|
|
33
|
+
*/
|
|
34
|
+
async function doReconnect(ops) {
|
|
35
|
+
const normalizedMax = Math.max(1, ops.maxConnectionAttempts);
|
|
36
|
+
const attemptIndex = Math.min(Math.max(0, ops.getReconnectCount() - 1), normalizedMax - 1);
|
|
37
|
+
const delay = Math.min(ops.connectionRetryDelay * Math.pow(2, attemptIndex), RECONNECT_MAX_DELAY_MS);
|
|
38
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
39
|
+
try {
|
|
40
|
+
ops.setConnected(false);
|
|
41
|
+
await ops.connect();
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
console.error(`❌ Reconnection to ${ops.serverLabel} failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Log a retry warning and, if disconnected, attempt to reconnect before waiting.
|
|
49
|
+
*
|
|
50
|
+
* @param lastError - The error from the failed attempt
|
|
51
|
+
* @param attempt - Zero-based current attempt index
|
|
52
|
+
* @param retries - Total retry count
|
|
53
|
+
* @param ops - Reconnect operations adapter
|
|
54
|
+
* @returns Promise that resolves after logging, optional reconnect, and inter-retry delay
|
|
55
|
+
*/
|
|
56
|
+
export async function handleRetryAttempt(lastError, attempt, retries, ops) {
|
|
57
|
+
if (lastError.message.toLowerCase().includes('timeout')) {
|
|
58
|
+
ops.setTimeoutCount(ops.getTimeoutCount() + 1);
|
|
59
|
+
console.warn(`⏱️ Request timeout (total: ${ops.getTimeoutCount()}), retrying ${attempt + 1}/${retries}...`);
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
console.warn(`⚠️ Request failed, retrying ${attempt + 1}/${retries}: ${lastError.message}`);
|
|
63
|
+
}
|
|
64
|
+
if (!ops.isConnected()) {
|
|
65
|
+
await performReconnect(ops);
|
|
66
|
+
}
|
|
67
|
+
await new Promise((r) => setTimeout(r, ops.connectionRetryDelay * (attempt + 1)));
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Call an MCP tool with automatic retry on timeout or connection loss.
|
|
71
|
+
* Non-retriable errors are re-thrown immediately without consuming retry budget.
|
|
72
|
+
*
|
|
73
|
+
* @param name - Tool name
|
|
74
|
+
* @param args - Tool arguments (plain object, non-null, not an array)
|
|
75
|
+
* @param retries - Maximum number of retries (validated ≥ 0 by caller)
|
|
76
|
+
* @param ops - Reconnect operations adapter
|
|
77
|
+
* @returns Tool execution result
|
|
78
|
+
*/
|
|
79
|
+
export async function runWithRetry(name, args, retries, ops) {
|
|
80
|
+
let lastError = new Error(`Failed to call tool '${name}' after ${retries} retries`);
|
|
81
|
+
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
82
|
+
try {
|
|
83
|
+
return await ops.callTool(name, args);
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
87
|
+
if (!isRetriableError(lastError))
|
|
88
|
+
throw lastError;
|
|
89
|
+
if (attempt === retries)
|
|
90
|
+
break;
|
|
91
|
+
await handleRetryAttempt(lastError, attempt, retries, ops);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
throw lastError;
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=reconnect.js.map
|
|
@@ -34,9 +34,25 @@
|
|
|
34
34
|
* is a machine-readable fixed token; dropping a diagram silently breaks
|
|
35
35
|
* downstream HTML rendering.
|
|
36
36
|
*
|
|
37
|
+
* **Skeleton-aware mode**: when a translation file declares itself as a
|
|
38
|
+
* Phase A skeleton via the `<!-- translation-skeleton: lang=<code> ... -->`
|
|
39
|
+
* marker (written by the `news-translate` workflow's 2-phase
|
|
40
|
+
* largeSource strategy), gates 3–7 are SKIPPED and a single
|
|
41
|
+
* `skeleton-incomplete` advisory is emitted instead. The advisory is
|
|
42
|
+
* classified as `severity: warning` and does NOT cause a non-zero exit
|
|
43
|
+
* unless `--strict-skeletons` is passed. This is intentional: emergency
|
|
44
|
+
* partial flushes (Step 4b of the translate workflow) write skeleton
|
|
45
|
+
* stubs for languages that did not reach Phase B before the wall-clock
|
|
46
|
+
* budget expired; those stubs would otherwise trigger 5+ cascading
|
|
47
|
+
* violations per file (length-floor, fixed-token-preservation,
|
|
48
|
+
* heading-parity, mermaid-parity) that drown out real defects in fully
|
|
49
|
+
* translated siblings. Real translations in the same brief continue to
|
|
50
|
+
* receive strict validation.
|
|
51
|
+
*
|
|
37
52
|
* Each translation that fails any gate produces a structured report entry.
|
|
38
53
|
* The process exits with code 1 if any failures are present (unless
|
|
39
|
-
* `--no-fail` is passed for advisory mode
|
|
54
|
+
* `--no-fail` is passed for advisory mode, or unless every remaining
|
|
55
|
+
* violation has `severity: warning`).
|
|
40
56
|
*
|
|
41
57
|
* This script is invoked by:
|
|
42
58
|
* - `npm run validate:translations` (CI + local)
|
|
@@ -48,6 +64,8 @@
|
|
|
48
64
|
* [--paths <glob>...] # validate specific translation files only
|
|
49
65
|
* [--report <path>] # write JSON report; default stdout
|
|
50
66
|
* [--no-fail] # exit 0 even when violations found
|
|
67
|
+
* [--strict-skeletons] # treat skeleton-incomplete advisories as
|
|
68
|
+
* # blocking violations (default: warning)
|
|
51
69
|
* [--quiet] # suppress per-file logging
|
|
52
70
|
*/
|
|
53
71
|
|
|
@@ -199,6 +217,45 @@ export function countMermaidBlocks(text) {
|
|
|
199
217
|
return countGlobal(text, MERMAID_OPENER);
|
|
200
218
|
}
|
|
201
219
|
|
|
220
|
+
/**
|
|
221
|
+
* Marker that the `news-translate` workflow Phase A writes at the very top
|
|
222
|
+
* of every skeleton file (before the H1 line). When the validator sees this
|
|
223
|
+
* marker it knows the file is a deliberately incomplete Phase A skeleton
|
|
224
|
+
* from an emergency partial flush and emits a single `skeleton-incomplete`
|
|
225
|
+
* advisory instead of cascading length/token/heading/mermaid violations.
|
|
226
|
+
*
|
|
227
|
+
* The marker is intentionally a forgiving regex: any HTML comment starting
|
|
228
|
+
* with `<!-- translation-skeleton` within the first 10 lines counts. This
|
|
229
|
+
* accommodates both `<!-- translation-skeleton: lang=sv phase=A -->` and
|
|
230
|
+
* the legacy `<!-- translation-skeleton -->` formats.
|
|
231
|
+
*/
|
|
232
|
+
export const SKELETON_MARKER_RE = /<!--\s*translation-skeleton\b/;
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Heuristic skeleton detector. A file is a skeleton if EITHER:
|
|
236
|
+
* - it contains the explicit `SKELETON_MARKER_RE` marker in its first
|
|
237
|
+
* 10 lines (preferred, set by Phase A), OR
|
|
238
|
+
* - it matches the fallback heuristic for older Phase A output that
|
|
239
|
+
* pre-dates the marker convention: ≥3 H2 headings AND the number of
|
|
240
|
+
* `<!-- pending -->` / `<PENDING>` / `PENDING` placeholders is at
|
|
241
|
+
* least equal to the H2 count (i.e. every H2 section appears to be
|
|
242
|
+
* an unfilled stub).
|
|
243
|
+
*
|
|
244
|
+
* @param {string} text
|
|
245
|
+
* @returns {boolean}
|
|
246
|
+
*/
|
|
247
|
+
export function isSkeletonStub(text) {
|
|
248
|
+
if (typeof text !== 'string' || text.length === 0) return false;
|
|
249
|
+
const head = text.split('\n', 10).join('\n');
|
|
250
|
+
if (SKELETON_MARKER_RE.test(head)) return true;
|
|
251
|
+
// Fallback heuristic: many H2s, almost every body line is a pending marker.
|
|
252
|
+
const h2Count = countHeadings(text, 2);
|
|
253
|
+
if (h2Count < 3) return false;
|
|
254
|
+
const pendingRe = /(<!--\s*pending\s*-->|<PENDING>|\bPENDING\b)/i;
|
|
255
|
+
const pendingHits = (text.match(new RegExp(pendingRe.source, 'gi')) || []).length;
|
|
256
|
+
return pendingHits >= h2Count;
|
|
257
|
+
}
|
|
258
|
+
|
|
202
259
|
/**
|
|
203
260
|
* Extract H2 section titles from markdown text. Mirrors the shape returned
|
|
204
261
|
* by `scripts/discover-untranslated-briefs.js#extractH2Titles` so the
|
|
@@ -324,6 +381,10 @@ export function aggregateByKey(items, key) {
|
|
|
324
381
|
* @property {string} lang
|
|
325
382
|
* @property {string} gate
|
|
326
383
|
* @property {string} message
|
|
384
|
+
* @property {'error'|'warning'} [severity] - When present, controls
|
|
385
|
+
* blocking semantics: `'warning'` entries (e.g. `skeleton-incomplete`)
|
|
386
|
+
* do not cause a non-zero exit unless `--strict-skeletons` is passed.
|
|
387
|
+
* Omitted entries default to blocking (`'error'` equivalent).
|
|
327
388
|
*/
|
|
328
389
|
|
|
329
390
|
/** Parse CLI argv. Exported for unit tests. */
|
|
@@ -334,6 +395,7 @@ export function parseArgs(argv) {
|
|
|
334
395
|
report: null,
|
|
335
396
|
fail: true,
|
|
336
397
|
quiet: false,
|
|
398
|
+
strictSkeletons: false,
|
|
337
399
|
};
|
|
338
400
|
for (let i = 0; i < argv.length; i += 1) {
|
|
339
401
|
const arg = argv[i];
|
|
@@ -355,6 +417,9 @@ export function parseArgs(argv) {
|
|
|
355
417
|
case '--no-fail':
|
|
356
418
|
opts.fail = false;
|
|
357
419
|
break;
|
|
420
|
+
case '--strict-skeletons':
|
|
421
|
+
opts.strictSkeletons = true;
|
|
422
|
+
break;
|
|
358
423
|
case '--quiet':
|
|
359
424
|
opts.quiet = true;
|
|
360
425
|
break;
|
|
@@ -362,7 +427,8 @@ export function parseArgs(argv) {
|
|
|
362
427
|
case '-h':
|
|
363
428
|
process.stdout.write(
|
|
364
429
|
'Usage: validate-brief-translations.js [--repo-root <path>] ' +
|
|
365
|
-
'[--paths <file>...] [--report <path>] [--no-fail]
|
|
430
|
+
'[--paths <file>...] [--report <path>] [--no-fail] ' +
|
|
431
|
+
'[--strict-skeletons] [--quiet]\n'
|
|
366
432
|
);
|
|
367
433
|
process.exit(0);
|
|
368
434
|
break;
|
|
@@ -444,6 +510,34 @@ export function validateTranslation(translationPath, repoRoot) {
|
|
|
444
510
|
return violations;
|
|
445
511
|
}
|
|
446
512
|
|
|
513
|
+
// Skeleton short-circuit: when a translation file declares itself as a
|
|
514
|
+
// Phase A skeleton (via the `<!-- translation-skeleton -->` marker the
|
|
515
|
+
// news-translate workflow writes during emergency partial flushes), skip
|
|
516
|
+
// gates 3–7 and emit a single non-blocking `skeleton-incomplete`
|
|
517
|
+
// advisory. The marker is a deliberate contract between the workflow
|
|
518
|
+
// and this validator: an emergency partial flush is a SUCCESSFUL
|
|
519
|
+
// outcome (some real translations were saved), and the unfilled
|
|
520
|
+
// skeleton stubs for languages that did not reach Phase B should not
|
|
521
|
+
// generate 5+ cascading violations that drown out real defects in
|
|
522
|
+
// the fully translated siblings. The next scheduled run will pick up
|
|
523
|
+
// the skeleton languages via the discovery queue's missing-language
|
|
524
|
+
// detection. See `.github/workflows/news-translate.md` §"🐘
|
|
525
|
+
// LARGE-SOURCE 2-PHASE STRATEGY" and §"4b. Wall-clock safety net".
|
|
526
|
+
const targetTextEarly = fs.readFileSync(translationPath, 'utf8');
|
|
527
|
+
if (isSkeletonStub(targetTextEarly)) {
|
|
528
|
+
violations.push({
|
|
529
|
+
translationPath: rel,
|
|
530
|
+
sourcePath: sourceRel,
|
|
531
|
+
lang,
|
|
532
|
+
gate: 'skeleton-incomplete',
|
|
533
|
+
severity: 'warning',
|
|
534
|
+
message:
|
|
535
|
+
`Phase A skeleton stub — translation for "${lang}" did not reach Phase B before the wall-clock budget expired. ` +
|
|
536
|
+
`Re-queue this language on the next scheduled run; the discovery script will detect it as a missing sibling.`,
|
|
537
|
+
});
|
|
538
|
+
return violations;
|
|
539
|
+
}
|
|
540
|
+
|
|
447
541
|
const sourceBytes = fs.statSync(sourcePath).size;
|
|
448
542
|
const targetBytes = fs.statSync(translationPath).size;
|
|
449
543
|
if (sourceBytes > 0 && targetBytes < sourceBytes * LENGTH_FLOOR_RATIO) {
|
|
@@ -458,7 +552,7 @@ export function validateTranslation(translationPath, repoRoot) {
|
|
|
458
552
|
});
|
|
459
553
|
}
|
|
460
554
|
|
|
461
|
-
const targetText =
|
|
555
|
+
const targetText = targetTextEarly;
|
|
462
556
|
let englishHits = 0;
|
|
463
557
|
for (const re of EN_PATTERNS) {
|
|
464
558
|
if (re.test(targetText)) englishHits += 1;
|
|
@@ -503,7 +597,9 @@ export function validateTranslation(translationPath, repoRoot) {
|
|
|
503
597
|
`Self-check before flush: \`node scripts/validate-brief-translations.js --paths ${relQuoted}\` ` +
|
|
504
598
|
`(or \`--paths ${siblingGlobQuoted}\` to validate every sibling). ` +
|
|
505
599
|
`Dutch example: \`IMF\` stays \`IMF\` (never \`IMV\`); \`WEO\` stays \`WEO\` ` +
|
|
506
|
-
`(never \`Wereldwijde Economische Vooruitzichten\`)
|
|
600
|
+
`(never \`Wereldwijde Economische Vooruitzichten\`). ` +
|
|
601
|
+
`Norwegian example: \`IMF\` forblir \`IMF\` (never \`IPF\` / \`IMV\` / ` +
|
|
602
|
+
`\`Det internasjonale valutafondet\` / \`Pengefondet\`); \`WEO\` forblir \`WEO\`.`,
|
|
507
603
|
});
|
|
508
604
|
}
|
|
509
605
|
}
|
|
@@ -600,8 +696,9 @@ export function runValidation(translationPaths, repoRoot, { quiet = false } = {}
|
|
|
600
696
|
allViolations.push(...v);
|
|
601
697
|
if (!quiet) {
|
|
602
698
|
for (const entry of v) {
|
|
699
|
+
const icon = entry.severity === 'warning' ? '⚠️' : '❌';
|
|
603
700
|
process.stderr.write(
|
|
604
|
-
|
|
701
|
+
`${icon} ${entry.translationPath} [${entry.gate}] ${entry.message}\n`
|
|
605
702
|
);
|
|
606
703
|
}
|
|
607
704
|
}
|
|
@@ -612,6 +709,23 @@ export function runValidation(translationPaths, repoRoot, { quiet = false } = {}
|
|
|
612
709
|
return allViolations;
|
|
613
710
|
}
|
|
614
711
|
|
|
712
|
+
/**
|
|
713
|
+
* Count the entries in a violations list that should be treated as
|
|
714
|
+
* blocking (cause a non-zero exit). When `strictSkeletons` is false
|
|
715
|
+
* (the default), entries with `severity: 'warning'` — i.e. the
|
|
716
|
+
* skeleton-incomplete advisory emitted for Phase A stubs from
|
|
717
|
+
* emergency partial flushes — are not counted as blocking.
|
|
718
|
+
*/
|
|
719
|
+
export function countBlockingViolations(violations, { strictSkeletons = false } = {}) {
|
|
720
|
+
if (strictSkeletons) {
|
|
721
|
+
// Promote skeleton-incomplete advisories to blocking, but leave any
|
|
722
|
+
// other warning-level advisory types non-blocking so that future
|
|
723
|
+
// additions of new warning gates don't inadvertently become strict.
|
|
724
|
+
return violations.filter((v) => v.severity !== 'warning' || v.gate === 'skeleton-incomplete').length;
|
|
725
|
+
}
|
|
726
|
+
return violations.filter((v) => v.severity !== 'warning').length;
|
|
727
|
+
}
|
|
728
|
+
|
|
615
729
|
/** Main entry point. */
|
|
616
730
|
export function main(argv) {
|
|
617
731
|
const opts = parseArgs(argv);
|
|
@@ -620,12 +734,14 @@ export function main(argv) {
|
|
|
620
734
|
: findAllTranslations(opts.repoRoot);
|
|
621
735
|
|
|
622
736
|
const violations = runValidation(paths, opts.repoRoot, { quiet: opts.quiet });
|
|
737
|
+
const blocking = countBlockingViolations(violations, { strictSkeletons: opts.strictSkeletons });
|
|
623
738
|
|
|
624
739
|
const report = {
|
|
625
740
|
generatedAt: new Date().toISOString(),
|
|
626
741
|
totals: {
|
|
627
742
|
filesChecked: paths.length,
|
|
628
743
|
violations: violations.length,
|
|
744
|
+
blocking,
|
|
629
745
|
byGate: aggregateByKey(violations, 'gate'),
|
|
630
746
|
byLang: aggregateByKey(violations, 'lang'),
|
|
631
747
|
},
|
|
@@ -639,7 +755,7 @@ export function main(argv) {
|
|
|
639
755
|
process.stdout.write(json);
|
|
640
756
|
}
|
|
641
757
|
|
|
642
|
-
if (
|
|
758
|
+
if (blocking > 0 && opts.fail) {
|
|
643
759
|
process.exit(1);
|
|
644
760
|
}
|
|
645
761
|
return report;
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module breaking-strings-eu
|
|
3
|
-
* @description Per-language entries (en, sv, da, no, fi, de, fr) for BREAKING_STRINGS.
|
|
4
|
-
*/
|
|
5
|
-
import type { LanguageMap, BreakingStrings } from '../../types/index.js';
|
|
6
|
-
export declare const BREAKING_STRINGS_EU: Pick<LanguageMap<BreakingStrings>, 'en' | 'sv' | 'da' | 'no' | 'fi' | 'de' | 'fr'>;
|
|
7
|
-
//# sourceMappingURL=breaking-strings-eu.d.ts.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module breaking-strings-global
|
|
3
|
-
* @description Per-language entries (es, nl, ar, he, ja, ko, zh) for BREAKING_STRINGS.
|
|
4
|
-
*/
|
|
5
|
-
import type { LanguageMap, BreakingStrings } from '../../types/index.js';
|
|
6
|
-
export declare const BREAKING_STRINGS_GLOBAL: Pick<LanguageMap<BreakingStrings>, 'es' | 'nl' | 'ar' | 'he' | 'ja' | 'ko' | 'zh'>;
|
|
7
|
-
//# sourceMappingURL=breaking-strings-global.d.ts.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module dashboard-builder-eu
|
|
3
|
-
* @description Per-language entries (en, sv, da, no, fi, de, fr) for DASHBOARD_BUILDER_STRINGS.
|
|
4
|
-
*/
|
|
5
|
-
import type { LanguageMap, DashboardBuilderStrings } from '../../types/index.js';
|
|
6
|
-
export declare const DASHBOARD_BUILDER_STRINGS_EU: Pick<LanguageMap<DashboardBuilderStrings>, 'en' | 'sv' | 'da' | 'no' | 'fi' | 'de' | 'fr'>;
|
|
7
|
-
//# sourceMappingURL=dashboard-builder-eu.d.ts.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module dashboard-builder-global
|
|
3
|
-
* @description Per-language entries (es, nl, ar, he, ja, ko, zh) for DASHBOARD_BUILDER_STRINGS.
|
|
4
|
-
*/
|
|
5
|
-
import type { LanguageMap, DashboardBuilderStrings } from '../../types/index.js';
|
|
6
|
-
export declare const DASHBOARD_BUILDER_STRINGS_GLOBAL: Pick<LanguageMap<DashboardBuilderStrings>, 'es' | 'nl' | 'ar' | 'he' | 'ja' | 'ko' | 'zh'>;
|
|
7
|
-
//# sourceMappingURL=dashboard-builder-global.d.ts.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module deep-analysis-strings-eu
|
|
3
|
-
* @description Per-language entries (en, sv, da, no, fi, de, fr) for DEEP_ANALYSIS_STRINGS.
|
|
4
|
-
*/
|
|
5
|
-
import type { LanguageMap, DeepAnalysisStrings } from '../../types/index.js';
|
|
6
|
-
export declare const DEEP_ANALYSIS_STRINGS_EU: Pick<LanguageMap<DeepAnalysisStrings>, 'en' | 'sv' | 'da' | 'no' | 'fi' | 'de' | 'fr'>;
|
|
7
|
-
//# sourceMappingURL=deep-analysis-strings-eu.d.ts.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module deep-analysis-strings-global
|
|
3
|
-
* @description Per-language entries (es, nl, ar, he, ja, ko, zh) for DEEP_ANALYSIS_STRINGS.
|
|
4
|
-
*/
|
|
5
|
-
import type { LanguageMap, DeepAnalysisStrings } from '../../types/index.js';
|
|
6
|
-
export declare const DEEP_ANALYSIS_STRINGS_GLOBAL: Pick<LanguageMap<DeepAnalysisStrings>, 'es' | 'nl' | 'ar' | 'he' | 'ja' | 'ko' | 'zh'>;
|
|
7
|
-
//# sourceMappingURL=deep-analysis-strings-global.d.ts.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module localized-keywords-eu
|
|
3
|
-
* @description Per-language entries (en, sv, da, no, fi, de, fr) for LOCALIZED_KEYWORDS.
|
|
4
|
-
*/
|
|
5
|
-
import type { LanguageMap } from '../../types/index.js';
|
|
6
|
-
export declare const LOCALIZED_KEYWORDS_EU: Pick<LanguageMap<Record<string, readonly string[]>>, 'en' | 'sv' | 'da' | 'no' | 'fi' | 'de' | 'fr'>;
|
|
7
|
-
//# sourceMappingURL=localized-keywords-eu.d.ts.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module swot-builder-eu
|
|
3
|
-
* @description Per-language entries (en, sv, da, no, fi, de, fr) for SWOT_BUILDER_STRINGS.
|
|
4
|
-
*/
|
|
5
|
-
import type { LanguageMap, SwotBuilderStrings } from '../../types/index.js';
|
|
6
|
-
export declare const SWOT_BUILDER_STRINGS_EU: Pick<LanguageMap<SwotBuilderStrings>, 'en' | 'sv' | 'da' | 'no' | 'fi' | 'de' | 'fr'>;
|
|
7
|
-
//# sourceMappingURL=swot-builder-eu.d.ts.map
|