chainlesschain 0.47.8 → 0.49.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/bin/chainlesschain.js +0 -0
- package/package.json +10 -8
- package/src/assets/web-panel/.build-hash +1 -1
- package/src/assets/web-panel/assets/{AppLayout-6SPt_8Y_.js → AppLayout-Rvi759IS.js} +1 -1
- package/src/assets/web-panel/assets/Dashboard-BS-tzGNj.css +1 -0
- package/src/assets/web-panel/assets/{Dashboard-Br7kCwKJ.js → Dashboard-DBhFxXYQ.js} +2 -2
- package/src/assets/web-panel/assets/{index-tN-8TosE.js → index-uL0cZ8N_.js} +2 -2
- package/src/assets/web-panel/index.html +2 -2
- package/src/commands/activitypub.js +533 -0
- package/src/commands/codegen.js +303 -0
- package/src/commands/collab.js +482 -0
- package/src/commands/compliance.js +597 -6
- package/src/commands/crosschain.js +382 -0
- package/src/commands/dbevo.js +388 -0
- package/src/commands/dev.js +411 -0
- package/src/commands/federation.js +427 -0
- package/src/commands/fusion.js +332 -0
- package/src/commands/governance.js +505 -0
- package/src/commands/hardening.js +110 -0
- package/src/commands/incentive.js +373 -0
- package/src/commands/inference.js +304 -0
- package/src/commands/infra.js +361 -0
- package/src/commands/kg.js +371 -0
- package/src/commands/marketplace.js +326 -0
- package/src/commands/matrix.js +283 -0
- package/src/commands/mcp.js +441 -18
- package/src/commands/nlprog.js +329 -0
- package/src/commands/nostr.js +196 -7
- package/src/commands/ops.js +408 -0
- package/src/commands/perception.js +385 -0
- package/src/commands/pqc.js +34 -0
- package/src/commands/privacy.js +345 -0
- package/src/commands/quantization.js +280 -0
- package/src/commands/recommend.js +336 -0
- package/src/commands/reputation.js +349 -0
- package/src/commands/runtime.js +500 -0
- package/src/commands/sla.js +352 -0
- package/src/commands/social.js +265 -0
- package/src/commands/stress.js +252 -0
- package/src/commands/tech.js +268 -0
- package/src/commands/tenant.js +576 -0
- package/src/commands/trust.js +366 -0
- package/src/harness/mcp-client.js +330 -54
- package/src/index.js +114 -0
- package/src/lib/activitypub-bridge.js +623 -0
- package/src/lib/aiops.js +523 -0
- package/src/lib/autonomous-developer.js +524 -0
- package/src/lib/code-agent.js +442 -0
- package/src/lib/collaboration-governance.js +556 -0
- package/src/lib/community-governance.js +649 -0
- package/src/lib/compliance-framework-reporter.js +600 -0
- package/src/lib/content-recommendation.js +600 -0
- package/src/lib/cross-chain.js +669 -0
- package/src/lib/dbevo.js +669 -0
- package/src/lib/decentral-infra.js +445 -0
- package/src/lib/federation-hardening.js +587 -0
- package/src/lib/hardening-manager.js +409 -0
- package/src/lib/inference-network.js +407 -0
- package/src/lib/knowledge-graph.js +530 -0
- package/src/lib/matrix-bridge.js +252 -0
- package/src/lib/mcp-client.js +3 -0
- package/src/lib/mcp-registry.js +347 -0
- package/src/lib/mcp-scaffold.js +385 -0
- package/src/lib/multimodal.js +698 -0
- package/src/lib/nl-programming.js +595 -0
- package/src/lib/nostr-bridge.js +214 -38
- package/src/lib/perception.js +500 -0
- package/src/lib/pqc-manager.js +141 -9
- package/src/lib/privacy-computing.js +575 -0
- package/src/lib/protocol-fusion.js +535 -0
- package/src/lib/quantization.js +362 -0
- package/src/lib/reputation-optimizer.js +509 -0
- package/src/lib/skill-marketplace.js +397 -0
- package/src/lib/sla-manager.js +484 -0
- package/src/lib/social-graph.js +408 -0
- package/src/lib/stix-parser.js +167 -0
- package/src/lib/stress-tester.js +383 -0
- package/src/lib/tech-learning-engine.js +651 -0
- package/src/lib/tenant-saas.js +831 -0
- package/src/lib/threat-intel.js +268 -0
- package/src/lib/token-incentive.js +513 -0
- package/src/lib/topic-classifier.js +400 -0
- package/src/lib/trust-security.js +473 -0
- package/src/lib/ueba.js +403 -0
- package/src/lib/universal-runtime.js +771 -0
- package/src/assets/web-panel/assets/Dashboard-CKeMmCoT.css +0 -1
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stress Tester — federation stress test orchestration.
|
|
3
|
+
*
|
|
4
|
+
* CLI-first simulation: we don't drive real peer RPCs here (no federation
|
|
5
|
+
* fixture in the CLI boot path). Instead we generate deterministic synthetic
|
|
6
|
+
* metrics from the load profile so operators can sanity-check capacity planning
|
|
7
|
+
* wiring, bottleneck heuristics, and report shapes before running the real
|
|
8
|
+
* Desktop stress harness.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import crypto from "crypto";
|
|
12
|
+
|
|
13
|
+
/* ── Load levels ──────────────────────────────────────────── */
|
|
14
|
+
|
|
15
|
+
export const LOAD_LEVELS = Object.freeze({
|
|
16
|
+
LIGHT: {
|
|
17
|
+
name: "light",
|
|
18
|
+
concurrency: 10,
|
|
19
|
+
requestsPerSecond: 50,
|
|
20
|
+
duration: 300000,
|
|
21
|
+
},
|
|
22
|
+
MEDIUM: {
|
|
23
|
+
name: "medium",
|
|
24
|
+
concurrency: 50,
|
|
25
|
+
requestsPerSecond: 200,
|
|
26
|
+
duration: 600000,
|
|
27
|
+
},
|
|
28
|
+
HEAVY: {
|
|
29
|
+
name: "heavy",
|
|
30
|
+
concurrency: 100,
|
|
31
|
+
requestsPerSecond: 500,
|
|
32
|
+
duration: 900000,
|
|
33
|
+
},
|
|
34
|
+
EXTREME: {
|
|
35
|
+
name: "extreme",
|
|
36
|
+
concurrency: 200,
|
|
37
|
+
requestsPerSecond: 1000,
|
|
38
|
+
duration: 1800000,
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const LEVEL_INDEX = new Map(Object.values(LOAD_LEVELS).map((l) => [l.name, l]));
|
|
43
|
+
|
|
44
|
+
export function resolveLevel(name) {
|
|
45
|
+
if (!name) return null;
|
|
46
|
+
const key = String(name).toLowerCase();
|
|
47
|
+
return LEVEL_INDEX.get(key) || null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function listLoadLevels() {
|
|
51
|
+
return Object.values(LOAD_LEVELS).map((l) => ({ ...l }));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const RUN_STATUS = {
|
|
55
|
+
RUNNING: "running",
|
|
56
|
+
COMPLETE: "complete",
|
|
57
|
+
STOPPED: "stopped",
|
|
58
|
+
FAILED: "failed",
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
/* ── In-memory stores ─────────────────────────────────────── */
|
|
62
|
+
const _runs = new Map();
|
|
63
|
+
const _results = new Map();
|
|
64
|
+
let _seq = 0;
|
|
65
|
+
|
|
66
|
+
/* ── Schema ────────────────────────────────────────────────── */
|
|
67
|
+
|
|
68
|
+
export function ensureStressTables(db) {
|
|
69
|
+
db.exec(`
|
|
70
|
+
CREATE TABLE IF NOT EXISTS stress_test_runs (
|
|
71
|
+
test_id TEXT PRIMARY KEY,
|
|
72
|
+
load_level TEXT NOT NULL,
|
|
73
|
+
concurrency INTEGER NOT NULL,
|
|
74
|
+
requests_per_second INTEGER,
|
|
75
|
+
duration INTEGER NOT NULL,
|
|
76
|
+
status TEXT DEFAULT 'running',
|
|
77
|
+
started_at INTEGER NOT NULL,
|
|
78
|
+
completed_at INTEGER
|
|
79
|
+
)
|
|
80
|
+
`);
|
|
81
|
+
db.exec(`
|
|
82
|
+
CREATE TABLE IF NOT EXISTS stress_test_results (
|
|
83
|
+
result_id TEXT PRIMARY KEY,
|
|
84
|
+
test_id TEXT NOT NULL,
|
|
85
|
+
tps REAL,
|
|
86
|
+
avg_response_time REAL,
|
|
87
|
+
p50_response_time REAL,
|
|
88
|
+
p95_response_time REAL,
|
|
89
|
+
p99_response_time REAL,
|
|
90
|
+
error_rate REAL,
|
|
91
|
+
bottlenecks TEXT,
|
|
92
|
+
capacity_recommendations TEXT,
|
|
93
|
+
created_at INTEGER NOT NULL
|
|
94
|
+
)
|
|
95
|
+
`);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/* ── Synthetic metrics ─────────────────────────────────────── */
|
|
99
|
+
|
|
100
|
+
// Deterministic quasi-random: stable across runs for the same inputs. Tests
|
|
101
|
+
// assert shape + monotonicity, not exact floats, so this is plenty.
|
|
102
|
+
function _mix(seed, offset) {
|
|
103
|
+
const v = Math.sin(seed * 12.9898 + offset * 78.233) * 43758.5453;
|
|
104
|
+
return v - Math.floor(v);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function _synthesizeMetrics(config, seed) {
|
|
108
|
+
const concurrency = config.concurrency;
|
|
109
|
+
const targetRps = config.requestsPerSecond;
|
|
110
|
+
|
|
111
|
+
// Model: realized TPS decays as concurrency pushes past optimal; latency
|
|
112
|
+
// grows super-linearly. Error rate stays near zero until EXTREME territory.
|
|
113
|
+
const saturation = Math.min(1, concurrency / 150);
|
|
114
|
+
const jitter = 0.85 + _mix(seed, 1) * 0.3; // 0.85..1.15
|
|
115
|
+
const tps = targetRps * (1 - saturation * 0.25) * jitter;
|
|
116
|
+
|
|
117
|
+
const baseLatency = 8 + concurrency * 0.8;
|
|
118
|
+
const p50 = baseLatency * (0.7 + _mix(seed, 2) * 0.2);
|
|
119
|
+
const p95 = baseLatency * (1.8 + _mix(seed, 3) * 0.5);
|
|
120
|
+
const p99 = baseLatency * (3.2 + _mix(seed, 4) * 1.2);
|
|
121
|
+
const avg = p50 * 0.6 + p95 * 0.3 + p99 * 0.1;
|
|
122
|
+
|
|
123
|
+
const errorRate = Math.min(
|
|
124
|
+
0.5,
|
|
125
|
+
Math.max(0, (saturation - 0.5) * 0.18 + _mix(seed, 5) * 0.02),
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
tps: Number(tps.toFixed(2)),
|
|
130
|
+
avgResponseTime: Number(avg.toFixed(2)),
|
|
131
|
+
p50ResponseTime: Number(p50.toFixed(2)),
|
|
132
|
+
p95ResponseTime: Number(p95.toFixed(2)),
|
|
133
|
+
p99ResponseTime: Number(p99.toFixed(2)),
|
|
134
|
+
errorRate: Number(errorRate.toFixed(4)),
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/* ── Bottleneck heuristics ─────────────────────────────────── */
|
|
139
|
+
|
|
140
|
+
function _deriveBottlenecks(metrics, config) {
|
|
141
|
+
const bottlenecks = [];
|
|
142
|
+
if (metrics.errorRate > 0.05) {
|
|
143
|
+
bottlenecks.push({
|
|
144
|
+
kind: "error-rate",
|
|
145
|
+
severity: "high",
|
|
146
|
+
detail: `Error rate ${(metrics.errorRate * 100).toFixed(2)}% exceeds 5%`,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
if (metrics.p99ResponseTime > 1500) {
|
|
150
|
+
bottlenecks.push({
|
|
151
|
+
kind: "tail-latency",
|
|
152
|
+
severity: "high",
|
|
153
|
+
detail: `p99 ${metrics.p99ResponseTime.toFixed(0)}ms above 1500ms tail`,
|
|
154
|
+
});
|
|
155
|
+
} else if (metrics.p95ResponseTime > 500) {
|
|
156
|
+
bottlenecks.push({
|
|
157
|
+
kind: "response-time",
|
|
158
|
+
severity: "medium",
|
|
159
|
+
detail: `p95 ${metrics.p95ResponseTime.toFixed(0)}ms above 500ms target`,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
const targetRps = config.requestsPerSecond;
|
|
163
|
+
if (metrics.tps < targetRps * 0.85) {
|
|
164
|
+
bottlenecks.push({
|
|
165
|
+
kind: "throughput",
|
|
166
|
+
severity: "medium",
|
|
167
|
+
detail: `Realized TPS ${metrics.tps.toFixed(0)} below 85% of target ${targetRps}`,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
return bottlenecks;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function _capacityRecommendations(metrics, config, bottlenecks) {
|
|
174
|
+
const recs = [];
|
|
175
|
+
for (const b of bottlenecks) {
|
|
176
|
+
if (b.kind === "error-rate") {
|
|
177
|
+
recs.push("Reduce concurrency or add retry/backoff on failing RPCs");
|
|
178
|
+
}
|
|
179
|
+
if (b.kind === "tail-latency") {
|
|
180
|
+
recs.push(
|
|
181
|
+
"Investigate slow-path outliers (DB lock / GC pause / cold peer)",
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
if (b.kind === "response-time") {
|
|
185
|
+
recs.push("Profile hot handlers; consider caching or connection pooling");
|
|
186
|
+
}
|
|
187
|
+
if (b.kind === "throughput") {
|
|
188
|
+
recs.push(
|
|
189
|
+
`Scale horizontally: estimated ${Math.ceil(config.requestsPerSecond / Math.max(1, metrics.tps))}× current capacity needed for target`,
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
if (recs.length === 0) {
|
|
194
|
+
recs.push("System is within targets at this load level");
|
|
195
|
+
}
|
|
196
|
+
return recs;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/* ── Test lifecycle ────────────────────────────────────────── */
|
|
200
|
+
|
|
201
|
+
export function startStressTest(db, config = {}) {
|
|
202
|
+
const levelName = config.level || "medium";
|
|
203
|
+
const level = resolveLevel(levelName);
|
|
204
|
+
if (!level) {
|
|
205
|
+
throw new Error(
|
|
206
|
+
`Unknown load level: ${levelName} (known: ${[...LEVEL_INDEX.keys()].join("/")})`,
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const concurrency = Number(config.concurrency ?? level.concurrency);
|
|
211
|
+
const requestsPerSecond = Number(
|
|
212
|
+
config.requestsPerSecond ?? level.requestsPerSecond,
|
|
213
|
+
);
|
|
214
|
+
const duration = Number(config.duration ?? level.duration);
|
|
215
|
+
if (concurrency <= 0 || duration <= 0 || requestsPerSecond <= 0) {
|
|
216
|
+
throw new Error("concurrency/duration/requestsPerSecond must all be > 0");
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const testId = crypto.randomUUID();
|
|
220
|
+
const now = Date.now();
|
|
221
|
+
|
|
222
|
+
const run = {
|
|
223
|
+
testId,
|
|
224
|
+
loadLevel: level.name,
|
|
225
|
+
concurrency,
|
|
226
|
+
requestsPerSecond,
|
|
227
|
+
duration,
|
|
228
|
+
status: RUN_STATUS.RUNNING,
|
|
229
|
+
startedAt: now,
|
|
230
|
+
completedAt: null,
|
|
231
|
+
_seq: ++_seq,
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
db.prepare(
|
|
235
|
+
`INSERT INTO stress_test_runs (test_id, load_level, concurrency, requests_per_second, duration, status, started_at, completed_at)
|
|
236
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
237
|
+
).run(
|
|
238
|
+
testId,
|
|
239
|
+
level.name,
|
|
240
|
+
concurrency,
|
|
241
|
+
requestsPerSecond,
|
|
242
|
+
duration,
|
|
243
|
+
run.status,
|
|
244
|
+
now,
|
|
245
|
+
null,
|
|
246
|
+
);
|
|
247
|
+
|
|
248
|
+
const seed = _hashSeed(testId);
|
|
249
|
+
const metrics = _synthesizeMetrics(
|
|
250
|
+
{ concurrency, requestsPerSecond, duration },
|
|
251
|
+
seed,
|
|
252
|
+
);
|
|
253
|
+
const bottlenecks = _deriveBottlenecks(metrics, { requestsPerSecond });
|
|
254
|
+
const recommendations = _capacityRecommendations(
|
|
255
|
+
metrics,
|
|
256
|
+
{ requestsPerSecond },
|
|
257
|
+
bottlenecks,
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
const resultId = crypto.randomUUID();
|
|
261
|
+
const result = {
|
|
262
|
+
resultId,
|
|
263
|
+
testId,
|
|
264
|
+
...metrics,
|
|
265
|
+
bottlenecks,
|
|
266
|
+
capacityRecommendations: recommendations,
|
|
267
|
+
createdAt: now,
|
|
268
|
+
};
|
|
269
|
+
_results.set(testId, result);
|
|
270
|
+
|
|
271
|
+
db.prepare(
|
|
272
|
+
`INSERT INTO stress_test_results (result_id, test_id, tps, avg_response_time, p50_response_time, p95_response_time, p99_response_time, error_rate, bottlenecks, capacity_recommendations, created_at)
|
|
273
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
274
|
+
).run(
|
|
275
|
+
resultId,
|
|
276
|
+
testId,
|
|
277
|
+
metrics.tps,
|
|
278
|
+
metrics.avgResponseTime,
|
|
279
|
+
metrics.p50ResponseTime,
|
|
280
|
+
metrics.p95ResponseTime,
|
|
281
|
+
metrics.p99ResponseTime,
|
|
282
|
+
metrics.errorRate,
|
|
283
|
+
JSON.stringify(bottlenecks),
|
|
284
|
+
JSON.stringify(recommendations),
|
|
285
|
+
now,
|
|
286
|
+
);
|
|
287
|
+
|
|
288
|
+
run.status = RUN_STATUS.COMPLETE;
|
|
289
|
+
run.completedAt = now;
|
|
290
|
+
_runs.set(testId, run);
|
|
291
|
+
|
|
292
|
+
db.prepare(
|
|
293
|
+
`UPDATE stress_test_runs SET status = ?, completed_at = ? WHERE test_id = ?`,
|
|
294
|
+
).run(RUN_STATUS.COMPLETE, now, testId);
|
|
295
|
+
|
|
296
|
+
return { ...run, result };
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
export function stopStressTest(testId) {
|
|
300
|
+
const run = _runs.get(testId);
|
|
301
|
+
if (!run) throw new Error(`Stress test not found: ${testId}`);
|
|
302
|
+
if (run.status === RUN_STATUS.RUNNING) {
|
|
303
|
+
run.status = RUN_STATUS.STOPPED;
|
|
304
|
+
run.completedAt = Date.now();
|
|
305
|
+
}
|
|
306
|
+
return { ...run };
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
export function getTestResults(testId) {
|
|
310
|
+
const run = _runs.get(testId);
|
|
311
|
+
if (!run) throw new Error(`Stress test not found: ${testId}`);
|
|
312
|
+
const result = _results.get(testId) || null;
|
|
313
|
+
return { ...run, result };
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
export function listTestHistory(filter = {}) {
|
|
317
|
+
let runs = [..._runs.values()];
|
|
318
|
+
if (filter.level) runs = runs.filter((r) => r.loadLevel === filter.level);
|
|
319
|
+
if (filter.status) runs = runs.filter((r) => r.status === filter.status);
|
|
320
|
+
runs.sort((a, b) => b.startedAt - a.startedAt || b._seq - a._seq);
|
|
321
|
+
const limit = filter.limit || 10;
|
|
322
|
+
return runs.slice(0, limit).map((r) => {
|
|
323
|
+
const { _seq: _omit, ...rest } = r;
|
|
324
|
+
void _omit;
|
|
325
|
+
return rest;
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
export function analyzeBottlenecks(testId) {
|
|
330
|
+
const run = _runs.get(testId);
|
|
331
|
+
if (!run) throw new Error(`Stress test not found: ${testId}`);
|
|
332
|
+
const result = _results.get(testId);
|
|
333
|
+
if (!result) {
|
|
334
|
+
return { testId, bottlenecks: [], summary: "No results recorded" };
|
|
335
|
+
}
|
|
336
|
+
return {
|
|
337
|
+
testId,
|
|
338
|
+
bottlenecks: result.bottlenecks,
|
|
339
|
+
summary: result.bottlenecks.length
|
|
340
|
+
? `${result.bottlenecks.length} bottleneck(s) detected`
|
|
341
|
+
: "No bottlenecks detected",
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
export function generateCapacityPlan(testId) {
|
|
346
|
+
const run = _runs.get(testId);
|
|
347
|
+
if (!run) throw new Error(`Stress test not found: ${testId}`);
|
|
348
|
+
const result = _results.get(testId);
|
|
349
|
+
if (!result) {
|
|
350
|
+
return { testId, recommendations: [], scale: 1 };
|
|
351
|
+
}
|
|
352
|
+
const targetRps = run.requestsPerSecond;
|
|
353
|
+
const realizedTps = result.tps || 0;
|
|
354
|
+
const scale = realizedTps > 0 ? Math.max(1, targetRps / realizedTps) : 1;
|
|
355
|
+
return {
|
|
356
|
+
testId,
|
|
357
|
+
loadLevel: run.loadLevel,
|
|
358
|
+
targetRps,
|
|
359
|
+
realizedTps,
|
|
360
|
+
recommendations: result.capacityRecommendations,
|
|
361
|
+
scale: Number(scale.toFixed(2)),
|
|
362
|
+
headroom:
|
|
363
|
+
realizedTps >= targetRps
|
|
364
|
+
? "sufficient"
|
|
365
|
+
: realizedTps >= targetRps * 0.85
|
|
366
|
+
? "marginal"
|
|
367
|
+
: "insufficient",
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function _hashSeed(testId) {
|
|
372
|
+
let h = 0;
|
|
373
|
+
for (let i = 0; i < testId.length; i++) {
|
|
374
|
+
h = (h * 31 + testId.charCodeAt(i)) >>> 0;
|
|
375
|
+
}
|
|
376
|
+
return (h % 100000) / 100000;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
export function _resetState() {
|
|
380
|
+
_runs.clear();
|
|
381
|
+
_results.clear();
|
|
382
|
+
_seq = 0;
|
|
383
|
+
}
|