JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 2
  • Score
    100M100P100Q24122F
  • License MIT

Core QNCE (Quantum Narrative Convergence Engine) - Framework agnostic narrative engine with performance optimization

Package Exports

  • qnce-engine
  • qnce-engine/dist/index.js

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (qnce-engine) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

QNCE Engine

Quantum Narrative Convergence Engine - A framework-agnostic TypeScript library for creating interactive narrative experiences with quantum-inspired mechanics.

๐Ÿš€ Latest v1.4.0 (Adaptive Performance & Quantum Primitives): Adaptive perf pipeline (dynamic batch sizing, backoff, guarded retry, smoothed p95), expanded telemetry metrics, structured logging, memory optimizations, and first experimental quantum primitives (Entangler, Phase, Measurement). Previous 1.3.x delivered import & persistence foundations.

npm version npm downloads TypeScript License: MIT

New: Lightweight, privacy-safe telemetry (opt-in). CLI now supports --telemetry and --telemetry-report, with p50/p95 batch send latency. See the wiki: Analytics & Telemetry.

TL;DR Quickstart

# Install
npm install qnce-engine

# Play the demo story (from this repo)
node examples/quickstart-demo.js

# Live performance monitor (global CLI)
npm install -g qnce-engine
qnce-perf dashboard
qnce-perf stream 1000 | jq '.'   # NDJSON streaming (optional)

New to coding? Start here:

  • Beginner Guide (no code): wiki/Beginner-Guide.md
  • Getting Started: wiki/Getting-Started.md

Documentation & Guides

  • Getting Started (wiki)
  • Analytics & Telemetry (wiki)
  • Branching Guide (wiki)
  • Performance Tuning (wiki)
  • Error Handling & Debug (docs/ERROR-HANDLING-AND-DEBUG.md)
  • Adapter Lifecycle (docs/ADAPTER-LIFECYCLE.md)
  • Deprecation Policy (docs/DEPRECATION-POLICY.md)

Public Roadmap

High-level, non-sensitive roadmap: docs/PUBLIC_ROADMAP.md. Internal sprint artifacts are intentionally excluded from the repository for security/privacy.

Core Concepts

  • Superposition: Multiple narrative outcomes exist simultaneously until a choice is made
  • Collapse: Player choices "collapse" the narrative to a specific path, updating state and flags
  • Entanglement: Early decisions affect later outcomes, enabling complex, interconnected stories

New to coding? See the no-code Beginner Guide to create and play a story from the terminal.

โœจ Current Features (v1.3.0)

๐Ÿงช Experimental (Opt-in)

Early quantum helpers are available behind feature flags. They are off by default and safe to ignore. See the wiki for details and examples:

๐Ÿ’พ State Persistence & Checkpoints

  • Complete save/load system with data integrity validation
  • Lightweight checkpoints for undo/redo functionality
  • Automatic state serialization with metadata and versioning
  • Migration support for upgrading saved states between versions

๐ŸŒฟ Advanced Branching System

  • Multi-path narratives with conditional logic and flag-based branching
  • AI-driven content generation for dynamic story expansion
  • Real-time branch insertion/removal for live content updates
  • Comprehensive analytics for narrative optimization

๐Ÿ”„ Autosave & Undo/Redo System

  • Intelligent state tracking with automatic snapshots on key events
  • Sub-millisecond undo/redo operations with configurable history depth
  • Autosave throttling prevents excessive saves during rapid changes
  • Memory efficient with automatic cleanup and history limits

๐ŸŽฏ Conditional Choice Display

  • Flag-based choice filtering with complex logical expressions
  • Custom validation rules for advanced choice availability
  • Real-time choice updates based on dynamic game state
  • Intuitive condition syntax supporting multiple data types

๐Ÿ–ฅ๏ธ UI Integration & React Components

  • Ready-to-use React components for common UI patterns
  • Keyboard shortcuts with customizable bindings
  • Accessibility features with ARIA support and screen reader compatibility
  • Theming system with customizable appearance

โšก Enterprise Performance

  • Object pooling reduces memory allocations by 90%+
  • Background processing for non-blocking operations
  • Hot-reload capabilities with <3.5ms story updates
  • Comprehensive monitoring with built-in CLI tools
  • Adaptive perf pipeline (beta) with dynamic batch sizing & backoff

Basic Flag-Based Conditions

import { createQNCEEngine } from 'qnce-engine';

const storyData = {
  currentNodeId: 'town-square',
  nodes: {
    'town-square': {
      id: 'town-square',
      text: 'You stand in the bustling town square.',
      choices: [
        {
          id: 'enter-tavern',
          text: 'Enter the tavern',
          nextNodeId: 'tavern-inside'
        },
        {
          id: 'approach-guard',
          text: 'Approach the suspicious guard',
          nextNodeId: 'guard-encounter',
          condition: 'flags.curiosity >= 3'  // Only show if curious enough
        },
        {
          id: 'use-disguise',
          text: 'Use your disguise to blend in',
          nextNodeId: 'disguised-approach',
          condition: 'flags.hasDisguise && !flags.disguiseUsed'
        }
      ]
    }
  },
  flags: {}
};

