qnce-engine 1.2.0 → 1.2.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.
Files changed (123) hide show
  1. package/README.md +713 -7
  2. package/dist/cli/audit.js +0 -0
  3. package/dist/cli/init.js +0 -0
  4. package/dist/cli/perf.d.ts.map +1 -1
  5. package/dist/cli/perf.js +2 -1
  6. package/dist/cli/perf.js.map +1 -1
  7. package/dist/cli/play.d.ts +4 -0
  8. package/dist/cli/play.d.ts.map +1 -0
  9. package/dist/cli/play.js +259 -0
  10. package/dist/cli/play.js.map +1 -0
  11. package/dist/engine/condition.d.ts +69 -0
  12. package/dist/engine/condition.d.ts.map +1 -0
  13. package/dist/engine/condition.js +195 -0
  14. package/dist/engine/condition.js.map +1 -0
  15. package/dist/engine/core.d.ts +274 -3
  16. package/dist/engine/core.d.ts.map +1 -1
  17. package/dist/engine/core.js +1148 -9
  18. package/dist/engine/core.js.map +1 -1
  19. package/dist/engine/demo-story.d.ts.map +1 -1
  20. package/dist/engine/demo-story.js +99 -13
  21. package/dist/engine/demo-story.js.map +1 -1
  22. package/dist/engine/errors.d.ts +76 -0
  23. package/dist/engine/errors.d.ts.map +1 -0
  24. package/dist/engine/errors.js +178 -0
  25. package/dist/engine/errors.js.map +1 -0
  26. package/dist/engine/types.d.ts +445 -0
  27. package/dist/engine/types.d.ts.map +1 -0
  28. package/dist/engine/types.js +9 -0
  29. package/dist/engine/types.js.map +1 -0
  30. package/dist/engine/validation.d.ts +110 -0
  31. package/dist/engine/validation.d.ts.map +1 -0
  32. package/dist/engine/validation.js +261 -0
  33. package/dist/engine/validation.js.map +1 -0
  34. package/dist/examples/examples/autosave-undo-demo.js +248 -0
  35. package/dist/examples/examples/persistence-demo.js +63 -0
  36. package/dist/examples/src/engine/condition.js +194 -0
  37. package/dist/examples/src/engine/core.js +1382 -0
  38. package/dist/examples/src/engine/demo-story.js +200 -0
  39. package/dist/examples/src/engine/types.js +8 -0
  40. package/dist/examples/src/index.js +35 -0
  41. package/dist/examples/src/integrations/react.js +322 -0
  42. package/dist/examples/src/narrative/branching/engine-simple.js +348 -0
  43. package/dist/examples/src/narrative/branching/index.js +55 -0
  44. package/dist/examples/src/narrative/branching/models.js +5 -0
  45. package/dist/examples/src/performance/ObjectPool.js +296 -0
  46. package/dist/examples/src/performance/PerfReporter.js +280 -0
  47. package/dist/examples/src/performance/ThreadPool.js +347 -0
  48. package/dist/index.d.ts +4 -0
  49. package/dist/index.d.ts.map +1 -1
  50. package/dist/index.js +12 -1
  51. package/dist/index.js.map +1 -1
  52. package/dist/integrations/react.d.ts +200 -0
  53. package/dist/integrations/react.d.ts.map +1 -0
  54. package/dist/integrations/react.js +365 -0
  55. package/dist/integrations/react.js.map +1 -0
  56. package/dist/narrative/branching/engine-simple.js +3 -3
  57. package/dist/narrative/branching/engine-simple.js.map +1 -1
  58. package/dist/narrative/branching/engine.d.ts +1 -0
  59. package/dist/narrative/branching/engine.d.ts.map +1 -0
  60. package/dist/narrative/branching/engine.js +2 -0
  61. package/dist/narrative/branching/engine.js.map +1 -0
  62. package/dist/narrative/branching/models.d.ts.map +1 -1
  63. package/dist/performance/HotReloadDelta.d.ts +25 -8
  64. package/dist/performance/HotReloadDelta.d.ts.map +1 -1
  65. package/dist/performance/HotReloadDelta.js +10 -15
  66. package/dist/performance/HotReloadDelta.js.map +1 -1
  67. package/dist/ui/__tests__/AutosaveIndicator.test.d.ts +2 -0
  68. package/dist/ui/__tests__/AutosaveIndicator.test.d.ts.map +1 -0
  69. package/dist/ui/__tests__/AutosaveIndicator.test.js +329 -0
  70. package/dist/ui/__tests__/AutosaveIndicator.test.js.map +1 -0
  71. package/dist/ui/__tests__/UndoRedoControls.test.d.ts +2 -0
  72. package/dist/ui/__tests__/UndoRedoControls.test.d.ts.map +1 -0
  73. package/dist/ui/__tests__/UndoRedoControls.test.js +245 -0
  74. package/dist/ui/__tests__/UndoRedoControls.test.js.map +1 -0
  75. package/dist/ui/__tests__/autosave-simple.test.d.ts +2 -0
  76. package/dist/ui/__tests__/autosave-simple.test.d.ts.map +1 -0
  77. package/dist/ui/__tests__/autosave-simple.test.js +29 -0
  78. package/dist/ui/__tests__/autosave-simple.test.js.map +1 -0
  79. package/dist/ui/__tests__/setup.d.ts +2 -0
  80. package/dist/ui/__tests__/setup.d.ts.map +1 -0
  81. package/dist/ui/__tests__/setup.js +40 -0
  82. package/dist/ui/__tests__/setup.js.map +1 -0
  83. package/dist/ui/__tests__/smoke-test.d.ts +2 -0
  84. package/dist/ui/__tests__/smoke-test.d.ts.map +1 -0
  85. package/dist/ui/__tests__/smoke-test.js +18 -0
  86. package/dist/ui/__tests__/smoke-test.js.map +1 -0
  87. package/dist/ui/__tests__/smoke-test.test.d.ts +2 -0
  88. package/dist/ui/__tests__/smoke-test.test.d.ts.map +1 -0
  89. package/dist/ui/__tests__/smoke-test.test.js +18 -0
  90. package/dist/ui/__tests__/smoke-test.test.js.map +1 -0
  91. package/dist/ui/__tests__/useKeyboardShortcuts.test.d.ts +2 -0
  92. package/dist/ui/__tests__/useKeyboardShortcuts.test.d.ts.map +1 -0
  93. package/dist/ui/__tests__/useKeyboardShortcuts.test.js +374 -0
  94. package/dist/ui/__tests__/useKeyboardShortcuts.test.js.map +1 -0
  95. package/dist/ui/components/AutosaveIndicator.d.ts +18 -0
  96. package/dist/ui/components/AutosaveIndicator.d.ts.map +1 -0
  97. package/dist/ui/components/AutosaveIndicator.js +175 -0
  98. package/dist/ui/components/AutosaveIndicator.js.map +1 -0
  99. package/dist/ui/components/UndoRedoControls.d.ts +16 -0
  100. package/dist/ui/components/UndoRedoControls.d.ts.map +1 -0
  101. package/dist/ui/components/UndoRedoControls.js +144 -0
  102. package/dist/ui/components/UndoRedoControls.js.map +1 -0
  103. package/dist/ui/hooks/useKeyboardShortcuts.d.ts +22 -0
  104. package/dist/ui/hooks/useKeyboardShortcuts.d.ts.map +1 -0
  105. package/dist/ui/hooks/useKeyboardShortcuts.js +162 -0
  106. package/dist/ui/hooks/useKeyboardShortcuts.js.map +1 -0
  107. package/dist/ui/index.d.ts +9 -0
  108. package/dist/ui/index.d.ts.map +1 -0
  109. package/dist/ui/index.js +14 -0
  110. package/dist/ui/index.js.map +1 -0
  111. package/dist/ui/types.d.ts +141 -0
  112. package/dist/ui/types.d.ts.map +1 -0
  113. package/dist/ui/types.js +51 -0
  114. package/dist/ui/types.js.map +1 -0
  115. package/examples/autosave-undo-demo.ts +306 -0
  116. package/examples/branching-demo-simple.ts +0 -0
  117. package/examples/branching-demo.ts +0 -0
  118. package/examples/persistence-demo.ts +84 -0
  119. package/examples/tsconfig.json +13 -0
  120. package/examples/ui-components-demo.tsx +320 -0
  121. package/examples/validation-demo-story.json +177 -0
  122. package/examples/validation-demo.js +163 -0
  123. package/package.json +23 -3
