@ruvector/ruvllm 4.0.0 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +449 -158
- package/bin/cli.js +1066 -0
- package/bin/gpu-delegate.js +67 -0
- package/bin/ruvllm-adapters.js +5 -0
- package/bin/ruvllm-gpu-loop.js +5 -0
- package/bin/serve.js +265 -0
- package/dist/cjs/benchmarks/embedding-benchmark.d.ts +102 -0
- package/dist/cjs/benchmarks/embedding-benchmark.d.ts.map +1 -0
- package/dist/cjs/benchmarks/embedding-benchmark.js +436 -0
- package/dist/cjs/benchmarks/index.d.ts +34 -0
- package/dist/cjs/benchmarks/index.d.ts.map +1 -0
- package/dist/cjs/benchmarks/index.js +132 -0
- package/dist/cjs/benchmarks/model-comparison.d.ts +71 -0
- package/dist/cjs/benchmarks/model-comparison.d.ts.map +1 -0
- package/dist/cjs/benchmarks/model-comparison.js +476 -0
- package/dist/cjs/benchmarks/routing-benchmark.d.ts +70 -0
- package/dist/cjs/benchmarks/routing-benchmark.d.ts.map +1 -0
- package/dist/cjs/benchmarks/routing-benchmark.js +289 -0
- package/dist/cjs/contrastive.d.ts +229 -0
- package/dist/cjs/contrastive.d.ts.map +1 -0
- package/dist/cjs/contrastive.js +589 -0
- package/dist/cjs/engine.d.ts +160 -0
- package/dist/cjs/engine.d.ts.map +1 -0
- package/dist/cjs/engine.js +552 -0
- package/dist/cjs/export.d.ts +182 -0
- package/dist/cjs/export.d.ts.map +1 -0
- package/dist/cjs/export.js +422 -0
- package/dist/cjs/federated.d.ts +233 -0
- package/dist/cjs/federated.d.ts.map +1 -0
- package/dist/cjs/federated.js +525 -0
- package/dist/cjs/index.d.ts +68 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +104 -0
- package/dist/cjs/intelligence.d.ts +136 -0
- package/dist/cjs/intelligence.d.ts.map +1 -0
- package/dist/cjs/intelligence.js +226 -0
- package/dist/cjs/lora.d.ts +251 -0
- package/dist/cjs/lora.d.ts.map +1 -0
- package/dist/cjs/lora.js +509 -0
- package/dist/cjs/models.d.ts +129 -0
- package/dist/cjs/models.d.ts.map +1 -0
- package/dist/cjs/models.js +323 -0
- package/dist/cjs/native.d.ts +140 -0
- package/dist/cjs/native.d.ts.map +1 -0
- package/dist/cjs/native.js +98 -0
- package/dist/cjs/session.d.ts +80 -0
- package/dist/cjs/session.d.ts.map +1 -0
- package/dist/cjs/session.js +203 -0
- package/dist/cjs/simd.d.ts +90 -0
- package/dist/cjs/simd.d.ts.map +1 -0
- package/dist/cjs/simd.js +209 -0
- package/dist/cjs/sona.d.ts +223 -0
- package/dist/cjs/sona.d.ts.map +1 -0
- package/dist/cjs/sona.js +562 -0
- package/dist/cjs/streaming.d.ts +55 -0
- package/dist/cjs/streaming.d.ts.map +1 -0
- package/dist/cjs/streaming.js +131 -0
- package/dist/cjs/training.d.ts +312 -0
- package/dist/cjs/training.d.ts.map +1 -0
- package/dist/cjs/training.js +610 -0
- package/dist/cjs/types.d.ts +696 -0
- package/dist/cjs/types.d.ts.map +1 -0
- package/dist/cjs/types.js +6 -0
- package/dist/esm/benchmarks/embedding-benchmark.d.ts +102 -0
- package/dist/esm/benchmarks/embedding-benchmark.d.ts.map +1 -0
- package/dist/esm/benchmarks/embedding-benchmark.js +427 -0
- package/dist/esm/benchmarks/index.d.ts +34 -0
- package/dist/esm/benchmarks/index.d.ts.map +1 -0
- package/dist/esm/benchmarks/index.js +110 -0
- package/dist/esm/benchmarks/model-comparison.d.ts +71 -0
- package/dist/esm/benchmarks/model-comparison.d.ts.map +1 -0
- package/dist/esm/benchmarks/model-comparison.js +434 -0
- package/dist/esm/benchmarks/routing-benchmark.d.ts +70 -0
- package/dist/esm/benchmarks/routing-benchmark.d.ts.map +1 -0
- package/dist/esm/benchmarks/routing-benchmark.js +283 -0
- package/dist/esm/contrastive.d.ts +229 -0
- package/dist/esm/contrastive.d.ts.map +1 -0
- package/dist/esm/contrastive.js +578 -0
- package/dist/esm/engine.d.ts +160 -0
- package/dist/esm/engine.d.ts.map +1 -0
- package/dist/esm/engine.js +548 -0
- package/dist/esm/export.d.ts +182 -0
- package/dist/esm/export.d.ts.map +1 -0
- package/dist/esm/export.js +414 -0
- package/dist/esm/federated.d.ts +233 -0
- package/dist/esm/federated.d.ts.map +1 -0
- package/dist/esm/federated.js +520 -0
- package/dist/esm/index.d.ts +68 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +84 -0
- package/dist/esm/intelligence.d.ts +136 -0
- package/dist/esm/intelligence.d.ts.map +1 -0
- package/dist/esm/intelligence.js +188 -0
- package/dist/esm/lora.d.ts +251 -0
- package/dist/esm/lora.d.ts.map +1 -0
- package/dist/esm/lora.js +504 -0
- package/dist/esm/models.d.ts +129 -0
- package/dist/esm/models.d.ts.map +1 -0
- package/dist/esm/models.js +282 -0
- package/dist/esm/native.d.ts +140 -0
- package/dist/esm/native.d.ts.map +1 -0
- package/dist/esm/native.js +100 -0
- package/dist/esm/session.d.ts +80 -0
- package/dist/esm/session.d.ts.map +1 -0
- package/dist/esm/session.js +199 -0
- package/dist/esm/simd.d.ts +90 -0
- package/dist/esm/simd.d.ts.map +1 -0
- package/dist/esm/simd.js +205 -0
- package/dist/esm/sona.d.ts +223 -0
- package/dist/esm/sona.d.ts.map +1 -0
- package/dist/esm/sona.js +556 -0
- package/dist/esm/streaming.d.ts +55 -0
- package/dist/esm/streaming.d.ts.map +1 -0
- package/dist/esm/streaming.js +126 -0
- package/dist/esm/training.d.ts +312 -0
- package/dist/esm/training.d.ts.map +1 -0
- package/dist/esm/training.js +603 -0
- package/dist/esm/types.d.ts +696 -0
- package/dist/esm/types.d.ts.map +1 -0
- package/dist/esm/types.js +5 -0
- package/gpu/_load.cjs +38 -0
- package/gpu/_load.mjs +33 -0
- package/gpu/adapters.cjs +3 -0
- package/gpu/adapters.mjs +5 -0
- package/gpu/engine.cjs +3 -0
- package/gpu/engine.mjs +5 -0
- package/gpu/fixture.cjs +3 -0
- package/gpu/fixture.mjs +5 -0
- package/gpu/gpu-host.cjs +3 -0
- package/gpu/gpu-host.mjs +5 -0
- package/gpu/gpu-loop.cjs +3 -0
- package/gpu/gpu-loop.mjs +5 -0
- package/gpu/gpu-spool-adapter.cjs +3 -0
- package/gpu/gpu-spool-adapter.mjs +5 -0
- package/gpu/vast-host-adapter.cjs +3 -0
- package/gpu/vast-host-adapter.mjs +5 -0
- package/package.json +159 -49
- package/scripts/ensemble-model-compare.js +287 -0
- package/scripts/fix-esm-extensions.mjs +220 -0
- package/scripts/huggingface/README.md +537 -0
- package/scripts/huggingface/publish.sh +112 -0
- package/scripts/hybrid-model-compare.js +373 -0
- package/scripts/improved-model-compare.js +288 -0
- package/scripts/optimized-model-compare.js +364 -0
- package/scripts/real-model-compare.js +280 -0
- package/scripts/smoke-esm.mjs +82 -0
- package/scripts/training/agentic-flow-capabilities.json +329 -0
- package/scripts/training/claude-code-synth.js +505 -0
- package/scripts/training/claude-flow-capabilities.json +1322 -0
- package/scripts/training/claude-hard-negatives.js +222 -0
- package/scripts/training/contrastive-finetune.js +517 -0
- package/scripts/training/ecosystem-triplets.jsonl +1467 -0
- package/scripts/training/generate-ecosystem-triplets.js +1324 -0
- package/scripts/training/routing-dataset.js +634 -0
- package/scripts/training/ruvector-capabilities.json +798 -0
- package/scripts/training/validate-ecosystem.js +381 -0
- package/scripts/training/validation-results.json +23 -0
- package/GPU-LOOP.md +0 -229
- package/LICENSE +0 -201
- package/NOTICE +0 -9
- package/examples/gpu-loop.example.json +0 -26
- package/src/adapters-cli.mjs +0 -23
- package/src/adapters.mjs +0 -91
- package/src/cli.mjs +0 -30
- package/src/engine.mjs +0 -238
- package/src/fixture.mjs +0 -38
- package/src/gpu-host.mjs +0 -47
- package/src/gpu-loop-cli.mjs +0 -67
- package/src/gpu-loop.mjs +0 -184
- package/src/gpu-spool-adapter.mjs +0 -80
- package/src/vast-host-adapter.mjs +0 -274
|
@@ -0,0 +1,548 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RuvLLM Engine - Main orchestrator for self-learning LLM
|
|
3
|
+
*/
|
|
4
|
+
import { getNativeModule, } from './native.js';
|
|
5
|
+
/**
|
|
6
|
+
* Convert JS config to native config format
|
|
7
|
+
*/
|
|
8
|
+
function toNativeConfig(config) {
|
|
9
|
+
if (!config)
|
|
10
|
+
return undefined;
|
|
11
|
+
return {
|
|
12
|
+
embedding_dim: config.embeddingDim,
|
|
13
|
+
router_hidden_dim: config.routerHiddenDim,
|
|
14
|
+
hnsw_m: config.hnswM,
|
|
15
|
+
hnsw_ef_construction: config.hnswEfConstruction,
|
|
16
|
+
hnsw_ef_search: config.hnswEfSearch,
|
|
17
|
+
learning_enabled: config.learningEnabled,
|
|
18
|
+
quality_threshold: config.qualityThreshold,
|
|
19
|
+
ewc_lambda: config.ewcLambda,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Convert JS generation config to native format
|
|
24
|
+
*/
|
|
25
|
+
function toNativeGenConfig(config) {
|
|
26
|
+
if (!config)
|
|
27
|
+
return undefined;
|
|
28
|
+
// camelCase: napi-rs ignores snake_case keys on `#[napi(object)]` inputs.
|
|
29
|
+
return {
|
|
30
|
+
maxTokens: config.maxTokens,
|
|
31
|
+
temperature: config.temperature,
|
|
32
|
+
topP: config.topP,
|
|
33
|
+
topK: config.topK,
|
|
34
|
+
repetitionPenalty: config.repetitionPenalty,
|
|
35
|
+
stopSequences: config.stopSequences,
|
|
36
|
+
seed: config.seed,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function toGenerationResult(r) {
|
|
40
|
+
return {
|
|
41
|
+
text: r.text,
|
|
42
|
+
promptTokens: r.promptTokens,
|
|
43
|
+
completionTokens: r.completionTokens,
|
|
44
|
+
finishReason: r.finishReason,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function toModelInfo(i) {
|
|
48
|
+
return {
|
|
49
|
+
name: i.name,
|
|
50
|
+
architecture: i.architecture,
|
|
51
|
+
numParameters: i.numParameters,
|
|
52
|
+
vocabSize: i.vocabSize,
|
|
53
|
+
hiddenSize: i.hiddenSize,
|
|
54
|
+
numLayers: i.numLayers,
|
|
55
|
+
maxContextLength: i.maxContextLength,
|
|
56
|
+
quantization: i.quantization ?? null,
|
|
57
|
+
memoryUsageBytes: i.memoryUsageBytes,
|
|
58
|
+
chatTemplate: i.chatTemplate ?? null,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
const warned = new Set();
|
|
62
|
+
/** Warn once per process (or throw when `strict`). */
|
|
63
|
+
function notice(code, message, strict) {
|
|
64
|
+
if (strict)
|
|
65
|
+
throw codedError(code, message);
|
|
66
|
+
if (warned.has(code))
|
|
67
|
+
return;
|
|
68
|
+
warned.add(code);
|
|
69
|
+
const g = globalThis;
|
|
70
|
+
if (typeof g.process?.emitWarning === 'function')
|
|
71
|
+
g.process.emitWarning(message, { code });
|
|
72
|
+
else
|
|
73
|
+
g.console?.warn?.(`${code}: ${message}`);
|
|
74
|
+
}
|
|
75
|
+
/** An Error whose message starts with, and whose `code` is, `code`. */
|
|
76
|
+
function codedError(code, message) {
|
|
77
|
+
const err = new Error(`${code}: ${message}`);
|
|
78
|
+
err.code = code;
|
|
79
|
+
return err;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Call into the native engine, giving its errors the same `code` as the
|
|
83
|
+
* wrapper's own: native messages start with `RUVLLM_<CODE>:`, but napi-rs
|
|
84
|
+
* sets `code` to a generic status such as `GenericFailure`.
|
|
85
|
+
*/
|
|
86
|
+
function callNative(f) {
|
|
87
|
+
try {
|
|
88
|
+
return f();
|
|
89
|
+
}
|
|
90
|
+
catch (e) {
|
|
91
|
+
const match = /^(RUVLLM_[A-Z_]+):/.exec(e instanceof Error ? e.message : '');
|
|
92
|
+
if (match)
|
|
93
|
+
e.code = match[1];
|
|
94
|
+
throw e;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function platformKey() {
|
|
98
|
+
const p = globalThis.process;
|
|
99
|
+
return `${p?.platform ?? 'unknown'}-${p?.arch ?? 'unknown'}`;
|
|
100
|
+
}
|
|
101
|
+
function envAllowsPlaceholder() {
|
|
102
|
+
const p = globalThis.process;
|
|
103
|
+
return p?.env?.RUVLLM_ALLOW_MOCK === '1';
|
|
104
|
+
}
|
|
105
|
+
/** Returned by generate()/query() only with `allowPlaceholder` / `RUVLLM_ALLOW_MOCK=1`. */
|
|
106
|
+
export const PLACEHOLDER_TEXT = '[RuvLLM placeholder: no language model is loaded; this text is not model output]';
|
|
107
|
+
/**
|
|
108
|
+
* RuvLLM - Self-learning LLM orchestrator
|
|
109
|
+
*
|
|
110
|
+
* Combines SONA adaptive learning with HNSW memory, FastGRNN routing, and
|
|
111
|
+
* GGUF inference through the native candle backend.
|
|
112
|
+
*
|
|
113
|
+
* Text comes only from a model loaded with `loadModel()` (or the `modelPath`
|
|
114
|
+
* option). Without one, `generate()`, `query()`, `chat()` and
|
|
115
|
+
* `generateDetailed()` throw `RUVLLM_NO_LANGUAGE_MODEL`.
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* ```typescript
|
|
119
|
+
* import { RuvLLM } from '@ruvector/ruvllm';
|
|
120
|
+
*
|
|
121
|
+
* const llm = new RuvLLM();
|
|
122
|
+
* llm.loadModel('./qwen2.5-0.5b-instruct-q4_k_m.gguf');
|
|
123
|
+
*
|
|
124
|
+
* const out = llm.chat([{ role: 'user', content: 'What is machine learning?' }], {
|
|
125
|
+
* maxTokens: 128,
|
|
126
|
+
* });
|
|
127
|
+
* console.log(out.text, out.finishReason, out.completionTokens);
|
|
128
|
+
*
|
|
129
|
+
* // Routed query, answered by the loaded model
|
|
130
|
+
* const response = llm.query('What is machine learning?');
|
|
131
|
+
* llm.feedback({ requestId: response.requestId, rating: 5 });
|
|
132
|
+
* ```
|
|
133
|
+
*/
|
|
134
|
+
export class RuvLLM {
|
|
135
|
+
/**
|
|
136
|
+
* Create a new RuvLLM instance
|
|
137
|
+
*/
|
|
138
|
+
constructor(config) {
|
|
139
|
+
this.native = null;
|
|
140
|
+
this.nativeVersion = null;
|
|
141
|
+
// Fallback state for when native module is not available
|
|
142
|
+
this.fallbackState = {
|
|
143
|
+
memory: new Map(),
|
|
144
|
+
nextId: 1,
|
|
145
|
+
queryCount: 0,
|
|
146
|
+
};
|
|
147
|
+
this.config = config ?? {};
|
|
148
|
+
this.allowPlaceholder = this.config.allowPlaceholder === true || envAllowsPlaceholder();
|
|
149
|
+
const extra = this.config;
|
|
150
|
+
if (extra.backend !== undefined) {
|
|
151
|
+
notice('RUVLLM_UNSUPPORTED_OPTION', '`backend` is not supported by @ruvector/ruvllm and is ignored: models loaded with ' +
|
|
152
|
+
'loadModel() run on the built-in candle backend.', this.config.strict);
|
|
153
|
+
}
|
|
154
|
+
const mod = getNativeModule();
|
|
155
|
+
if (mod) {
|
|
156
|
+
try {
|
|
157
|
+
this.native = new mod.RuvLLMEngine(toNativeConfig(config));
|
|
158
|
+
this.nativeVersion = mod.version();
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
// Silently fall back to JS implementation
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
if (this.config.modelPath !== undefined) {
|
|
165
|
+
this.loadModel(this.config.modelPath, this.config.modelOptions);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Whether the native binary can load and run a language model. False
|
|
170
|
+
* without a native binary, for binaries that predate `loadModel` (the 2.0.x
|
|
171
|
+
* `@ruvector/ruvllm-<platform>` packages), and for binaries built without
|
|
172
|
+
* the `candle` inference backend.
|
|
173
|
+
*/
|
|
174
|
+
supportsModelLoading() {
|
|
175
|
+
const native = this.native;
|
|
176
|
+
if (typeof native?.loadModel !== 'function')
|
|
177
|
+
return false;
|
|
178
|
+
// Builds that predate this probe all had the backend (they were local
|
|
179
|
+
// `npm run build:native` builds, which enable `candle`).
|
|
180
|
+
return typeof native.supportsModelLoading === 'function' ? native.supportsModelLoading() : true;
|
|
181
|
+
}
|
|
182
|
+
/** Error for a binary that has `loadModel` but no inference backend. */
|
|
183
|
+
noBackendError() {
|
|
184
|
+
return codedError('RUVLLM_NO_INFERENCE_BACKEND', `the installed native binary (version ${this.nativeVersion ?? 'unknown'}) was built ` +
|
|
185
|
+
'without the `candle` inference backend and cannot load models. Rebuild it with ' +
|
|
186
|
+
'`npm run build:native` (which enables `candle`) and set RUVLLM_NATIVE_PATH to it, ' +
|
|
187
|
+
'or use the ruvllm CLI (`ruvllm serve <model>`).');
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Load a GGUF model (or a directory holding one) for `generate()`,
|
|
191
|
+
* `chat()` and `query()`. Supported GGUF architectures: llama, mistral,
|
|
192
|
+
* qwen2, qwen3; others fail with an error naming the architecture. The
|
|
193
|
+
* tokenizer comes from a `tokenizer.json` beside the file, else from the
|
|
194
|
+
* GGUF itself. Replaces any loaded model; if loading fails, no model is
|
|
195
|
+
* loaded. Synchronous: blocks the event loop while the weights load.
|
|
196
|
+
*
|
|
197
|
+
* @throws `RUVLLM_NATIVE_UNAVAILABLE` without a native binary,
|
|
198
|
+
* `RUVLLM_NATIVE_TOO_OLD` when the binary predates model loading,
|
|
199
|
+
* `RUVLLM_NO_INFERENCE_BACKEND` when it was built without `candle`,
|
|
200
|
+
* `RUVLLM_MODEL_NOT_FOUND` / `RUVLLM_MODEL_LOAD_FAILED` from the backend.
|
|
201
|
+
*/
|
|
202
|
+
loadModel(path, options) {
|
|
203
|
+
if (typeof path !== 'string' || path.length === 0) {
|
|
204
|
+
throw new TypeError('loadModel: path must be a non-empty string');
|
|
205
|
+
}
|
|
206
|
+
if (!this.native) {
|
|
207
|
+
throw codedError('RUVLLM_NATIVE_UNAVAILABLE', `no native RuvLLM binary could be loaded for ${platformKey()}, so models cannot be ` +
|
|
208
|
+
'loaded. Install the matching @ruvector/ruvllm-<platform> package, or build one ' +
|
|
209
|
+
'(`npm run build:native`) and set RUVLLM_NATIVE_PATH to it.');
|
|
210
|
+
}
|
|
211
|
+
if (typeof this.native.loadModel !== 'function') {
|
|
212
|
+
throw codedError('RUVLLM_NATIVE_TOO_OLD', `the installed native binary (version ${this.nativeVersion ?? 'unknown'}) cannot load ` +
|
|
213
|
+
'models: it predates loadModel(). The @ruvector/ruvllm-<platform> 2.0.x packages are ' +
|
|
214
|
+
'such builds. Install the 3.x platform package, or build the binary from source ' +
|
|
215
|
+
'(`npm run build:native`, which enables the `candle` feature) and set ' +
|
|
216
|
+
'RUVLLM_NATIVE_PATH to it, or use the ruvllm CLI (`ruvllm serve <model>`).');
|
|
217
|
+
}
|
|
218
|
+
if (!this.supportsModelLoading())
|
|
219
|
+
throw this.noBackendError();
|
|
220
|
+
const native = this.native;
|
|
221
|
+
return toModelInfo(callNative(() => native.loadModel(path, options)));
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Unload the language model and free its memory
|
|
225
|
+
*/
|
|
226
|
+
unloadModel() {
|
|
227
|
+
this.native?.unloadModel?.();
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Whether a language model is loaded
|
|
231
|
+
*/
|
|
232
|
+
isModelLoaded() {
|
|
233
|
+
return this.native?.isModelLoaded?.() ?? false;
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* The loaded language model, or null
|
|
237
|
+
*/
|
|
238
|
+
modelInfo() {
|
|
239
|
+
const info = this.native?.modelInfo?.();
|
|
240
|
+
return info ? toModelInfo(info) : null;
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Route `text`, then answer it with the loaded model (as one user message
|
|
244
|
+
* under the model's chat template).
|
|
245
|
+
*
|
|
246
|
+
* @throws `RUVLLM_NO_LANGUAGE_MODEL` when no model is loaded, unless
|
|
247
|
+
* `allowPlaceholder` is set (then `text` is `PLACEHOLDER_TEXT`).
|
|
248
|
+
*/
|
|
249
|
+
query(text, config) {
|
|
250
|
+
if (!this.isModelLoaded()) {
|
|
251
|
+
const placeholder = this.placeholderOrThrow();
|
|
252
|
+
this.fallbackState.queryCount++;
|
|
253
|
+
const route = this.route(text);
|
|
254
|
+
return {
|
|
255
|
+
text: placeholder,
|
|
256
|
+
confidence: route.confidence,
|
|
257
|
+
model: route.model,
|
|
258
|
+
contextSize: route.contextSize,
|
|
259
|
+
latencyMs: 0,
|
|
260
|
+
requestId: `placeholder-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
const native = this.native;
|
|
264
|
+
const result = callNative(() => native.query(text, toNativeGenConfig(config)));
|
|
265
|
+
return {
|
|
266
|
+
text: result.text,
|
|
267
|
+
confidence: result.confidence,
|
|
268
|
+
model: result.model,
|
|
269
|
+
contextSize: result.contextSize,
|
|
270
|
+
latencyMs: result.latencyMs,
|
|
271
|
+
requestId: result.requestId,
|
|
272
|
+
promptTokens: result.promptTokens,
|
|
273
|
+
completionTokens: result.completionTokens,
|
|
274
|
+
finishReason: result.finishReason,
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Continue `prompt` verbatim with the loaded model (no chat template; use
|
|
279
|
+
* `chat()` for instruction-tuned models). Returns the generated text.
|
|
280
|
+
*
|
|
281
|
+
* @throws `RUVLLM_NO_LANGUAGE_MODEL` when no model is loaded, unless
|
|
282
|
+
* `allowPlaceholder` is set (then it returns `PLACEHOLDER_TEXT`).
|
|
283
|
+
*/
|
|
284
|
+
generate(prompt, config) {
|
|
285
|
+
if (!this.isModelLoaded())
|
|
286
|
+
return this.placeholderOrThrow();
|
|
287
|
+
return this.generateDetailed(prompt, config).text;
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Like `generate()`, with token counts (from the model's tokenizer) and
|
|
291
|
+
* the finish reason. Always throws `RUVLLM_NO_LANGUAGE_MODEL` without a
|
|
292
|
+
* loaded model.
|
|
293
|
+
*/
|
|
294
|
+
generateDetailed(prompt, config) {
|
|
295
|
+
const native = this.requireModel();
|
|
296
|
+
return toGenerationResult(callNative(() => native.generate(prompt, toNativeGenConfig(config))));
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Run the loaded model on a conversation, formatted with the model's chat
|
|
300
|
+
* template (ChatML for Qwen, Llama 3 / Mistral formats for those models).
|
|
301
|
+
* Always throws `RUVLLM_NO_LANGUAGE_MODEL` without a loaded model.
|
|
302
|
+
*/
|
|
303
|
+
chat(messages, config) {
|
|
304
|
+
const native = this.requireModel();
|
|
305
|
+
return toGenerationResult(callNative(() => native.chat(messages, toNativeGenConfig(config))));
|
|
306
|
+
}
|
|
307
|
+
requireModel() {
|
|
308
|
+
if (!this.isModelLoaded())
|
|
309
|
+
throw this.noModelError();
|
|
310
|
+
return this.native;
|
|
311
|
+
}
|
|
312
|
+
noModelError() {
|
|
313
|
+
let fix;
|
|
314
|
+
if (!this.native) {
|
|
315
|
+
fix =
|
|
316
|
+
`no native RuvLLM binary is available for ${platformKey()}, so no model can be loaded ` +
|
|
317
|
+
'(see RUVLLM_NATIVE_UNAVAILABLE in the README)';
|
|
318
|
+
}
|
|
319
|
+
else if (typeof this.native.loadModel !== 'function') {
|
|
320
|
+
fix =
|
|
321
|
+
`the installed native binary (version ${this.nativeVersion ?? 'unknown'}) cannot load ` +
|
|
322
|
+
'models (see RUVLLM_NATIVE_TOO_OLD in the README)';
|
|
323
|
+
}
|
|
324
|
+
else if (!this.supportsModelLoading()) {
|
|
325
|
+
fix =
|
|
326
|
+
`the installed native binary (version ${this.nativeVersion ?? 'unknown'}) was built ` +
|
|
327
|
+
'without the candle inference backend, so it cannot load models (see ' +
|
|
328
|
+
'RUVLLM_NO_INFERENCE_BACKEND in the README)';
|
|
329
|
+
}
|
|
330
|
+
else {
|
|
331
|
+
fix = 'call loadModel(<path to a GGUF file>) first';
|
|
332
|
+
}
|
|
333
|
+
return codedError('RUVLLM_NO_LANGUAGE_MODEL', `no language model is loaded, so there is no model output to return: ${fix}. Routing, ` +
|
|
334
|
+
'memory and embeddings work without a model. For an HTTP server use the ruvllm CLI ' +
|
|
335
|
+
'(`ruvllm serve <model>`).');
|
|
336
|
+
}
|
|
337
|
+
/** Throw `RUVLLM_NO_LANGUAGE_MODEL`, or return the labelled placeholder when opted in. */
|
|
338
|
+
placeholderOrThrow() {
|
|
339
|
+
if (!this.allowPlaceholder)
|
|
340
|
+
throw this.noModelError();
|
|
341
|
+
notice('RUVLLM_PLACEHOLDER_TEXT', 'no language model is loaded: generate()/query() are returning a labelled placeholder, ' +
|
|
342
|
+
'not model output, because allowPlaceholder (or RUVLLM_ALLOW_MOCK=1) is set.', false);
|
|
343
|
+
return PLACEHOLDER_TEXT;
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Get routing decision for a query
|
|
347
|
+
*/
|
|
348
|
+
route(text) {
|
|
349
|
+
if (this.native) {
|
|
350
|
+
const result = this.native.route(text);
|
|
351
|
+
return {
|
|
352
|
+
model: result.model,
|
|
353
|
+
contextSize: result.contextSize ?? result.context_size,
|
|
354
|
+
temperature: result.temperature,
|
|
355
|
+
topP: result.topP ?? result.top_p,
|
|
356
|
+
confidence: result.confidence,
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
// Fallback
|
|
360
|
+
return {
|
|
361
|
+
model: 'M700',
|
|
362
|
+
contextSize: 512,
|
|
363
|
+
temperature: 0.7,
|
|
364
|
+
topP: 0.9,
|
|
365
|
+
confidence: 0.5,
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* Search memory for similar content
|
|
370
|
+
*/
|
|
371
|
+
searchMemory(text, k = 10) {
|
|
372
|
+
if (this.native) {
|
|
373
|
+
const results = this.native.searchMemory(text, k);
|
|
374
|
+
return results.map(r => {
|
|
375
|
+
// The native side reports `distance` (smaller is closer). MemoryResult
|
|
376
|
+
// documents `score` as a similarity, so convert rather than passing a
|
|
377
|
+
// distance through under a similarity's name. Older builds that already
|
|
378
|
+
// emit `score` are used as-is.
|
|
379
|
+
const anyR = r;
|
|
380
|
+
const score = typeof anyR.score === 'number'
|
|
381
|
+
? anyR.score
|
|
382
|
+
: typeof anyR.distance === 'number'
|
|
383
|
+
? 1 / (1 + Math.max(0, anyR.distance))
|
|
384
|
+
: 0;
|
|
385
|
+
return {
|
|
386
|
+
id: r.id,
|
|
387
|
+
score,
|
|
388
|
+
content: r.content,
|
|
389
|
+
metadata: JSON.parse(r.metadata || '{}'),
|
|
390
|
+
};
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
// Fallback - simple search
|
|
394
|
+
return Array.from(this.fallbackState.memory.entries())
|
|
395
|
+
.slice(0, k)
|
|
396
|
+
.map(([id, data]) => ({
|
|
397
|
+
id,
|
|
398
|
+
score: 0.5,
|
|
399
|
+
content: data.content,
|
|
400
|
+
metadata: data.metadata,
|
|
401
|
+
}));
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Add content to memory
|
|
405
|
+
*/
|
|
406
|
+
addMemory(content, metadata) {
|
|
407
|
+
if (this.native) {
|
|
408
|
+
return this.native.addMemory(content, metadata ? JSON.stringify(metadata) : undefined);
|
|
409
|
+
}
|
|
410
|
+
// Fallback
|
|
411
|
+
const id = this.fallbackState.nextId++;
|
|
412
|
+
this.fallbackState.memory.set(id, {
|
|
413
|
+
content,
|
|
414
|
+
embedding: this.embed(content),
|
|
415
|
+
metadata: metadata ?? {},
|
|
416
|
+
});
|
|
417
|
+
return id;
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* Provide feedback for learning
|
|
421
|
+
*/
|
|
422
|
+
feedback(fb) {
|
|
423
|
+
if (this.native) {
|
|
424
|
+
return this.native.feedback(fb.requestId, fb.rating, fb.correction);
|
|
425
|
+
}
|
|
426
|
+
return false;
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* Get engine statistics
|
|
430
|
+
*/
|
|
431
|
+
stats() {
|
|
432
|
+
if (this.native) {
|
|
433
|
+
const s = this.native.stats();
|
|
434
|
+
// Map native stats (snake_case) to TypeScript interface (camelCase)
|
|
435
|
+
// Handle both old and new field names for backward compatibility
|
|
436
|
+
return {
|
|
437
|
+
totalQueries: s.totalQueries ?? s.total_queries ?? 0,
|
|
438
|
+
memoryNodes: s.memoryNodes ?? s.memory_nodes ?? 0,
|
|
439
|
+
patternsLearned: s.trainingSteps ??
|
|
440
|
+
s.patterns_learned ??
|
|
441
|
+
s.training_steps ??
|
|
442
|
+
0,
|
|
443
|
+
avgLatencyMs: s.avgLatencyMs ?? s.avg_latency_ms ?? 0,
|
|
444
|
+
cacheHitRate: s.cacheHitRate ?? s.cache_hit_rate ?? 0,
|
|
445
|
+
routerAccuracy: s.routerAccuracy ?? s.router_accuracy ?? 0.5,
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
// Fallback
|
|
449
|
+
return {
|
|
450
|
+
totalQueries: this.fallbackState.queryCount,
|
|
451
|
+
memoryNodes: this.fallbackState.memory.size,
|
|
452
|
+
patternsLearned: 0,
|
|
453
|
+
avgLatencyMs: 1.0,
|
|
454
|
+
cacheHitRate: 0.0,
|
|
455
|
+
routerAccuracy: 0.5,
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
/**
|
|
459
|
+
* Force router learning cycle
|
|
460
|
+
*/
|
|
461
|
+
forceLearn() {
|
|
462
|
+
if (this.native) {
|
|
463
|
+
return this.native.forceLearn();
|
|
464
|
+
}
|
|
465
|
+
return 'Learning not available in fallback mode';
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* Get embedding for text
|
|
469
|
+
*/
|
|
470
|
+
embed(text) {
|
|
471
|
+
if (this.native) {
|
|
472
|
+
return this.native.embed(text);
|
|
473
|
+
}
|
|
474
|
+
// Fallback - simple hash-based embedding
|
|
475
|
+
const dim = this.config.embeddingDim ?? 768;
|
|
476
|
+
const embedding = new Array(dim).fill(0);
|
|
477
|
+
for (let i = 0; i < text.length; i++) {
|
|
478
|
+
const idx = (text.charCodeAt(i) * (i + 1)) % dim;
|
|
479
|
+
embedding[idx] += 0.1;
|
|
480
|
+
}
|
|
481
|
+
// Normalize
|
|
482
|
+
const norm = Math.sqrt(embedding.reduce((sum, x) => sum + x * x, 0)) || 1;
|
|
483
|
+
return embedding.map(x => x / norm);
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* Compute similarity between two texts
|
|
487
|
+
*/
|
|
488
|
+
similarity(text1, text2) {
|
|
489
|
+
if (this.native) {
|
|
490
|
+
// f32 accumulation can land just outside the mathematical range — an
|
|
491
|
+
// identical pair measures 1.0000001 — which turns Math.acos(sim) into
|
|
492
|
+
// NaN downstream. Cosine similarity is defined on [-1, 1], so clamp.
|
|
493
|
+
const raw = this.native.similarity(text1, text2);
|
|
494
|
+
return Math.min(1, Math.max(-1, raw));
|
|
495
|
+
}
|
|
496
|
+
// Fallback - cosine similarity
|
|
497
|
+
const emb1 = this.embed(text1);
|
|
498
|
+
const emb2 = this.embed(text2);
|
|
499
|
+
let dot = 0;
|
|
500
|
+
let norm1 = 0;
|
|
501
|
+
let norm2 = 0;
|
|
502
|
+
for (let i = 0; i < emb1.length; i++) {
|
|
503
|
+
dot += emb1[i] * emb2[i];
|
|
504
|
+
norm1 += emb1[i] * emb1[i];
|
|
505
|
+
norm2 += emb2[i] * emb2[i];
|
|
506
|
+
}
|
|
507
|
+
const denom = Math.sqrt(norm1) * Math.sqrt(norm2);
|
|
508
|
+
const similarity = denom > 0 ? dot / denom : 0;
|
|
509
|
+
// Clamp to [0, 1] to handle floating point errors
|
|
510
|
+
return Math.max(0, Math.min(1, similarity));
|
|
511
|
+
}
|
|
512
|
+
/**
|
|
513
|
+
* Check if SIMD is available
|
|
514
|
+
*/
|
|
515
|
+
hasSimd() {
|
|
516
|
+
if (this.native) {
|
|
517
|
+
return this.native.hasSimd();
|
|
518
|
+
}
|
|
519
|
+
return false;
|
|
520
|
+
}
|
|
521
|
+
/**
|
|
522
|
+
* Get SIMD capabilities
|
|
523
|
+
*/
|
|
524
|
+
simdCapabilities() {
|
|
525
|
+
if (this.native) {
|
|
526
|
+
return this.native.simdCapabilities();
|
|
527
|
+
}
|
|
528
|
+
return ['Scalar (fallback)'];
|
|
529
|
+
}
|
|
530
|
+
/**
|
|
531
|
+
* Batch query multiple prompts
|
|
532
|
+
*/
|
|
533
|
+
batchQuery(request) {
|
|
534
|
+
const start = Date.now();
|
|
535
|
+
const responses = request.queries.map(q => this.query(q, request.config));
|
|
536
|
+
return {
|
|
537
|
+
responses,
|
|
538
|
+
totalLatencyMs: Date.now() - start,
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
/**
|
|
542
|
+
* Check if native module is loaded
|
|
543
|
+
*/
|
|
544
|
+
isNativeLoaded() {
|
|
545
|
+
return this.native !== null;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZW5naW5lLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2VuZ2luZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7R0FFRztBQXFCSCxPQUFPLEVBQ0wsZUFBZSxHQU1oQixNQUFNLFVBQVUsQ0FBQztBQUVsQjs7R0FFRztBQUNILFNBQVMsY0FBYyxDQUFDLE1BQXFCO0lBQzNDLElBQUksQ0FBQyxNQUFNO1FBQUUsT0FBTyxTQUFTLENBQUM7SUFFOUIsT0FBTztRQUNMLGFBQWEsRUFBRSxNQUFNLENBQUMsWUFBWTtRQUNsQyxpQkFBaUIsRUFBRSxNQUFNLENBQUMsZUFBZTtRQUN6QyxNQUFNLEVBQUUsTUFBTSxDQUFDLEtBQUs7UUFDcEIsb0JBQW9CLEVBQUUsTUFBTSxDQUFDLGtCQUFrQjtRQUMvQyxjQUFjLEVBQUUsTUFBTSxDQUFDLFlBQVk7UUFDbkMsZ0JBQWdCLEVBQUUsTUFBTSxDQUFDLGVBQWU7UUFDeEMsaUJBQWlCLEVBQUUsTUFBTSxDQUFDLGdCQUFnQjtRQUMxQyxVQUFVLEVBQUUsTUFBTSxDQUFDLFNBQVM7S0FDN0IsQ0FBQztBQUNKLENBQUM7QUFFRDs7R0FFRztBQUNILFNBQVMsaUJBQWlCLENBQUMsTUFBeUI7SUFDbEQsSUFBSSxDQUFDLE1BQU07UUFBRSxPQUFPLFNBQVMsQ0FBQztJQUU5QiwwRUFBMEU7SUFDMUUsT0FBTztRQUNMLFNBQVMsRUFBRSxNQUFNLENBQUMsU0FBUztRQUMzQixXQUFXLEVBQUUsTUFBTSxDQUFDLFdBQVc7UUFDL0IsSUFBSSxFQUFFLE1BQU0sQ0FBQyxJQUFJO1FBQ2pCLElBQUksRUFBRSxNQUFNLENBQUMsSUFBSTtRQUNqQixpQkFBaUIsRUFBRSxNQUFNLENBQUMsaUJBQWlCO1FBQzNDLGFBQWEsRUFBRSxNQUFNLENBQUMsYUFBYTtRQUNuQyxJQUFJLEVBQUUsTUFBTSxDQUFDLElBQUk7S0FDbEIsQ0FBQztBQUNKLENBQUM7QUFFRCxTQUFTLGtCQUFrQixDQUFDLENBQXlC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RUFBRSxHQUFHLEtBQUs7U0FDbkMsQ0FBQztJQUNKLENBQUM7SUFFRDs7T0FFRztJQUNILGNBQWM7UUFDWixPQUFPLElBQUksQ0FBQyxNQUFNLEtBQUssSUFBSSxDQUFDO0lBQzlCLENBQUM7Q0FDRiIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogUnV2TExNIEVuZ2luZSAtIE1haW4gb3JjaGVzdHJhdG9yIGZvciBzZWxmLWxlYXJuaW5nIExMTVxuICovXG5cbmltcG9ydCB7XG4gIFJ1dkxMTUNvbmZpZyxcbiAgR2VuZXJhdGlvbkNvbmZpZyxcbiAgUXVlcnlSZXNwb25zZSxcbiAgUm91dGluZ0RlY2lzaW9uLFxuICBNZW1vcnlSZXN1bHQsXG4gIE1lbW9yeUlkLFxuICBSdXZMTE1TdGF0cyxcbiAgRmVlZGJhY2ssXG4gIEVtYmVkZGluZyxcbiAgQmF0Y2hRdWVyeVJlcXVlc3QsXG4gIEJhdGNoUXVlcnlSZXNwb25zZSxcbiAgQ2hhdE1lc3NhZ2UsXG4gIEZpbmlzaFJlYXNvbixcbiAgR2VuZXJhdGlvblJlc3VsdCxcbiAgTG9hZE1vZGVsT3B0aW9ucyxcbiAgTG9hZGVkTW9kZWxJbmZvLFxufSBmcm9tICcuL3R5cGVzJztcblxuaW1wb3J0IHtcbiAgZ2V0TmF0aXZlTW9kdWxlLFxuICBOYXRpdmVFbmdpbmUsXG4gIE5hdGl2ZUNvbmZpZyxcbiAgTmF0aXZlR2VuQ29uZmlnLFxuICBOYXRpdmVHZW5lcmF0aW9uUmVzdWx0LFxuICBOYXRpdmVNb2RlbEluZm8sXG59IGZyb20gJy4vbmF0aXZlJztcblxuLyoqXG4gKiBDb252ZXJ0IEpTIGNvbmZpZyB0byBuYXRpdmUgY29uZmlnIGZvcm1hdFxuICovXG5mdW5jdGlvbiB0b05hdGl2ZUNvbmZpZyhjb25maWc/OiBSdXZMTE1Db25maWcpOiBOYXRpdmVDb25maWcgfCB1bmRlZmluZWQge1xuICBpZiAoIWNvbmZpZykgcmV0dXJuIHVuZGVmaW5lZDtcblxuICByZXR1cm4ge1xuICAgIGVtYmVkZGluZ19kaW06IGNvbmZpZy5lbWJlZGRpbmdEaW0sXG4gICAgcm91dGVyX2hpZGRlbl9kaW06IGNvbmZpZy5yb3V0ZXJIaWRkZW5EaW0sXG4gICAgaG5zd19tOiBjb25maWcuaG5zd00sXG4gICAgaG5zd19lZl9jb25zdHJ1Y3Rpb246IGNvbmZpZy5obnN3RWZDb25zdHJ1Y3Rpb24sXG4gICAgaG5zd19lZl9zZWFyY2g6IGNvbmZpZy5obnN3RWZTZWFyY2gsXG4gICAgbGVhcm5pbmdfZW5hYmxlZDogY29uZmlnLmxlYXJuaW5nRW5hYmxlZCxcbiAgICBxdWFsaXR5X3RocmVzaG9sZDogY29uZmlnLnF1YWxpdHlUaHJlc2hvbGQsXG4gICAgZXdjX2xhbWJkYTogY29uZmlnLmV3Y0xhbWJkYSxcbiAgfTtcbn1cblxuLyoqXG4gKiBDb252ZXJ0IEpTIGdlbmVyYXRpb24gY29uZmlnIHRvIG5hdGl2ZSBmb3JtYXRcbiAqL1xuZnVuY3Rpb24gdG9OYXRpdmVHZW5Db25maWcoY29uZmlnPzogR2VuZXJhdGlvbkNvbmZpZyk6IE5hdGl2ZUdlbkNvbmZpZyB8IHVuZGVmaW5lZCB7XG4gIGlmICghY29uZmlnKSByZXR1cm4gdW5kZWZpbmVkO1xuXG4gIC8vIGNhbWVsQ2FzZTogbmFwaS1ycyBpZ25vcmVzIHNuYWtlX2Nhc2Uga2V5cyBvbiBgI1tuYXBpKG9iamVjdCldYCBpbnB1dHMuXG4gIHJldHVybiB7XG4gICAgbWF4VG9rZW5zOiBjb25maWcubWF4VG9rZW5zLFxuICAgIHRlbXBlcmF0dXJlOiBjb25maWcudGVtcGVyYXR1cmUsXG4gICAgdG9wUDogY29uZmlnLnRvcFAsXG4gICAgdG9wSzogY29uZmlnLnRvcEssXG4gICAgcmVwZXRpdGlvblBlbmFsdHk6IGNvbmZpZy5yZXBldGl0aW9uUGVuYWx0eSxcbiAgICBzdG9wU2VxdWVuY2VzOiBjb25maWcuc3RvcFNlcXVlbmNlcyxcbiAgICBzZWVkOiBjb25maWcuc2VlZCxcbiAgfTtcbn1cblxuZnVuY3Rpb24gdG9HZW5lcmF0aW9uUmVzdWx0KHI6IE5hdGl2ZUdlbmVyYXRpb25SZXN1bHQpOiBHZW5lcmF0aW9uUmVzdWx0IHtcbiAgcmV0dXJuIHtcbiAgICB0ZXh0OiByLnRleHQsXG4gICAgcHJvbXB0VG9rZW5zOiByLnByb21wdFRva2VucyxcbiAgICBjb21wbGV0aW9uVG9rZW5zOiByLmNvbXBsZXRpb25Ub2tlbnMsXG4gICAgZmluaXNoUmVhc29uOiByLmZpbmlzaFJlYXNvbiBhcyBGaW5pc2hSZWFzb24sXG4gIH07XG59XG5cbmZ1bmN0aW9uIHRvTW9kZWxJbmZvKGk6IE5hdGl2ZU1vZGVsSW5mbyk6IExvYWRlZE1vZGVsSW5mbyB7XG4gIHJldHVybiB7XG4gICAgbmFtZTogaS5uYW1lLFxuICAgIGFyY2hpdGVjdHVyZTogaS5hcmNoaXRlY3R1cmUsXG4gICAgbnVtUGFyYW1ldGVyczogaS5udW1QYXJhbWV0ZXJzLFxuICAgIHZvY2FiU2l6ZTogaS52b2NhYlNpemUsXG4gICAgaGlkZGVuU2l6ZTogaS5oaWRkZW5TaXplLFxuICAgIG51bUxheWVyczogaS5udW1MYXllcnMsXG4gICAgbWF4Q29udGV4dExlbmd0aDogaS5tYXhDb250ZXh0TGVuZ3RoLFxuICAgIHF1YW50aXphdGlvbjogaS5xdWFudGl6YXRpb24gPz8gbnVsbCxcbiAgICBtZW1vcnlVc2FnZUJ5dGVzOiBpLm1lbW9yeVVzYWdlQnl0ZXMsXG4gICAgY2hhdFRlbXBsYXRlOiBpLmNoYXRUZW1wbGF0ZSA/PyBudWxsLFxuICB9O1xufVxuXG5jb25zdCB3YXJuZWQgPSBuZXcgU2V0PHN0cmluZz4oKTtcblxuLyoqIFdhcm4gb25jZSBwZXIgcHJvY2VzcyAob3IgdGhyb3cgd2hlbiBgc3RyaWN0YCkuICovXG5mdW5jdGlvbiBub3RpY2UoY29kZTogc3RyaW5nLCBtZXNzYWdlOiBzdHJpbmcsIHN0cmljdDogYm9vbGVhbiB8IHVuZGVmaW5lZCk6IHZvaWQge1xuICBpZiAoc3RyaWN0KSB0aHJvdyBjb2RlZEVycm9yKGNvZGUsIG1lc3NhZ2UpO1xuICBpZiAod2FybmVkLmhhcyhjb2RlKSkgcmV0dXJuO1xuICB3YXJuZWQuYWRkKGNvZGUpO1xuICBjb25zdCBnID0gZ2xvYmFsVGhpcyBhcyB1bmtub3duIGFzIHtcbiAgICBwcm9jZXNzPzogeyBlbWl0V2FybmluZz86IChtOiBzdHJpbmcsIG8/OiB7IGNvZGU/OiBzdHJpbmcgfSkgPT4gdm9pZCB9O1xuICAgIGNvbnNvbGU/OiB7IHdhcm4/OiAobTogc3RyaW5nKSA9PiB2b2lkIH07XG4gIH07XG4gIGlmICh0eXBlb2YgZy5wcm9jZXNzPy5lbWl0V2FybmluZyA9PT0gJ2Z1bmN0aW9uJykgZy5wcm9jZXNzLmVtaXRXYXJuaW5nKG1lc3NhZ2UsIHsgY29kZSB9KTtcbiAgZWxzZSBnLmNvbnNvbGU/Lndhcm4/LihgJHtjb2RlfTogJHttZXNzYWdlfWApO1xufVxuXG4vKiogQW4gRXJyb3Igd2hvc2UgbWVzc2FnZSBzdGFydHMgd2l0aCwgYW5kIHdob3NlIGBjb2RlYCBpcywgYGNvZGVgLiAqL1xuZnVuY3Rpb24gY29kZWRFcnJvcihjb2RlOiBzdHJpbmcsIG1lc3NhZ2U6IHN0cmluZyk6IEVycm9yICYgeyBjb2RlOiBzdHJpbmcgfSB7XG4gIGNvbnN0IGVyciA9IG5ldyBFcnJvcihgJHtjb2RlfTogJHttZXNzYWdlfWApIGFzIEVycm9yICYgeyBjb2RlOiBzdHJpbmcgfTtcbiAgZXJyLmNvZGUgPSBjb2RlO1xuICByZXR1cm4gZXJyO1xufVxuXG4vKipcbiAqIENhbGwgaW50byB0aGUgbmF0aXZlIGVuZ2luZSwgZ2l2aW5nIGl0cyBlcnJvcnMgdGhlIHNhbWUgYGNvZGVgIGFzIHRoZVxuICogd3JhcHBlcidzIG93bjogbmF0aXZlIG1lc3NhZ2VzIHN0YXJ0IHdpdGggYFJVVkxMTV88Q09ERT46YCwgYnV0IG5hcGktcnNcbiAqIHNldHMgYGNvZGVgIHRvIGEgZ2VuZXJpYyBzdGF0dXMgc3VjaCBhcyBgR2VuZXJpY0ZhaWx1cmVgLlxuICovXG5mdW5jdGlvbiBjYWxsTmF0aXZlPFQ+KGY6ICgpID0+IFQpOiBUIHtcbiAgdHJ5IHtcbiAgICByZXR1cm4gZigpO1xuICB9IGNhdGNoIChlKSB7XG4gICAgY29uc3QgbWF0Y2ggPSAvXihSVVZMTE1fW0EtWl9dKyk6Ly5leGVjKGUgaW5zdGFuY2VvZiBFcnJvciA/IGUubWVzc2FnZSA6ICcnKTtcbiAgICBpZiAobWF0Y2gpIChlIGFzIHsgY29kZT86IHN0cmluZyB9KS5jb2RlID0gbWF0Y2hbMV07XG4gICAgdGhyb3cgZTtcbiAgfVxufVxuXG5mdW5jdGlvbiBwbGF0Zm9ybUtleSgpOiBzdHJpbmcge1xuICBjb25zdCBwID0gKGdsb2JhbFRoaXMgYXMgeyBwcm9jZXNzPzogeyBwbGF0Zm9ybT86IHN0cmluZzsgYXJjaD86IHN0cmluZyB9IH0pLnByb2Nlc3M7XG4gIHJldHVybiBgJHtwPy5wbGF0Zm9ybSA/PyAndW5rbm93bid9LSR7cD8uYXJjaCA/PyAndW5rbm93bid9YDtcbn1cblxuZnVuY3Rpb24gZW52QWxsb3dzUGxhY2Vob2xkZXIoKTogYm9vbGVhbiB7XG4gIGNvbnN0IHAgPSAoZ2xvYmFsVGhpcyBhcyB7IHByb2Nlc3M/OiB7IGVudj86IFJlY29yZDxzdHJpbmcsIHN0cmluZyB8IHVuZGVmaW5lZD4gfSB9KS5wcm9jZXNzO1xuICByZXR1cm4gcD8uZW52Py5SVVZMTE1fQUxMT1dfTU9DSyA9PT0gJzEnO1xufVxuXG4vKiogUmV0dXJuZWQgYnkgZ2VuZXJhdGUoKS9xdWVyeSgpIG9ubHkgd2l0aCBgYWxsb3dQbGFjZWhvbGRlcmAgLyBgUlVWTExNX0FMTE9XX01PQ0s9MWAuICovXG5leHBvcnQgY29uc3QgUExBQ0VIT0xERVJfVEVYVCA9XG4gICdbUnV2TExNIHBsYWNlaG9sZGVyOiBubyBsYW5ndWFnZSBtb2RlbCBpcyBsb2FkZWQ7IHRoaXMgdGV4dCBpcyBub3QgbW9kZWwgb3V0cHV0XSc7XG5cbi8qKlxuICogUnV2TExNIC0gU2VsZi1sZWFybmluZyBMTE0gb3JjaGVzdHJhdG9yXG4gKlxuICogQ29tYmluZXMgU09OQSBhZGFwdGl2ZSBsZWFybmluZyB3aXRoIEhOU1cgbWVtb3J5LCBGYXN0R1JOTiByb3V0aW5nLCBhbmRcbiAqIEdHVUYgaW5mZXJlbmNlIHRocm91Z2ggdGhlIG5hdGl2ZSBjYW5kbGUgYmFja2VuZC5cbiAqXG4gKiBUZXh0IGNvbWVzIG9ubHkgZnJvbSBhIG1vZGVsIGxvYWRlZCB3aXRoIGBsb2FkTW9kZWwoKWAgKG9yIHRoZSBgbW9kZWxQYXRoYFxuICogb3B0aW9uKS4gV2l0aG91dCBvbmUsIGBnZW5lcmF0ZSgpYCwgYHF1ZXJ5KClgLCBgY2hhdCgpYCBhbmRcbiAqIGBnZW5lcmF0ZURldGFpbGVkKClgIHRocm93IGBSVVZMTE1fTk9fTEFOR1VBR0VfTU9ERUxgLlxuICpcbiAqIEBleGFtcGxlXG4gKiBgYGB0eXBlc2NyaXB0XG4gKiBpbXBvcnQgeyBSdXZMTE0gfSBmcm9tICdAcnV2ZWN0b3IvcnV2bGxtJztcbiAqXG4gKiBjb25zdCBsbG0gPSBuZXcgUnV2TExNKCk7XG4gKiBsbG0ubG9hZE1vZGVsKCcuL3F3ZW4yLjUtMC41Yi1pbnN0cnVjdC1xNF9rX20uZ2d1ZicpO1xuICpcbiAqIGNvbnN0IG91dCA9IGxsbS5jaGF0KFt7IHJvbGU6ICd1c2VyJywgY29udGVudDogJ1doYXQgaXMgbWFjaGluZSBsZWFybmluZz8nIH1dLCB7XG4gKiAgIG1heFRva2VuczogMTI4LFxuICogfSk7XG4gKiBjb25zb2xlLmxvZyhvdXQudGV4dCwgb3V0LmZpbmlzaFJlYXNvbiwgb3V0LmNvbXBsZXRpb25Ub2tlbnMpO1xuICpcbiAqIC8vIFJvdXRlZCBxdWVyeSwgYW5zd2VyZWQgYnkgdGhlIGxvYWRlZCBtb2RlbFxuICogY29uc3QgcmVzcG9uc2UgPSBsbG0ucXVlcnkoJ1doYXQgaXMgbWFjaGluZSBsZWFybmluZz8nKTtcbiAqIGxsbS5mZWVkYmFjayh7IHJlcXVlc3RJZDogcmVzcG9uc2UucmVxdWVzdElkLCByYXRpbmc6IDUgfSk7XG4gKiBgYGBcbiAqL1xuZXhwb3J0IGNsYXNzIFJ1dkxMTSB7XG4gIHByaXZhdGUgbmF0aXZlOiBOYXRpdmVFbmdpbmUgfCBudWxsID0gbnVsbDtcbiAgcHJpdmF0ZSBuYXRpdmVWZXJzaW9uOiBzdHJpbmcgfCBudWxsID0gbnVsbDtcbiAgcHJpdmF0ZSBjb25maWc6IFJ1dkxMTUNvbmZpZztcbiAgcHJpdmF0ZSBhbGxvd1BsYWNlaG9sZGVyOiBib29sZWFuO1xuXG4gIC8vIEZhbGxiYWNrIHN0YXRlIGZvciB3aGVuIG5hdGl2ZSBtb2R1bGUgaXMgbm90IGF2YWlsYWJsZVxuICBwcml2YXRlIGZhbGxiYWNrU3RhdGUgPSB7XG4gICAgbWVtb3J5OiBuZXcgTWFwPG51bWJlciwgeyBjb250ZW50OiBzdHJpbmc7IGVtYmVkZGluZzogbnVtYmVyW107IG1ldGFkYXRhOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiB9PigpLFxuICAgIG5leHRJZDogMSxcbiAgICBxdWVyeUNvdW50OiAwLFxuICB9O1xuXG4gIC8qKlxuICAgKiBDcmVhdGUgYSBuZXcgUnV2TExNIGluc3RhbmNlXG4gICAqL1xuICBjb25zdHJ1Y3Rvcihjb25maWc/OiBSdXZMTE1Db25maWcpIHtcbiAgICB0aGlzLmNvbmZpZyA9IGNvbmZpZyA/PyB7fTtcbiAgICB0aGlzLmFsbG93UGxhY2Vob2xkZXIgPSB0aGlzLmNvbmZpZy5hbGxvd1BsYWNlaG9sZGVyID09PSB0cnVlIHx8IGVudkFsbG93c1BsYWNlaG9sZGVyKCk7XG4gICAgY29uc3QgZXh0cmEgPSB0aGlzLmNvbmZpZyBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPjtcbiAgICBpZiAoZXh0cmEuYmFja2VuZCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICBub3RpY2UoXG4gICAgICAgICdSVVZMTE1fVU5TVVBQT1JURURfT1BUSU9OJyxcbiAgICAgICAgJ2BiYWNrZW5kYCBpcyBub3Qgc3VwcG9ydGVkIGJ5IEBydXZlY3Rvci9ydXZsbG0gYW5kIGlzIGlnbm9yZWQ6IG1vZGVscyBsb2FkZWQgd2l0aCAnICtcbiAgICAgICAgICAnbG9hZE1vZGVsKCkgcnVuIG9uIHRoZSBidWlsdC1pbiBjYW5kbGUgYmFja2VuZC4nLFxuICAgICAgICB0aGlzLmNvbmZpZy5zdHJpY3QsXG4gICAgICApO1xuICAgIH1cblxuICAgIGNvbnN0IG1vZCA9IGdldE5hdGl2ZU1vZHVsZSgpO1xuICAgIGlmIChtb2QpIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIHRoaXMubmF0aXZlID0gbmV3IG1vZC5SdXZMTE1FbmdpbmUodG9OYXRpdmVDb25maWcoY29uZmlnKSk7XG4gICAgICAgIHRoaXMubmF0aXZlVmVyc2lvbiA9IG1vZC52ZXJzaW9uKCk7XG4gICAgICB9IGNhdGNoIHtcbiAgICAgICAgLy8gU2lsZW50bHkgZmFsbCBiYWNrIHRvIEpTIGltcGxlbWVudGF0aW9uXG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKHRoaXMuY29uZmlnLm1vZGVsUGF0aCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICB0aGlzLmxvYWRNb2RlbCh0aGlzLmNvbmZpZy5tb2RlbFBhdGgsIHRoaXMuY29uZmlnLm1vZGVsT3B0aW9ucyk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFdoZXRoZXIgdGhlIG5hdGl2ZSBiaW5hcnkgY2FuIGxvYWQgYW5kIHJ1biBhIGxhbmd1YWdlIG1vZGVsLiBGYWxzZVxuICAgKiB3aXRob3V0IGEgbmF0aXZlIGJpbmFyeSwgZm9yIGJpbmFyaWVzIHRoYXQgcHJlZGF0ZSBgbG9hZE1vZGVsYCAodGhlIDIuMC54XG4gICAqIGBAcnV2ZWN0b3IvcnV2bGxtLTxwbGF0Zm9ybT5gIHBhY2thZ2VzKSwgYW5kIGZvciBiaW5hcmllcyBidWlsdCB3aXRob3V0XG4gICAqIHRoZSBgY2FuZGxlYCBpbmZlcmVuY2UgYmFja2VuZC5cbiAgICovXG4gIHN1cHBvcnRzTW9kZWxMb2FkaW5nKCk6IGJvb2xlYW4ge1xuICAgIGNvbnN0IG5hdGl2ZSA9IHRoaXMubmF0aXZlO1xuICAgIGlmICh0eXBlb2YgbmF0aXZlPy5sb2FkTW9kZWwgIT09ICdmdW5jdGlvbicpIHJldHVybiBmYWxzZTtcbiAgICAvLyBCdWlsZHMgdGhhdCBwcmVkYXRlIHRoaXMgcHJvYmUgYWxsIGhhZCB0aGUgYmFja2VuZCAodGhleSB3ZXJlIGxvY2FsXG4gICAgLy8gYG5wbSBydW4gYnVpbGQ6bmF0aXZlYCBidWlsZHMsIHdoaWNoIGVuYWJsZSBgY2FuZGxlYCkuXG4gICAgcmV0dXJuIHR5cGVvZiBuYXRpdmUuc3VwcG9ydHNNb2RlbExvYWRpbmcgPT09ICdmdW5jdGlvbicgPyBuYXRpdmUuc3VwcG9ydHNNb2RlbExvYWRpbmcoKSA6IHRydWU7XG4gIH1cblxuICAvKiogRXJyb3IgZm9yIGEgYmluYXJ5IHRoYXQgaGFzIGBsb2FkTW9kZWxgIGJ1dCBubyBpbmZlcmVuY2UgYmFja2VuZC4gKi9cbiAgcHJpdmF0ZSBub0JhY2tlbmRFcnJvcigpOiBFcnJvciB7XG4gICAgcmV0dXJuIGNvZGVkRXJyb3IoXG4gICAgICAnUlVWTExNX05PX0lORkVSRU5DRV9CQUNLRU5EJyxcbiAgICAgIGB0aGUgaW5zdGFsbGVkIG5hdGl2ZSBiaW5hcnkgKHZlcnNpb24gJHt0aGlzLm5hdGl2ZVZlcnNpb24gPz8gJ3Vua25vd24nfSkgd2FzIGJ1aWx0IGAgK1xuICAgICAgICAnd2l0aG91dCB0aGUgYGNhbmRsZWAgaW5mZXJlbmNlIGJhY2tlbmQgYW5kIGNhbm5vdCBsb2FkIG1vZGVscy4gUmVidWlsZCBpdCB3aXRoICcgK1xuICAgICAgICAnYG5wbSBydW4gYnVpbGQ6bmF0aXZlYCAod2hpY2ggZW5hYmxlcyBgY2FuZGxlYCkgYW5kIHNldCBSVVZMTE1fTkFUSVZFX1BBVEggdG8gaXQsICcgK1xuICAgICAgICAnb3IgdXNlIHRoZSBydXZsbG0gQ0xJIChgcnV2bGxtIHNlcnZlIDxtb2RlbD5gKS4nLFxuICAgICk7XG4gIH1cblxuICAvKipcbiAgICogTG9hZCBhIEdHVUYgbW9kZWwgKG9yIGEgZGlyZWN0b3J5IGhvbGRpbmcgb25lKSBmb3IgYGdlbmVyYXRlKClgLFxuICAgKiBgY2hhdCgpYCBhbmQgYHF1ZXJ5KClgLiBTdXBwb3J0ZWQgR0dVRiBhcmNoaXRlY3R1cmVzOiBsbGFtYSwgbWlzdHJhbCxcbiAgICogcXdlbjIsIHF3ZW4zOyBvdGhlcnMgZmFpbCB3aXRoIGFuIGVycm9yIG5hbWluZyB0aGUgYXJjaGl0ZWN0dXJlLiBUaGVcbiAgICogdG9rZW5pemVyIGNvbWVzIGZyb20gYSBgdG9rZW5pemVyLmpzb25gIGJlc2lkZSB0aGUgZmlsZSwgZWxzZSBmcm9tIHRoZVxuICAgKiBHR1VGIGl0c2VsZi4gUmVwbGFjZXMgYW55IGxvYWRlZCBtb2RlbDsgaWYgbG9hZGluZyBmYWlscywgbm8gbW9kZWwgaXNcbiAgICogbG9hZGVkLiBTeW5jaHJvbm91czogYmxvY2tzIHRoZSBldmVudCBsb29wIHdoaWxlIHRoZSB3ZWlnaHRzIGxvYWQuXG4gICAqXG4gICAqIEB0aHJvd3MgYFJVVkxMTV9OQVRJVkVfVU5BVkFJTEFCTEVgIHdpdGhvdXQgYSBuYXRpdmUgYmluYXJ5LFxuICAgKiAgIGBSVVZMTE1fTkFUSVZFX1RPT19PTERgIHdoZW4gdGhlIGJpbmFyeSBwcmVkYXRlcyBtb2RlbCBsb2FkaW5nLFxuICAgKiAgIGBSVVZMTE1fTk9fSU5GRVJFTkNFX0JBQ0tFTkRgIHdoZW4gaXQgd2FzIGJ1aWx0IHdpdGhvdXQgYGNhbmRsZWAsXG4gICAqICAgYFJVVkxMTV9NT0RFTF9OT1RfRk9VTkRgIC8gYFJVVkxMTV9NT0RFTF9MT0FEX0ZBSUxFRGAgZnJvbSB0aGUgYmFja2VuZC5cbiAgICovXG4gIGxvYWRNb2RlbChwYXRoOiBzdHJpbmcsIG9wdGlvbnM/OiBMb2FkTW9kZWxPcHRpb25zKTogTG9hZGVkTW9kZWxJbmZvIHtcbiAgICBpZiAodHlwZW9mIHBhdGggIT09ICdzdHJpbmcnIHx8IHBhdGgubGVuZ3RoID09PSAwKSB7XG4gICAgICB0aHJvdyBuZXcgVHlwZUVycm9yKCdsb2FkTW9kZWw6IHBhdGggbXVzdCBiZSBhIG5vbi1lbXB0eSBzdHJpbmcnKTtcbiAgICB9XG4gICAgaWYgKCF0aGlzLm5hdGl2ZSkge1xuICAgICAgdGhyb3cgY29kZWRFcnJvcihcbiAgICAgICAgJ1JVVkxMTV9OQVRJVkVfVU5BVkFJTEFCTEUnLFxuICAgICAgICBgbm8gbmF0aXZlIFJ1dkxMTSBiaW5hcnkgY291bGQgYmUgbG9hZGVkIGZvciAke3BsYXRmb3JtS2V5KCl9LCBzbyBtb2RlbHMgY2Fubm90IGJlIGAgK1xuICAgICAgICAgICdsb2FkZWQuIEluc3RhbGwgdGhlIG1hdGNoaW5nIEBydXZlY3Rvci9ydXZsbG0tPHBsYXRmb3JtPiBwYWNrYWdlLCBvciBidWlsZCBvbmUgJyArXG4gICAgICAgICAgJyhgbnBtIHJ1biBidWlsZDpuYXRpdmVgKSBhbmQgc2V0IFJVVkxMTV9OQVRJVkVfUEFUSCB0byBpdC4nLFxuICAgICAgKTtcbiAgICB9XG4gICAgaWYgKHR5cGVvZiB0aGlzLm5hdGl2ZS5sb2FkTW9kZWwgIT09ICdmdW5jdGlvbicpIHtcbiAgICAgIHRocm93IGNvZGVkRXJyb3IoXG4gICAgICAgICdSVVZMTE1fTkFUSVZFX1RPT19PTEQnLFxuICAgICAgICBgdGhlIGluc3RhbGxlZCBuYXRpdmUgYmluYXJ5ICh2ZXJzaW9uICR7dGhpcy5uYXRpdmVWZXJzaW9uID8/ICd1bmtub3duJ30pIGNhbm5vdCBsb2FkIGAgK1xuICAgICAgICAgICdtb2RlbHM6IGl0IHByZWRhdGVzIGxvYWRNb2RlbCgpLiBUaGUgQHJ1dmVjdG9yL3J1dmxsbS08cGxhdGZvcm0+IDIuMC54IHBhY2thZ2VzIGFyZSAnICtcbiAgICAgICAgICAnc3VjaCBidWlsZHMuIEluc3RhbGwgdGhlIDMueCBwbGF0Zm9ybSBwYWNrYWdlLCBvciBidWlsZCB0aGUgYmluYXJ5IGZyb20gc291cmNlICcgK1xuICAgICAgICAgICcoYG5wbSBydW4gYnVpbGQ6bmF0aXZlYCwgd2hpY2ggZW5hYmxlcyB0aGUgYGNhbmRsZWAgZmVhdHVyZSkgYW5kIHNldCAnICtcbiAgICAgICAgICAnUlVWTExNX05BVElWRV9QQVRIIHRvIGl0LCBvciB1c2UgdGhlIHJ1dmxsbSBDTEkgKGBydXZsbG0gc2VydmUgPG1vZGVsPmApLicsXG4gICAgICApO1xuICAgIH1cbiAgICBpZiAoIXRoaXMuc3VwcG9ydHNNb2RlbExvYWRpbmcoKSkgdGhyb3cgdGhpcy5ub0JhY2tlbmRFcnJvcigpO1xuICAgIGNvbnN0IG5hdGl2ZSA9IHRoaXMubmF0aXZlO1xuICAgIHJldHVybiB0b01vZGVsSW5mbyhjYWxsTmF0aXZlKCgpID0+IG5hdGl2ZS5sb2FkTW9kZWwhKHBhdGgsIG9wdGlvbnMpKSk7XG4gIH1cblxuICAvKipcbiAgICogVW5sb2FkIHRoZSBsYW5ndWFnZSBtb2RlbCBhbmQgZnJlZSBpdHMgbWVtb3J5XG4gICAqL1xuICB1bmxvYWRNb2RlbCgpOiB2b2lkIHtcbiAgICB0aGlzLm5hdGl2ZT8udW5sb2FkTW9kZWw/LigpO1xuICB9XG5cbiAgLyoqXG4gICAqIFdoZXRoZXIgYSBsYW5ndWFnZSBtb2RlbCBpcyBsb2FkZWRcbiAgICovXG4gIGlzTW9kZWxMb2FkZWQoKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHRoaXMubmF0aXZlPy5pc01vZGVsTG9hZGVkPy4oKSA/PyBmYWxzZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBUaGUgbG9hZGVkIGxhbmd1YWdlIG1vZGVsLCBvciBudWxsXG4gICAqL1xuICBtb2RlbEluZm8oKTogTG9hZGVkTW9kZWxJbmZvIHwgbnVsbCB7XG4gICAgY29uc3QgaW5mbyA9IHRoaXMubmF0aXZlPy5tb2RlbEluZm8/LigpO1xuICAgIHJldHVybiBpbmZvID8gdG9Nb2RlbEluZm8oaW5mbykgOiBudWxsO1xuICB9XG5cbiAgLyoqXG4gICAqIFJvdXRlIGB0ZXh0YCwgdGhlbiBhbnN3ZXIgaXQgd2l0aCB0aGUgbG9hZGVkIG1vZGVsIChhcyBvbmUgdXNlciBtZXNzYWdlXG4gICAqIHVuZGVyIHRoZSBtb2RlbCdzIGNoYXQgdGVtcGxhdGUpLlxuICAgKlxuICAgKiBAdGhyb3dzIGBSVVZMTE1fTk9fTEFOR1VBR0VfTU9ERUxgIHdoZW4gbm8gbW9kZWwgaXMgbG9hZGVkLCB1bmxlc3NcbiAgICogICBgYWxsb3dQbGFjZWhvbGRlcmAgaXMgc2V0ICh0aGVuIGB0ZXh0YCBpcyBgUExBQ0VIT0xERVJfVEVYVGApLlxuICAgKi9cbiAgcXVlcnkodGV4dDogc3RyaW5nLCBjb25maWc/OiBHZW5lcmF0aW9uQ29uZmlnKTogUXVlcnlSZXNwb25zZSB7XG4gICAgaWYgKCF0aGlzLmlzTW9kZWxMb2FkZWQoKSkge1xuICAgICAgY29uc3QgcGxhY2Vob2xkZXIgPSB0aGlzLnBsYWNlaG9sZGVyT3JUaHJvdygpO1xuICAgICAgdGhpcy5mYWxsYmFja1N0YXRlLnF1ZXJ5Q291bnQrKztcbiAgICAgIGNvbnN0IHJvdXRlID0gdGhpcy5yb3V0ZSh0ZXh0KTtcbiAgICAgIHJldHVybiB7XG4gICAgICAgIHRleHQ6IHBsYWNlaG9sZGVyLFxuICAgICAgICBjb25maWRlbmNlOiByb3V0ZS5jb25maWRlbmNlLFxuICAgICAgICBtb2RlbDogcm91dGUubW9kZWwsXG4gICAgICAgIGNvbnRleHRTaXplOiByb3V0ZS5jb250ZXh0U2l6ZSxcbiAgICAgICAgbGF0ZW5jeU1zOiAwLFxuICAgICAgICByZXF1ZXN0SWQ6IGBwbGFjZWhvbGRlci0ke0RhdGUubm93KCl9LSR7TWF0aC5yYW5kb20oKS50b1N0cmluZygzNikuc2xpY2UoMil9YCxcbiAgICAgIH07XG4gICAgfVxuXG4gICAgY29uc3QgbmF0aXZlID0gdGhpcy5uYXRpdmUhO1xuICAgIGNvbnN0IHJlc3VsdCA9IGNhbGxOYXRpdmUoKCkgPT4gbmF0aXZlLnF1ZXJ5KHRleHQsIHRvTmF0aXZlR2VuQ29uZmlnKGNvbmZpZykpKTtcbiAgICByZXR1cm4ge1xuICAgICAgdGV4dDogcmVzdWx0LnRleHQsXG4gICAgICBjb25maWRlbmNlOiByZXN1bHQuY29uZmlkZW5jZSxcbiAgICAgIG1vZGVsOiByZXN1bHQubW9kZWwsXG4gICAgICBjb250ZXh0U2l6ZTogcmVzdWx0LmNvbnRleHRTaXplLFxuICAgICAgbGF0ZW5jeU1zOiByZXN1bHQubGF0ZW5jeU1zLFxuICAgICAgcmVxdWVzdElkOiByZXN1bHQucmVxdWVzdElkLFxuICAgICAgcHJvbXB0VG9rZW5zOiByZXN1bHQucHJvbXB0VG9rZW5zLFxuICAgICAgY29tcGxldGlvblRva2VuczogcmVzdWx0LmNvbXBsZXRpb25Ub2tlbnMsXG4gICAgICBmaW5pc2hSZWFzb246IHJlc3VsdC5maW5pc2hSZWFzb24gYXMgRmluaXNoUmVhc29uIHwgdW5kZWZpbmVkLFxuICAgIH07XG4gIH1cblxuICAvKipcbiAgICogQ29udGludWUgYHByb21wdGAgdmVyYmF0aW0gd2l0aCB0aGUgbG9hZGVkIG1vZGVsIChubyBjaGF0IHRlbXBsYXRlOyB1c2VcbiAgICogYGNoYXQoKWAgZm9yIGluc3RydWN0aW9uLXR1bmVkIG1vZGVscykuIFJldHVybnMgdGhlIGdlbmVyYXRlZCB0ZXh0LlxuICAgKlxuICAgKiBAdGhyb3dzIGBSVVZMTE1fTk9fTEFOR1VBR0VfTU9ERUxgIHdoZW4gbm8gbW9kZWwgaXMgbG9hZGVkLCB1bmxlc3NcbiAgICogICBgYWxsb3dQbGFjZWhvbGRlcmAgaXMgc2V0ICh0aGVuIGl0IHJldHVybnMgYFBMQUNFSE9MREVSX1RFWFRgKS5cbiAgICovXG4gIGdlbmVyYXRlKHByb21wdDogc3RyaW5nLCBjb25maWc/OiBHZW5lcmF0aW9uQ29uZmlnKTogc3RyaW5nIHtcbiAgICBpZiAoIXRoaXMuaXNNb2RlbExvYWRlZCgpKSByZXR1cm4gdGhpcy5wbGFjZWhvbGRlck9yVGhyb3coKTtcbiAgICByZXR1cm4gdGhpcy5nZW5lcmF0ZURldGFpbGVkKHByb21wdCwgY29uZmlnKS50ZXh0O1xuICB9XG5cbiAgLyoqXG4gICAqIExpa2UgYGdlbmVyYXRlKClgLCB3aXRoIHRva2VuIGNvdW50cyAoZnJvbSB0aGUgbW9kZWwncyB0b2tlbml6ZXIpIGFuZFxuICAgKiB0aGUgZmluaXNoIHJlYXNvbi4gQWx3YXlzIHRocm93cyBgUlVWTExNX05PX0xBTkdVQUdFX01PREVMYCB3aXRob3V0IGFcbiAgICogbG9hZGVkIG1vZGVsLlxuICAgKi9cbiAgZ2VuZXJhdGVEZXRhaWxlZChwcm9tcHQ6IHN0cmluZywgY29uZmlnPzogR2VuZXJhdGlvbkNvbmZpZyk6IEdlbmVyYXRpb25SZXN1bHQge1xuICAgIGNvbnN0IG5hdGl2ZSA9IHRoaXMucmVxdWlyZU1vZGVsKCk7XG4gICAgcmV0dXJuIHRvR2VuZXJhdGlvblJlc3VsdChcbiAgICAgIGNhbGxOYXRpdmUoKCkgPT4gbmF0aXZlLmdlbmVyYXRlKHByb21wdCwgdG9OYXRpdmVHZW5Db25maWcoY29uZmlnKSkpIGFzIE5hdGl2ZUdlbmVyYXRpb25SZXN1bHQsXG4gICAgKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSdW4gdGhlIGxvYWRlZCBtb2RlbCBvbiBhIGNvbnZlcnNhdGlvbiwgZm9ybWF0dGVkIHdpdGggdGhlIG1vZGVsJ3MgY2hhdFxuICAgKiB0ZW1wbGF0ZSAoQ2hhdE1MIGZvciBRd2VuLCBMbGFtYSAzIC8gTWlzdHJhbCBmb3JtYXRzIGZvciB0aG9zZSBtb2RlbHMpLlxuICAgKiBBbHdheXMgdGhyb3dzIGBSVVZMTE1fTk9fTEFOR1VBR0VfTU9ERUxgIHdpdGhvdXQgYSBsb2FkZWQgbW9kZWwuXG4gICAqL1xuICBjaGF0KG1lc3NhZ2VzOiBDaGF0TWVzc2FnZVtdLCBjb25maWc/OiBHZW5lcmF0aW9uQ29uZmlnKTogR2VuZXJhdGlvblJlc3VsdCB7XG4gICAgY29uc3QgbmF0aXZlID0gdGhpcy5yZXF1aXJlTW9kZWwoKTtcbiAgICByZXR1cm4gdG9HZW5lcmF0aW9uUmVzdWx0KGNhbGxOYXRpdmUoKCkgPT4gbmF0aXZlLmNoYXQhKG1lc3NhZ2VzLCB0b05hdGl2ZUdlbkNvbmZpZyhjb25maWcpKSkpO1xuICB9XG5cbiAgcHJpdmF0ZSByZXF1aXJlTW9kZWwoKTogTmF0aXZlRW5naW5lIHtcbiAgICBpZiAoIXRoaXMuaXNNb2RlbExvYWRlZCgpKSB0aHJvdyB0aGlzLm5vTW9kZWxFcnJvcigpO1xuICAgIHJldHVybiB0aGlzLm5hdGl2ZSE7XG4gIH1cblxuICBwcml2YXRlIG5vTW9kZWxFcnJvcigpOiBFcnJvciB7XG4gICAgbGV0IGZpeDogc3RyaW5nO1xuICAgIGlmICghdGhpcy5uYXRpdmUpIHtcbiAgICAgIGZpeCA9XG4gICAgICAgIGBubyBuYXRpdmUgUnV2TExNIGJpbmFyeSBpcyBhdmFpbGFibGUgZm9yICR7cGxhdGZvcm1LZXkoKX0sIHNvIG5vIG1vZGVsIGNhbiBiZSBsb2FkZWQgYCArXG4gICAgICAgICcoc2VlIFJVVkxMTV9OQVRJVkVfVU5BVkFJTEFCTEUgaW4gdGhlIFJFQURNRSknO1xuICAgIH0gZWxzZSBpZiAodHlwZW9mIHRoaXMubmF0aXZlLmxvYWRNb2RlbCAhPT0gJ2Z1bmN0aW9uJykge1xuICAgICAgZml4ID1cbiAgICAgICAgYHRoZSBpbnN0YWxsZWQgbmF0aXZlIGJpbmFyeSAodmVyc2lvbiAke3RoaXMubmF0aXZlVmVyc2lvbiA/PyAndW5rbm93bid9KSBjYW5ub3QgbG9hZCBgICtcbiAgICAgICAgJ21vZGVscyAoc2VlIFJVVkxMTV9OQVRJVkVfVE9PX09MRCBpbiB0aGUgUkVBRE1FKSc7XG4gICAgfSBlbHNlIGlmICghdGhpcy5zdXBwb3J0c01vZGVsTG9hZGluZygpKSB7XG4gICAgICBmaXggPVxuICAgICAgICBgdGhlIGluc3RhbGxlZCBuYXRpdmUgYmluYXJ5ICh2ZXJzaW9uICR7dGhpcy5uYXRpdmVWZXJzaW9uID8/ICd1bmtub3duJ30pIHdhcyBidWlsdCBgICtcbiAgICAgICAgJ3dpdGhvdXQgdGhlIGNhbmRsZSBpbmZlcmVuY2UgYmFja2VuZCwgc28gaXQgY2Fubm90IGxvYWQgbW9kZWxzIChzZWUgJyArXG4gICAgICAgICdSVVZMTE1fTk9fSU5GRVJFTkNFX0JBQ0tFTkQgaW4gdGhlIFJFQURNRSknO1xuICAgIH0gZWxzZSB7XG4gICAgICBmaXggPSAnY2FsbCBsb2FkTW9kZWwoPHBhdGggdG8gYSBHR1VGIGZpbGU+KSBmaXJzdCc7XG4gICAgfVxuICAgIHJldHVybiBjb2RlZEVycm9yKFxuICAgICAgJ1JVVkxMTV9OT19MQU5HVUFHRV9NT0RFTCcsXG4gICAgICBgbm8gbGFuZ3VhZ2UgbW9kZWwgaXMgbG9hZGVkLCBzbyB0aGVyZSBpcyBubyBtb2RlbCBvdXRwdXQgdG8gcmV0dXJuOiAke2ZpeH0uIFJvdXRpbmcsIGAgK1xuICAgICAgICAnbWVtb3J5IGFuZCBlbWJlZGRpbmdzIHdvcmsgd2l0aG91dCBhIG1vZGVsLiBGb3IgYW4gSFRUUCBzZXJ2ZXIgdXNlIHRoZSBydXZsbG0gQ0xJICcgK1xuICAgICAgICAnKGBydXZsbG0gc2VydmUgPG1vZGVsPmApLicsXG4gICAgKTtcbiAgfVxuXG4gIC8qKiBUaHJvdyBgUlVWTExNX05PX0xBTkdVQUdFX01PREVMYCwgb3IgcmV0dXJuIHRoZSBsYWJlbGxlZCBwbGFjZWhvbGRlciB3aGVuIG9wdGVkIGluLiAqL1xuICBwcml2YXRlIHBsYWNlaG9sZGVyT3JUaHJvdygpOiBzdHJpbmcge1xuICAgIGlmICghdGhpcy5hbGxvd1BsYWNlaG9sZGVyKSB0aHJvdyB0aGlzLm5vTW9kZWxFcnJvcigpO1xuICAgIG5vdGljZShcbiAgICAgICdSVVZMTE1fUExBQ0VIT0xERVJfVEVYVCcsXG4gICAgICAnbm8gbGFuZ3VhZ2UgbW9kZWwgaXMgbG9hZGVkOiBnZW5lcmF0ZSgpL3F1ZXJ5KCkgYXJlIHJldHVybmluZyBhIGxhYmVsbGVkIHBsYWNlaG9sZGVyLCAnICtcbiAgICAgICAgJ25vdCBtb2RlbCBvdXRwdXQsIGJlY2F1c2UgYWxsb3dQbGFjZWhvbGRlciAob3IgUlVWTExNX0FMTE9XX01PQ0s9MSkgaXMgc2V0LicsXG4gICAgICBmYWxzZSxcbiAgICApO1xuICAgIHJldHVybiBQTEFDRUhPTERFUl9URVhUO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldCByb3V0aW5nIGRlY2lzaW9uIGZvciBhIHF1ZXJ5XG4gICAqL1xuICByb3V0ZSh0ZXh0OiBzdHJpbmcpOiBSb3V0aW5nRGVjaXNpb24ge1xuICAgIGlmICh0aGlzLm5hdGl2ZSkge1xuICAgICAgY29uc3QgcmVzdWx0ID0gdGhpcy5uYXRpdmUucm91dGUodGV4dCk7XG4gICAgICByZXR1cm4ge1xuICAgICAgICBtb2RlbDogcmVzdWx0Lm1vZGVsIGFzIGFueSxcbiAgICAgICAgY29udGV4dFNpemU6IHJlc3VsdC5jb250ZXh0U2l6ZSA/PyByZXN1bHQuY29udGV4dF9zaXplLFxuICAgICAgICB0ZW1wZXJhdHVyZTogcmVzdWx0LnRlbXBlcmF0dXJlLFxuICAgICAgICB0b3BQOiByZXN1bHQudG9wUCA/PyByZXN1bHQudG9wX3AsXG4gICAgICAgIGNvbmZpZGVuY2U6IHJlc3VsdC5jb25maWRlbmNlLFxuICAgICAgfTtcbiAgICB9XG5cbiAgICAvLyBGYWxsYmFja1xuICAgIHJldHVybiB7XG4gICAgICBtb2RlbDogJ003MDAnLFxuICAgICAgY29udGV4dFNpemU6IDUxMixcbiAgICAgIHRlbXBlcmF0dXJlOiAwLjcsXG4gICAgICB0b3BQOiAwLjksXG4gICAgICBjb25maWRlbmNlOiAwLjUsXG4gICAgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTZWFyY2ggbWVtb3J5IGZvciBzaW1pbGFyIGNvbnRlbnRcbiAgICovXG4gIHNlYXJjaE1lbW9yeSh0ZXh0OiBzdHJpbmcsIGsgPSAxMCk6IE1lbW9yeVJlc3VsdFtdIHtcbiAgICBpZiAodGhpcy5uYXRpdmUpIHtcbiAgICAgIGNvbnN0IHJlc3VsdHMgPSB0aGlzLm5hdGl2ZS5zZWFyY2hNZW1vcnkodGV4dCwgayk7XG4gICAgICByZXR1cm4gcmVzdWx0cy5tYXAociA9PiB7XG4gICAgICAgIC8vIFRoZSBuYXRpdmUgc2lkZSByZXBvcnRzIGBkaXN0YW5jZWAgKHNtYWxsZXIgaXMgY2xvc2VyKS4gTWVtb3J5UmVzdWx0XG4gICAgICAgIC8vIGRvY3VtZW50cyBgc2NvcmVgIGFzIGEgc2ltaWxhcml0eSwgc28gY29udmVydCByYXRoZXIgdGhhbiBwYXNzaW5nIGFcbiAgICAgICAgLy8gZGlzdGFuY2UgdGhyb3VnaCB1bmRlciBhIHNpbWlsYXJpdHkncyBuYW1lLiBPbGRlciBidWlsZHMgdGhhdCBhbHJlYWR5XG4gICAgICAgIC8vIGVtaXQgYHNjb3JlYCBhcmUgdXNlZCBhcy1pcy5cbiAgICAgICAgY29uc3QgYW55UiA9IHIgYXMgYW55O1xuICAgICAgICBjb25zdCBzY29yZSA9XG4gICAgICAgICAgdHlwZW9mIGFueVIuc2NvcmUgPT09ICdudW1iZXInXG4gICAgICAgICAgICA/IGFueVIuc2NvcmVcbiAgICAgICAgICAgIDogdHlwZW9mIGFueVIuZGlzdGFuY2UgPT09ICdudW1iZXInXG4gICAgICAgICAgICAgID8gMSAvICgxICsgTWF0aC5tYXgoMCwgYW55Ui5kaXN0YW5jZSkpXG4gICAgICAgICAgICAgIDogMDtcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICBpZDogci5pZCxcbiAgICAgICAgICBzY29yZSxcbiAgICAgICAgICBjb250ZW50OiByLmNvbnRlbnQsXG4gICAgICAgICAgbWV0YWRhdGE6IEpTT04ucGFyc2Uoci5tZXRhZGF0YSB8fCAne30nKSxcbiAgICAgICAgfTtcbiAgICAgIH0pO1xuICAgIH1cblxuICAgIC8vIEZhbGxiYWNrIC0gc2ltcGxlIHNlYXJjaFxuICAgIHJldHVybiBBcnJheS5mcm9tKHRoaXMuZmFsbGJhY2tTdGF0ZS5tZW1vcnkuZW50cmllcygpKVxuICAgICAgLnNsaWNlKDAsIGspXG4gICAgICAubWFwKChbaWQsIGRhdGFdKSA9PiAoe1xuICAgICAgICBpZCxcbiAgICAgICAgc2NvcmU6IDAuNSxcbiAgICAgICAgY29udGVudDogZGF0YS5jb250ZW50LFxuICAgICAgICBtZXRhZGF0YTogZGF0YS5tZXRhZGF0YSxcbiAgICAgIH0pKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBBZGQgY29udGVudCB0byBtZW1vcnlcbiAgICovXG4gIGFkZE1lbW9yeShjb250ZW50OiBzdHJpbmcsIG1ldGFkYXRhPzogUmVjb3JkPHN0cmluZywgdW5rbm93bj4pOiBNZW1vcnlJZCB7XG4gICAgaWYgKHRoaXMubmF0aXZlKSB7XG4gICAgICByZXR1cm4gdGhpcy5uYXRpdmUuYWRkTWVtb3J5KGNvbnRlbnQsIG1ldGFkYXRhID8gSlNPTi5zdHJpbmdpZnkobWV0YWRhdGEpIDogdW5kZWZpbmVkKTtcbiAgICB9XG5cbiAgICAvLyBGYWxsYmFja1xuICAgIGNvbnN0IGlkID0gdGhpcy5mYWxsYmFja1N0YXRlLm5leHRJZCsrO1xuICAgIHRoaXMuZmFsbGJhY2tTdGF0ZS5tZW1vcnkuc2V0KGlkLCB7XG4gICAgICBjb250ZW50LFxuICAgICAgZW1iZWRkaW5nOiB0aGlzLmVtYmVkKGNvbnRlbnQpLFxuICAgICAgbWV0YWRhdGE6IG1ldGFkYXRhID8/IHt9LFxuICAgIH0pO1xuICAgIHJldHVybiBpZDtcbiAgfVxuXG4gIC8qKlxuICAgKiBQcm92aWRlIGZlZWRiYWNrIGZvciBsZWFybmluZ1xuICAgKi9cbiAgZmVlZGJhY2soZmI6IEZlZWRiYWNrKTogYm9vbGVhbiB7XG4gICAgaWYgKHRoaXMubmF0aXZlKSB7XG4gICAgICByZXR1cm4gdGhpcy5uYXRpdmUuZmVlZGJhY2soZmIucmVxdWVzdElkLCBmYi5yYXRpbmcsIGZiLmNvcnJlY3Rpb24pO1xuICAgIH1cbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cblxuICAvKipcbiAgICogR2V0IGVuZ2luZSBzdGF0aXN0aWNzXG4gICAqL1xuICBzdGF0cygpOiBSdXZMTE1TdGF0cyB7XG4gICAgaWYgKHRoaXMubmF0aXZlKSB7XG4gICAgICBjb25zdCBzID0gdGhpcy5uYXRpdmUuc3RhdHMoKTtcbiAgICAgIC8vIE1hcCBuYXRpdmUgc3RhdHMgKHNuYWtlX2Nhc2UpIHRvIFR5cGVTY3JpcHQgaW50ZXJmYWNlIChjYW1lbENhc2UpXG4gICAgICAvLyBIYW5kbGUgYm90aCBvbGQgYW5kIG5ldyBmaWVsZCBuYW1lcyBmb3IgYmFja3dhcmQgY29tcGF0aWJpbGl0eVxuICAgICAgcmV0dXJuIHtcbiAgICAgICAgdG90YWxRdWVyaWVzOiAocyBhcyBhbnkpLnRvdGFsUXVlcmllcyA/PyBzLnRvdGFsX3F1ZXJpZXMgPz8gMCxcbiAgICAgICAgbWVtb3J5Tm9kZXM6IChzIGFzIGFueSkubWVtb3J5Tm9kZXMgPz8gcy5tZW1vcnlfbm9kZXMgPz8gMCxcbiAgICAgICAgcGF0dGVybnNMZWFybmVkOlxuICAgICAgICAgIChzIGFzIGFueSkudHJhaW5pbmdTdGVwcyA/P1xuICAgICAgICAgIHMucGF0dGVybnNfbGVhcm5lZCA/P1xuICAgICAgICAgIChzIGFzIGFueSkudHJhaW5pbmdfc3RlcHMgPz9cbiAgICAgICAgICAwLFxuICAgICAgICBhdmdMYXRlbmN5TXM6IChzIGFzIGFueSkuYXZnTGF0ZW5jeU1zID8/IHMuYXZnX2xhdGVuY3lfbXMgPz8gMCxcbiAgICAgICAgY2FjaGVIaXRSYXRlOiAocyBhcyBhbnkpLmNhY2hlSGl0UmF0ZSA/PyBzLmNhY2hlX2hpdF9yYXRlID8/IDAsXG4gICAgICAgIHJvdXRlckFjY3VyYWN5OiAocyBhcyBhbnkpLnJvdXRlckFjY3VyYWN5ID8/IHMucm91dGVyX2FjY3VyYWN5ID8/IDAuNSxcbiAgICAgIH07XG4gICAgfVxuXG4gICAgLy8gRmFsbGJhY2tcbiAgICByZXR1cm4ge1xuICAgICAgdG90YWxRdWVyaWVzOiB0aGlzLmZhbGxiYWNrU3RhdGUucXVlcnlDb3VudCxcbiAgICAgIG1lbW9yeU5vZGVzOiB0aGlzLmZhbGxiYWNrU3RhdGUubWVtb3J5LnNpemUsXG4gICAgICBwYXR0ZXJuc0xlYXJuZWQ6IDAsXG4gICAgICBhdmdMYXRlbmN5TXM6IDEuMCxcbiAgICAgIGNhY2hlSGl0UmF0ZTogMC4wLFxuICAgICAgcm91dGVyQWNjdXJhY3k6IDAuNSxcbiAgICB9O1xuICB9XG5cbiAgLyoqXG4gICAqIEZvcmNlIHJvdXRlciBsZWFybmluZyBjeWNsZVxuICAgKi9cbiAgZm9yY2VMZWFybigpOiBzdHJpbmcge1xuICAgIGlmICh0aGlzLm5hdGl2ZSkge1xuICAgICAgcmV0dXJuIHRoaXMubmF0aXZlLmZvcmNlTGVhcm4oKTtcbiAgICB9XG4gICAgcmV0dXJuICdMZWFybmluZyBub3QgYXZhaWxhYmxlIGluIGZhbGxiYWNrIG1vZGUnO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldCBlbWJlZGRpbmcgZm9yIHRleHRcbiAgICovXG4gIGVtYmVkKHRleHQ6IHN0cmluZyk6IEVtYmVkZGluZyB7XG4gICAgaWYgKHRoaXMubmF0aXZlKSB7XG4gICAgICByZXR1cm4gdGhpcy5uYXRpdmUuZW1iZWQodGV4dCk7XG4gICAgfVxuXG4gICAgLy8gRmFsbGJhY2sgLSBzaW1wbGUgaGFzaC1iYXNlZCBlbWJlZGRpbmdcbiAgICBjb25zdCBkaW0gPSB0aGlzLmNvbmZpZy5lbWJlZGRpbmdEaW0gPz8gNzY4O1xuICAgIGNvbnN0IGVtYmVkZGluZyA9IG5ldyBBcnJheShkaW0pLmZpbGwoMCk7XG5cbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRleHQubGVuZ3RoOyBpKyspIHtcbiAgICAgIGNvbnN0IGlkeCA9ICh0ZXh0LmNoYXJDb2RlQXQoaSkgKiAoaSArIDEpKSAlIGRpbTtcbiAgICAgIGVtYmVkZGluZ1tpZHhdICs9IDAuMTtcbiAgICB9XG5cbiAgICAvLyBOb3JtYWxpemVcbiAgICBjb25zdCBub3JtID0gTWF0aC5zcXJ0KGVtYmVkZGluZy5yZWR1Y2UoKHN1bSwgeCkgPT4gc3VtICsgeCAqIHgsIDApKSB8fCAxO1xuICAgIHJldHVybiBlbWJlZGRpbmcubWFwKHggPT4geCAvIG5vcm0pO1xuICB9XG5cbiAgLyoqXG4gICAqIENvbXB1dGUgc2ltaWxhcml0eSBiZXR3ZWVuIHR3byB0ZXh0c1xuICAgKi9cbiAgc2ltaWxhcml0eSh0ZXh0MTogc3RyaW5nLCB0ZXh0Mjogc3RyaW5nKTogbnVtYmVyIHtcbiAgICBpZiAodGhpcy5uYXRpdmUpIHtcbiAgICAgIC8vIGYzMiBhY2N1bXVsYXRpb24gY2FuIGxhbmQganVzdCBvdXRzaWRlIHRoZSBtYXRoZW1hdGljYWwgcmFuZ2Ug4oCUIGFuXG4gICAgICAvLyBpZGVudGljYWwgcGFpciBtZWFzdXJlcyAxLjAwMDAwMDEg4oCUIHdoaWNoIHR1cm5zIE1hdGguYWNvcyhzaW0pIGludG9cbiAgICAgIC8vIE5hTiBkb3duc3RyZWFtLiBDb3NpbmUgc2ltaWxhcml0eSBpcyBkZWZpbmVkIG9uIFstMSwgMV0sIHNvIGNsYW1wLlxuICAgICAgY29uc3QgcmF3ID0gdGhpcy5uYXRpdmUuc2ltaWxhcml0eSh0ZXh0MSwgdGV4dDIpO1xuICAgICAgcmV0dXJuIE1hdGgubWluKDEsIE1hdGgubWF4KC0xLCByYXcpKTtcbiAgICB9XG5cbiAgICAvLyBGYWxsYmFjayAtIGNvc2luZSBzaW1pbGFyaXR5XG4gICAgY29uc3QgZW1iMSA9IHRoaXMuZW1iZWQodGV4dDEpO1xuICAgIGNvbnN0IGVtYjIgPSB0aGlzLmVtYmVkKHRleHQyKTtcblxuICAgIGxldCBkb3QgPSAwO1xuICAgIGxldCBub3JtMSA9IDA7XG4gICAgbGV0IG5vcm0yID0gMDtcblxuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgZW1iMS5sZW5ndGg7IGkrKykge1xuICAgICAgZG90ICs9IGVtYjFbaV0gKiBlbWIyW2ldO1xuICAgICAgbm9ybTEgKz0gZW1iMVtpXSAqIGVtYjFbaV07XG4gICAgICBub3JtMiArPSBlbWIyW2ldICogZW1iMltpXTtcbiAgICB9XG5cbiAgICBjb25zdCBkZW5vbSA9IE1hdGguc3FydChub3JtMSkgKiBNYXRoLnNxcnQobm9ybTIpO1xuICAgIGNvbnN0IHNpbWlsYXJpdHkgPSBkZW5vbSA+IDAgPyBkb3QgLyBkZW5vbSA6IDA7XG4gICAgLy8gQ2xhbXAgdG8gWzAsIDFdIHRvIGhhbmRsZSBmbG9hdGluZyBwb2ludCBlcnJvcnNcbiAgICByZXR1cm4gTWF0aC5tYXgoMCwgTWF0aC5taW4oMSwgc2ltaWxhcml0eSkpO1xuICB9XG5cbiAgLyoqXG4gICAqIENoZWNrIGlmIFNJTUQgaXMgYXZhaWxhYmxlXG4gICAqL1xuICBoYXNTaW1kKCk6IGJvb2xlYW4ge1xuICAgIGlmICh0aGlzLm5hdGl2ZSkge1xuICAgICAgcmV0dXJuIHRoaXMubmF0aXZlLmhhc1NpbWQoKTtcbiAgICB9XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldCBTSU1EIGNhcGFiaWxpdGllc1xuICAgKi9cbiAgc2ltZENhcGFiaWxpdGllcygpOiBzdHJpbmdbXSB7XG4gICAgaWYgKHRoaXMubmF0aXZlKSB7XG4gICAgICByZXR1cm4gdGhpcy5uYXRpdmUuc2ltZENhcGFiaWxpdGllcygpO1xuICAgIH1cbiAgICByZXR1cm4gWydTY2FsYXIgKGZhbGxiYWNrKSddO1xuICB9XG5cbiAgLyoqXG4gICAqIEJhdGNoIHF1ZXJ5IG11bHRpcGxlIHByb21wdHNcbiAgICovXG4gIGJhdGNoUXVlcnkocmVxdWVzdDogQmF0Y2hRdWVyeVJlcXVlc3QpOiBCYXRjaFF1ZXJ5UmVzcG9uc2Uge1xuICAgIGNvbnN0IHN0YXJ0ID0gRGF0ZS5ub3coKTtcbiAgICBjb25zdCByZXNwb25zZXMgPSByZXF1ZXN0LnF1ZXJpZXMubWFwKHEgPT4gdGhpcy5xdWVyeShxLCByZXF1ZXN0LmNvbmZpZykpO1xuICAgIHJldHVybiB7XG4gICAgICByZXNwb25zZXMsXG4gICAgICB0b3RhbExhdGVuY3lNczogRGF0ZS5ub3coKSAtIHN0YXJ0LFxuICAgIH07XG4gIH1cblxuICAvKipcbiAgICogQ2hlY2sgaWYgbmF0aXZlIG1vZHVsZSBpcyBsb2FkZWRcbiAgICovXG4gIGlzTmF0aXZlTG9hZGVkKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLm5hdGl2ZSAhPT0gbnVsbDtcbiAgfVxufVxuIl19
|