const engine = createQNCEEngine(storyData);

// Set flags to test conditional choices
engine.setFlag('curiosity', 4);
engine.setFlag('hasDisguise', true);

// Get available choices - only those meeting conditions
const choices = engine.getAvailableChoices();
console.log(`Available choices: ${choices.length}`); // Shows 3 choices

// Change flags to see different options
engine.setFlag('curiosity', 1); // Below threshold
const reducedChoices = engine.getAvailableChoices();
console.log(`Available choices: ${reducedChoices.length}`); // Shows 2 choices

Complex Conditional Expressions

const complexStory = {
  currentNodeId: 'critical-moment',
  nodes: {
    'critical-moment': {
      id: 'critical-moment',
      text: 'The fate of the kingdom hangs in the balance.',
      choices: [
        {
          id: 'diplomatic-solution',
          text: 'Attempt diplomatic negotiation',
          nextNodeId: 'peaceful-resolution',
          condition: 'flags.charisma >= 5 && flags.hasAlliance'
        },
        {
          id: 'magical-intervention',
          text: 'Cast the ancient spell',
          nextNodeId: 'magical-outcome',
          condition: 'flags.magicPower > 3 && flags.spellComponents >= 2 && !flags.cursed'
        },
        {
          id: 'time-limited-escape',
          text: 'Escape through the secret passage',
          id: 'sacrifice-play',
          text: 'Make the ultimate sacrifice',
          nextNodeId: 'heroic-end',
          condition: '(flags.loyalty >= 8 || flags.desperate) && flags.hasArtifact'
        }
      ]
    }
  },
  flags: {}
};

Custom Condition Evaluators

For advanced scenarios, you can provide custom logic for condition evaluation:

// Set up custom evaluator for complex game logic
engine.setConditionEvaluator((expression, context) => {
  // Custom logic for special conditions
  if (expression === 'canUseSpecialAbility') {
    return context.flags.level >= 10 && 
           context.flags.mana > 50 && 
           !context.flags.abilityOnCooldown;
  }
  
  if (expression.startsWith('inventory:')) {
    const itemName = expression.replace('inventory:', '');
    return context.flags[`has_${itemName}`] === true;
  }
  
  // Fall back to default expression evaluation
  return null;
});

// Use custom conditions in story
const storyWithCustom = {
  currentNodeId: 'boss-fight',
  nodes: {
    'boss-fight': {
      id: 'boss-fight',
      text: 'The dragon roars menacingly.',
      choices: [
        {
          id: 'special-attack',
          text: 'Use your special ability',
          nextNodeId: 'special-victory',
          condition: 'canUseSpecialAbility'
        },
        {
          id: 'use-potion',
          text: 'Drink healing potion',
          nextNodeId: 'healed-state',
          condition: 'inventory:healing_potion'
        }
      ]
    }
  }
};

Condition Validation & Debugging

// Validate conditions during development
try {
  engine.validateCondition('flags.strength >= 5 && flags.weapon');
  console.log('Condition is valid');
} catch (error) {
  console.error('Invalid condition:', error.message);
}

// Get flags referenced in a condition for debugging
const referencedFlags = engine.getConditionFlags('flags.curiosity >= 3 && flags.hasKey');
console.log('Referenced flags:', referencedFlags); // ['curiosity', 'hasKey']

// Clear custom evaluator
engine.clearConditionEvaluator();

Performance Considerations

  • Expression Caching: Conditions are compiled once and cached for subsequent evaluations
  • Safe Evaluation: All expressions are sanitized to prevent code injection
  • Minimal Overhead: Choice filtering adds <1ms to getAvailableChoices() calls

Examples

  • Optโ€‘in quantum helpers (FeatureFlags + attachQuantumFeatures): examples/quantum-integration-demo.ts
  • Error Isolation: Invalid conditions don't affect other choices in the same node

โšก Performance Infrastructure

  • ๐ŸŠโ€โ™‚๏ธ Object Pooling: 90%+ allocation reduction, eliminating GC pressure
  • ๐Ÿงต Background Processing: Non-blocking cache preloading and telemetry writes
  • ๐Ÿ”ฅ Hot-Reload: <3.5ms live story updates with delta patching
  • ๐Ÿ“Š Real-time Profiling: Comprehensive event instrumentation and analysis
  • ๐Ÿ–ฅ๏ธ Live Monitoring: qnce-perf CLI dashboard with performance alerts
  • ๐Ÿง  Adaptive Flush (Beta): Dynamic telemetry/perf batch sizing + rejection backoff for smoother throughput

