ruvector 0.2.18 → 0.2.20
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 +20 -7
- package/bin/cli.js +1569 -1553
- package/bin/mcp-server.js +508 -892
- package/package.json +23 -13
- package/src/decompiler/index.js +407 -0
- package/src/decompiler/metrics.js +86 -0
- package/src/decompiler/module-splitter.js +498 -0
- package/src/decompiler/module-tree.js +142 -0
- package/src/decompiler/name-predictor.js +400 -0
- package/src/decompiler/npm-fetch.js +176 -0
- package/src/decompiler/reconstructor.js +499 -0
- package/src/decompiler/reference-tracker.js +285 -0
- package/src/decompiler/statement-parser.js +285 -0
- package/src/decompiler/style-improver.js +438 -0
- package/src/decompiler/subcategories.js +339 -0
- package/src/decompiler/validator.js +379 -0
- package/src/decompiler/witness.js +140 -0
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* subcategories.js - Fine-grained module classification keywords.
|
|
3
|
+
*
|
|
4
|
+
* Each key is a hierarchical module path (e.g. 'tools/bash').
|
|
5
|
+
* Keywords can be plain strings (exact match) or contain '.*' for regex.
|
|
6
|
+
* Used by module-splitter.js to classify statements into ~30-40 modules
|
|
7
|
+
* instead of the original ~9 broad categories.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
'use strict';
|
|
11
|
+
|
|
12
|
+
// ── Fine-grained module classification ─────────────────────────────────────
|
|
13
|
+
const SUBCATEGORIES = {
|
|
14
|
+
// ── tools/* ────────────────────────────────────────────────────────────
|
|
15
|
+
'tools/bash': [
|
|
16
|
+
'BashTool', 'child_process', 'execSync', 'spawnSync', 'spawn(',
|
|
17
|
+
'shell.*command', 'shellArgs', 'commandLine', 'bashCommand',
|
|
18
|
+
'killProcess', 'processExit', 'childProcess',
|
|
19
|
+
],
|
|
20
|
+
'tools/read': [
|
|
21
|
+
'FileReadTool', 'ReadTool', 'readFile', 'readFileSync',
|
|
22
|
+
'FileRead', 'fileContents', 'readContent',
|
|
23
|
+
],
|
|
24
|
+
'tools/edit': [
|
|
25
|
+
'FileEditTool', 'EditTool', 'old_string', 'new_string',
|
|
26
|
+
'applyEdit', 'textEdit', 'replaceInFile', 'editContent',
|
|
27
|
+
],
|
|
28
|
+
'tools/write': [
|
|
29
|
+
'FileWriteTool', 'WriteTool', 'writeFile', 'writeFileSync',
|
|
30
|
+
'createFile', 'FileWrite', 'writeContent',
|
|
31
|
+
],
|
|
32
|
+
'tools/glob': [
|
|
33
|
+
'GlobTool', 'glob(', 'globSync', 'minimatch', 'picomatch',
|
|
34
|
+
'ListFilesTool', 'filePattern', 'globPattern',
|
|
35
|
+
],
|
|
36
|
+
'tools/grep': [
|
|
37
|
+
'GrepTool', 'ripgrep', 'SearchTool', 'searchPattern',
|
|
38
|
+
'contentSearch', 'grepResult', 'matchLine',
|
|
39
|
+
],
|
|
40
|
+
'tools/agent': [
|
|
41
|
+
'AgentTool', 'AgentOutputTool', 'subagent', 'spawnAgent',
|
|
42
|
+
'agentTask', 'taskResult', 'delegateTask',
|
|
43
|
+
],
|
|
44
|
+
'tools/web-fetch': [
|
|
45
|
+
'WebFetch', 'httpGet', 'fetchUrl', 'urlFetch',
|
|
46
|
+
'webRequest', 'httpRequest',
|
|
47
|
+
],
|
|
48
|
+
'tools/web-search': [
|
|
49
|
+
'WebSearch', 'searchResults', 'webQuery',
|
|
50
|
+
'searchEngine', 'searchWeb',
|
|
51
|
+
],
|
|
52
|
+
'tools/notebook': [
|
|
53
|
+
'NotebookEdit', 'notebook', 'jupyter', 'ipynb',
|
|
54
|
+
'cellOutput', 'notebookCell',
|
|
55
|
+
],
|
|
56
|
+
'tools/mcp-dispatch': [
|
|
57
|
+
'ToolUse', 'ToolResult',
|
|
58
|
+
'toolDefinition', 'toolSchema', 'inputSchema',
|
|
59
|
+
'toolChoice', 'toolRunner', 'dispatchTool',
|
|
60
|
+
],
|
|
61
|
+
'tools/todo': [
|
|
62
|
+
'TodoWrite', 'TodoRead', 'todoList', 'todoItem',
|
|
63
|
+
],
|
|
64
|
+
|
|
65
|
+
// ── core/* ─────────────────────────────────────────────────────────────
|
|
66
|
+
'core/agent-loop': [
|
|
67
|
+
'agentLoop', 'mainLoop', 'querySource', 'toolUseContext',
|
|
68
|
+
'systemPrompt', 'conversationTurn', 'assistantMessage',
|
|
69
|
+
'userMessage', 'messageHistory', 'handleToolUse',
|
|
70
|
+
'processMessage', 'runLoop', 'loopIteration',
|
|
71
|
+
],
|
|
72
|
+
'core/streaming': [
|
|
73
|
+
'content_block_delta', 'message_start', 'message_stop',
|
|
74
|
+
'message_delta', 'content_block_start', 'content_block_stop',
|
|
75
|
+
'text_delta', 'input_json_delta', 'StreamEvent',
|
|
76
|
+
'onStream', 'streamHandler', 'stream_event',
|
|
77
|
+
'streamResponse', 'streamingMode',
|
|
78
|
+
],
|
|
79
|
+
'core/context-manager': [
|
|
80
|
+
'tengu_compact', 'microcompact', 'auto_compact',
|
|
81
|
+
'compact_boundary', 'preCompactTokenCount',
|
|
82
|
+
'postCompactTokenCount', 'compaction',
|
|
83
|
+
'tokenCount', 'contextWindow', 'maxTokens',
|
|
84
|
+
'promptCache', 'cacheControl', 'truncat',
|
|
85
|
+
'contextOverflow', 'compactMessages',
|
|
86
|
+
],
|
|
87
|
+
'core/session': [
|
|
88
|
+
'sessionId', 'conversationId', 'sessionState',
|
|
89
|
+
'persistSession', 'checkpoint', 'resume.*session',
|
|
90
|
+
'restore.*session', 'turnCount', 'sessionHistory',
|
|
91
|
+
'saveSession', 'loadSession',
|
|
92
|
+
],
|
|
93
|
+
'core/error-handler': [
|
|
94
|
+
'ErrorHandler', 'errorBoundary', 'handleError',
|
|
95
|
+
'retryWith', 'isRetryable', 'overloaded',
|
|
96
|
+
'rateLimited', 'backoff', 'retryAfter',
|
|
97
|
+
'APIError', 'NetworkError',
|
|
98
|
+
],
|
|
99
|
+
|
|
100
|
+
// ── permissions/* ──────────────────────────────────────────────────────
|
|
101
|
+
'permissions/checker': [
|
|
102
|
+
'canUseTool', 'Permission', 'permission',
|
|
103
|
+
'allowedTools', 'permissionMode', 'isAllowed',
|
|
104
|
+
'checkPermission', 'grantPermission', 'allowList',
|
|
105
|
+
'denyList', 'alwaysAllowRules', 'denyWrite',
|
|
106
|
+
'permissionCheck', 'allowRule', 'denyRule',
|
|
107
|
+
],
|
|
108
|
+
'permissions/sandbox': [
|
|
109
|
+
'sandbox', 'bubblewrap', 'seatbelt', 'firejail',
|
|
110
|
+
'containerize', 'isolat', 'sandboxMode',
|
|
111
|
+
'seccomp', 'landlock', 'pledg',
|
|
112
|
+
],
|
|
113
|
+
'permissions/rules': [
|
|
114
|
+
'permissionRule', 'ruleSet', 'matchRule',
|
|
115
|
+
'pathRule', 'toolRule', 'readOnlyRule',
|
|
116
|
+
'globRule', 'regexRule',
|
|
117
|
+
],
|
|
118
|
+
|
|
119
|
+
// ── auth/* ─────────────────────────────────────────────────────────────
|
|
120
|
+
'auth/oauth': [
|
|
121
|
+
'OAuth', 'PKCE', 'authorization_code', 'token.*endpoint',
|
|
122
|
+
'refresh.*token', 'authorizationUrl', 'codeVerifier',
|
|
123
|
+
'codeChallenge', 'oauthFlow', 'oauthCallback',
|
|
124
|
+
],
|
|
125
|
+
'auth/api-key': [
|
|
126
|
+
'x-api-key', 'ANTHROPIC_API_KEY', 'apiKeyHelper',
|
|
127
|
+
'apiKey.*valid', 'loadApiKey',
|
|
128
|
+
'keyring',
|
|
129
|
+
],
|
|
130
|
+
'auth/bedrock': [
|
|
131
|
+
'Bedrock', 'BedrockRuntime', 'aws.*region',
|
|
132
|
+
'awsProfile', 'sigv4', 'awsCredentials',
|
|
133
|
+
],
|
|
134
|
+
'auth/vertex': [
|
|
135
|
+
'Vertex', 'vertex.*ai', 'google.*cloud',
|
|
136
|
+
'googleAuth', 'serviceAccount', 'vertexProject',
|
|
137
|
+
],
|
|
138
|
+
|
|
139
|
+
// ── mcp/* ──────────────────────────────────────────────────────────────
|
|
140
|
+
'mcp/client': [
|
|
141
|
+
'McpClient', 'mcp.*connect', 'mcp.*initialize',
|
|
142
|
+
'mcpConnection', 'mcp_client', 'connectMcp',
|
|
143
|
+
],
|
|
144
|
+
'mcp/transport': [
|
|
145
|
+
'StdioTransport', 'SseTransport', 'StreamableHttp',
|
|
146
|
+
'McpTransport', 'transport.*type', 'transportLayer',
|
|
147
|
+
'stdio.*transport', 'websocket.*transport',
|
|
148
|
+
],
|
|
149
|
+
'mcp/protocol': [
|
|
150
|
+
'jsonrpc', 'tools/list', 'tools/call',
|
|
151
|
+
'resources/list', 'prompts/list', 'McpError',
|
|
152
|
+
'mcp__', 'McpServer', 'mcp_server',
|
|
153
|
+
'callTool', 'listTools',
|
|
154
|
+
],
|
|
155
|
+
'mcp/servers': [
|
|
156
|
+
'mcpServers', 'serverConfig', 'serverList',
|
|
157
|
+
'registeredServers', 'spawnServer', 'serverProcess',
|
|
158
|
+
],
|
|
159
|
+
|
|
160
|
+
// ── config/* ───────────────────────────────────────────────────────────
|
|
161
|
+
'config/settings': [
|
|
162
|
+
'settings.*json', 'loadSettings', 'saveSettings',
|
|
163
|
+
'userSettings', 'Settings', 'configuration',
|
|
164
|
+
'loadConfig', 'parseConfig',
|
|
165
|
+
],
|
|
166
|
+
'config/env-vars': [
|
|
167
|
+
'CLAUDE_CODE_', 'ANTHROPIC_',
|
|
168
|
+
'envVar', 'dotenv', 'loadEnv',
|
|
169
|
+
],
|
|
170
|
+
'config/models': [
|
|
171
|
+
'modelId', 'modelName', 'model.*select',
|
|
172
|
+
'mainLoopModel', 'availableModels', 'modelOverrides',
|
|
173
|
+
'modelPreference', 'defaultModel',
|
|
174
|
+
],
|
|
175
|
+
'config/feature-flags': [
|
|
176
|
+
'featureFlag', 'isEnabled', 'flagValue',
|
|
177
|
+
'experimentId', 'feature.*gate', 'rollout',
|
|
178
|
+
'featureEnabled', 'featureConfig',
|
|
179
|
+
],
|
|
180
|
+
|
|
181
|
+
// ── telemetry/* ────────────────────────────────────────────────────────
|
|
182
|
+
'telemetry/otel': [
|
|
183
|
+
'opentelemetry', 'OTEL_', 'TraceProvider',
|
|
184
|
+
'SpanProcessor', 'tracing', 'span',
|
|
185
|
+
'tracer', 'otelExporter',
|
|
186
|
+
],
|
|
187
|
+
'telemetry/datadog': [
|
|
188
|
+
'datadog', 'DD_', 'ddTrace', 'datadogExporter',
|
|
189
|
+
],
|
|
190
|
+
'telemetry/events': [
|
|
191
|
+
'tengu_', 'trackEvent', 'analytics',
|
|
192
|
+
'Telemetry', 'sentry',
|
|
193
|
+
'eventEmit', 'emitEvent', 'telemetryEvent',
|
|
194
|
+
],
|
|
195
|
+
'telemetry/cost': [
|
|
196
|
+
'cost', 'tokenUsage', 'inputTokens', 'outputTokens',
|
|
197
|
+
'cacheRead', 'cacheCreation', 'pricing',
|
|
198
|
+
'costTracker', 'usageMetrics',
|
|
199
|
+
],
|
|
200
|
+
'telemetry/perfetto': [
|
|
201
|
+
'perfetto', 'perfTrace', 'traceBegin',
|
|
202
|
+
'traceEnd', 'traceCounter',
|
|
203
|
+
],
|
|
204
|
+
|
|
205
|
+
// ── ui/* ────────────────────────────────────────────────────────────────
|
|
206
|
+
'ui/slash-commands': [
|
|
207
|
+
'slashCommand', 'registerCommand', 'commandHandler',
|
|
208
|
+
'parseCommand', '/help', '/clear', '/compact',
|
|
209
|
+
'/bug', '/init', '/login', '/logout',
|
|
210
|
+
'/doctor', '/config', '/cost', '/memory',
|
|
211
|
+
],
|
|
212
|
+
'ui/ink-components': [
|
|
213
|
+
'useInput', 'useFocus', 'useApp', 'useStdin', 'useStdout',
|
|
214
|
+
'inkRenderer', 'InkProvider', 'measureElement',
|
|
215
|
+
],
|
|
216
|
+
'ui/keybindings': [
|
|
217
|
+
'keybinding', 'keyHandler', 'hotkey',
|
|
218
|
+
'onKeyPress', 'keyMap', 'shortcut',
|
|
219
|
+
],
|
|
220
|
+
'ui/terminal': [
|
|
221
|
+
'ansiColor', 'chalk', 'stripAnsi',
|
|
222
|
+
'cursorMove', 'clearLine', 'terminalWidth',
|
|
223
|
+
'isTerminal', 'ttyColumns',
|
|
224
|
+
],
|
|
225
|
+
|
|
226
|
+
// ── model-provider/* ───────────────────────────────────────────────────
|
|
227
|
+
'model-provider/anthropic': [
|
|
228
|
+
'anthropic', 'Anthropic', 'claude-', 'claude_',
|
|
229
|
+
'messagesCreate', 'AnthropicClient',
|
|
230
|
+
],
|
|
231
|
+
'model-provider/openai': [
|
|
232
|
+
'openai', 'OpenAI', 'chatCompletion',
|
|
233
|
+
'gpt-', 'openAiClient',
|
|
234
|
+
],
|
|
235
|
+
'model-provider/router': [
|
|
236
|
+
'provider', 'routeModel', 'selectProvider',
|
|
237
|
+
'providerConfig', 'modelRouter',
|
|
238
|
+
],
|
|
239
|
+
|
|
240
|
+
// ── git/* ──────────────────────────────────────────────────────────────
|
|
241
|
+
'git/operations': [
|
|
242
|
+
'gitDiff', 'gitStatus', 'gitLog', 'gitCommit',
|
|
243
|
+
'gitAdd', 'gitBranch', 'gitCheckout',
|
|
244
|
+
'isGitRepo', 'getGitRoot', 'gitStash',
|
|
245
|
+
],
|
|
246
|
+
|
|
247
|
+
// ── filesystem/* ───────────────────────────────────────────────────────
|
|
248
|
+
'filesystem/operations': [
|
|
249
|
+
'readdirSync', 'mkdirSync', 'statSync', 'lstatSync',
|
|
250
|
+
'renameSync', 'unlinkSync', 'copyFileSync',
|
|
251
|
+
'existsSync', 'realpathSync', 'accessSync',
|
|
252
|
+
'fs.readdir', 'fs.mkdir', 'fs.stat', 'fs.lstat',
|
|
253
|
+
],
|
|
254
|
+
|
|
255
|
+
// ── network/* ──────────────────────────────────────────────────────────
|
|
256
|
+
'network/http': [
|
|
257
|
+
'http.*request', 'https.*request', 'fetch(',
|
|
258
|
+
'axios', 'got(', 'requestOptions',
|
|
259
|
+
'responseBody', 'statusCode',
|
|
260
|
+
],
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
// ── String-literal patterns for minified code ─────────────────────────────
|
|
264
|
+
// Minified bundles mangle identifiers but preserve string literals.
|
|
265
|
+
// These patterns match quoted strings commonly found in each domain.
|
|
266
|
+
// Each pattern is matched against the raw code (not just identifiers).
|
|
267
|
+
const STRING_PATTERNS = {
|
|
268
|
+
'tools/bash': ['"bash"', '"shell"', '"command"', '"child_process"', '"spawn"', '"BashTool"'],
|
|
269
|
+
'tools/read': ['"FileReadTool"', '"ReadFileTool"', '"cat "', '"readFile"'],
|
|
270
|
+
'tools/edit': ['"FileEditTool"', '"old_string"', '"new_string"', '"EditFileTool"'],
|
|
271
|
+
'tools/write': ['"FileWriteTool"', '"WriteFileTool"', '"createFile"'],
|
|
272
|
+
'tools/glob': ['"GlobTool"', '"ListFilesTool"', '"glob"', '"minimatch"'],
|
|
273
|
+
'tools/grep': ['"GrepTool"', '"ripgrep"', '"rg "', '"SearchTool"'],
|
|
274
|
+
'tools/agent': ['"AgentTool"', '"Task"', '"subagent"'],
|
|
275
|
+
'tools/web-fetch': ['"WebFetchTool"', '"url_fetch"'],
|
|
276
|
+
'tools/web-search': ['"WebSearchTool"', '"web_search"'],
|
|
277
|
+
'tools/notebook': ['"NotebookEditTool"', '"ipynb"', '"jupyter"'],
|
|
278
|
+
'tools/mcp-dispatch': ['"inputSchema"', '"toolSchema"', '"toolDefinition"'],
|
|
279
|
+
'tools/todo': ['"TodoWriteTool"', '"TodoReadTool"'],
|
|
280
|
+
'core/agent-loop': ['"assistant"', '"user"', '"system"', '"systemPrompt"', '"messageHistory"'],
|
|
281
|
+
'core/streaming': [
|
|
282
|
+
'"content_block_delta"', '"message_start"', '"message_stop"',
|
|
283
|
+
'"message_delta"', '"content_block_start"', '"content_block_stop"',
|
|
284
|
+
'"text_delta"', '"input_json_delta"', '"stream_event"',
|
|
285
|
+
],
|
|
286
|
+
'core/context-manager': [
|
|
287
|
+
'"tengu_compact"', '"auto_compact"', '"compact"',
|
|
288
|
+
'"contextWindow"', '"maxTokens"', '"cacheControl"',
|
|
289
|
+
],
|
|
290
|
+
'core/session': ['"sessionId"', '"conversationId"', '"checkpoint"', '"resume"'],
|
|
291
|
+
'core/error-handler': ['"overloaded"', '"rate_limit"', '"retryAfter"', '"APIError"'],
|
|
292
|
+
'permissions/checker': [
|
|
293
|
+
'"canUseTool"', '"permission"', '"allowedTools"',
|
|
294
|
+
'"permissionMode"', '"alwaysAllow"',
|
|
295
|
+
],
|
|
296
|
+
'permissions/sandbox': ['"sandbox"', '"bubblewrap"', '"seatbelt"', '"firejail"'],
|
|
297
|
+
'auth/oauth': ['"OAuth"', '"PKCE"', '"authorization_code"', '"refresh_token"', '"code_verifier"'],
|
|
298
|
+
'auth/api-key': ['"x-api-key"', '"ANTHROPIC_API_KEY"', '"apiKeyHelper"'],
|
|
299
|
+
'auth/bedrock': ['"bedrock"', '"BedrockRuntime"', '"aws-region"'],
|
|
300
|
+
'auth/vertex': ['"vertex"', '"vertexai"', '"google-cloud"'],
|
|
301
|
+
'mcp/client': ['"McpClient"', '"mcp_client"'],
|
|
302
|
+
'mcp/transport': ['"stdio"', '"sse"', '"streamable-http"', '"StdioTransport"'],
|
|
303
|
+
'mcp/protocol': ['"jsonrpc"', '"tools/list"', '"tools/call"', '"resources/list"', '"mcp__"'],
|
|
304
|
+
'mcp/servers': ['"mcpServers"', '"serverConfig"'],
|
|
305
|
+
'config/settings': ['"settings.json"', '"userSettings"', '".claude"'],
|
|
306
|
+
'config/env-vars': ['"CLAUDE_CODE_"', '"ANTHROPIC_"', '"CLAUDE_CONFIG"', '"CLAUDE_SKIP"'],
|
|
307
|
+
'config/models': ['"modelId"', '"claude-sonnet"', '"claude-opus"', '"claude-haiku"'],
|
|
308
|
+
'config/feature-flags': ['"featureFlag"', '"experiment"', '"rollout"'],
|
|
309
|
+
'telemetry/otel': ['"opentelemetry"', '"OTEL_"', '"TraceProvider"'],
|
|
310
|
+
'telemetry/datadog': ['"datadog"', '"DD_TRACE"'],
|
|
311
|
+
'telemetry/events': ['"tengu_"', '"trackEvent"', '"analytics"', '"telemetryEvent"'],
|
|
312
|
+
'telemetry/cost': ['"inputTokens"', '"outputTokens"', '"cacheRead"', '"cacheCreation"'],
|
|
313
|
+
'ui/slash-commands': ['"/help"', '"/clear"', '"/compact"', '"/bug"', '"/init"', '"/doctor"'],
|
|
314
|
+
'ui/ink-components': ['"useInput"', '"useFocus"', '"useApp"', '"inkRenderer"'],
|
|
315
|
+
'ui/keybindings': ['"keybinding"', '"shortcut"', '"hotkey"'],
|
|
316
|
+
'ui/terminal': ['"chalk"', '"stripAnsi"', '"ansiColor"'],
|
|
317
|
+
'model-provider/anthropic': ['"anthropic"', '"claude-"', '"Anthropic"', '"messages"'],
|
|
318
|
+
'model-provider/openai': ['"openai"', '"gpt-"', '"chatCompletion"'],
|
|
319
|
+
'git/operations': ['"git diff"', '"git status"', '"git log"', '"git commit"'],
|
|
320
|
+
'network/http': ['"Content-Type"', '"application/json"', '"Authorization"'],
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
// ── Legacy MODULE_KEYWORDS alias ───────────────────────────────────────────
|
|
324
|
+
// Maps old broad categories for backward compat.
|
|
325
|
+
const MODULE_KEYWORDS = {
|
|
326
|
+
'tool-dispatch': SUBCATEGORIES['tools/mcp-dispatch'],
|
|
327
|
+
'permission-system': SUBCATEGORIES['permissions/checker'],
|
|
328
|
+
'mcp-client': SUBCATEGORIES['mcp/protocol'],
|
|
329
|
+
'streaming-handler': SUBCATEGORIES['core/streaming'],
|
|
330
|
+
'context-manager': SUBCATEGORIES['core/context-manager'],
|
|
331
|
+
'agent-loop': SUBCATEGORIES['core/agent-loop'],
|
|
332
|
+
'commands': SUBCATEGORIES['ui/slash-commands'],
|
|
333
|
+
'telemetry': SUBCATEGORIES['telemetry/events'],
|
|
334
|
+
'config': SUBCATEGORIES['config/settings'],
|
|
335
|
+
'session': SUBCATEGORIES['core/session'],
|
|
336
|
+
'model-provider': SUBCATEGORIES['model-provider/anthropic'],
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
module.exports = { SUBCATEGORIES, MODULE_KEYWORDS, STRING_PATTERNS };
|