@@ -0,0 +1,348 @@
1
+ "use strict";
2
+ // QNCE Branching API - Simplified Runtime Implementation
3
+ // Sprint #3 - Advanced Narrative & AI Integration
4
+ Object.defineProperty(exports, "__esModule", { value: true });
5
+ exports.default = exports.QNCEBranchingEngine = void 0;
6
+ exports.createBranchingEngine = createBranchingEngine;
7
+ /**
8
+ * QNCE Branching Engine - Core API for dynamic narrative branching
9
+ * Simplified implementation focusing on core functionality
10
+ */
11
+ class QNCEBranchingEngine {
12
+ story;
13
+ context;
14
+ aiContext;
15
+ constructor(story, initialState) {
16
+ this.story = story;
17
+ this.context = this.createBranchContext(story, initialState);
18
+ }
19
+ // ================================
20
+ // Core Branching Operations
21
+ // ================================
22
+ /**
23
+ * Evaluate available branches from current position
24
+ */
25
+ async evaluateAvailableBranches() {
26
+ const currentNode = this.getCurrentNode();
27
+ const availableBranches = this.findBranchPointsForNode(this.context.currentFlow.id, currentNode.id);
28
+ const validOptions = [];
29
+ for (const branchPoint of availableBranches) {
30
+ // Evaluate branch point conditions
31
+ if (await this.evaluateConditions(branchPoint.conditions || [])) {
32
+ // Evaluate individual option conditions
33
+ for (const option of branchPoint.branchOptions) {
34
+ if (await this.evaluateConditions(option.conditions || [])) {
35
+ validOptions.push(option);
36
+ }
37
+ }
38
+ }
39
+ }
40
+ return validOptions;
41
+ }
42
+ /**
43
+ * Execute a branch transition
44
+ */
45
+ async executeBranch(optionId) {
46
+ const option = await this.findBranchOption(optionId);
47
+ if (!option) {
48
+ throw new Error(`Branch option not found: ${optionId}`);
49
+ }
50
+ // Apply flag effects
51
+ if (option.flagEffects) {
52
+ Object.assign(this.context.activeState.flags, option.flagEffects);
53
+ }
54
+ // Execute flow transition
55
+ const targetFlow = this.findFlow(option.targetFlowId);
56
+ if (!targetFlow) {
57
+ throw new Error(`Target flow not found: ${option.targetFlowId}`);
58
+ }
59
+ const success = await this.transitionToFlow(targetFlow, option.targetNodeId);
60
+ if (success) {
61
+ // Record branch execution in history
62
+ this.recordBranchHistory(optionId, option);
63
+ // Update analytics
64
+ this.updateBranchAnalytics(optionId);
65
+ }
66
+ return success;
67
+ }
68
+ /**
69
+ * Dynamic branch insertion at runtime
70
+ */
71
+ async insertDynamicBranch(operation) {
72
+ // Validate operation conditions
73
+ if (operation.conditions && !await this.evaluateConditions(operation.conditions)) {
74
+ return false;
75
+ }
76
+ // Find target location
77
+ const chapter = this.findChapter(operation.targetLocation.chapterId);
78
+ if (!chapter) {
79
+ throw new Error(`Chapter not found: ${operation.targetLocation.chapterId}`);
80
+ }
81
+ // Create new branch point
82
+ const branchPoint = {
83
+ id: operation.branchId,
84
+ name: operation.payload?.name || `Dynamic Branch ${operation.branchId}`,
85
+ sourceFlowId: operation.targetLocation.flowId,
86
+ sourceNodeId: operation.targetLocation.nodeId,
87
+ branchType: operation.payload?.branchType || 'conditional',
88
+ branchOptions: operation.payload?.branchOptions || [],
89
+ conditions: operation.conditions,
90
+ metadata: {
91
+ usageCount: 0,
92
+ avgTraversalTime: 0,
93
+ playerPreference: 0,
94
+ lastUsed: new Date()
95
+ }
96
+ };
97
+ // Insert into chapter
98
+ chapter.branches.push(branchPoint);
99
+ return true;
100
+ }
101
+ /**
102
+ * Remove dynamic branch
103
+ */
104
+ async removeDynamicBranch(branchId) {
105
+ for (const chapter of this.story.chapters) {
106
+ const index = chapter.branches.findIndex(b => b.id === branchId);
107
+ if (index !== -1) {
108
+ chapter.branches.splice(index, 1);
109
+ return true;
110
+ }
111
+ }
112
+ return false;
113
+ }
114
+ // ================================
115
+ // AI Integration Methods
116
+ // ================================
117
+ /**
118
+ * Set AI context for enhanced branching decisions
119
+ */
120
+ setAIContext(aiContext) {
121
+ this.aiContext = aiContext;
122
+ }
123
+ /**
124
+ * Generate AI-driven branch options
125
+ */
126
+ async generateAIBranches(maxOptions = 3) {
127
+ if (!this.aiContext) {
128
+ throw new Error('AI context not set. Call setAIContext() first.');
129
+ }
130
+ const generatedOptions = [];
131
+ const playerProfile = this.aiContext.playerProfile;
132
+ // Example: Generate options based on player style
133
+ if (playerProfile.playStyle === 'explorer') {
134
+ generatedOptions.push({
135
+ id: `ai-explore-${Date.now()}`,
136
+ targetFlowId: this.context.currentFlow.id,
137
+ displayText: "Investigate the mysterious artifact",
138
+ weight: 0.8
139
+ });
140
+ }
141
+ if (playerProfile.playStyle === 'socializer') {
142
+ generatedOptions.push({
143
+ id: `ai-social-${Date.now()}`,
144
+ targetFlowId: this.context.currentFlow.id,
145
+ displayText: "Ask your companion about their past",
146
+ weight: 0.7
147
+ });
148
+ }
149
+ return generatedOptions.slice(0, maxOptions);
150
+ }
151
+ // ================================
152
+ // Analytics & Monitoring
153
+ // ================================
154
+ /**
155
+ * Get current branching analytics
156
+ */
157
+ getBranchingAnalytics() {
158
+ return {
159
+ ...this.context.analytics,
160
+ currentChapter: this.context.currentChapter.id,
161
+ currentFlow: this.context.currentFlow.id,
162
+ historyLength: this.context.branchHistory.length,
163
+ pendingBranches: this.context.pendingBranches.length
164
+ };
165
+ }
166
+ /**
167
+ * Export branching data for external analysis
168
+ */
169
+ exportBranchingData() {
170
+ return {
171
+ story: {
172
+ id: this.story.id,
173
+ title: this.story.title,
174
+ version: this.story.version
175
+ },
176
+ session: {
177
+ startTime: this.context.analytics.sessionStartTime,
178
+ currentState: this.context.activeState,
179
+ branchHistory: this.context.branchHistory,
180
+ analytics: this.context.analytics
181
+ }
182
+ };
183
+ }
184
+ // ================================
185
+ // Private Helper Methods
186
+ // ================================
187
+ createBranchContext(story, initialState) {
188
+ const initialChapter = story.chapters[0];
189
+ const initialFlow = initialChapter.flows[0];
190
+ return {
191
+ currentStory: story,
192
+ currentChapter: initialChapter,
193
+ currentFlow: initialFlow,
194
+ activeState: initialState,
195
+ branchHistory: [],
196
+ pendingBranches: [],
197
+ analytics: {
198
+ totalBranchesTraversed: 0,
199
+ avgBranchDecisionTime: 0,
200
+ mostPopularBranches: [],
201
+ abandonmentPoints: [],
202
+ completionRate: 0,
203
+ sessionStartTime: new Date()
204
+ }
205
+ };
206
+ }
207
+ getCurrentNode() {
208
+ const currentNodeId = this.context.activeState.currentNodeId;
209
+ const node = this.context.currentFlow.nodes.find(n => n.id === currentNodeId);
210
+ if (!node) {
211
+ throw new Error(`Current node not found: ${currentNodeId}`);
212
+ }
213
+ return node;
214
+ }
215
+ findBranchPointsForNode(flowId, nodeId) {
216
+ return this.context.currentChapter.branches.filter(b => b.sourceFlowId === flowId && b.sourceNodeId === nodeId);
217
+ }
218
+ async evaluateConditions(conditions) {
219
+ for (const condition of conditions) {
220
+ if (!await this.evaluateCondition(condition)) {
221
+ return false;
222
+ }
223
+ }
224
+ return true;
225
+ }
226
+ async evaluateCondition(condition) {
227
+ const { operator, key, value, evaluator } = condition;
228
+ // Custom evaluator takes precedence
229
+ if (evaluator) {
230
+ return evaluator(this.context.activeState, this.context);
231
+ }
232
+ // Standard condition evaluation
233
+ const stateValue = this.context.activeState.flags[key];
234
+ switch (operator) {
235
+ case 'equals':
236
+ return stateValue === value;
237
+ case 'not_equals':
238
+ return stateValue !== value;
239
+ case 'greater':
240
+ return Number(stateValue) > Number(value);
241
+ case 'less':
242
+ return Number(stateValue) < Number(value);
243
+ case 'contains':
244
+ return Array.isArray(stateValue) && stateValue.includes(value);
245
+ case 'exists':
246
+ return stateValue !== undefined;
247
+ default:
248
+ return false;
249
+ }
250
+ }
251
+ async findBranchOption(optionId) {
252
+ for (const chapter of this.story.chapters) {
253
+ for (const branchPoint of chapter.branches) {
254
+ const option = branchPoint.branchOptions.find(o => o.id === optionId);
255
+ if (option) {
256
+ return option;
257
+ }
258
+ }
259
+ }
260
+ return null;
261
+ }
262
+ findFlow(flowId) {
263
+ for (const chapter of this.story.chapters) {
264
+ const flow = chapter.flows.find(f => f.id === flowId);
265
+ if (flow) {
266
+ return flow;
267
+ }
268
+ }
269
+ return null;
270
+ }
271
+ findChapter(chapterId) {
272
+ return this.story.chapters.find(c => c.id === chapterId) || null;
273
+ }
274
+ async transitionToFlow(targetFlow, targetNodeId) {
275
+ // Find entry point
276
+ let entryNodeId;
277
+ if (targetNodeId) {
278
+ entryNodeId = targetNodeId;
279
+ }
280
+ else if (targetFlow.entryPoints.length > 0) {
281
+ // Use highest priority entry point
282
+ const entryPoint = targetFlow.entryPoints.sort((a, b) => b.priority - a.priority)[0];
283
+ entryNodeId = entryPoint.nodeId;
284
+ }
285
+ else if (targetFlow.nodes.length > 0) {
286
+ // Use first node as fallback
287
+ entryNodeId = targetFlow.nodes[0].id;
288
+ }
289
+ else {
290
+ return false;
291
+ }
292
+ // Update context
293
+ this.context.currentFlow = targetFlow;
294
+ this.context.activeState.currentNodeId = entryNodeId;
295
+ this.context.activeState.history.push(entryNodeId);
296
+ // Update chapter if necessary
297
+ const targetChapter = this.story.chapters.find(c => c.flows.some(f => f.id === targetFlow.id));
298
+ if (targetChapter) {
299
+ this.context.currentChapter = targetChapter;
300
+ }
301
+ return true;
302
+ }
303
+ recordBranchHistory(optionId, option) {
304
+ const historyEntry = {
305
+ id: `history-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
306
+ branchPointId: 'unknown', // Would need to track this
307
+ chosenOptionId: optionId,
308
+ timestamp: new Date(),
309
+ executionTime: 0, // Would measure actual execution time
310
+ context: {
311
+ currentNodeId: this.context.activeState.currentNodeId,
312
+ flags: { ...this.context.activeState.flags }
313
+ }
314
+ };
315
+ this.context.branchHistory.push(historyEntry);
316
+ // Limit history size for memory management
317
+ if (this.context.branchHistory.length > 1000) {
318
+ this.context.branchHistory.splice(0, 100);
319
+ }
320
+ }
321
+ updateBranchAnalytics(optionId) {
322
+ this.context.analytics.totalBranchesTraversed++;
323
+ // Update popular branches
324
+ const popular = this.context.analytics.mostPopularBranches;
325
+ const index = popular.indexOf(optionId);
326
+ if (index !== -1) {
327
+ // Move to front
328
+ popular.splice(index, 1);
329
+ popular.unshift(optionId);
330
+ }
331
+ else {
332
+ // Add to front
333
+ popular.unshift(optionId);
334
+ // Limit to top 10
335
+ if (popular.length > 10) {
336
+ popular.pop();
337
+ }
338
+ }
339
+ }
340
+ }
341
+ exports.QNCEBranchingEngine = QNCEBranchingEngine;
342
+ exports.default = QNCEBranchingEngine;
343
+ /**
344
+ * Factory function for creating branching engines
345
+ */
346
+ function createBranchingEngine(story, initialState) {
347
+ return new QNCEBranchingEngine(story, initialState);
348
+ }
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ // QNCE Branching System - Main Export
3
+ // Sprint #3 - Advanced Narrative & AI Integration
4
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
5
+ if (k2 === undefined) k2 = k;
6
+ var desc = Object.getOwnPropertyDescriptor(m, k);
7
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
8
+ desc = { enumerable: true, get: function() { return m[k]; } };
9
+ }
10
+ Object.defineProperty(o, k2, desc);
11
+ }) : (function(o, m, k, k2) {
12
+ if (k2 === undefined) k2 = k;
13
+ o[k2] = m[k];
14
+ }));
15
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
16
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
17
+ };
18
+ Object.defineProperty(exports, "__esModule", { value: true });
19
+ exports.example = exports.createBranchingEngine = exports.QNCEBranchingEngine = void 0;
20
+ // Core branching engine
21
+ var engine_simple_1 = require("./engine-simple");
22
+ Object.defineProperty(exports, "QNCEBranchingEngine", { enumerable: true, get: function () { return engine_simple_1.QNCEBranchingEngine; } });
23
+ Object.defineProperty(exports, "createBranchingEngine", { enumerable: true, get: function () { return engine_simple_1.createBranchingEngine; } });
24
+ // Complete type system
25
+ __exportStar(require("./models"), exports);
26
+ // Usage example for documentation
27
+ exports.example = {
28
+ // Example story structure showing branching capabilities
29
+ storyStructure: {
30
+ story: 'QNCEStory with chapters and branching config',
31
+ chapters: 'Logical groupings of flows and branch points',
32
+ flows: 'Sequences of narrative nodes with entry/exit points',
33
+ branches: 'Dynamic decision points with conditions and options'
34
+ },
35
+ // Example API usage
36
+ usage: `
37
+ import { createBranchingEngine, QNCEStory } from 'qnce-engine/branching';
38
+
39
+ // Create branching engine
40
+ const engine = createBranchingEngine(story, initialState);
41
+
42
+ // Evaluate available branches
43
+ const options = await engine.evaluateAvailableBranches();
44
+
45
+ // Execute player choice
46
+ await engine.executeBranch(selectedOptionId);
47
+
48
+ // Dynamic content insertion
49
+ await engine.insertDynamicBranch(branchOperation);
50
+
51
+ // AI-driven content generation
52
+ engine.setAIContext(aiContext);
53
+ const aiBranches = await engine.generateAIBranches();
54
+ `
55
+ };
@@ -0,0 +1,5 @@
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
@@ -0,0 +1,296 @@
1
+ "use strict";
2
+ // S2-T1: Object Pooling for Narrative Objects
3
+ // Generic object pool to eliminate runtime allocations and reduce GC pressure
4
+ Object.defineProperty(exports, "__esModule", { value: true });
5
+ exports.poolManager = exports.PoolManager = exports.PooledAsset = exports.PooledNode = exports.PooledFlow = exports.ObjectPool = void 0;
6
+ /**
7
+ * Generic Object Pool for QNCE narrative objects
8
+ * Reduces GC pressure by reusing objects instead of creating new ones
9
+ */
10
+ class ObjectPool {
11
+ pool = [];
12
+ createFn;
13
+ maxSize;
14
+ created = 0;
15
+ borrowed = 0;
16
+ returned = 0;
17
+ constructor(createFn, initialSize = 10, maxSize = 100) {
18
+ this.createFn = createFn;
19
+ this.maxSize = maxSize;
20
+ // Pre-populate pool
21
+ for (let i = 0; i < initialSize; i++) {
22
+ const obj = this.createFn();
23
+ obj.setInUse(false);
24
+ this.pool.push(obj);
25
+ this.created++;
26
+ }
27
+ }
28
+ /**
29
+ * Borrow an object from the pool
30
+ */
31
+ borrow() {
32
+ let obj;
33
+ if (this.pool.length > 0) {
34
+ obj = this.pool.pop();
35
+ }
36
+ else {
37
+ // Pool exhausted, create new object
38
+ obj = this.createFn();
39
+ this.created++;
40
+ }
41
+ obj.reset();
42
+ obj.setInUse(true);
43
+ this.borrowed++;
44
+ return obj;
45
+ }
46
+ /**
47
+ * Return an object to the pool
48
+ */
49
+ return(obj) {
50
+ if (!obj.isInUse()) {
51
+ console.warn('Attempting to return object that is not in use');
52
+ return;
53
+ }
54
+ obj.setInUse(false);
55
+ this.returned++;
56
+ // Only return to pool if under max size
57
+ if (this.pool.length < this.maxSize) {
58
+ this.pool.push(obj);
59
+ }
60
+ // Otherwise let GC handle it (controlled disposal)
61
+ }
62
+ /**
63
+ * Get pool statistics for monitoring
64
+ */
65
+ getStats() {
66
+ return {
67
+ poolSize: this.pool.length,
68
+ maxSize: this.maxSize,
69
+ created: this.created,
70
+ borrowed: this.borrowed,
71
+ returned: this.returned,
72
+ inUse: this.borrowed - this.returned,
73
+ hitRate: this.borrowed > 0 ? ((this.borrowed - this.created) / this.borrowed) * 100 : 0
74
+ };
75
+ }
76
+ /**
77
+ * Clear the pool (useful for testing)
78
+ */
79
+ clear() {
80
+ this.pool.forEach(obj => obj.setInUse(false));
81
+ this.pool.length = 0;
82
+ this.created = 0;
83
+ this.borrowed = 0;
84
+ this.returned = 0;
85
+ }
86
+ /**
87
+ * Resize the pool
88
+ */
89
+ resize(newSize) {
90
+ if (newSize < this.pool.length) {
91
+ // Shrink pool
92
+ this.pool.splice(newSize);
93
+ }
94
+ else if (newSize > this.pool.length) {
95
+ // Grow pool
96
+ const toAdd = newSize - this.pool.length;
97
+ for (let i = 0; i < toAdd && this.pool.length < this.maxSize; i++) {
98
+ const obj = this.createFn();
99
+ obj.setInUse(false);
100
+ this.pool.push(obj);
101
+ this.created++;
102
+ }
103
+ }
104
+ }
105
+ }
106
+ exports.ObjectPool = ObjectPool;
107
+ /**
108
+ * Pooled Flow object for narrative state management
109
+ */
110
+ class PooledFlow {
111
+ _inUse = false;
112
+ nodeId = '';
113
+ timestamp = 0;
114
+ metadata = {};
115
+ transitions = [];
116
+ constructor() {
117
+ this.reset();
118
+ }
119
+ reset() {
120
+ this.nodeId = '';
121
+ this.timestamp = 0;
122
+ this.metadata = {};
123
+ this.transitions.length = 0;
124
+ }
125
+ isInUse() {
126
+ return this._inUse;
127
+ }
128
+ setInUse(inUse) {
129
+ this._inUse = inUse;
130
+ }
131
+ // Flow-specific methods
132
+ initialize(nodeId, metadata) {
133
+ this.nodeId = nodeId;
134
+ this.timestamp = performance.now();
135
+ if (metadata) {
136
+ this.metadata = { ...metadata };
137
+ }
138
+ }
139
+ addTransition(fromNodeId, toNodeId) {
140
+ this.transitions.push(`${fromNodeId}->${toNodeId}`);
141
+ }
142
+ getDuration() {
143
+ return performance.now() - this.timestamp;
144
+ }
145
+ }
146
+ exports.PooledFlow = PooledFlow;
147
+ /**
148
+ * Pooled Node object for narrative content
149
+ */
150
+ class PooledNode {
151
+ _inUse = false;
152
+ id = '';
153
+ text = '';
154
+ choices = [];
155
+ flags = {};
156
+ lastAccessed = 0;
157
+ constructor() {
158
+ this.reset();
159
+ }
160
+ reset() {
161
+ this.id = '';
162
+ this.text = '';
163
+ this.choices.length = 0;
164
+ this.flags = {};
165
+ this.lastAccessed = 0;
166
+ }
167
+ isInUse() {
168
+ return this._inUse;
169
+ }
170
+ setInUse(inUse) {
171
+ this._inUse = inUse;
172
+ }
173
+ // Node-specific methods
174
+ initialize(id, text, choices = []) {
175
+ this.id = id;
176
+ this.text = text;
177
+ this.choices = [...choices];
178
+ this.lastAccessed = performance.now();
179
+ }
180
+ touch() {
181
+ this.lastAccessed = performance.now();
182
+ }
183
+ }
184
+ exports.PooledNode = PooledNode;
185
+ /**
186
+ * Pooled Asset object for narrative resources
187
+ */
188
+ class PooledAsset {
189
+ _inUse = false;
190
+ id = '';
191
+ type = '';
192
+ data = null;
193
+ size = 0;
194
+ loaded = false;
195
+ constructor() {
196
+ this.reset();
197
+ }
198
+ reset() {
199
+ this.id = '';
200
+ this.type = '';
201
+ this.data = null;
202
+ this.size = 0;
203
+ this.loaded = false;
204
+ }
205
+ isInUse() {
206
+ return this._inUse;
207
+ }
208
+ setInUse(inUse) {
209
+ this._inUse = inUse;
210
+ }
211
+ // Asset-specific methods
212
+ initialize(id, type, data) {
213
+ this.id = id;
214
+ this.type = type;
215
+ this.data = data;
216
+ this.size = this.calculateSize(data);
217
+ this.loaded = true;
218
+ }
219
+ calculateSize(data) {
220
+ if (typeof data === 'string') {
221
+ return data.length * 2; // Approximate UTF-16 size
222
+ }
223
+ if (data && typeof data === 'object') {
224
+ return JSON.stringify(data).length * 2;
225
+ }
226
+ return 0;
227
+ }
228
+ }
229
+ exports.PooledAsset = PooledAsset;
230
+ /**
231
+ * Pool Manager - Centralized management of all object pools
232
+ */
233
+ class PoolManager {
234
+ static instance;
235
+ flowPool;
236
+ nodePool;
237
+ assetPool;
238
+ constructor() {
239
+ // Initialize pools with different sizes based on expected usage
240
+ this.flowPool = new ObjectPool(() => new PooledFlow(), 5, 50);
241
+ this.nodePool = new ObjectPool(() => new PooledNode(), 20, 200);
242
+ this.assetPool = new ObjectPool(() => new PooledAsset(), 10, 100);
243
+ }
244
+ static getInstance() {
245
+ if (!PoolManager.instance) {
246
+ PoolManager.instance = new PoolManager();
247
+ }
248
+ return PoolManager.instance;
249
+ }
250
+ // Flow pool methods
251
+ borrowFlow() {
252
+ return this.flowPool.borrow();
253
+ }
254
+ returnFlow(flow) {
255
+ this.flowPool.return(flow);
256
+ }
257
+ // Node pool methods
258
+ borrowNode() {
259
+ return this.nodePool.borrow();
260
+ }
261
+ returnNode(node) {
262
+ this.nodePool.return(node);
263
+ }
264
+ // Asset pool methods
265
+ borrowAsset() {
266
+ return this.assetPool.borrow();
267
+ }
268
+ returnAsset(asset) {
269
+ this.assetPool.return(asset);
270
+ }
271
+ // Statistics and monitoring
272
+ getAllStats() {
273
+ return {
274
+ flows: this.flowPool.getStats(),
275
+ nodes: this.nodePool.getStats(),
276
+ assets: this.assetPool.getStats()
277
+ };
278
+ }
279
+ // Performance monitoring
280
+ getGCPressureReduction() {
281
+ const stats = this.getAllStats();
282
+ const totalHitRate = (stats.flows.hitRate +
283
+ stats.nodes.hitRate +
284
+ stats.assets.hitRate) / 3;
285
+ return totalHitRate;
286
+ }
287
+ // Cleanup for testing
288
+ clearAllPools() {
289
+ this.flowPool.clear();
290
+ this.nodePool.clear();
291
+ this.assetPool.clear();
292
+ }
293
+ }
294
+ exports.PoolManager = PoolManager;
295
+ // Export singleton instance
296
+ exports.poolManager = PoolManager.getInstance();