Performance Dashboard

# Real-time performance monitoring
qnce-perf dashboard

# Live monitoring with updates
qnce-perf live 1000

# Export performance data
qnce-perf export > performance-report.json

# Stream NDJSON (metrics+thread stats per line)
qnce-perf stream 1000 | jq '.'

๐Ÿ“š Complete Performance Guide โ†’

Adaptive Performance (Beta)

The PerfReporter now implements adaptive heuristics to optimize background flush behavior.

Key components:

  1. Exponential Backoff (R2)
  • Triggered after consecutive flush dispatch rejections (e.g. thread pool queue full)
  • Delay doubles each rejection (base 20ms, cap 500ms)
  • Prevents tight retry loops and reduces queue pressure
  1. Dynamic Batch Sizing (R6)
  • Starts from a configured base batch size
  • Upscales when: backlog is large AND p95 send latency remains low (<25ms or <50ms tiers)
    • Low latency + backlog > 2ร— base โ‡’ scale toward up to ~3ร— base
    • Low latency + backlog > 4ร— base โ‡’ scale toward up to ~4ร— base (ceiling)
  • Downscales aggressively when rejection streak โ‰ฅ2 (divides batch by streak factor, never below 10)
  • Exposed via lastEffectiveBatchSize in flush metrics snapshot
  1. Rejection Rate Metric
  • rejectionRate (0โ€“1) = rejectedFlushes / (accepted + rejected)
  • Fast signal for sustained downstream pressure; enables dashboard or future auto-tuning hooks
  1. Debug Accessor
  • __getInternalPerfDebug() includes: consecutiveRejects, backoffDelayMs, nextAllowedFlushTime, lastEffectiveBatchSize
  1. Environment Flag
  • Set QNCE_SUPPRESS_PERF_WARN=1 to silence repetitive flush failure warnings (e.g. in CI)
  • Set QNCE_DISABLE_ADAPTIVE_BATCH=1 to force fixed batchSize (disables dynamic scaling but keeps backoff)

Status & Stability:

  • Marked @beta and subject to heuristic tuning (thresholds & scaling formula may evolve)
  • Safe to consume metrics (rejectionRate, lastEffectiveBatchSize, adaptiveEnabled) for observability, not yet for strict alert SLAs

Example (inspection inside tests / diagnostics):

import { getPerfReporter } from 'qnce-engine/performance';

const reporter = getPerfReporter();
const snapshot = reporter.getFlushMetrics();
console.log(snapshot.rejectionRate, snapshot.lastEffectiveBatchSize, snapshot.adaptiveEnabled);

// Internal (beta) debug details
// @ts-expect-error internal accessor
console.log(reporter.__getInternalPerfDebug());

// Force fixed batch sizing (example)
// (Re-create reporter before use to apply config/env values)
// const fixed = getPerfReporter({ batchSize: 100, disableAdaptiveBatch: true });
// Snapshot field `adaptiveEnabled` will be false when dynamic sizing disabled.

If you experiment with tuning, please open an issue with: base batch size, average backlog, p95 latency trajectory, and observed rejectionRate.

Installation

npm install qnce-engine

# Global CLI installation for performance monitoring
npm install -g qnce-engine

Quick Start

Basic Usage

import { createQNCEEngine, DEMO_STORY } from 'qnce-engine';

// Create engine instance with demo story
const engine = createQNCEEngine(DEMO_STORY);

// Get current narrative state
const currentNode = engine.getCurrentNode();
console.log(currentNode.text);

// Get available choices
const choices = engine.getAvailableChoices();
console.log(choices);

// Make a choice
if (choices.length > 0) {
  engine.selectChoice(choices[0]);
}

// Check narrative flags
const flags = engine.getFlags();
console.log('Current flags:', flags);
import { createQNCEEngine, DEMO_STORY } from 'qnce-engine';

// Enable performance optimizations
const engine = createQNCEEngine(DEMO_STORY, {}, true, {
  maxWorkers: 4,
  enableProfiling: true
});

// Background cache preloading happens automatically
// Object pooling reduces memory allocations by 90%+
// Performance events are collected for monitoring

// Get performance statistics
const poolStats = engine.getPoolStats();
console.log(`Pool efficiency: ${poolStats.flow.hitRate}%`);

๐Ÿ’พ State Persistence & Checkpoints

QNCE Engine provides robust state persistence and checkpointing, allowing you to save and load the complete narrative state. This is useful for implementing save games, undo/redo functionality, and scenario replay.

Key Features:

  • Full State Serialization: Save and load the entire engine state, including narrative position, flags, history, and branching context.
  • Lightweight Checkpoints: Create fast, in-memory snapshots for undo operations or temporary state saves.
  • Data Integrity: Optional checksum verification ensures that saved data is not corrupted.
  • Cross-Version Compatibility: A migration system helps upgrade older save states to the latest version.

