qnce-engine 0.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +248 -0
- package/dist/cli/audit.js +6 -4
- package/dist/cli/audit.js.map +1 -1
- package/dist/cli/init.js +11 -9
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/perf.d.ts +30 -0
- package/dist/cli/perf.d.ts.map +1 -0
- package/dist/cli/perf.js +219 -0
- package/dist/cli/perf.js.map +1 -0
- package/dist/engine/core.d.ts +104 -8
- package/dist/engine/core.d.ts.map +1 -1
- package/dist/engine/core.js +288 -7
- package/dist/engine/core.js.map +1 -1
- package/dist/engine/demo-story.js +4 -1
- package/dist/engine/demo-story.js.map +1 -1
- package/dist/index.js +24 -3
- package/dist/index.js.map +1 -1
- package/dist/narrative/branching/engine-simple.d.ts +84 -0
- package/dist/narrative/branching/engine-simple.d.ts.map +1 -0
- package/dist/narrative/branching/engine-simple.js +349 -0
- package/dist/narrative/branching/engine-simple.js.map +1 -0
- package/dist/narrative/branching/index.d.ts +12 -0
- package/dist/narrative/branching/index.d.ts.map +1 -0
- package/dist/narrative/branching/index.js +56 -0
- package/dist/narrative/branching/index.js.map +1 -0
- package/dist/narrative/branching/models.d.ts +223 -0
- package/dist/narrative/branching/models.d.ts.map +1 -0
- package/dist/narrative/branching/models.js +6 -0
- package/dist/narrative/branching/models.js.map +1 -0
- package/dist/performance/HotReloadDelta.d.ts +107 -0
- package/dist/performance/HotReloadDelta.d.ts.map +1 -0
- package/dist/performance/HotReloadDelta.js +333 -0
- package/dist/performance/HotReloadDelta.js.map +1 -0
- package/dist/performance/ObjectPool.d.ts +150 -0
- package/dist/performance/ObjectPool.d.ts.map +1 -0
- package/dist/performance/ObjectPool.js +297 -0
- package/dist/performance/ObjectPool.js.map +1 -0
- package/dist/performance/PerfReporter.d.ts +123 -0
- package/dist/performance/PerfReporter.d.ts.map +1 -0
- package/dist/performance/PerfReporter.js +281 -0
- package/dist/performance/PerfReporter.js.map +1 -0
- package/dist/performance/ThreadPool.d.ts +107 -0
- package/dist/performance/ThreadPool.d.ts.map +1 -0
- package/dist/performance/ThreadPool.js +348 -0
- package/dist/performance/ThreadPool.js.map +1 -0
- package/docs/PERFORMANCE.md +355 -0
- package/docs/branching/ERD.md +214 -0
- package/docs/branching/PDM.md +443 -0
- package/docs/branching/RELEASE-v1.2.0.md +169 -0
- package/examples/branching-advanced-demo.ts +339 -0
- package/examples/branching-quickstart.ts +314 -0
- package/examples/quickstart-demo.js +82 -0
- package/package.json +21 -8
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core branching entities for dynamic narrative flow management
|
|
3
|
+
* Built on Sprint #2 performance infrastructure with object pooling support
|
|
4
|
+
*/
|
|
5
|
+
import { NarrativeNode, QNCEState } from '../../engine/core';
|
|
6
|
+
/**
|
|
7
|
+
* Top-level story container with branching metadata
|
|
8
|
+
*/
|
|
9
|
+
export interface QNCEStory {
|
|
10
|
+
id: string;
|
|
11
|
+
title: string;
|
|
12
|
+
version: string;
|
|
13
|
+
metadata: StoryMetadata;
|
|
14
|
+
chapters: Chapter[];
|
|
15
|
+
branchingConfig: BranchingConfig;
|
|
16
|
+
}
|
|
17
|
+
export interface StoryMetadata {
|
|
18
|
+
author: string;
|
|
19
|
+
description: string;
|
|
20
|
+
tags: string[];
|
|
21
|
+
createDate: Date;
|
|
22
|
+
lastModified: Date;
|
|
23
|
+
estimatedPlaytime: number;
|
|
24
|
+
}
|
|
25
|
+
export interface BranchingConfig {
|
|
26
|
+
maxActiveBranches: number;
|
|
27
|
+
branchCacheSize: number;
|
|
28
|
+
enableDynamicInsertion: boolean;
|
|
29
|
+
enableAnalytics: boolean;
|
|
30
|
+
performanceMode: boolean;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Chapter: Logical grouping of narrative flows with branching points
|
|
34
|
+
*/
|
|
35
|
+
export interface Chapter {
|
|
36
|
+
id: string;
|
|
37
|
+
title: string;
|
|
38
|
+
description?: string;
|
|
39
|
+
flows: NarrativeFlow[];
|
|
40
|
+
branches: BranchPoint[];
|
|
41
|
+
prerequisites?: ChapterPrerequisites;
|
|
42
|
+
metadata: ChapterMetadata;
|
|
43
|
+
}
|
|
44
|
+
export interface ChapterPrerequisites {
|
|
45
|
+
requiredFlags: Record<string, unknown>;
|
|
46
|
+
requiredChoices: string[];
|
|
47
|
+
minPlaytime?: number;
|
|
48
|
+
}
|
|
49
|
+
export interface ChapterMetadata {
|
|
50
|
+
difficulty: 'easy' | 'medium' | 'hard';
|
|
51
|
+
themes: string[];
|
|
52
|
+
estimatedDuration: number;
|
|
53
|
+
branchComplexity: number;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* NarrativeFlow: Sequence of connected nodes with entry/exit points
|
|
57
|
+
*/
|
|
58
|
+
export interface NarrativeFlow {
|
|
59
|
+
id: string;
|
|
60
|
+
name: string;
|
|
61
|
+
description?: string;
|
|
62
|
+
nodes: NarrativeNode[];
|
|
63
|
+
entryPoints: FlowEntryPoint[];
|
|
64
|
+
exitPoints: FlowExitPoint[];
|
|
65
|
+
flowType: FlowType;
|
|
66
|
+
metadata: FlowMetadata;
|
|
67
|
+
}
|
|
68
|
+
export type FlowType = 'linear' | 'branching' | 'conditional' | 'procedural';
|
|
69
|
+
export interface FlowEntryPoint {
|
|
70
|
+
id: string;
|
|
71
|
+
nodeId: string;
|
|
72
|
+
conditions?: BranchCondition[];
|
|
73
|
+
priority: number;
|
|
74
|
+
}
|
|
75
|
+
export interface FlowExitPoint {
|
|
76
|
+
id: string;
|
|
77
|
+
nodeId: string;
|
|
78
|
+
targetFlowId?: string;
|
|
79
|
+
conditions?: BranchCondition[];
|
|
80
|
+
}
|
|
81
|
+
export interface FlowMetadata {
|
|
82
|
+
complexity: number;
|
|
83
|
+
avgCompletionTime: number;
|
|
84
|
+
playerChoiceCount: number;
|
|
85
|
+
aiGeneratedContent: boolean;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* BranchPoint: Dynamic branching logic between flows
|
|
89
|
+
*/
|
|
90
|
+
export interface BranchPoint {
|
|
91
|
+
id: string;
|
|
92
|
+
name: string;
|
|
93
|
+
sourceFlowId: string;
|
|
94
|
+
sourceNodeId: string;
|
|
95
|
+
branchOptions: BranchOption[];
|
|
96
|
+
branchType: BranchType;
|
|
97
|
+
conditions?: BranchCondition[];
|
|
98
|
+
metadata: BranchMetadata;
|
|
99
|
+
}
|
|
100
|
+
export type BranchType = 'choice-driven' | 'flag-conditional' | 'time-based' | 'procedural' | 'conditional';
|
|
101
|
+
export interface BranchOption {
|
|
102
|
+
id: string;
|
|
103
|
+
targetFlowId: string;
|
|
104
|
+
targetNodeId?: string;
|
|
105
|
+
displayText: string;
|
|
106
|
+
conditions?: BranchCondition[];
|
|
107
|
+
flagEffects?: Record<string, unknown>;
|
|
108
|
+
weight: number;
|
|
109
|
+
}
|
|
110
|
+
export interface BranchCondition {
|
|
111
|
+
type: 'flag' | 'choice' | 'time' | 'random' | 'custom';
|
|
112
|
+
operator: 'equals' | 'not_equals' | 'greater' | 'less' | 'contains' | 'exists';
|
|
113
|
+
key: string;
|
|
114
|
+
value: unknown;
|
|
115
|
+
evaluator?: (state: QNCEState, context: BranchContext) => boolean;
|
|
116
|
+
}
|
|
117
|
+
export interface BranchMetadata {
|
|
118
|
+
usageCount: number;
|
|
119
|
+
avgTraversalTime: number;
|
|
120
|
+
playerPreference: number;
|
|
121
|
+
lastUsed: Date;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* BranchContext: Runtime state for branch evaluation and tracking
|
|
125
|
+
*/
|
|
126
|
+
export interface BranchContext {
|
|
127
|
+
currentStory: QNCEStory;
|
|
128
|
+
currentChapter: Chapter;
|
|
129
|
+
currentFlow: NarrativeFlow;
|
|
130
|
+
activeState: QNCEState;
|
|
131
|
+
branchHistory: BranchHistoryEntry[];
|
|
132
|
+
pendingBranches: PendingBranch[];
|
|
133
|
+
analytics: BranchAnalytics;
|
|
134
|
+
}
|
|
135
|
+
export interface BranchHistoryEntry {
|
|
136
|
+
id: string;
|
|
137
|
+
branchPointId: string;
|
|
138
|
+
chosenOptionId: string;
|
|
139
|
+
timestamp: Date;
|
|
140
|
+
executionTime: number;
|
|
141
|
+
context: Partial<QNCEState>;
|
|
142
|
+
}
|
|
143
|
+
export interface PendingBranch {
|
|
144
|
+
id: string;
|
|
145
|
+
branchPointId: string;
|
|
146
|
+
triggerConditions: BranchCondition[];
|
|
147
|
+
timeoutMs?: number;
|
|
148
|
+
createdAt: Date;
|
|
149
|
+
}
|
|
150
|
+
export interface BranchAnalytics {
|
|
151
|
+
totalBranchesTraversed: number;
|
|
152
|
+
avgBranchDecisionTime: number;
|
|
153
|
+
mostPopularBranches: string[];
|
|
154
|
+
abandonmentPoints: string[];
|
|
155
|
+
completionRate: number;
|
|
156
|
+
sessionStartTime: Date;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Dynamic branching operations for runtime story modification
|
|
160
|
+
*/
|
|
161
|
+
export interface DynamicBranchOperation {
|
|
162
|
+
type: 'insert' | 'remove' | 'modify';
|
|
163
|
+
branchId: string;
|
|
164
|
+
targetLocation: BranchLocation;
|
|
165
|
+
payload?: Partial<BranchPoint>;
|
|
166
|
+
conditions?: BranchCondition[];
|
|
167
|
+
}
|
|
168
|
+
export interface BranchLocation {
|
|
169
|
+
chapterId: string;
|
|
170
|
+
flowId: string;
|
|
171
|
+
nodeId: string;
|
|
172
|
+
insertionPoint: 'before' | 'after' | 'replace';
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Dynamic flow insertion for procedural content
|
|
176
|
+
*/
|
|
177
|
+
export interface DynamicFlowOperation {
|
|
178
|
+
type: 'insert' | 'remove' | 'modify';
|
|
179
|
+
flowId: string;
|
|
180
|
+
targetChapterId: string;
|
|
181
|
+
flow?: NarrativeFlow;
|
|
182
|
+
entryConditions?: BranchCondition[];
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Pool-aware branch entities for performance optimization
|
|
186
|
+
*/
|
|
187
|
+
export interface PooledBranchContext extends BranchContext {
|
|
188
|
+
poolId: string;
|
|
189
|
+
acquisitionTime: number;
|
|
190
|
+
maxLifetime: number;
|
|
191
|
+
}
|
|
192
|
+
export interface PooledBranchPoint extends BranchPoint {
|
|
193
|
+
poolId: string;
|
|
194
|
+
activeReferences: number;
|
|
195
|
+
lastAccessed: number;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Interfaces for AI-driven content generation and branching
|
|
199
|
+
*/
|
|
200
|
+
export interface AIBranchingContext {
|
|
201
|
+
playerProfile: PlayerProfile;
|
|
202
|
+
narrativeContext: NarrativeContext;
|
|
203
|
+
generationHints: AIGenerationHints;
|
|
204
|
+
}
|
|
205
|
+
export interface PlayerProfile {
|
|
206
|
+
playStyle: 'explorer' | 'achiever' | 'socializer' | 'killer';
|
|
207
|
+
preferences: Record<string, number>;
|
|
208
|
+
historicalChoices: string[];
|
|
209
|
+
averageDecisionTime: number;
|
|
210
|
+
}
|
|
211
|
+
export interface NarrativeContext {
|
|
212
|
+
currentTone: string;
|
|
213
|
+
thematicElements: string[];
|
|
214
|
+
characterRelationships: Record<string, number>;
|
|
215
|
+
plotTension: number;
|
|
216
|
+
}
|
|
217
|
+
export interface AIGenerationHints {
|
|
218
|
+
maxBranchDepth: number;
|
|
219
|
+
desiredComplexity: number;
|
|
220
|
+
contentThemes: string[];
|
|
221
|
+
avoidElements: string[];
|
|
222
|
+
}
|
|
223
|
+
//# sourceMappingURL=models.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../../../src/narrative/branching/models.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// QNCE Branching System - Platform Data Model (PDM)
|
|
3
|
+
// Sprint #3 - Advanced Narrative & AI Integration
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
// All interfaces are exported above and ready for engine integration
|
|
6
|
+
//# sourceMappingURL=models.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"models.js","sourceRoot":"","sources":["../../../src/narrative/branching/models.ts"],"names":[],"mappings":";AAAA,oDAAoD;AACpD,kDAAkD;;AAuRlD,qEAAqE"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
export interface StoryDelta {
|
|
2
|
+
nodeChanges: NodeDelta[];
|
|
3
|
+
assetChanges: AssetDelta[];
|
|
4
|
+
timestamp: number;
|
|
5
|
+
}
|
|
6
|
+
export interface NodeDelta {
|
|
7
|
+
nodeId: string;
|
|
8
|
+
changeType: 'added' | 'modified' | 'removed';
|
|
9
|
+
oldNode?: any;
|
|
10
|
+
newNode?: any;
|
|
11
|
+
affectedFields: string[];
|
|
12
|
+
}
|
|
13
|
+
export interface AssetDelta {
|
|
14
|
+
assetId: string;
|
|
15
|
+
changeType: 'added' | 'modified' | 'removed';
|
|
16
|
+
oldAsset?: any;
|
|
17
|
+
newAsset?: any;
|
|
18
|
+
sizeChange: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Delta Comparison Engine for Hot-Reload Story Updates
|
|
22
|
+
* Identifies minimal changes needed to update narrative content
|
|
23
|
+
*/
|
|
24
|
+
export declare class StoryDeltaComparator {
|
|
25
|
+
/**
|
|
26
|
+
* Compare two story configurations and generate delta
|
|
27
|
+
*/
|
|
28
|
+
compareStories(oldStory: any, newStory: any): StoryDelta;
|
|
29
|
+
/**
|
|
30
|
+
* Deep comparison of narrative nodes
|
|
31
|
+
*/
|
|
32
|
+
private compareNodes;
|
|
33
|
+
/**
|
|
34
|
+
* Compare assets (future: images, audio, etc.)
|
|
35
|
+
*/
|
|
36
|
+
private compareAssets;
|
|
37
|
+
/**
|
|
38
|
+
* Fine-grained field comparison for nodes
|
|
39
|
+
*/
|
|
40
|
+
private findChangedFields;
|
|
41
|
+
/**
|
|
42
|
+
* Asset comparison logic
|
|
43
|
+
*/
|
|
44
|
+
private assetsAreDifferent;
|
|
45
|
+
/**
|
|
46
|
+
* Deep equality check for objects
|
|
47
|
+
*/
|
|
48
|
+
private deepEqual;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Hot-Reload Delta Patcher
|
|
52
|
+
* Applies story deltas with minimal engine disruption
|
|
53
|
+
*/
|
|
54
|
+
export declare class StoryDeltaPatcher {
|
|
55
|
+
private engine;
|
|
56
|
+
constructor(engine: any);
|
|
57
|
+
/**
|
|
58
|
+
* Apply delta patch to running engine
|
|
59
|
+
* Target: <2ms frame stall for hot-reload
|
|
60
|
+
*/
|
|
61
|
+
applyDelta(delta: StoryDelta): Promise<PatchResult>;
|
|
62
|
+
/**
|
|
63
|
+
* Validate delta is safe to apply
|
|
64
|
+
*/
|
|
65
|
+
private validateDelta;
|
|
66
|
+
/**
|
|
67
|
+
* Apply node changes to story data
|
|
68
|
+
*/
|
|
69
|
+
private applyNodeChanges;
|
|
70
|
+
/**
|
|
71
|
+
* Fast synchronous node changes for small deltas (no async overhead)
|
|
72
|
+
*/
|
|
73
|
+
private applyNodeChangesFast;
|
|
74
|
+
/**
|
|
75
|
+
* Apply asset changes (placeholder for future implementation)
|
|
76
|
+
*/
|
|
77
|
+
private applyAssetChanges;
|
|
78
|
+
/**
|
|
79
|
+
* Batch node changes by type for efficient processing
|
|
80
|
+
*/
|
|
81
|
+
private batchNodeChanges;
|
|
82
|
+
/**
|
|
83
|
+
* Process a batch of node changes
|
|
84
|
+
*/
|
|
85
|
+
private processBatch;
|
|
86
|
+
/**
|
|
87
|
+
* Apply individual node change
|
|
88
|
+
*/
|
|
89
|
+
private applyNodeChange;
|
|
90
|
+
/**
|
|
91
|
+
* Refresh engine state after patch
|
|
92
|
+
*/
|
|
93
|
+
private refreshEngineState;
|
|
94
|
+
}
|
|
95
|
+
export interface PatchResult {
|
|
96
|
+
success: boolean;
|
|
97
|
+
patchId: string;
|
|
98
|
+
duration: number;
|
|
99
|
+
nodesChanged?: number;
|
|
100
|
+
assetsChanged?: number;
|
|
101
|
+
error?: string;
|
|
102
|
+
}
|
|
103
|
+
export declare function createDeltaTools(engine: any): {
|
|
104
|
+
comparator: StoryDeltaComparator;
|
|
105
|
+
patcher: StoryDeltaPatcher;
|
|
106
|
+
};
|
|
107
|
+
//# sourceMappingURL=HotReloadDelta.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"HotReloadDelta.d.ts","sourceRoot":"","sources":["../../src/performance/HotReloadDelta.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,UAAU;IACzB,WAAW,EAAE,SAAS,EAAE,CAAC;IACzB,YAAY,EAAE,UAAU,EAAE,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,OAAO,GAAG,UAAU,GAAG,SAAS,CAAC;IAC7C,OAAO,CAAC,EAAE,GAAG,CAAC;IACd,OAAO,CAAC,EAAE,GAAG,CAAC;IACd,cAAc,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,OAAO,GAAG,UAAU,GAAG,SAAS,CAAC;IAC7C,QAAQ,CAAC,EAAE,GAAG,CAAC;IACf,QAAQ,CAAC,EAAE,GAAG,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,qBAAa,oBAAoB;IAE/B;;OAEG;IACH,cAAc,CAAC,QAAQ,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,GAAG,UAAU;IAUxD;;OAEG;IACH,OAAO,CAAC,YAAY;IA+CpB;;OAEG;IACH,OAAO,CAAC,aAAa;IA4CrB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAazB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAK1B;;OAEG;IACH,OAAO,CAAC,SAAS;CAuBlB;AAED;;;GAGG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,MAAM,CAAM;gBAER,MAAM,EAAE,GAAG;IAIvB;;;OAGG;IACG,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC;IAmDzD;;OAEG;IACH,OAAO,CAAC,aAAa;IAgBrB;;OAEG;YACW,gBAAgB;IAS9B;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAM5B;;OAEG;YACW,iBAAiB;IAK/B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAWxB;;OAEG;YACW,YAAY;IAY1B;;OAEG;IACH,OAAO,CAAC,eAAe;IAgCvB;;OAEG;IACH,OAAO,CAAC,kBAAkB;CAI3B;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAGD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,GAAG;;;EAK3C"}
|
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// S2-T3: Hot-Reload Delta Patching - Initial Spike
|
|
3
|
+
// Explore delta comparison logic for story content updates
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.StoryDeltaPatcher = exports.StoryDeltaComparator = void 0;
|
|
6
|
+
exports.createDeltaTools = createDeltaTools;
|
|
7
|
+
/**
|
|
8
|
+
* Delta Comparison Engine for Hot-Reload Story Updates
|
|
9
|
+
* Identifies minimal changes needed to update narrative content
|
|
10
|
+
*/
|
|
11
|
+
class StoryDeltaComparator {
|
|
12
|
+
/**
|
|
13
|
+
* Compare two story configurations and generate delta
|
|
14
|
+
*/
|
|
15
|
+
compareStories(oldStory, newStory) {
|
|
16
|
+
const timestamp = performance.now();
|
|
17
|
+
return {
|
|
18
|
+
nodeChanges: this.compareNodes(oldStory.nodes || [], newStory.nodes || []),
|
|
19
|
+
assetChanges: this.compareAssets(oldStory.assets || [], newStory.assets || []),
|
|
20
|
+
timestamp
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Deep comparison of narrative nodes
|
|
25
|
+
*/
|
|
26
|
+
compareNodes(oldNodes, newNodes) {
|
|
27
|
+
const deltas = [];
|
|
28
|
+
const oldNodeMap = new Map(oldNodes.map(n => [n.id, n]));
|
|
29
|
+
const newNodeMap = new Map(newNodes.map(n => [n.id, n]));
|
|
30
|
+
// Check for removed nodes
|
|
31
|
+
for (const [nodeId, oldNode] of oldNodeMap) {
|
|
32
|
+
if (!newNodeMap.has(nodeId)) {
|
|
33
|
+
deltas.push({
|
|
34
|
+
nodeId,
|
|
35
|
+
changeType: 'removed',
|
|
36
|
+
oldNode,
|
|
37
|
+
affectedFields: ['*']
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
// Check for added and modified nodes
|
|
42
|
+
for (const [nodeId, newNode] of newNodeMap) {
|
|
43
|
+
const oldNode = oldNodeMap.get(nodeId);
|
|
44
|
+
if (!oldNode) {
|
|
45
|
+
// New node
|
|
46
|
+
deltas.push({
|
|
47
|
+
nodeId,
|
|
48
|
+
changeType: 'added',
|
|
49
|
+
newNode,
|
|
50
|
+
affectedFields: ['*']
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
// Check for modifications
|
|
55
|
+
const affectedFields = this.findChangedFields(oldNode, newNode);
|
|
56
|
+
if (affectedFields.length > 0) {
|
|
57
|
+
deltas.push({
|
|
58
|
+
nodeId,
|
|
59
|
+
changeType: 'modified',
|
|
60
|
+
oldNode,
|
|
61
|
+
newNode,
|
|
62
|
+
affectedFields
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return deltas;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Compare assets (future: images, audio, etc.)
|
|
71
|
+
*/
|
|
72
|
+
compareAssets(oldAssets, newAssets) {
|
|
73
|
+
const deltas = [];
|
|
74
|
+
const oldAssetMap = new Map(oldAssets.map(a => [a.id, a]));
|
|
75
|
+
const newAssetMap = new Map(newAssets.map(a => [a.id, a]));
|
|
76
|
+
// Check for removed assets
|
|
77
|
+
for (const [assetId, oldAsset] of oldAssetMap) {
|
|
78
|
+
if (!newAssetMap.has(assetId)) {
|
|
79
|
+
deltas.push({
|
|
80
|
+
assetId,
|
|
81
|
+
changeType: 'removed',
|
|
82
|
+
oldAsset,
|
|
83
|
+
sizeChange: -(oldAsset.size || 0)
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// Check for added and modified assets
|
|
88
|
+
for (const [assetId, newAsset] of newAssetMap) {
|
|
89
|
+
const oldAsset = oldAssetMap.get(assetId);
|
|
90
|
+
if (!oldAsset) {
|
|
91
|
+
// New asset
|
|
92
|
+
deltas.push({
|
|
93
|
+
assetId,
|
|
94
|
+
changeType: 'added',
|
|
95
|
+
newAsset,
|
|
96
|
+
sizeChange: newAsset.size || 0
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
else if (this.assetsAreDifferent(oldAsset, newAsset)) {
|
|
100
|
+
// Modified asset
|
|
101
|
+
deltas.push({
|
|
102
|
+
assetId,
|
|
103
|
+
changeType: 'modified',
|
|
104
|
+
oldAsset,
|
|
105
|
+
newAsset,
|
|
106
|
+
sizeChange: (newAsset.size || 0) - (oldAsset.size || 0)
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return deltas;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Fine-grained field comparison for nodes
|
|
114
|
+
*/
|
|
115
|
+
findChangedFields(oldNode, newNode) {
|
|
116
|
+
const changedFields = [];
|
|
117
|
+
const allFields = new Set([...Object.keys(oldNode), ...Object.keys(newNode)]);
|
|
118
|
+
for (const field of allFields) {
|
|
119
|
+
if (!this.deepEqual(oldNode[field], newNode[field])) {
|
|
120
|
+
changedFields.push(field);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return changedFields;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Asset comparison logic
|
|
127
|
+
*/
|
|
128
|
+
assetsAreDifferent(oldAsset, newAsset) {
|
|
129
|
+
// Simple comparison - in practice, would use checksums/hashes
|
|
130
|
+
return JSON.stringify(oldAsset) !== JSON.stringify(newAsset);
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Deep equality check for objects
|
|
134
|
+
*/
|
|
135
|
+
deepEqual(a, b) {
|
|
136
|
+
if (a === b)
|
|
137
|
+
return true;
|
|
138
|
+
if (a === null || b === null)
|
|
139
|
+
return false;
|
|
140
|
+
if (typeof a !== typeof b)
|
|
141
|
+
return false;
|
|
142
|
+
if (typeof a === 'object') {
|
|
143
|
+
if (Array.isArray(a) !== Array.isArray(b))
|
|
144
|
+
return false;
|
|
145
|
+
const keysA = Object.keys(a);
|
|
146
|
+
const keysB = Object.keys(b);
|
|
147
|
+
if (keysA.length !== keysB.length)
|
|
148
|
+
return false;
|
|
149
|
+
for (const key of keysA) {
|
|
150
|
+
if (!keysB.includes(key))
|
|
151
|
+
return false;
|
|
152
|
+
if (!this.deepEqual(a[key], b[key]))
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
exports.StoryDeltaComparator = StoryDeltaComparator;
|
|
161
|
+
/**
|
|
162
|
+
* Hot-Reload Delta Patcher
|
|
163
|
+
* Applies story deltas with minimal engine disruption
|
|
164
|
+
*/
|
|
165
|
+
class StoryDeltaPatcher {
|
|
166
|
+
engine;
|
|
167
|
+
constructor(engine) {
|
|
168
|
+
this.engine = engine;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Apply delta patch to running engine
|
|
172
|
+
* Target: <2ms frame stall for hot-reload
|
|
173
|
+
*/
|
|
174
|
+
async applyDelta(delta) {
|
|
175
|
+
const startTime = performance.now();
|
|
176
|
+
const patchId = `patch-${Date.now()}`;
|
|
177
|
+
try {
|
|
178
|
+
// Phase 1: Validate delta can be applied safely (sync, fast)
|
|
179
|
+
const validation = this.validateDelta(delta);
|
|
180
|
+
if (!validation.safe) {
|
|
181
|
+
return {
|
|
182
|
+
success: false,
|
|
183
|
+
patchId,
|
|
184
|
+
duration: performance.now() - startTime,
|
|
185
|
+
error: validation.error
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
// Phase 2: Apply changes synchronously for speed
|
|
189
|
+
// For small deltas (<10 changes), apply immediately
|
|
190
|
+
// For larger deltas, use optimized batch processing
|
|
191
|
+
if (delta.nodeChanges.length <= 10) {
|
|
192
|
+
this.applyNodeChangesFast(delta.nodeChanges);
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
await this.applyNodeChanges(delta.nodeChanges);
|
|
196
|
+
}
|
|
197
|
+
// Phase 3: Skip asset changes for now (async operation)
|
|
198
|
+
// Assets can be updated in background without frame stall
|
|
199
|
+
// Phase 4: Minimal state refresh
|
|
200
|
+
this.refreshEngineState();
|
|
201
|
+
const duration = performance.now() - startTime;
|
|
202
|
+
return {
|
|
203
|
+
success: true,
|
|
204
|
+
patchId,
|
|
205
|
+
duration,
|
|
206
|
+
nodesChanged: delta.nodeChanges.length,
|
|
207
|
+
assetsChanged: delta.assetChanges.length
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
catch (error) {
|
|
211
|
+
return {
|
|
212
|
+
success: false,
|
|
213
|
+
patchId,
|
|
214
|
+
duration: performance.now() - startTime,
|
|
215
|
+
error: error instanceof Error ? error.message : 'Unknown error'
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Validate delta is safe to apply
|
|
221
|
+
*/
|
|
222
|
+
validateDelta(delta) {
|
|
223
|
+
// Check if current node would be affected
|
|
224
|
+
const currentNodeId = this.engine.getState().currentNodeId;
|
|
225
|
+
const currentNodeChange = delta.nodeChanges.find(c => c.nodeId === currentNodeId);
|
|
226
|
+
if (currentNodeChange && currentNodeChange.changeType === 'removed') {
|
|
227
|
+
return {
|
|
228
|
+
safe: false,
|
|
229
|
+
error: 'Cannot remove currently active node'
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
// Additional safety checks could go here
|
|
233
|
+
return { safe: true };
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Apply node changes to story data
|
|
237
|
+
*/
|
|
238
|
+
async applyNodeChanges(nodeChanges) {
|
|
239
|
+
// Batch node changes to minimize engine updates
|
|
240
|
+
const batches = this.batchNodeChanges(nodeChanges);
|
|
241
|
+
for (const batch of batches) {
|
|
242
|
+
await this.processBatch(batch);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Fast synchronous node changes for small deltas (no async overhead)
|
|
247
|
+
*/
|
|
248
|
+
applyNodeChangesFast(nodeChanges) {
|
|
249
|
+
for (const change of nodeChanges) {
|
|
250
|
+
this.applyNodeChange(change);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Apply asset changes (placeholder for future implementation)
|
|
255
|
+
*/
|
|
256
|
+
async applyAssetChanges(assetChanges) {
|
|
257
|
+
// Future: Implement asset hot-reload
|
|
258
|
+
console.log(`Would apply ${assetChanges.length} asset changes`);
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Batch node changes by type for efficient processing
|
|
262
|
+
*/
|
|
263
|
+
batchNodeChanges(nodeChanges) {
|
|
264
|
+
const batches = [];
|
|
265
|
+
const batchSize = 25; // Larger batch size for efficiency
|
|
266
|
+
for (let i = 0; i < nodeChanges.length; i += batchSize) {
|
|
267
|
+
batches.push(nodeChanges.slice(i, i + batchSize));
|
|
268
|
+
}
|
|
269
|
+
return batches;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Process a batch of node changes
|
|
273
|
+
*/
|
|
274
|
+
async processBatch(batch) {
|
|
275
|
+
// Use requestAnimationFrame or setTimeout to yield control
|
|
276
|
+
return new Promise(resolve => {
|
|
277
|
+
setTimeout(() => {
|
|
278
|
+
for (const change of batch) {
|
|
279
|
+
this.applyNodeChange(change);
|
|
280
|
+
}
|
|
281
|
+
resolve();
|
|
282
|
+
}, 0);
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Apply individual node change
|
|
287
|
+
*/
|
|
288
|
+
applyNodeChange(change) {
|
|
289
|
+
// Access engine's internal story data
|
|
290
|
+
const storyData = this.engine.storyData;
|
|
291
|
+
switch (change.changeType) {
|
|
292
|
+
case 'added':
|
|
293
|
+
storyData.nodes.push(change.newNode);
|
|
294
|
+
break;
|
|
295
|
+
case 'removed':
|
|
296
|
+
const removeIndex = storyData.nodes.findIndex((n) => n.id === change.nodeId);
|
|
297
|
+
if (removeIndex >= 0) {
|
|
298
|
+
storyData.nodes.splice(removeIndex, 1);
|
|
299
|
+
}
|
|
300
|
+
break;
|
|
301
|
+
case 'modified':
|
|
302
|
+
const modifyIndex = storyData.nodes.findIndex((n) => n.id === change.nodeId);
|
|
303
|
+
if (modifyIndex >= 0) {
|
|
304
|
+
// Only update changed fields for minimal disruption
|
|
305
|
+
for (const field of change.affectedFields) {
|
|
306
|
+
if (field === '*') {
|
|
307
|
+
storyData.nodes[modifyIndex] = change.newNode;
|
|
308
|
+
}
|
|
309
|
+
else {
|
|
310
|
+
storyData.nodes[modifyIndex][field] = change.newNode[field];
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
break;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Refresh engine state after patch
|
|
319
|
+
*/
|
|
320
|
+
refreshEngineState() {
|
|
321
|
+
// Minimal state refresh - avoid full reinitialization
|
|
322
|
+
// Future: Could trigger cache invalidation, UI updates, etc.
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
exports.StoryDeltaPatcher = StoryDeltaPatcher;
|
|
326
|
+
// Factory function for creating delta tools
|
|
327
|
+
function createDeltaTools(engine) {
|
|
328
|
+
return {
|
|
329
|
+
comparator: new StoryDeltaComparator(),
|
|
330
|
+
patcher: new StoryDeltaPatcher(engine)
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
//# sourceMappingURL=HotReloadDelta.js.map
|