qnce-engine 1.2.0 → 1.2.2
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 +713 -7
- package/dist/cli/audit.js +0 -0
- package/dist/cli/init.js +0 -0
- package/dist/cli/perf.d.ts.map +1 -1
- package/dist/cli/perf.js +2 -1
- package/dist/cli/perf.js.map +1 -1
- package/dist/cli/play.d.ts +4 -0
- package/dist/cli/play.d.ts.map +1 -0
- package/dist/cli/play.js +259 -0
- package/dist/cli/play.js.map +1 -0
- package/dist/engine/condition.d.ts +69 -0
- package/dist/engine/condition.d.ts.map +1 -0
- package/dist/engine/condition.js +195 -0
- package/dist/engine/condition.js.map +1 -0
- package/dist/engine/core.d.ts +274 -3
- package/dist/engine/core.d.ts.map +1 -1
- package/dist/engine/core.js +1148 -9
- package/dist/engine/core.js.map +1 -1
- package/dist/engine/demo-story.d.ts.map +1 -1
- package/dist/engine/demo-story.js +99 -13
- package/dist/engine/demo-story.js.map +1 -1
- package/dist/engine/errors.d.ts +76 -0
- package/dist/engine/errors.d.ts.map +1 -0
- package/dist/engine/errors.js +178 -0
- package/dist/engine/errors.js.map +1 -0
- package/dist/engine/types.d.ts +445 -0
- package/dist/engine/types.d.ts.map +1 -0
- package/dist/engine/types.js +9 -0
- package/dist/engine/types.js.map +1 -0
- package/dist/engine/validation.d.ts +110 -0
- package/dist/engine/validation.d.ts.map +1 -0
- package/dist/engine/validation.js +261 -0
- package/dist/engine/validation.js.map +1 -0
- package/dist/examples/examples/autosave-undo-demo.js +248 -0
- package/dist/examples/examples/persistence-demo.js +63 -0
- package/dist/examples/src/engine/condition.js +194 -0
- package/dist/examples/src/engine/core.js +1382 -0
- package/dist/examples/src/engine/demo-story.js +200 -0
- package/dist/examples/src/engine/types.js +8 -0
- package/dist/examples/src/index.js +35 -0
- package/dist/examples/src/integrations/react.js +322 -0
- package/dist/examples/src/narrative/branching/engine-simple.js +348 -0
- package/dist/examples/src/narrative/branching/index.js +55 -0
- package/dist/examples/src/narrative/branching/models.js +5 -0
- package/dist/examples/src/performance/ObjectPool.js +296 -0
- package/dist/examples/src/performance/PerfReporter.js +280 -0
- package/dist/examples/src/performance/ThreadPool.js +347 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -1
- package/dist/index.js.map +1 -1
- package/dist/integrations/react.d.ts +200 -0
- package/dist/integrations/react.d.ts.map +1 -0
- package/dist/integrations/react.js +365 -0
- package/dist/integrations/react.js.map +1 -0
- package/dist/narrative/branching/engine-simple.js +3 -3
- package/dist/narrative/branching/engine-simple.js.map +1 -1
- package/dist/narrative/branching/engine.d.ts +1 -0
- package/dist/narrative/branching/engine.d.ts.map +1 -0
- package/dist/narrative/branching/engine.js +2 -0
- package/dist/narrative/branching/engine.js.map +1 -0
- package/dist/narrative/branching/models.d.ts.map +1 -1
- package/dist/performance/HotReloadDelta.d.ts +25 -8
- package/dist/performance/HotReloadDelta.d.ts.map +1 -1
- package/dist/performance/HotReloadDelta.js +10 -15
- package/dist/performance/HotReloadDelta.js.map +1 -1
- package/dist/ui/__tests__/AutosaveIndicator.test.d.ts +2 -0
- package/dist/ui/__tests__/AutosaveIndicator.test.d.ts.map +1 -0
- package/dist/ui/__tests__/AutosaveIndicator.test.js +329 -0
- package/dist/ui/__tests__/AutosaveIndicator.test.js.map +1 -0
- package/dist/ui/__tests__/UndoRedoControls.test.d.ts +2 -0
- package/dist/ui/__tests__/UndoRedoControls.test.d.ts.map +1 -0
- package/dist/ui/__tests__/UndoRedoControls.test.js +245 -0
- package/dist/ui/__tests__/UndoRedoControls.test.js.map +1 -0
- package/dist/ui/__tests__/autosave-simple.test.d.ts +2 -0
- package/dist/ui/__tests__/autosave-simple.test.d.ts.map +1 -0
- package/dist/ui/__tests__/autosave-simple.test.js +29 -0
- package/dist/ui/__tests__/autosave-simple.test.js.map +1 -0
- package/dist/ui/__tests__/setup.d.ts +2 -0
- package/dist/ui/__tests__/setup.d.ts.map +1 -0
- package/dist/ui/__tests__/setup.js +40 -0
- package/dist/ui/__tests__/setup.js.map +1 -0
- package/dist/ui/__tests__/smoke-test.d.ts +2 -0
- package/dist/ui/__tests__/smoke-test.d.ts.map +1 -0
- package/dist/ui/__tests__/smoke-test.js +18 -0
- package/dist/ui/__tests__/smoke-test.js.map +1 -0
- package/dist/ui/__tests__/smoke-test.test.d.ts +2 -0
- package/dist/ui/__tests__/smoke-test.test.d.ts.map +1 -0
- package/dist/ui/__tests__/smoke-test.test.js +18 -0
- package/dist/ui/__tests__/smoke-test.test.js.map +1 -0
- package/dist/ui/__tests__/useKeyboardShortcuts.test.d.ts +2 -0
- package/dist/ui/__tests__/useKeyboardShortcuts.test.d.ts.map +1 -0
- package/dist/ui/__tests__/useKeyboardShortcuts.test.js +374 -0
- package/dist/ui/__tests__/useKeyboardShortcuts.test.js.map +1 -0
- package/dist/ui/components/AutosaveIndicator.d.ts +18 -0
- package/dist/ui/components/AutosaveIndicator.d.ts.map +1 -0
- package/dist/ui/components/AutosaveIndicator.js +175 -0
- package/dist/ui/components/AutosaveIndicator.js.map +1 -0
- package/dist/ui/components/UndoRedoControls.d.ts +16 -0
- package/dist/ui/components/UndoRedoControls.d.ts.map +1 -0
- package/dist/ui/components/UndoRedoControls.js +144 -0
- package/dist/ui/components/UndoRedoControls.js.map +1 -0
- package/dist/ui/hooks/useKeyboardShortcuts.d.ts +22 -0
- package/dist/ui/hooks/useKeyboardShortcuts.d.ts.map +1 -0
- package/dist/ui/hooks/useKeyboardShortcuts.js +162 -0
- package/dist/ui/hooks/useKeyboardShortcuts.js.map +1 -0
- package/dist/ui/index.d.ts +9 -0
- package/dist/ui/index.d.ts.map +1 -0
- package/dist/ui/index.js +14 -0
- package/dist/ui/index.js.map +1 -0
- package/dist/ui/types.d.ts +141 -0
- package/dist/ui/types.d.ts.map +1 -0
- package/dist/ui/types.js +51 -0
- package/dist/ui/types.js.map +1 -0
- package/examples/autosave-undo-demo.ts +306 -0
- package/examples/branching-demo-simple.ts +0 -0
- package/examples/branching-demo.ts +0 -0
- package/examples/persistence-demo.ts +84 -0
- package/examples/tsconfig.json +13 -0
- package/examples/ui-components-demo.tsx +320 -0
- package/examples/validation-demo-story.json +177 -0
- package/examples/validation-demo.js +163 -0
- package/package.json +24 -4
- package/docs/branching/PDM.md +0 -443
- package/docs/branching/RELEASE-v1.2.0.md +0 -169
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// QNCE Condition Evaluator - Sprint 3.4
|
|
3
|
+
// Parses and evaluates conditional expressions for choice visibility
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.conditionEvaluator = exports.ConditionEvaluator = exports.ConditionEvaluationError = void 0;
|
|
6
|
+
/**
|
|
7
|
+
* Error thrown when condition evaluation fails
|
|
8
|
+
*/
|
|
9
|
+
class ConditionEvaluationError extends Error {
|
|
10
|
+
expression;
|
|
11
|
+
cause;
|
|
12
|
+
constructor(message, expression, cause) {
|
|
13
|
+
super(message);
|
|
14
|
+
this.expression = expression;
|
|
15
|
+
this.cause = cause;
|
|
16
|
+
this.name = 'ConditionEvaluationError';
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
exports.ConditionEvaluationError = ConditionEvaluationError;
|
|
20
|
+
/**
|
|
21
|
+
* Built-in expression operators
|
|
22
|
+
*/
|
|
23
|
+
const OPERATORS = {
|
|
24
|
+
'>=': (a, b) => a >= b,
|
|
25
|
+
'<=': (a, b) => a <= b,
|
|
26
|
+
'>': (a, b) => a > b,
|
|
27
|
+
'<': (a, b) => a < b,
|
|
28
|
+
'==': (a, b) => a == b,
|
|
29
|
+
'===': (a, b) => a === b,
|
|
30
|
+
'!=': (a, b) => a != b,
|
|
31
|
+
'!==': (a, b) => a !== b,
|
|
32
|
+
'&&': (a, b) => a && b,
|
|
33
|
+
'||': (a, b) => a || b,
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Condition evaluator service for parsing and executing conditional expressions
|
|
37
|
+
*/
|
|
38
|
+
class ConditionEvaluator {
|
|
39
|
+
customEvaluator;
|
|
40
|
+
// Cache compiled functions for better performance
|
|
41
|
+
functionCache = new Map();
|
|
42
|
+
maxCacheSize = 100;
|
|
43
|
+
/**
|
|
44
|
+
* Set a custom evaluator function for handling complex conditions
|
|
45
|
+
*/
|
|
46
|
+
setCustomEvaluator(evaluator) {
|
|
47
|
+
this.customEvaluator = evaluator;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Clear the custom evaluator
|
|
51
|
+
*/
|
|
52
|
+
clearCustomEvaluator() {
|
|
53
|
+
this.customEvaluator = undefined;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Evaluate a condition expression against the provided context
|
|
57
|
+
*/
|
|
58
|
+
evaluate(expression, context) {
|
|
59
|
+
try {
|
|
60
|
+
// If custom evaluator is set, use it first
|
|
61
|
+
if (this.customEvaluator) {
|
|
62
|
+
return this.customEvaluator(expression, context);
|
|
63
|
+
}
|
|
64
|
+
// Use built-in evaluator
|
|
65
|
+
return this.evaluateBuiltIn(expression, context);
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
throw new ConditionEvaluationError(`Failed to evaluate condition: ${expression}`, expression, error instanceof Error ? error : new Error(String(error)));
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Built-in expression evaluator with support for common patterns
|
|
73
|
+
*/
|
|
74
|
+
evaluateBuiltIn(expression, context) {
|
|
75
|
+
// Handle empty expressions
|
|
76
|
+
if (!expression || expression.trim() === '') {
|
|
77
|
+
throw new ConditionEvaluationError('Empty or whitespace-only condition expression', expression);
|
|
78
|
+
}
|
|
79
|
+
// Sanitize and prepare expression
|
|
80
|
+
const sanitizedExpression = this.sanitizeExpression(expression);
|
|
81
|
+
// Handle simple cases first
|
|
82
|
+
if (sanitizedExpression === 'true')
|
|
83
|
+
return true;
|
|
84
|
+
if (sanitizedExpression === 'false')
|
|
85
|
+
return false;
|
|
86
|
+
// Create safe evaluation context
|
|
87
|
+
const evalContext = this.createEvaluationContext(context);
|
|
88
|
+
// Check function cache for performance
|
|
89
|
+
let func = this.functionCache.get(sanitizedExpression);
|
|
90
|
+
if (!func) {
|
|
91
|
+
try {
|
|
92
|
+
// Use Function constructor for safe evaluation (better than eval)
|
|
93
|
+
func = new Function('flags', 'state', 'timestamp', 'customData', `
|
|
94
|
+
"use strict";
|
|
95
|
+
return ${sanitizedExpression};
|
|
96
|
+
`);
|
|
97
|
+
// Cache the function for future use
|
|
98
|
+
if (this.functionCache.size >= this.maxCacheSize) {
|
|
99
|
+
// Remove oldest entry when cache is full
|
|
100
|
+
const firstKey = this.functionCache.keys().next().value;
|
|
101
|
+
if (firstKey) {
|
|
102
|
+
this.functionCache.delete(firstKey);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
this.functionCache.set(sanitizedExpression, func);
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
throw new ConditionEvaluationError(`Invalid expression syntax: ${expression}`, expression, error instanceof Error ? error : new Error(String(error)));
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
try {
|
|
112
|
+
return !!func(evalContext.flags, evalContext.state, evalContext.timestamp, evalContext.customData);
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
throw new ConditionEvaluationError(`Runtime error evaluating: ${expression}`, expression, error instanceof Error ? error : new Error(String(error)));
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Sanitize expression to prevent code injection
|
|
120
|
+
*/
|
|
121
|
+
sanitizeExpression(expression) {
|
|
122
|
+
// Remove potentially dangerous patterns
|
|
123
|
+
const dangerous = [
|
|
124
|
+
/\beval\b/g,
|
|
125
|
+
/\bFunction\b/g,
|
|
126
|
+
/\bconstructor\b/g,
|
|
127
|
+
/\bprototype\b/g,
|
|
128
|
+
/\b__proto__\b/g,
|
|
129
|
+
/\bimport\b/g,
|
|
130
|
+
/\brequire\b/g,
|
|
131
|
+
/\bprocess\b/g,
|
|
132
|
+
/\bglobal\b/g,
|
|
133
|
+
/\bwindow\b/g,
|
|
134
|
+
/\bdocument\b/g,
|
|
135
|
+
];
|
|
136
|
+
let sanitized = expression.trim();
|
|
137
|
+
for (const pattern of dangerous) {
|
|
138
|
+
if (pattern.test(sanitized)) {
|
|
139
|
+
throw new ConditionEvaluationError(`Potentially unsafe expression detected: ${expression}`, expression);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return sanitized;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Create a safe evaluation context from the condition context
|
|
146
|
+
*/
|
|
147
|
+
createEvaluationContext(context) {
|
|
148
|
+
return {
|
|
149
|
+
flags: { ...context.state.flags },
|
|
150
|
+
state: {
|
|
151
|
+
currentNodeId: context.state.currentNodeId,
|
|
152
|
+
flags: { ...context.state.flags },
|
|
153
|
+
history: [...context.state.history],
|
|
154
|
+
},
|
|
155
|
+
timestamp: context.timestamp,
|
|
156
|
+
customData: context.customData ? { ...context.customData } : {},
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Validate that an expression is syntactically correct without evaluating it
|
|
161
|
+
*/
|
|
162
|
+
validateExpression(expression) {
|
|
163
|
+
try {
|
|
164
|
+
this.sanitizeExpression(expression);
|
|
165
|
+
// Try to create the function without executing it
|
|
166
|
+
new Function('flags', 'state', 'timestamp', 'customData', `
|
|
167
|
+
"use strict";
|
|
168
|
+
return ${expression};
|
|
169
|
+
`);
|
|
170
|
+
return { valid: true };
|
|
171
|
+
}
|
|
172
|
+
catch (error) {
|
|
173
|
+
return {
|
|
174
|
+
valid: false,
|
|
175
|
+
error: error instanceof Error ? error.message : String(error)
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Get list of flag names referenced in an expression
|
|
181
|
+
*/
|
|
182
|
+
getReferencedFlags(expression) {
|
|
183
|
+
const flagPattern = /flags\.([a-zA-Z_$][a-zA-Z0-9_$]*)/g;
|
|
184
|
+
const matches = [];
|
|
185
|
+
let match;
|
|
186
|
+
while ((match = flagPattern.exec(expression)) !== null) {
|
|
187
|
+
matches.push(match[1]);
|
|
188
|
+
}
|
|
189
|
+
return [...new Set(matches)]; // Remove duplicates
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
exports.ConditionEvaluator = ConditionEvaluator;
|
|
193
|
+
// Create a global instance for the engine to use
|
|
194
|
+
exports.conditionEvaluator = new ConditionEvaluator();
|