Example Usage:

import { createQNCEEngine, DEMO_STORY } from 'qnce-engine';

const engine = createQNCEEngine(DEMO_STORY);

// ...progress through the story...
const choices = engine.getAvailableChoices();
if (choices.length > 0) {
  engine.selectChoice(choices[0]);
}


// Save the current state to a JSON string
const savedState = await engine.saveState();
console.log('State saved!');

// ...later, or in a new session...

// Create a new engine instance
const newEngine = createQNCEEngine(DEMO_STORY);

// Load the state
await newEngine.loadState(JSON.parse(savedState));

console.log('State loaded successfully!');
console.log('Current Node:', newEngine.getCurrentNode().text);

// Create a lightweight checkpoint
const checkpoint = await engine.createCheckpoint('Before a risky choice');

// ...make a choice...

// Restore to the checkpoint
await engine.restoreFromCheckpoint(checkpoint.id);
console.log('Restored to checkpoint:', engine.getCurrentNode().text);

๐Ÿ”„ Autosave & Undo/Redo System

QNCE Engine v1.2.2 introduces an advanced autosave and undo/redo system that automatically tracks state changes and provides instant rollback capabilities with sub-millisecond performance.

Key Features:

  • Automatic State Tracking: Intelligently captures state snapshots on key events (choice selection, flag changes, state loading)
  • High-Performance Undo/Redo: Sub-millisecond undo/redo operations with configurable history depth
  • Autosave Throttling: Configurable throttling prevents excessive saves during rapid state changes
  • Memory Efficient: Capped history with automatic cleanup of older entries

Basic Usage:

import { createQNCEEngine, DEMO_STORY } from 'qnce-engine';

const engine = createQNCEEngine(DEMO_STORY);

// Autosave is enabled by default and will track state changes automatically
console.log('Can undo:', engine.canUndo()); // false initially

// Make some choices (autosave will track each change)
const choices = engine.getAvailableChoices();
engine.selectChoice(choices[0]);

console.log('Can undo:', engine.canUndo()); // true after making a choice

// Undo the last action
const undoResult = engine.undo();
if (undoResult.success) {
  console.log('Undid:', undoResult.description);
  console.log('Can redo:', engine.canRedo()); // true
}

// Redo the undone action
const redoResult = engine.redo();
if (redoResult.success) {
  console.log('Redid:', redoResult.description);
}

// Get history summary
const history = engine.getHistorySummary();
console.log(`History: ${history.undoCount} undo, ${history.redoCount} redo entries`);

Advanced Configuration:

// Configure undo/redo system
engine.configureUndoRedo({
  maxUndoEntries: 100,    // Maximum undo operations to remember
  maxRedoEntries: 50,     // Maximum redo operations to remember
  enabled: true           // Enable/disable undo/redo tracking
});

// Configure autosave behavior
engine.configureAutosave({
  enabled: true,          // Enable/disable autosave
  throttleMs: 100,        // Minimum time between saves (milliseconds)
  events: ['choice', 'flag', 'load'] // Which events trigger autosave
});

// Manual autosave trigger
await engine.autosave();

// Clear all undo/redo history
engine.clearHistory();

Performance Guarantees:

  • Undo operations: <1ms for normal state
  • Redo operations: <1ms for normal state
  • Autosave overhead: <1ms per operation
  • Memory efficient with configurable history limits

Live Performance Monitoring

# Real-time performance dashboard
qnce-perf dashboard

# Live monitoring with updates every 2 seconds  
qnce-perf live

# Export performance data
qnce-perf export > performance-report.json

๐ŸŒฟ Advanced Branching & AI Integration

Basic Branching

import { createQNCEEngine, createBranchingEngine } from 'qnce-engine';

// Create core engine
const engine = createQNCEEngine(storyData);

// Enable advanced branching
const branchingEngine = engine.enableBranching(advancedStoryData);

// Evaluate available branches
const branches = await branchingEngine.evaluateAvailableBranches();
console.log(`Available paths: ${branches.length}`);

// Execute a narrative branch
await branchingEngine.executeBranch(branches[0].id);

AI-Driven Content Generation

// Set AI context for personalized content
branchingEngine.setAIContext({
  playerProfile: {
    playStyle: 'explorer',
    preferences: { adventure: 0.8, mystery: 0.6 },
    historicalChoices: ['brave-path', 'investigate-clue']
  },
  narrativeContext: {
    currentTone: 'mysterious',
    thematicElements: ['exploration', 'discovery'],
    plotTension: 0.7
  }
});

// Generate AI-enhanced branches
const aiBranches = await branchingEngine.generateAIBranches(3);
console.log('AI-generated options:', aiBranches.map(b => b.displayText));

