qnce-engine 1.3.1 → 1.4.1
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 +136 -2
- package/dist/adapters/contracts.d.ts +1 -1
- package/dist/adapters/contracts.d.ts.map +1 -1
- package/dist/adapters/storage/MockAdapters.d.ts +89 -0
- package/dist/adapters/storage/MockAdapters.d.ts.map +1 -0
- package/dist/adapters/storage/MockAdapters.js +109 -0
- package/dist/adapters/storage/MockAdapters.js.map +1 -0
- package/dist/adapters/story/CustomJSONAdapter.d.ts +1 -1
- package/dist/adapters/story/CustomJSONAdapter.d.ts.map +1 -1
- package/dist/adapters/story/CustomJSONAdapter.js +20 -14
- package/dist/adapters/story/CustomJSONAdapter.js.map +1 -1
- package/dist/adapters/story/InkAdapter.d.ts +1 -1
- package/dist/adapters/story/InkAdapter.d.ts.map +1 -1
- package/dist/adapters/story/InkAdapter.js +7 -10
- package/dist/adapters/story/InkAdapter.js.map +1 -1
- package/dist/adapters/story/TwisonAdapter.d.ts +1 -1
- package/dist/adapters/story/TwisonAdapter.d.ts.map +1 -1
- package/dist/adapters/story/TwisonAdapter.js +2 -2
- package/dist/adapters/story/TwisonAdapter.js.map +1 -1
- package/dist/cli/audit.js +1 -0
- package/dist/cli/audit.js.map +1 -1
- package/dist/cli/import.d.ts.map +1 -1
- package/dist/cli/import.js +56 -20
- package/dist/cli/import.js.map +1 -1
- package/dist/cli/init.js +1 -0
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/perf.d.ts.map +1 -1
- package/dist/cli/perf.js +30 -0
- package/dist/cli/perf.js.map +1 -1
- package/dist/cli/play.d.ts.map +1 -1
- package/dist/cli/play.js +135 -60
- package/dist/cli/play.js.map +1 -1
- package/dist/engine/condition.d.ts +25 -3
- package/dist/engine/condition.d.ts.map +1 -1
- package/dist/engine/condition.js +358 -64
- package/dist/engine/condition.js.map +1 -1
- package/dist/engine/core.d.ts +109 -3
- package/dist/engine/core.d.ts.map +1 -1
- package/dist/engine/core.js +486 -36
- package/dist/engine/core.js.map +1 -1
- package/dist/engine/demo-story.d.ts +1 -0
- package/dist/engine/demo-story.d.ts.map +1 -1
- package/dist/engine/demo-story.js +1 -0
- package/dist/engine/demo-story.js.map +1 -1
- package/dist/engine/error-factory.d.ts +86 -0
- package/dist/engine/error-factory.d.ts.map +1 -0
- package/dist/engine/error-factory.js +87 -0
- package/dist/engine/error-factory.js.map +1 -0
- package/dist/engine/errors.d.ts +3 -0
- package/dist/engine/errors.d.ts.map +1 -1
- package/dist/engine/errors.js +3 -0
- package/dist/engine/errors.js.map +1 -1
- package/dist/engine/validation.js +1 -1
- package/dist/engine/validation.js.map +1 -1
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +34 -1
- package/dist/index.js.map +1 -1
- package/dist/integrations/react.d.ts +20 -3
- package/dist/integrations/react.d.ts.map +1 -1
- package/dist/integrations/react.js +15 -2
- package/dist/integrations/react.js.map +1 -1
- package/dist/narrative/branching/models.d.ts +1 -0
- package/dist/narrative/branching/models.d.ts.map +1 -1
- package/dist/narrative/branching/models.js.map +1 -1
- package/dist/performance/AdaptiveControllers.d.ts +51 -0
- package/dist/performance/AdaptiveControllers.d.ts.map +1 -0
- package/dist/performance/AdaptiveControllers.js +87 -0
- package/dist/performance/AdaptiveControllers.js.map +1 -0
- package/dist/performance/ContextPool.d.ts +1 -0
- package/dist/performance/ContextPool.d.ts.map +1 -0
- package/dist/performance/ContextPool.js +2 -0
- package/dist/performance/ContextPool.js.map +1 -0
- package/dist/performance/HotReloadDelta.d.ts +5 -0
- package/dist/performance/HotReloadDelta.d.ts.map +1 -1
- package/dist/performance/HotReloadDelta.js +18 -4
- package/dist/performance/HotReloadDelta.js.map +1 -1
- package/dist/performance/ObjectPool.d.ts.map +1 -1
- package/dist/performance/ObjectPool.js +4 -1
- package/dist/performance/ObjectPool.js.map +1 -1
- package/dist/performance/PerfReporter.d.ts +69 -0
- package/dist/performance/PerfReporter.d.ts.map +1 -1
- package/dist/performance/PerfReporter.js +314 -28
- package/dist/performance/PerfReporter.js.map +1 -1
- package/dist/performance/ThreadPool.d.ts +5 -0
- package/dist/performance/ThreadPool.d.ts.map +1 -1
- package/dist/performance/ThreadPool.js +33 -5
- package/dist/performance/ThreadPool.js.map +1 -1
- package/dist/persistence/StorageAdapters.d.ts.map +1 -1
- package/dist/persistence/StorageAdapters.js +13 -19
- package/dist/persistence/StorageAdapters.js.map +1 -1
- package/dist/qnce-engine.d.ts +2258 -0
- package/dist/quantum/entangler.d.ts +13 -0
- package/dist/quantum/entangler.d.ts.map +1 -0
- package/dist/quantum/entangler.js +24 -0
- package/dist/quantum/entangler.js.map +1 -0
- package/dist/quantum/flags.d.ts +24 -0
- package/dist/quantum/flags.d.ts.map +1 -0
- package/dist/quantum/flags.js +29 -0
- package/dist/quantum/flags.js.map +1 -0
- package/dist/quantum/integration.d.ts +26 -0
- package/dist/quantum/integration.d.ts.map +1 -0
- package/dist/quantum/integration.js +38 -0
- package/dist/quantum/integration.js.map +1 -0
- package/dist/quantum/measurement.d.ts +22 -0
- package/dist/quantum/measurement.d.ts.map +1 -0
- package/dist/quantum/measurement.js +44 -0
- package/dist/quantum/measurement.js.map +1 -0
- package/dist/quantum/phase.d.ts +20 -0
- package/dist/quantum/phase.d.ts.map +1 -0
- package/dist/quantum/phase.js +22 -0
- package/dist/quantum/phase.js.map +1 -0
- package/dist/quantum/types.d.ts +12 -0
- package/dist/quantum/types.d.ts.map +1 -0
- package/dist/quantum/types.js +5 -0
- package/dist/quantum/types.js.map +1 -0
- package/dist/schemas/validateStoryData.d.ts.map +1 -1
- package/dist/schemas/validateStoryData.js +8 -2
- package/dist/schemas/validateStoryData.js.map +1 -1
- package/dist/telemetry/core.d.ts +88 -0
- package/dist/telemetry/core.d.ts.map +1 -0
- package/dist/telemetry/core.js +303 -0
- package/dist/telemetry/core.js.map +1 -0
- package/dist/telemetry/types.d.ts +76 -0
- package/dist/telemetry/types.d.ts.map +1 -0
- package/dist/telemetry/types.js +4 -0
- package/dist/telemetry/types.js.map +1 -0
- package/dist/tsdoc-metadata.json +11 -0
- package/dist/ui/__tests__/AutosaveIndicator.test.js +10 -13
- package/dist/ui/__tests__/AutosaveIndicator.test.js.map +1 -1
- package/dist/ui/__tests__/UndoRedoControls.test.js +7 -9
- package/dist/ui/__tests__/UndoRedoControls.test.js.map +1 -1
- package/dist/ui/components/AutosaveIndicator.d.ts +1 -0
- package/dist/ui/components/AutosaveIndicator.d.ts.map +1 -1
- package/dist/ui/components/AutosaveIndicator.js +2 -1
- package/dist/ui/components/AutosaveIndicator.js.map +1 -1
- package/dist/ui/components/UndoRedoControls.d.ts +1 -0
- package/dist/ui/components/UndoRedoControls.d.ts.map +1 -1
- package/dist/ui/components/UndoRedoControls.js +1 -0
- package/dist/ui/components/UndoRedoControls.js.map +1 -1
- package/dist/ui/hooks/useKeyboardShortcuts.d.ts +1 -0
- package/dist/ui/hooks/useKeyboardShortcuts.d.ts.map +1 -1
- package/dist/ui/hooks/useKeyboardShortcuts.js +17 -5
- package/dist/ui/hooks/useKeyboardShortcuts.js.map +1 -1
- package/dist/ui/types.d.ts +6 -0
- package/dist/ui/types.d.ts.map +1 -1
- package/dist/ui/types.js +1 -0
- package/dist/ui/types.js.map +1 -1
- package/dist/utils/debug-logger.d.ts +20 -0
- package/dist/utils/debug-logger.d.ts.map +1 -0
- package/dist/utils/debug-logger.js +24 -0
- package/dist/utils/debug-logger.js.map +1 -0
- package/dist/utils/hot-profiler.d.ts +21 -0
- package/dist/utils/hot-profiler.d.ts.map +1 -0
- package/dist/utils/hot-profiler.js +36 -0
- package/dist/utils/hot-profiler.js.map +1 -0
- package/dist/utils/intern.d.ts +11 -0
- package/dist/utils/intern.d.ts.map +1 -0
- package/dist/utils/intern.js +45 -0
- package/dist/utils/intern.js.map +1 -0
- package/dist/utils/logger.d.ts +34 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +115 -0
- package/dist/utils/logger.js.map +1 -0
- package/docs/PERFORMANCE.md +330 -0
- package/examples/fluent-builder-prototype.ts +71 -0
- package/examples/quantum-integration-demo.ts +51 -0
- package/package.json +25 -9
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.formatLog = formatLog;
|
|
4
|
+
exports.createLogger = createLogger;
|
|
5
|
+
exports.deriveLogLevel = deriveLogLevel;
|
|
6
|
+
const LEVEL_ORDER = {
|
|
7
|
+
silent: 100, // suppress all
|
|
8
|
+
error: 50,
|
|
9
|
+
warn: 40,
|
|
10
|
+
info: 30,
|
|
11
|
+
success: 30, // treat success same visibility as info
|
|
12
|
+
debug: 10
|
|
13
|
+
};
|
|
14
|
+
class ConsoleSink {
|
|
15
|
+
useColor;
|
|
16
|
+
constructor(useColor) {
|
|
17
|
+
this.useColor = useColor;
|
|
18
|
+
}
|
|
19
|
+
write(entry) {
|
|
20
|
+
const { level, message } = entry;
|
|
21
|
+
const color = this.colorFor(level);
|
|
22
|
+
const prefix = this.prefixFor(level);
|
|
23
|
+
// Use process.stdout / stderr directly to avoid triggering no-console
|
|
24
|
+
const out = (level === 'error' ? process.stderr : process.stdout);
|
|
25
|
+
out.write(`${color}${prefix}${message}${this.useColor ? '\u001b[0m' : ''}\n`);
|
|
26
|
+
}
|
|
27
|
+
colorFor(level) {
|
|
28
|
+
if (!this.useColor)
|
|
29
|
+
return '';
|
|
30
|
+
switch (level) {
|
|
31
|
+
case 'error': return '\u001b[31m'; // red
|
|
32
|
+
case 'warn': return '\u001b[33m'; // yellow
|
|
33
|
+
case 'success': return '\u001b[32m'; // green
|
|
34
|
+
case 'debug': return '\u001b[90m'; // gray
|
|
35
|
+
default: return '\u001b[36m'; // cyan/info
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
prefixFor(level) {
|
|
39
|
+
switch (level) {
|
|
40
|
+
case 'error': return '❌ ';
|
|
41
|
+
case 'warn': return '⚠️ ';
|
|
42
|
+
case 'success': return '✅ ';
|
|
43
|
+
case 'debug': return '🔍 ';
|
|
44
|
+
default: return '';
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/** Serialize arbitrary values safely for log messages (avoids generic [object Object]). */
|
|
49
|
+
function formatLog(value, depth = 0, seen = new WeakSet()) {
|
|
50
|
+
if (typeof value === 'string')
|
|
51
|
+
return value;
|
|
52
|
+
if (value instanceof Error)
|
|
53
|
+
return value.stack || value.message;
|
|
54
|
+
const t = typeof value;
|
|
55
|
+
if (value == null || t === 'number' || t === 'boolean')
|
|
56
|
+
return String(value);
|
|
57
|
+
if (t === 'function')
|
|
58
|
+
return `[Function ${value.name || 'anonymous'}]`;
|
|
59
|
+
if (t === 'symbol')
|
|
60
|
+
return value.toString();
|
|
61
|
+
if (Array.isArray(value)) {
|
|
62
|
+
if (depth > 2)
|
|
63
|
+
return `[Array(${value.length})]`;
|
|
64
|
+
return '[' + value.map(v => formatLog(v, depth + 1, seen)).join(', ') + ']';
|
|
65
|
+
}
|
|
66
|
+
if (t === 'object') {
|
|
67
|
+
if (seen.has(value))
|
|
68
|
+
return '[Circular]';
|
|
69
|
+
seen.add(value);
|
|
70
|
+
if (depth > 2)
|
|
71
|
+
return '{…}';
|
|
72
|
+
const entries = Object.entries(value)
|
|
73
|
+
.slice(0, 25)
|
|
74
|
+
.map(([k, v]) => `${k}: ${formatLog(v, depth + 1, seen)}`);
|
|
75
|
+
const truncated = Object.keys(value).length > 25 ? ' …' : '';
|
|
76
|
+
return '{ ' + entries.join(', ') + truncated + ' }';
|
|
77
|
+
}
|
|
78
|
+
try {
|
|
79
|
+
return JSON.stringify(value);
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return String(value);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function createLogger(opts = {}) {
|
|
86
|
+
const sink = opts.sink || new ConsoleSink(opts.useColor ?? process.stdout.isTTY);
|
|
87
|
+
let level = opts.level || 'info';
|
|
88
|
+
const basePrefix = '';
|
|
89
|
+
function emit(l, msg) {
|
|
90
|
+
// lower numeric = more verbose; allow if candidate order >= current level order
|
|
91
|
+
if (LEVEL_ORDER[l] < LEVEL_ORDER[level])
|
|
92
|
+
return; // require candidate >= threshold
|
|
93
|
+
sink.write({ level: l, message: basePrefix + formatLog(msg), ts: Date.now() });
|
|
94
|
+
}
|
|
95
|
+
const logger = {
|
|
96
|
+
get level() { return level; },
|
|
97
|
+
setLevel(lvl) { level = lvl; },
|
|
98
|
+
error(msg) { emit('error', msg); },
|
|
99
|
+
warn(msg) { emit('warn', msg); },
|
|
100
|
+
info(msg) { emit('info', msg); },
|
|
101
|
+
success(msg) { emit('success', msg); },
|
|
102
|
+
debug(msg) { emit('debug', msg); },
|
|
103
|
+
child() { return createLogger({ level, sink, useColor: (opts.useColor ?? process.stdout.isTTY) }); }
|
|
104
|
+
};
|
|
105
|
+
return logger;
|
|
106
|
+
}
|
|
107
|
+
/** Helper to derive level from flags */
|
|
108
|
+
function deriveLogLevel(flags) {
|
|
109
|
+
if (flags.quiet)
|
|
110
|
+
return 'warn';
|
|
111
|
+
if (flags.verbose)
|
|
112
|
+
return 'debug';
|
|
113
|
+
return 'info';
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"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"}
|
package/docs/PERFORMANCE.md
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
**Version:** 1.2.0-sprint2
|
|
4
4
|
**Sprint #2:** Core Performance Refactor Complete
|
|
5
5
|
|
|
6
|
+
> Documentation Consolidation: This file now consolidates all performance content. The previous `PERFORMANCE_GUIDE.md` has been merged and will be removed to prevent drift. All future edits should target this canonical document.
|
|
7
|
+
|
|
6
8
|
## 🚀 Overview
|
|
7
9
|
|
|
8
10
|
The QNCE engine now includes comprehensive performance optimization infrastructure delivering:
|
|
@@ -124,6 +126,18 @@ const spanId = perf.flowStart('my-flow', { context: 'gameplay' });
|
|
|
124
126
|
perf.flowComplete(spanId, 'target-node', { result: 'success' });
|
|
125
127
|
```
|
|
126
128
|
|
|
129
|
+
### 5. Performance Events (Unified Reference)
|
|
130
|
+
|
|
131
|
+
Events captured by the performance subsystem (names kept stable for dashboard & telemetry correlations):
|
|
132
|
+
|
|
133
|
+
- `flow-start` / `flow-complete` – Narrative flow spans
|
|
134
|
+
- `cache-hit` / `cache-miss` – Node retrieval / caching
|
|
135
|
+
- `hot-reload-start` / `hot-reload-end` – Live story delta application
|
|
136
|
+
- `state-transition` – Engine internal state changes
|
|
137
|
+
- `custom` – User defined events via `perf.record()`
|
|
138
|
+
|
|
139
|
+
These events funnel into the PerfReporter buffers and are aggregated for summary snapshots and flush heuristics (adaptive batching / backoff).
|
|
140
|
+
|
|
127
141
|
## 🖥️ CLI Performance Dashboard
|
|
128
142
|
|
|
129
143
|
Real-time monitoring and performance analysis via `qnce-perf` command.
|
|
@@ -179,6 +193,12 @@ qnce-perf live 1000 # Update every 1000ms
|
|
|
179
193
|
qnce-perf export > performance-report.json
|
|
180
194
|
```
|
|
181
195
|
|
|
196
|
+
#### NDJSON Streaming
|
|
197
|
+
```bash
|
|
198
|
+
# One JSON object per line with { summary, flush, thread }
|
|
199
|
+
qnce-perf stream 1000 | jq '.'
|
|
200
|
+
```
|
|
201
|
+
|
|
182
202
|
#### Reset Counters
|
|
183
203
|
```bash
|
|
184
204
|
qnce-perf reset
|
|
@@ -350,6 +370,316 @@ if (summary.cacheHitRate < 0.8) {
|
|
|
350
370
|
}
|
|
351
371
|
```
|
|
352
372
|
|
|
373
|
+
### 6. Adaptive Flush & Dynamic Batch Sizing (R2/R6 - Beta)
|
|
374
|
+
|
|
375
|
+
The performance pipeline includes adaptive heuristics for telemetry/perf flush operations.
|
|
376
|
+
|
|
377
|
+
| Feature | Purpose | Heuristic (Initial) | Status |
|
|
378
|
+
|---------|---------|---------------------|--------|
|
|
379
|
+
| Exponential Backoff (R2) | Avoid tight retry loops after dispatch rejections | Base 20ms * 2^streak (cap 500ms) | Stable |
|
|
380
|
+
| Dynamic Batch Sizing (R6) | Scale throughput under healthy latency; shrink under pressure | Upscale on backlog (>2×/4× base) with p95 <25ms / <50ms; shrink on streak ≥2 | Beta |
|
|
381
|
+
| Rejection Rate | Health signal for downstream pressure | rejected / (accepted + rejected) | Beta |
|
|
382
|
+
| Effective Batch Size Export | Observability of sizing decisions | `lastEffectiveBatchSize` | Beta |
|
|
383
|
+
| Disable Adaptive Batch Flag | Force fixed batch size (diagnostics/baseline) | `disableAdaptiveBatch` config or env `QNCE_DISABLE_ADAPTIVE_BATCH=1` | Beta |
|
|
384
|
+
| Adaptive Enabled Snapshot Flag | Observability: whether dynamic sizing active | `adaptiveEnabled` boolean in flush metrics snapshot | Beta |
|
|
385
|
+
|
|
386
|
+
Metric Extensions:
|
|
387
|
+
```ts
|
|
388
|
+
interface PerfFlushMetrics {
|
|
389
|
+
rejectionRate: number; // 0..1 ratio
|
|
390
|
+
lastEffectiveBatchSize: number; // adaptive batch size used
|
|
391
|
+
adaptiveEnabled: boolean; // true when dynamic sizing heuristics active
|
|
392
|
+
}
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
Simplified Sizing Logic:
|
|
396
|
+
```ts
|
|
397
|
+
if (consecutiveRejects >= 2) shrink();
|
|
398
|
+
else if (backlog > base*4 && p95 < 50) upscale(≈4x cap);
|
|
399
|
+
else if (backlog > base*2 && p95 < 25) upscale(≈3x cap);
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
Usage:
|
|
403
|
+
```ts
|
|
404
|
+
import { getPerfReporter } from 'qnce-engine/performance';
|
|
405
|
+
const reporter = getPerfReporter();
|
|
406
|
+
const m = reporter.getFlushMetrics();
|
|
407
|
+
console.log(m.rejectionRate, m.lastEffectiveBatchSize);
|
|
408
|
+
// Determine if adaptive sizing was active for this snapshot
|
|
409
|
+
console.log('Adaptive active:', m.adaptiveEnabled);
|
|
410
|
+
// Force fixed batch sizing for a deterministic baseline
|
|
411
|
+
// const fixed = getPerfReporter({ batchSize: 100, disableAdaptiveBatch: true });
|
|
412
|
+
// m.adaptiveEnabled will be false when forced fixed sizing is in effect.
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
Suppress warning noise in CI:
|
|
416
|
+
```bash
|
|
417
|
+
export QNCE_SUPPRESS_PERF_WARN=1
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
Internal debug (subject to change):
|
|
421
|
+
```ts
|
|
422
|
+
// @ts-expect-error internal
|
|
423
|
+
console.log(reporter.__getInternalPerfDebug());
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
Beta Caveats:
|
|
427
|
+
- Thresholds & scaling factors may evolve
|
|
428
|
+
- Single-sample p95 (no smoothing yet)
|
|
429
|
+
- Provide feedback with backlog, latency, rejection patterns
|
|
430
|
+
|
|
431
|
+
## 🧪 Performance Modes
|
|
432
|
+
|
|
433
|
+
Three operational modes patterns frequently referenced during tuning and benchmarking:
|
|
434
|
+
|
|
435
|
+
### Standard Mode (Default)
|
|
436
|
+
```ts
|
|
437
|
+
const engine = createQNCEEngine(storyData);
|
|
438
|
+
// Suitable for most dev scenarios; minimal overhead.
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
### Performance Mode
|
|
442
|
+
```ts
|
|
443
|
+
const engine = createQNCEEngine(storyData, {}, true);
|
|
444
|
+
// Enables: object pooling, ThreadPool, perf events.
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
### Performance Mode + Custom ThreadPool
|
|
448
|
+
```ts
|
|
449
|
+
const engine = createQNCEEngine(storyData, {}, true, {
|
|
450
|
+
maxWorkers: 4,
|
|
451
|
+
queueLimit: 200,
|
|
452
|
+
enableProfiling: true
|
|
453
|
+
});
|
|
454
|
+
// Fine‑tuned background execution & profiling.
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
## 🎯 Performance Targets & Benchmarks
|
|
458
|
+
|
|
459
|
+
Current sprint targets alongside representative observations (non-binding; provide directional validation):
|
|
460
|
+
|
|
461
|
+
| Metric | Target | Current | Status |
|
|
462
|
+
|--------|--------|---------|--------|
|
|
463
|
+
| Object Pool Hit Rate | >95% | 100% | ✅ |
|
|
464
|
+
| Memory Allocation Reduction | >90% | >90% | ✅ |
|
|
465
|
+
| Hot-Reload Frame Stall | <2ms | 3.35ms | 🔄 68% improvement |
|
|
466
|
+
| State Transition Time | <5ms | <1ms | ✅ |
|
|
467
|
+
| Cache Hit Rate | >95% | >92% | ✅ |
|
|
468
|
+
| ThreadPool Processing | Non-blocking | ✅ | ✅ |
|
|
469
|
+
|
|
470
|
+
### Benchmark Snapshots
|
|
471
|
+
|
|
472
|
+
Object Pooling:
|
|
473
|
+
```
|
|
474
|
+
Standard Mode: ~1000 allocations / 100 transitions
|
|
475
|
+
Performance Mode: ~100 allocations / 100 transitions (≈90% reduction)
|
|
476
|
+
GC Pressure: Eliminated for pooled classes
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
Hot-Reload (Story ~50 nodes):
|
|
480
|
+
```
|
|
481
|
+
Comparison: 0.14ms
|
|
482
|
+
Patch: 3.35ms
|
|
483
|
+
Total: 3.49ms (target <2ms still under optimization)
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
ThreadPool:
|
|
487
|
+
```
|
|
488
|
+
Throughput: 1000+ jobs/sec (synthetic)
|
|
489
|
+
Scheduling: Priority aware
|
|
490
|
+
Environment: Browser + Node compatible
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
## 🚀 Production Deployment Quick Start
|
|
494
|
+
|
|
495
|
+
### Installation
|
|
496
|
+
```bash
|
|
497
|
+
npm install qnce-engine
|
|
498
|
+
npm install -g qnce-engine # Optional CLI dashboard
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
### Example Production Init
|
|
502
|
+
```ts
|
|
503
|
+
const engine = createQNCEEngine(storyData, {}, true, {
|
|
504
|
+
maxWorkers: 4,
|
|
505
|
+
queueLimit: 500,
|
|
506
|
+
enableProfiling: true
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
// Periodic metrics export (example)
|
|
510
|
+
setInterval(() => {
|
|
511
|
+
const summary = perf.summary();
|
|
512
|
+
monitoringService.sendMetrics({
|
|
513
|
+
cacheHitRate: summary.cacheHitRate,
|
|
514
|
+
hotReloadAvg: summary.hotReloadPerformance.avgTime,
|
|
515
|
+
workerUtilization: engine.getThreadPoolStats().workerUtilization
|
|
516
|
+
});
|
|
517
|
+
}, 30000);
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
## 🔧 Performance Tuning Recipes
|
|
521
|
+
|
|
522
|
+
High Throughput:
|
|
523
|
+
```ts
|
|
524
|
+
createQNCEEngine(storyData, {}, true, { maxWorkers: 8, queueLimit: 1000, enableProfiling: false });
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
Development / Diagnostics:
|
|
528
|
+
```ts
|
|
529
|
+
createQNCEEngine(storyData, {}, true, { maxWorkers: 2, queueLimit: 50, enableProfiling: true });
|
|
530
|
+
// Use: qnce-perf live 500
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
## 🩺 Troubleshooting (Extended)
|
|
534
|
+
|
|
535
|
+
Memory Pressure:
|
|
536
|
+
1. Inspect pool stats: `engine.getPoolStats()`
|
|
537
|
+
2. Confirm perf mode enabled
|
|
538
|
+
3. Audit custom handlers for retained references
|
|
539
|
+
|
|
540
|
+
Slow Hot-Reload:
|
|
541
|
+
1. `qnce-perf dashboard` → check hot-reload section
|
|
542
|
+
2. Split very large story diffs into batches
|
|
543
|
+
3. Validate minimal unrelated node churn
|
|
544
|
+
|
|
545
|
+
Queue Overflows:
|
|
546
|
+
1. Inspect `engine.getThreadPoolStats().queuedJobs`
|
|
547
|
+
2. Increase `maxWorkers` / `queueLimit`
|
|
548
|
+
3. Reduce low-priority background submissions
|
|
549
|
+
|
|
550
|
+
## 🔀 Migration Guide (v0.1.0 → v1.2.0-sprint2)
|
|
551
|
+
|
|
552
|
+
No breaking API changes. Enable performance mode explicitly to opt into advanced systems:
|
|
553
|
+
```ts
|
|
554
|
+
// Before
|
|
555
|
+
const engine = createQNCEEngine(storyData);
|
|
556
|
+
// After
|
|
557
|
+
const engine = createQNCEEngine(storyData, {}, true);
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
CLI Adoption:
|
|
561
|
+
```bash
|
|
562
|
+
npm install -g qnce-engine@1.2.0-sprint2
|
|
563
|
+
qnce-perf live
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
## ✅ Sprint Summary
|
|
567
|
+
|
|
568
|
+
Sprint #2 Deliverables Achieved:
|
|
569
|
+
- 90%+ allocation reduction via pooling
|
|
570
|
+
- Background ThreadPool processing
|
|
571
|
+
- Hot-reload improvement (~68%)
|
|
572
|
+
- Comprehensive profiling & adaptive flush pipeline (beta heuristics)
|
|
573
|
+
- CLI dashboard for real-time insights
|
|
574
|
+
|
|
575
|
+
The engine is production-ready with evolving adaptive heuristics (provide feedback on backlog/latency/rejection traces for tuning).
|
|
576
|
+
|
|
577
|
+
|
|
578
|
+
## 🔄 Phase 2 (Wave 1) Additions (@beta)
|
|
579
|
+
|
|
580
|
+
Version: 1.4.0-beta.0 introduces incremental reliability & observability upgrades to the flush pipeline.
|
|
581
|
+
|
|
582
|
+
### 1. Retry-Once Dispatch
|
|
583
|
+
On the first rejected batch dispatch the reporter attempts exactly one immediate retry when:
|
|
584
|
+
* `consecutiveRejects === 1`
|
|
585
|
+
* `p95DispatchLatencyMs < 200`
|
|
586
|
+
|
|
587
|
+
This reduces transient loss without causing uncontrolled retry storms (subsequent failures rely solely on exponential backoff).
|
|
588
|
+
|
|
589
|
+
### 2. Smoothed p95 (EMA)
|
|
590
|
+
Metric: `smoothedP95DispatchLatencyMs` provides an exponential moving average (alpha=0.2) of raw p95 latency, reducing volatility for future adaptive controls.
|
|
591
|
+
|
|
592
|
+
Properties:
|
|
593
|
+
* Always trends toward raw p95
|
|
594
|
+
* Resets to 0 with no samples
|
|
595
|
+
* Lightweight (constant space, O(1) update)
|
|
596
|
+
|
|
597
|
+
### 3. Extended Flush Metrics
|
|
598
|
+
`PerfFlushMetrics` now exposes:
|
|
599
|
+
```ts
|
|
600
|
+
interface PerfFlushMetrics {
|
|
601
|
+
backoffActive?: boolean; // true if inside current backoff window
|
|
602
|
+
consecutiveRejects?: number; // current rejection streak count
|
|
603
|
+
smoothedP95DispatchLatencyMs?: number; // EMA-smoothed p95 latency
|
|
604
|
+
backoffDelayMs?: number; // current backoff duration (ms)
|
|
605
|
+
rejectedFlushesSinceLastSuccess?: number; // rolling failures since last success
|
|
606
|
+
}
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
Example:
|
|
610
|
+
```ts
|
|
611
|
+
const m = getPerfReporter().getFlushMetrics();
|
|
612
|
+
console.log({
|
|
613
|
+
p95: m.p95DispatchLatencyMs,
|
|
614
|
+
smoothed: m.smoothedP95DispatchLatencyMs,
|
|
615
|
+
backoffActive: m.backoffActive,
|
|
616
|
+
consecutiveRejects: m.consecutiveRejects,
|
|
617
|
+
rejectionRate: m.rejectionRate
|
|
618
|
+
});
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
### 4. Adaptive Sampling Scaffold
|
|
622
|
+
Disabled by default. Enable with:
|
|
623
|
+
```bash
|
|
624
|
+
export QNCE_ADAPTIVE_SAMPLING=1
|
|
625
|
+
```
|
|
626
|
+
Current prototype uses an EMA of events/sec to interpolate a sample rate between configured low/high watermarks. All events recorded when disabled.
|
|
627
|
+
|
|
628
|
+
### 5. Thread Pool Override (Test Only)
|
|
629
|
+
For deterministic retry testing (non-public, unstable):
|
|
630
|
+
```ts
|
|
631
|
+
// @ts-expect-error internal
|
|
632
|
+
PerfReporter.__setThreadPoolOverride({ writeTelemetry: () => Promise.resolve() });
|
|
633
|
+
```
|
|
634
|
+
Never rely on this in production; it may be removed without notice.
|
|
635
|
+
|
|
636
|
+
### 6. Persistence Micro-Latency Threshold Update
|
|
637
|
+
Save/load/checkpoint tests now assert <10ms (previous <2ms proved brittle after added instrumentation). Still enforces a tight micro-budget while avoiding CI flakiness.
|
|
638
|
+
|
|
639
|
+
### 7. Environment Flags Summary
|
|
640
|
+
| Flag | Purpose | Default |
|
|
641
|
+
|------|---------|---------|
|
|
642
|
+
| `QNCE_SUPPRESS_PERF_WARN` | Suppress console warnings for rejected flushes | Off |
|
|
643
|
+
| `QNCE_DISABLE_ADAPTIVE_BATCH` | Force fixed batch size (disable dynamic sizing) | Off |
|
|
644
|
+
| `QNCE_ADAPTIVE_SAMPLING` | Enable beta sampling scaffold | Off |
|
|
645
|
+
|
|
646
|
+
Config controls (code):
|
|
647
|
+
```ts
|
|
648
|
+
// Adjust smoothing strength for smoothed p95 (0.01..1.0)
|
|
649
|
+
getPerfReporter({ smoothingAlpha: 0.3 });
|
|
650
|
+
```
|
|
651
|
+
|
|
652
|
+
### 8. Planned Follow-Ups
|
|
653
|
+
* Configurable smoothing alpha & decay windows
|
|
654
|
+
* Structured retry outcome telemetry (success vs loss metrics)
|
|
655
|
+
* Critical event class exemption list for sampling
|
|
656
|
+
* Percentile window rotation + reservoir sampling
|
|
657
|
+
|
|
658
|
+
Please include `p95DispatchLatencyMs`, `smoothedP95DispatchLatencyMs`, `rejectionRate`, and `consecutiveRejects` snapshots when filing performance issues.
|
|
659
|
+
|
|
660
|
+
|
|
353
661
|
---
|
|
354
662
|
|
|
355
663
|
**Ready for Production:** QNCE v1.2.0-sprint2 delivers comprehensive performance optimization with real-time monitoring capabilities. All systems tested and production-ready! 🚀
|
|
664
|
+
|
|
665
|
+
<!-- PROFILING-SNAPSHOT:START -->
|
|
666
|
+
|
|
667
|
+
|
|
668
|
+
### Profiling Snapshot (Automated)
|
|
669
|
+
|
|
670
|
+
Generated: 2025-09-10T18:17:48.505Z
|
|
671
|
+
|
|
672
|
+
Condition evaluation benchmark cold vs warm cache latency (milliseconds). Targets: keep warm p50 < 0.002 ms for simple expressions.
|
|
673
|
+
|
|
674
|
+
|
|
675
|
+
| Expr | Cold p50 | Cold p95 | Warm p50 | Warm p95 |
|
|
676
|
+
|------|---------:|---------:|---------:|---------:|
|
|
677
|
+
|
|
678
|
+
|
|
679
|
+
| flags.score > 10 && flags.lives >= 2 | 0.0018 | 0.0022 | 0.0011 | 0.0012 |
|
|
680
|
+
| flags.mode === "hard" || flags.debug | 0.0012 | 0.0018 | 0.0006 | 0.0006 |
|
|
681
|
+
| flags.a && flags.b && flags.c && flags.d | 0.0011 | 0.0013 | 0.0005 | 0.0005 |
|
|
682
|
+
| flags.combo > 5 && (flags.streak >= 3… | 0.0012 | 0.0013 | 0.0004 | 0.0005 |
|
|
683
|
+
|
|
684
|
+
|
|
685
|
+
<!-- PROFILING-SNAPSHOT:END -->
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// Prototype: Fluent narrative builder to explore ergonomics
|
|
2
|
+
// Not part of the public API; for design validation only.
|
|
3
|
+
|
|
4
|
+
type Predicate<T> = (ctx: T) => boolean;
|
|
5
|
+
|
|
6
|
+
interface BuilderContext {
|
|
7
|
+
flags: Record<string, unknown>;
|
|
8
|
+
nodeId?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
class Phase {
|
|
12
|
+
constructor(public readonly name: string, public when?: Predicate<BuilderContext>) {}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
class Entangler<TKeys extends string = string> {
|
|
16
|
+
private bindings: Array<{ from: TKeys; to: TKeys; transform?: (v: any) => any }>= [];
|
|
17
|
+
bind(from: TKeys, to: TKeys, transform?: (v: any) => any) {
|
|
18
|
+
this.bindings.push({ from, to, transform });
|
|
19
|
+
return this;
|
|
20
|
+
}
|
|
21
|
+
apply(flags: Record<string, unknown>) {
|
|
22
|
+
for (const b of this.bindings) {
|
|
23
|
+
const v = flags[b.from];
|
|
24
|
+
flags[b.to] = b.transform ? b.transform(v) : v;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
class NarrativeBuilder {
|
|
30
|
+
private phases: Phase[] = [];
|
|
31
|
+
private entangler = new Entangler<string>();
|
|
32
|
+
|
|
33
|
+
phase(name: string, when?: Predicate<BuilderContext>) {
|
|
34
|
+
this.phases.push(new Phase(name, when));
|
|
35
|
+
return this;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
entangle(from: string, to: string, transform?: (v: any) => any) {
|
|
39
|
+
this.entangler.bind(from, to, transform);
|
|
40
|
+
return this;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
measure(cb: (ctx: BuilderContext) => void) {
|
|
44
|
+
// noop placeholder for prototype
|
|
45
|
+
return this;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
run(initial: BuilderContext) {
|
|
49
|
+
const ctx: BuilderContext = { flags: { ...initial.flags }, nodeId: initial.nodeId };
|
|
50
|
+
// apply entanglement first
|
|
51
|
+
this.entangler.apply(ctx.flags);
|
|
52
|
+
// evaluate phases
|
|
53
|
+
const active = this.phases.filter(p => !p.when || p.when(ctx)).map(p => p.name);
|
|
54
|
+
return { ctx, activePhases: active };
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Tiny smoke test for the prototype
|
|
59
|
+
if (require.main === module) {
|
|
60
|
+
const builder = new NarrativeBuilder()
|
|
61
|
+
.phase('dreaming', ({ flags }) => !!flags["asleep"]) // superposition-like conditional phase
|
|
62
|
+
.phase('awake', ({ flags }) => !flags["asleep"]) // mutually exclusive for demo
|
|
63
|
+
.entangle('mood', 'narrationTone', v => (v === 'calm' ? 'soft' : 'tense'))
|
|
64
|
+
.measure(() => {});
|
|
65
|
+
|
|
66
|
+
const result = builder.run({ flags: { asleep: true, mood: 'calm' } });
|
|
67
|
+
console.log('Active phases:', result.activePhases);
|
|
68
|
+
console.log('Flags:', result.ctx.flags);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export { NarrativeBuilder, Entangler, Phase };
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// Example: Opt-in quantum helpers (FeatureFlags + attachQuantumFeatures)
|
|
2
|
+
// This demo is safe to run in Node or a browser bundler.
|
|
3
|
+
|
|
4
|
+
import { createQNCEEngine, type StoryData } from '../src/engine/core';
|
|
5
|
+
import { FeatureFlags } from '../src/quantum/flags';
|
|
6
|
+
import { Phase } from '../src/quantum/phase';
|
|
7
|
+
import { attachQuantumFeatures } from '../src/quantum/integration';
|
|
8
|
+
|
|
9
|
+
// Minimal story
|
|
10
|
+
const story: StoryData = {
|
|
11
|
+
initialNodeId: 'start',
|
|
12
|
+
nodes: [
|
|
13
|
+
{
|
|
14
|
+
id: 'start',
|
|
15
|
+
text: 'You stand at a fork in the road.',
|
|
16
|
+
choices: [
|
|
17
|
+
{ text: 'Take the left path', nextNodeId: 'left', flagEffects: { courage: 1 } },
|
|
18
|
+
{ text: 'Take the right path', nextNodeId: 'right', flagEffects: { wisdom: 1 } }
|
|
19
|
+
]
|
|
20
|
+
},
|
|
21
|
+
{ id: 'left', text: 'The left path is quiet and calm.', choices: [] },
|
|
22
|
+
{ id: 'right', text: 'The right path is lively and bright.', choices: [] }
|
|
23
|
+
]
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const engine = createQNCEEngine(story);
|
|
27
|
+
|
|
28
|
+
// Start with phases enabled, entanglement disabled
|
|
29
|
+
const flags = new FeatureFlags({ 'quantum.phases': true, 'quantum.entanglement': false });
|
|
30
|
+
const q = attachQuantumFeatures(engine, flags);
|
|
31
|
+
|
|
32
|
+
// Define a phase that activates when a flag is present
|
|
33
|
+
const bravePhase = new Phase('brave', ({ flags }) => Boolean(flags.courage));
|
|
34
|
+
|
|
35
|
+
console.log('Phase active before choice?', q.isPhaseActive(bravePhase)); // false
|
|
36
|
+
|
|
37
|
+
// Make a choice that sets courage
|
|
38
|
+
engine.makeChoice(0); // left path sets courage: 1
|
|
39
|
+
|
|
40
|
+
console.log('Phase active after choice?', q.isPhaseActive(bravePhase)); // true when quantum.phases enabled
|
|
41
|
+
|
|
42
|
+
// Toggle entanglement on and bind flags
|
|
43
|
+
q.flags.enable('quantum.entanglement');
|
|
44
|
+
q.entangle((e) => e.bind('courage', 'bravery'));
|
|
45
|
+
|
|
46
|
+
// After entanglement, downstream systems could read `bravery` too
|
|
47
|
+
const allFlags = engine.getFlags();
|
|
48
|
+
console.log('All flags (post-entangle):', allFlags);
|
|
49
|
+
|
|
50
|
+
// Detach when done (optional cleanup)
|
|
51
|
+
q.detach();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "qnce-engine",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.1",
|
|
4
4
|
"description": "Core QNCE (Quantum Narrative Convergence Engine) - Framework agnostic narrative engine with performance optimization",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -13,18 +13,31 @@
|
|
|
13
13
|
},
|
|
14
14
|
"scripts": {
|
|
15
15
|
"build": "tsc",
|
|
16
|
+
"typecheck": "tsc --noEmit",
|
|
16
17
|
"build:watch": "tsc --watch",
|
|
17
18
|
"build:examples": "tsc --outDir dist/examples --target ES2022 --module CommonJS --moduleResolution Node --strict --esModuleInterop --skipLibCheck --declaration false examples/persistence-demo.ts examples/autosave-undo-demo.ts",
|
|
18
19
|
"lint": "eslint .",
|
|
19
|
-
"test": "
|
|
20
|
-
"test:watch": "
|
|
21
|
-
"test:coverage": "
|
|
20
|
+
"test": "jest",
|
|
21
|
+
"test:watch": "jest --watch",
|
|
22
|
+
"test:coverage": "jest --coverage",
|
|
22
23
|
"test:performance": "node scripts/performance-tests.js",
|
|
23
24
|
"demo:performance": "npm run build && node scripts/performance-demo.js",
|
|
24
25
|
"demo:quickstart": "npm run build && node examples/quickstart-demo.js",
|
|
25
26
|
"demo:persistence": "npm run build:examples && node dist/examples/examples/persistence-demo.js",
|
|
26
27
|
"demo:autosave": "npm run build:examples && node dist/examples/examples/autosave-undo-demo.js",
|
|
27
|
-
"
|
|
28
|
+
"demo:fluent": "npx -y ts-node --compiler-options '{\"module\":\"commonjs\"}' examples/fluent-builder-prototype.ts",
|
|
29
|
+
"perf:micro": "ts-node scripts/perf-micro.ts",
|
|
30
|
+
"perf:init-load": "ts-node scripts/benchmark-init-load.ts",
|
|
31
|
+
"perf:conditions": "ts-node scripts/perf-conditions.ts",
|
|
32
|
+
"dx:api-report": "npm run build && api-extractor run --local --verbose",
|
|
33
|
+
"dx:api-warnings": "npm run build && api-extractor run --local --verbose | grep -i 'ae-' || true",
|
|
34
|
+
"docs:typedoc": "typedoc",
|
|
35
|
+
"prepublishOnly": "npm run build && npm run test",
|
|
36
|
+
"dx:api-inventory": "node scripts/api-inventory.mjs",
|
|
37
|
+
"docs:sync": "ts-node scripts/sync-docs.ts",
|
|
38
|
+
"docs:perf-snapshot": "ts-node scripts/update-performance-doc.ts",
|
|
39
|
+
"check:sensitive": "node scripts/check-sensitive-files.mjs",
|
|
40
|
+
"audit:licenses": "node scripts/license-audit.mjs"
|
|
28
41
|
},
|
|
29
42
|
"keywords": [
|
|
30
43
|
"narrative",
|
|
@@ -38,13 +51,14 @@
|
|
|
38
51
|
"license": "MIT",
|
|
39
52
|
"repository": {
|
|
40
53
|
"type": "git",
|
|
41
|
-
"url": "https://github.com/ByteSower/qnce-engine.git"
|
|
54
|
+
"url": "git+https://github.com/ByteSower/qnce-engine.git"
|
|
42
55
|
},
|
|
43
56
|
"homepage": "https://github.com/ByteSower/qnce-engine#readme",
|
|
44
57
|
"bugs": {
|
|
45
58
|
"url": "https://github.com/ByteSower/qnce-engine/issues"
|
|
46
59
|
},
|
|
47
60
|
"devDependencies": {
|
|
61
|
+
"@microsoft/api-extractor": "^7.47.0",
|
|
48
62
|
"@testing-library/dom": "^10.4.0",
|
|
49
63
|
"@testing-library/jest-dom": "^6.6.3",
|
|
50
64
|
"@testing-library/react": "^15.0.7",
|
|
@@ -52,8 +66,8 @@
|
|
|
52
66
|
"@types/jest": "^29.5.14",
|
|
53
67
|
"@types/node": "^20.0.0",
|
|
54
68
|
"@types/react": "^18.0.0",
|
|
55
|
-
"@typescript-eslint/eslint-plugin": "^
|
|
56
|
-
"@typescript-eslint/parser": "^
|
|
69
|
+
"@typescript-eslint/eslint-plugin": "^8.56.1",
|
|
70
|
+
"@typescript-eslint/parser": "^8.56.1",
|
|
57
71
|
"eslint": "^8.57.1",
|
|
58
72
|
"eslint-plugin-react": "^7.33.0",
|
|
59
73
|
"eslint-plugin-react-hooks": "^4.6.0",
|
|
@@ -62,7 +76,9 @@
|
|
|
62
76
|
"react": "^18.0.0",
|
|
63
77
|
"react-dom": "^18.3.1",
|
|
64
78
|
"ts-jest": "^29.4.0",
|
|
65
|
-
"
|
|
79
|
+
"ts-node": "^10.9.2",
|
|
80
|
+
"typedoc": "^0.26.10",
|
|
81
|
+
"typescript": "~5.6.0"
|
|
66
82
|
},
|
|
67
83
|
"dependencies": {
|
|
68
84
|
"ajv": "^8.12.0"
|