Dynamic Content Management

// Insert new branch at runtime
const dynamicBranch = {
  type: 'insert',
  branchId: 'special-event',
  targetLocation: { chapterId: 'main', nodeId: 'crossroads' },
  payload: {
    name: 'Special Event',
    branchOptions: [{
      id: 'event-choice',
      displayText: 'Investigate the mysterious sound',
      flagEffects: { event_discovered: true }
    }]
  }
};

await branchingEngine.insertDynamicBranch(dynamicBranch);

// Remove branch when no longer needed
await branchingEngine.removeDynamicBranch('special-event');

Analytics & Monitoring

// Get branching analytics
const analytics = branchingEngine.getBranchingAnalytics();
console.log(`Branches traversed: ${analytics.totalBranchesTraversed}`);
console.log(`Popular choices: ${analytics.mostPopularBranches}`);

// Export comprehensive data
const exportData = branchingEngine.exportBranchingData();
// Contains: story structure, session data, player behavior, performance metrics

Live Performance Monitoring

# Real-time performance dashboard
qnce-perf dashboard

# Live monitoring with updates every 2 seconds  
qnce-perf live

# Export performance data
qnce-perf export > performance-report.json

๐Ÿš€ Performance Guide

QNCE v1.2.0-sprint2 includes advanced performance infrastructure for production applications.

Performance Benchmarks

Feature Performance Gain Impact
Object Pooling 90%+ allocation reduction Eliminates GC hitches
Hot-Reload 68% improvement (3.35ms) Near-instant story updates
Background Processing Non-blocking operations Smooth user experience
Performance Monitoring Real-time metrics Production visibility

CLI Performance Dashboard

# Install CLI globally
npm install -g qnce-engine

# Real-time performance monitoring
qnce-perf live

# Performance dashboard output:
๐Ÿš€ QNCE Performance Dashboard
=====================================
๐Ÿ“Š Session Duration: 45.2s
๐Ÿ”ข Total Events: 1,247

๐Ÿ’พ Cache Performance:
   โœ… Hit Rate: 92.3% (threshold: 80%)
   โœ… Avg Cache Time: 0.8ms (threshold: 50ms)

๐Ÿ”ฅ Hot-Reload Performance:
   โš ๏ธ Avg Time: 3.35ms (threshold: 2ms)
   ๐Ÿ“Š Max Time: 4.1ms
   ๐Ÿ”„ Total Reloads: 12

๐Ÿงต ThreadPool Status:
   ๐Ÿ“Š Completed Jobs: 445
   โณ Queued Jobs: 3
   ๐Ÿƒ Active Workers: 2

Performance Mode Usage

// Enable all performance optimizations
const engine = createQNCEEngine(storyData, {}, true, {
  maxWorkers: 4,           // Background processing
  enableProfiling: true    // Performance monitoring
});

// Object pooling and background caching happen automatically
// Monitor performance in real-time with CLI dashboard

๐Ÿ“– Complete Performance Guide: docs/PERFORMANCE.md

Core API

QNCEEngine Class

The main engine class for managing narrative state.

Methods

  • getCurrentNode(): Get the current narrative node
  • goToNodeById(nodeId): Navigate directly to a node by its ID
  • getState(): Get the complete engine state
  • getFlags(): Get current narrative flags
  • getHistory(): Get choice history
  • selectChoice(choice): Make a narrative choice
  • resetNarrative(): Reset to initial state
  • loadState(state): Load a saved state
  • checkFlag(name, value?): Check flag conditions
  • getAvailableChoices(): Get filtered available choices

Factory Functions

  • createQNCEEngine(storyData, initialState?): Create a new engine instance
  • loadStoryData(jsonData): Load and validate story data from JSON

Story Format

Stories are defined using JSON with the following structure:

{
  "initialNodeId": "start",
  "nodes": [
    {
      "id": "start",
      "text": "You stand at a crossroads...",
      "choices": [
        {
          "text": "Go left",
          "nextNodeId": "left_path",
          "flagEffects": { "direction": "left" }
        }
      ]
    }
  ]
}

CLI Tools

qnce-import (new in v1.3.0)

Normalize external story formats into QNCE StoryData with schema + semantic validation.

Supported formats:

  • Custom JSON: strict/lenient validation with JSON Schema
  • Twison/Twine JSON: passages, links, robust start detection; tags mapped to node.meta.tags
  • Ink JSON: minimal mapping (developer preview). Use --experimental-ink for extended best-effort mapping

Usage examples:

# Autodetect format and write normalized JSON to stdout
qnce-import path/to/story.json > story.normalized.json

# Force a format and fail on schema/semantic issues
qnce-import --format twison --strict input.json -o normalized.json

# Add an ID prefix when merging multiple sources
qnce-import --id-prefix libA_ a.json > a.norm.json

# Read from stdin, write to file
cat story.json | qnce-import --format custom -o out.json

Exit codes: 0 success, 1 validation failure, 2 unexpected error.

qnce-audit

Validate your story structure:

qnce-audit story.json

Features:

  • Checks for missing node references
  • Identifies unreachable nodes
  • Finds dead ends
  • Validates story structure

qnce-init

Scaffold a new QNCE project:

qnce-init my-story

Creates:

  • Basic story template
  • package.json with QNCE dependencies
  • README with usage instructions

qnce-play

Interactive narrative sessions with full undo/redo support:

qnce-play story.json

Features:

  • Real-time narrative playthrough
  • Instant undo/redo with u and r commands
  • State inspection and debugging
  • Performance monitoring
  • Session save/load functionality
  • Persistence backends via --storage (memory | local | session | file | indexeddb)
  • Non-interactive runs for scripting/CI with JSON summary output

Examples:

# Basic interactive play
qnce-play story.json

# Use file storage (directory configurable)
qnce-play story.json --storage file --storage-dir .qnce --save-key session1

# Resume a saved session
qnce-play story.json --storage file --storage-dir .qnce --load-key session1

# Scriptable non-interactive run (emits a JSON summary)
qnce-play story.json --non-interactive --storage memory

qnce-perf

Current in v1.2.2: Real-time performance monitoring and analytics:

# Launch interactive performance dashboard
qnce-perf dashboard

# Live monitoring with custom update interval
qnce-perf live [interval-ms]

# Export performance data to JSON
qnce-perf export [--format json|csv] [--output filename]

# Single performance snapshot
qnce-perf snapshot story.json

Dashboard Features:

  • Real-time memory usage and allocation tracking
  • Object pool efficiency monitoring
  • Performance hotspot identification
  • Live story update timing analysis
  • Historical performance trend graphs

Live Monitoring:

# Monitor with 1-second updates
qnce-perf live 1000

# Default 500ms updates
qnce-perf live

Export Options:

# Export to JSON with full metrics
qnce-perf export --format json --output metrics.json

# Export to CSV for spreadsheet analysis
qnce-perf export --format csv --output performance.csv

# Stream to stdout
qnce-perf export

Integration Examples

React Hooks

QNCE provides comprehensive React hooks for seamless integration:

import { useQNCE, useUndoRedo, useAutosave } from 'qnce-engine/react';
import { DEMO_STORY } from 'qnce-engine';

function NarrativeComponent() {
  // Core narrative hook
  const { engine, currentNode, choices, flags, selectChoice, resetNarrative } = useQNCE(DEMO_STORY);
  
  // Undo/redo functionality
  const { 
    undo, 
    redo, 
    canUndo, 
    canRedo, 
    undoCount, 
    redoCount,
    clearHistory 
  } = useUndoRedo(engine);
  
  // Autosave status
  const { isAutosaveEnabled, lastAutosave } = useAutosave(engine);

  return (
    <div>
      <h2>Current Scene</h2>
      <p>{currentNode.text}</p>
      
      <h3>Choices</h3>
      {choices.map((choice, index) => (
        <button key={index} onClick={() => selectChoice(choice)}>
          {choice.text}
        </button>
      ))}
      
      <div className="controls">
        <button onClick={undo} disabled={!canUndo}>
          Undo ({undoCount})
        </button>
        <button onClick={redo} disabled={!canRedo}>
          Redo ({redoCount})
        </button>
        <button onClick={resetNarrative}>Reset</button>
        <button onClick={clearHistory}>Clear History</button>
      </div>
      
      <div className="status">
        <p>Autosave: {isAutosaveEnabled ? 'Enabled' : 'Disabled'}</p>
        {lastAutosave && <p>Last saved: {lastAutosave.toLocaleTimeString()}</p>}
      </div>
      
      <details>
        <summary>Debug Info</summary>
        <pre>{JSON.stringify(flags, null, 2)}</pre>
      </details>
    </div>
  );
}

React UI Components

QNCE provides pre-built React components for common UI patterns:

UndoRedoControls Component

A complete undo/redo control panel with accessibility features:

import { UndoRedoControls } from 'qnce-engine/ui';
import { createQNCEEngine } from 'qnce-engine';

function MyApp() {
  const engine = createQNCEEngine(DEMO_STORY);

  return (
    <div>
      {/* Narrative content */}
      
      {/* Undo/Redo Controls */}
      <UndoRedoControls 
        engine={engine}
        size="md"              // sm, md, lg
        layout="horizontal"    // horizontal, vertical  
        showLabels={true}      // Show text labels
        labels={{              // Custom labels
          undo: "Go Back",
          redo: "Go Forward"
        }}
        onUndo={(result) => console.log('Undo:', result)}
        onRedo={(result) => console.log('Redo:', result)}
        theme={{               // Custom theming
          colors: {
            primary: '#007bff',
            disabled: '#6c757d'
          },
          borderRadius: { md: '8px' }
        }}
      />
    </div>
  );
}

AutosaveIndicator Component

Visual indicator for autosave status with animations:

import { AutosaveIndicator } from 'qnce-engine/ui';

function MyApp() {
  const engine = createQNCEEngine(DEMO_STORY);

  return (
    <div>
      {/* Narrative content */}
      
      {/* Autosave Status */}
      <AutosaveIndicator 
        engine={engine}
        variant="detailed"     // minimal, detailed, icon-only
        position="top-right"   // inline, top-right, bottom-left, etc.
        showTimestamp={true}   // Show last save time
        autoHideDelay={3000}   // Auto-hide after 3 seconds
        messages={{            // Custom messages
          idle: "Ready to save",
          saving: "Saving...",
          saved: "All changes saved",
          error: "Save failed"
        }}
        theme={{               // Custom theming
          colors: {
            success: '#28a745',
            error: '#dc3545',
            warning: '#ffc107'
          }
        }}
      />
    </div>
  );
}

Keyboard Shortcuts

Built-in keyboard support with the useKeyboardShortcuts hook:

import { useKeyboardShortcuts } from 'qnce-engine/ui';

function MyApp() {
  const engine = createQNCEEngine(DEMO_STORY);
  
  // Enable keyboard shortcuts
  useKeyboardShortcuts(engine, {
    undo: ['ctrl+z', 'cmd+z'],      // Undo shortcuts
    redo: ['ctrl+y', 'cmd+shift+z'], // Redo shortcuts  
    save: ['ctrl+s', 'cmd+s'],       // Manual save
    disabled: false                  // Enable/disable all shortcuts
  });

  return (
    <div>
      {/* Your narrative UI */}
      <p>Press Ctrl+Z to undo, Ctrl+Y to redo, Ctrl+S to save</p>
    </div>
  );
}

Basic React Hook (Legacy)

import { createQNCEEngine } from 'qnce-engine';
import { useState, useCallback } from 'react';

function useQNCE(storyData) {
  const [engine] = useState(() => createQNCEEngine(storyData));
  const [currentNode, setCurrentNode] = useState(engine.getCurrentNode());
  const [flags, setFlags] = useState(engine.getFlags());

  const selectChoice = useCallback((choice) => {
    engine.selectChoice(choice);
    setCurrentNode(engine.getCurrentNode());
    setFlags(engine.getFlags());
  }, [engine]);

  const goToNodeById = useCallback((nodeId) => {
    engine.goToNodeById(nodeId);
    setCurrentNode(engine.getCurrentNode());
    setFlags(engine.getFlags());
  }, [engine]);

  return { currentNode, flags, selectChoice, goToNodeById };
}

Vue Composition API

import { createQNCEEngine } from 'qnce-engine';
import { ref, reactive } from 'vue';

export function useQNCE(storyData) {
  const engine = createQNCEEngine(storyData);
  const currentNode = ref(engine.getCurrentNode());
  const flags = reactive(engine.getFlags());

  const selectChoice = (choice) => {
    engine.selectChoice(choice);
    currentNode.value = engine.getCurrentNode();
    Object.assign(flags, engine.getFlags());
  };

  return { currentNode, flags, selectChoice };
}

Node.js CLI

import { createQNCEEngine, loadStoryData } from 'qnce-engine';
import { readFileSync } from 'fs';
import * as readline from 'readline';

const storyData = loadStoryData(JSON.parse(readFileSync('story.json', 'utf-8')));
const engine = createQNCEEngine(storyData);

async function playStory() {
  const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
  });

  while (true) {
    const node = engine.getCurrentNode();
    console.log('\n' + node.text);
    
    if (node.choices.length === 0) break;
    
    node.choices.forEach((choice, i) => {
      console.log(`${i + 1}. ${choice.text}`);
    });
    
    // Get user input and make choice...
  }
}

๐Ÿ“š Examples & Demos

The repository includes comprehensive examples demonstrating all features:

๐Ÿš€ Quickstart Example

  • File: examples/branching-quickstart.ts
  • Features: Basic branching, AI integration, dynamic operations
  • Run: npm run build && node dist/examples/branching-quickstart.js

๐ŸŽญ Advanced Demo

  • File: examples/branching-advanced-demo.ts
  • Features: Complex narrative flows, conditional branching, analytics
  • Story: "The Mysterious Library" - Interactive mystery with multiple paths

๐Ÿ’พ Autosave & Undo Demo (Available in v1.2.2)

  • File: examples/autosave-undo-demo.ts
  • Features: Autosave, undo/redo, performance monitoring, state management
  • Run: npm run demo:autosave
  • Performance: Demonstrates <1ms undo/redo with real-time metrics

๐Ÿงช Validation Scripts

  • Real-world testing: scripts/validation-real-world.ts
  • Comprehensive testing: scripts/validation-comprehensive.ts
# Run the quickstart example
npm run build
node dist/examples/branching-quickstart.js

# Run the new autosave demo
npm run demo:autosave

# Try the interactive CLI tool
npm run build
qnce-play examples/demo-story.json

# Run validation tests
npm run build
node dist/scripts/validation-real-world.js

Development

# Clone and setup
git clone https://github.com/ByteSower/qnce-engine.git
cd qnce-engine
npm install

# Build
npm run build

# Watch mode
npm run build:watch

# Lint
npm run lint

๐Ÿ”’ Security

QNCE Engine follows security best practices and provides guidance for secure usage:

  • Vulnerability Reporting: Please report security vulnerabilities privately via GitHub Security Advisories
  • Supported Versions: Security updates are provided for current stable versions (1.3.x) and previous major version (1.2.x)
  • Input Validation: Always validate story data and user inputs from untrusted sources
  • State Security: Use QNCE's built-in integrity validation when loading serialized states

For detailed security guidelines, see our Security Policy.

License

MIT - See LICENSE file for details.

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Add tests if applicable
  5. Submit a pull request

QNCE Engine - Empowering interactive narratives with quantum-inspired mechanics.

๐Ÿ›ก๏ธ Choice Validation System

Ensure only valid choices can be executed with comprehensive validation rules.

Basic Validation

import { createQNCEEngine } from 'qnce-engine';

const engine = createQNCEEngine(storyData);

try {
  // makeChoice() automatically validates before executing
  engine.makeChoice(0);
} catch (error) {
  if (error instanceof ChoiceValidationError) {
    console.error('Invalid choice:', error.message);
    console.log('Available choices:', error.availableChoices);
  }
}

Advanced Choice Requirements

Define complex validation rules on your choices:

// Choice with flag requirements
const flagBasedChoice = {
  text: 'Use the magic key',
  nextNodeId: 'unlock_door',
  flagRequirements: {
    hasKey: true,
    playerLevel: 5
  }
};

// Choice with inventory requirements
const inventoryChoice = {
  text: 'Buy expensive sword',
  nextNodeId: 'shop_success',
  inventoryRequirements: {
    gold: 1000,
    gems: 2
  }
};

// Time-based availability
const timedChoice = {
  text: 'Enter the tavern',
  nextNodeId: 'tavern',
  timeRequirements: {
    availableAfter: new Date('2025-01-01T18:00:00'),
    availableBefore: new Date('2025-01-01T24:00:00')
  }
};

// Disabled choice
const disabledChoice = {
  text: 'Broken bridge',
  nextNodeId: 'fall',
  enabled: false
};

Custom Validation Rules

Create your own validation logic:

import { 
  StandardValidationRules, 
  createChoiceValidator 
} from 'qnce-engine';

const validator = createChoiceValidator();

// Add custom rule
validator.addRule({
  name: 'custom-rule',
  priority: 10,
  validate: (choice, context) => {
    // Your custom logic
    if (choice.text.includes('danger') && !context.state.flags.brave) {
      return {
        isValid: false,
        reason: 'You must be brave to take this path!',
        failedConditions: ['requires-bravery']
      };
    }
    return { isValid: true };
  }
});

// Apply to engine
engine.setChoiceValidator(validator);

Validation Error Handling

import { 
  ChoiceValidationError, 
  isChoiceValidationError 
} from 'qnce-engine';

try {
  engine.makeChoice(2);
} catch (error) {
  if (isChoiceValidationError(error)) {
    // Get user-friendly message
    const message = error.getUserFriendlyMessage();
    console.log(message);
    
    // Get debug information
    const debugInfo = error.getDebugInfo();
    console.log('Failed conditions:', debugInfo.validationResult.failedConditions);
    
    // Show alternatives
    console.log('Available choices:');
    error.availableChoices?.forEach((choice, i) => {
      console.log(`${i + 1}. ${choice.text}`);
    });
  }
}
## ๐Ÿงช Experimental (Opt-in)

These are off by default and safe to import. Enable with `FeatureFlags` when ready to experiment.

```ts
import { attachQuantumFeatures, FeatureFlags, Phase } from 'qnce-engine';
const engine = createQNCEEngine(story);
const q = attachQuantumFeatures(engine, new FeatureFlags({ 'quantum.phases': true, 'quantum.entanglement': true }));
const alpha = new Phase('alpha', ({ flags }) => !!flags.unlockAlpha);
q.isPhaseActive(alpha);
q.entangle(e => e.bind('a', 'b', v => Number(v) * 2));
```

APIs: `FeatureFlags`, `Phase`, `Entangler`, `attachQuantumFeatures` (all `@experimental`).