qnce-engine 0.1.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 (154) hide show
  1. package/README.md +959 -5
  2. package/dist/cli/audit.js +6 -4
  3. package/dist/cli/audit.js.map +1 -1
  4. package/dist/cli/init.js +11 -9
  5. package/dist/cli/init.js.map +1 -1
  6. package/dist/cli/perf.d.ts +30 -0
  7. package/dist/cli/perf.d.ts.map +1 -0
  8. package/dist/cli/perf.js +220 -0
  9. package/dist/cli/perf.js.map +1 -0
  10. package/dist/cli/play.d.ts +4 -0
  11. package/dist/cli/play.d.ts.map +1 -0
  12. package/dist/cli/play.js +259 -0
  13. package/dist/cli/play.js.map +1 -0
  14. package/dist/engine/condition.d.ts +69 -0
  15. package/dist/engine/condition.d.ts.map +1 -0
  16. package/dist/engine/condition.js +195 -0
  17. package/dist/engine/condition.js.map +1 -0
  18. package/dist/engine/core.d.ts +378 -11
  19. package/dist/engine/core.d.ts.map +1 -1
  20. package/dist/engine/core.js +1433 -13
  21. package/dist/engine/core.js.map +1 -1
  22. package/dist/engine/demo-story.d.ts.map +1 -1
  23. package/dist/engine/demo-story.js +103 -14
  24. package/dist/engine/demo-story.js.map +1 -1
  25. package/dist/engine/errors.d.ts +76 -0
  26. package/dist/engine/errors.d.ts.map +1 -0
  27. package/dist/engine/errors.js +178 -0
  28. package/dist/engine/errors.js.map +1 -0
  29. package/dist/engine/types.d.ts +445 -0
  30. package/dist/engine/types.d.ts.map +1 -0
  31. package/dist/engine/types.js +9 -0
  32. package/dist/engine/types.js.map +1 -0
  33. package/dist/engine/validation.d.ts +110 -0
  34. package/dist/engine/validation.d.ts.map +1 -0
  35. package/dist/engine/validation.js +261 -0
  36. package/dist/engine/validation.js.map +1 -0
  37. package/dist/examples/examples/autosave-undo-demo.js +248 -0
  38. package/dist/examples/examples/persistence-demo.js +63 -0
  39. package/dist/examples/src/engine/condition.js +194 -0
  40. package/dist/examples/src/engine/core.js +1382 -0
  41. package/dist/examples/src/engine/demo-story.js +200 -0
  42. package/dist/examples/src/engine/types.js +8 -0
  43. package/dist/examples/src/index.js +35 -0
  44. package/dist/examples/src/integrations/react.js +322 -0
  45. package/dist/examples/src/narrative/branching/engine-simple.js +348 -0
  46. package/dist/examples/src/narrative/branching/index.js +55 -0
  47. package/dist/examples/src/narrative/branching/models.js +5 -0
  48. package/dist/examples/src/performance/ObjectPool.js +296 -0
  49. package/dist/examples/src/performance/PerfReporter.js +280 -0
  50. package/dist/examples/src/performance/ThreadPool.js +347 -0
  51. package/dist/index.d.ts +4 -0
  52. package/dist/index.d.ts.map +1 -1
  53. package/dist/index.js +35 -3
  54. package/dist/index.js.map +1 -1
  55. package/dist/integrations/react.d.ts +200 -0
  56. package/dist/integrations/react.d.ts.map +1 -0
  57. package/dist/integrations/react.js +365 -0
  58. package/dist/integrations/react.js.map +1 -0
  59. package/dist/narrative/branching/engine-simple.d.ts +84 -0
  60. package/dist/narrative/branching/engine-simple.d.ts.map +1 -0
  61. package/dist/narrative/branching/engine-simple.js +349 -0
  62. package/dist/narrative/branching/engine-simple.js.map +1 -0
  63. package/dist/narrative/branching/engine.d.ts +1 -0
  64. package/dist/narrative/branching/engine.d.ts.map +1 -0
  65. package/dist/narrative/branching/engine.js +2 -0
  66. package/dist/narrative/branching/engine.js.map +1 -0
  67. package/dist/narrative/branching/index.d.ts +12 -0
  68. package/dist/narrative/branching/index.d.ts.map +1 -0
  69. package/dist/narrative/branching/index.js +56 -0
  70. package/dist/narrative/branching/index.js.map +1 -0
  71. package/dist/narrative/branching/models.d.ts +223 -0
  72. package/dist/narrative/branching/models.d.ts.map +1 -0
  73. package/dist/narrative/branching/models.js +6 -0
  74. package/dist/narrative/branching/models.js.map +1 -0
  75. package/dist/performance/HotReloadDelta.d.ts +124 -0
  76. package/dist/performance/HotReloadDelta.d.ts.map +1 -0
  77. package/dist/performance/HotReloadDelta.js +328 -0
  78. package/dist/performance/HotReloadDelta.js.map +1 -0
  79. package/dist/performance/ObjectPool.d.ts +150 -0
  80. package/dist/performance/ObjectPool.d.ts.map +1 -0
  81. package/dist/performance/ObjectPool.js +297 -0
  82. package/dist/performance/ObjectPool.js.map +1 -0
  83. package/dist/performance/PerfReporter.d.ts +123 -0
  84. package/dist/performance/PerfReporter.d.ts.map +1 -0
  85. package/dist/performance/PerfReporter.js +281 -0
  86. package/dist/performance/PerfReporter.js.map +1 -0
  87. package/dist/performance/ThreadPool.d.ts +107 -0
  88. package/dist/performance/ThreadPool.d.ts.map +1 -0
  89. package/dist/performance/ThreadPool.js +348 -0
  90. package/dist/performance/ThreadPool.js.map +1 -0
  91. package/dist/ui/__tests__/AutosaveIndicator.test.d.ts +2 -0
  92. package/dist/ui/__tests__/AutosaveIndicator.test.d.ts.map +1 -0
  93. package/dist/ui/__tests__/AutosaveIndicator.test.js +329 -0
  94. package/dist/ui/__tests__/AutosaveIndicator.test.js.map +1 -0
  95. package/dist/ui/__tests__/UndoRedoControls.test.d.ts +2 -0
  96. package/dist/ui/__tests__/UndoRedoControls.test.d.ts.map +1 -0
  97. package/dist/ui/__tests__/UndoRedoControls.test.js +245 -0
  98. package/dist/ui/__tests__/UndoRedoControls.test.js.map +1 -0
  99. package/dist/ui/__tests__/autosave-simple.test.d.ts +2 -0
  100. package/dist/ui/__tests__/autosave-simple.test.d.ts.map +1 -0
  101. package/dist/ui/__tests__/autosave-simple.test.js +29 -0
  102. package/dist/ui/__tests__/autosave-simple.test.js.map +1 -0
  103. package/dist/ui/__tests__/setup.d.ts +2 -0
  104. package/dist/ui/__tests__/setup.d.ts.map +1 -0
  105. package/dist/ui/__tests__/setup.js +40 -0
  106. package/dist/ui/__tests__/setup.js.map +1 -0
  107. package/dist/ui/__tests__/smoke-test.d.ts +2 -0
  108. package/dist/ui/__tests__/smoke-test.d.ts.map +1 -0
  109. package/dist/ui/__tests__/smoke-test.js +18 -0
  110. package/dist/ui/__tests__/smoke-test.js.map +1 -0
  111. package/dist/ui/__tests__/smoke-test.test.d.ts +2 -0
  112. package/dist/ui/__tests__/smoke-test.test.d.ts.map +1 -0
  113. package/dist/ui/__tests__/smoke-test.test.js +18 -0
  114. package/dist/ui/__tests__/smoke-test.test.js.map +1 -0
  115. package/dist/ui/__tests__/useKeyboardShortcuts.test.d.ts +2 -0
  116. package/dist/ui/__tests__/useKeyboardShortcuts.test.d.ts.map +1 -0
  117. package/dist/ui/__tests__/useKeyboardShortcuts.test.js +374 -0
  118. package/dist/ui/__tests__/useKeyboardShortcuts.test.js.map +1 -0
  119. package/dist/ui/components/AutosaveIndicator.d.ts +18 -0
  120. package/dist/ui/components/AutosaveIndicator.d.ts.map +1 -0
  121. package/dist/ui/components/AutosaveIndicator.js +175 -0
  122. package/dist/ui/components/AutosaveIndicator.js.map +1 -0
  123. package/dist/ui/components/UndoRedoControls.d.ts +16 -0
  124. package/dist/ui/components/UndoRedoControls.d.ts.map +1 -0
  125. package/dist/ui/components/UndoRedoControls.js +144 -0
  126. package/dist/ui/components/UndoRedoControls.js.map +1 -0
  127. package/dist/ui/hooks/useKeyboardShortcuts.d.ts +22 -0
  128. package/dist/ui/hooks/useKeyboardShortcuts.d.ts.map +1 -0
  129. package/dist/ui/hooks/useKeyboardShortcuts.js +162 -0
  130. package/dist/ui/hooks/useKeyboardShortcuts.js.map +1 -0
  131. package/dist/ui/index.d.ts +9 -0
  132. package/dist/ui/index.d.ts.map +1 -0
  133. package/dist/ui/index.js +14 -0
  134. package/dist/ui/index.js.map +1 -0
  135. package/dist/ui/types.d.ts +141 -0
  136. package/dist/ui/types.d.ts.map +1 -0
  137. package/dist/ui/types.js +51 -0
  138. package/dist/ui/types.js.map +1 -0
  139. package/docs/PERFORMANCE.md +355 -0
  140. package/docs/branching/ERD.md +214 -0
  141. package/docs/branching/PDM.md +443 -0
  142. package/docs/branching/RELEASE-v1.2.0.md +169 -0
  143. package/examples/autosave-undo-demo.ts +306 -0
  144. package/examples/branching-advanced-demo.ts +339 -0
  145. package/examples/branching-demo-simple.ts +0 -0
  146. package/examples/branching-demo.ts +0 -0
  147. package/examples/branching-quickstart.ts +314 -0
  148. package/examples/persistence-demo.ts +84 -0
  149. package/examples/quickstart-demo.js +82 -0
  150. package/examples/tsconfig.json +13 -0
  151. package/examples/ui-components-demo.tsx +320 -0
  152. package/examples/validation-demo-story.json +177 -0
  153. package/examples/validation-demo.js +163 -0
  154. package/package.json +41 -8
@@ -0,0 +1,280 @@
1
+ "use strict";
2
+ // S2-T4: Profiler Event Instrumentation
3
+ // PerfReporter for batched performance event collection and reporting
4
+ Object.defineProperty(exports, "__esModule", { value: true });
5
+ exports.perf = exports.PerfReporter = void 0;
6
+ exports.getPerfReporter = getPerfReporter;
7
+ exports.shutdownPerfReporter = shutdownPerfReporter;
8
+ /**
9
+ * PerfReporter - Batched performance event collection and analysis
10
+ * Designed to work off main thread for minimal performance impact
11
+ */
12
+ class PerfReporter {
13
+ events = [];
14
+ config;
15
+ flushTimer = null;
16
+ startTime;
17
+ activeSpans = new Map();
18
+ constructor(config = {}) {
19
+ this.config = {
20
+ batchSize: config.batchSize || 100,
21
+ flushInterval: config.flushInterval || 5000, // 5 seconds
22
+ enableBackgroundFlush: config.enableBackgroundFlush !== false,
23
+ maxEventHistory: config.maxEventHistory || 1000,
24
+ enableConsoleOutput: config.enableConsoleOutput || false
25
+ };
26
+ this.startTime = performance.now();
27
+ if (this.config.enableBackgroundFlush) {
28
+ this.startFlushTimer();
29
+ }
30
+ }
31
+ /**
32
+ * Record a performance event
33
+ */
34
+ record(type, metadata = {}, category = 'engine') {
35
+ const event = {
36
+ id: this.generateEventId(),
37
+ type,
38
+ timestamp: performance.now(),
39
+ metadata,
40
+ category
41
+ };
42
+ this.events.push(event);
43
+ if (this.config.enableConsoleOutput) {
44
+ console.log(`[PERF] ${type}:`, metadata);
45
+ }
46
+ // Auto-flush if batch size reached
47
+ if (this.events.length >= this.config.batchSize) {
48
+ this.flush();
49
+ }
50
+ return event.id;
51
+ }
52
+ /**
53
+ * Start a performance span (for measuring duration)
54
+ */
55
+ startSpan(type, metadata = {}, category = 'engine') {
56
+ const spanId = this.generateEventId();
57
+ const event = {
58
+ id: spanId,
59
+ type,
60
+ timestamp: performance.now(),
61
+ metadata: { ...metadata, spanStart: true },
62
+ category
63
+ };
64
+ this.activeSpans.set(spanId, event);
65
+ return spanId;
66
+ }
67
+ /**
68
+ * End a performance span and record the complete event
69
+ */
70
+ endSpan(spanId, additionalMetadata = {}) {
71
+ const startEvent = this.activeSpans.get(spanId);
72
+ if (!startEvent) {
73
+ console.warn(`[PERF] Span ${spanId} not found`);
74
+ return;
75
+ }
76
+ const endTime = performance.now();
77
+ const duration = endTime - startEvent.timestamp;
78
+ const completeEvent = {
79
+ ...startEvent,
80
+ duration,
81
+ metadata: {
82
+ ...startEvent.metadata,
83
+ ...additionalMetadata,
84
+ spanEnd: true
85
+ }
86
+ };
87
+ this.events.push(completeEvent);
88
+ this.activeSpans.delete(spanId);
89
+ if (this.config.enableConsoleOutput) {
90
+ console.log(`[PERF] ${startEvent.type} completed in ${duration.toFixed(2)}ms`);
91
+ }
92
+ // Auto-flush if batch size reached
93
+ if (this.events.length >= this.config.batchSize) {
94
+ this.flush();
95
+ }
96
+ }
97
+ /**
98
+ * Record flow start event (S2-T4 requirement)
99
+ */
100
+ recordFlowStart(nodeId, metadata = {}) {
101
+ return this.startSpan('flow-start', { nodeId, ...metadata }, 'engine');
102
+ }
103
+ /**
104
+ * Record flow completion event (S2-T4 requirement)
105
+ */
106
+ recordFlowComplete(spanId, nextNodeId, metadata = {}) {
107
+ this.endSpan(spanId, { nextNodeId, ...metadata });
108
+ }
109
+ /**
110
+ * Record cache hit event (S2-T4 requirement)
111
+ */
112
+ recordCacheHit(cacheKey, metadata = {}) {
113
+ return this.record('cache-hit', { cacheKey, ...metadata }, 'cache');
114
+ }
115
+ /**
116
+ * Record cache miss event (S2-T4 requirement)
117
+ */
118
+ recordCacheMiss(cacheKey, metadata = {}) {
119
+ return this.record('cache-miss', { cacheKey, ...metadata }, 'cache');
120
+ }
121
+ /**
122
+ * Record hot-reload start event (S2-T4 requirement)
123
+ */
124
+ recordHotReloadStart(deltaSize, metadata = {}) {
125
+ return this.startSpan('hot-reload-start', { deltaSize, ...metadata }, 'hot-reload');
126
+ }
127
+ /**
128
+ * Record hot-reload completion event (S2-T4 requirement)
129
+ */
130
+ recordHotReloadEnd(spanId, success, metadata = {}) {
131
+ this.endSpan(spanId, { success, ...metadata });
132
+ }
133
+ /**
134
+ * Generate performance summary for CLI dashboard
135
+ */
136
+ summary() {
137
+ const now = performance.now();
138
+ const eventsByType = {};
139
+ const durationsByType = {};
140
+ let cacheHits = 0;
141
+ let cacheMisses = 0;
142
+ let hotReloads = [];
143
+ // Analyze events
144
+ for (const event of this.events) {
145
+ // Count events by type
146
+ eventsByType[event.type] = (eventsByType[event.type] || 0) + 1;
147
+ // Collect durations
148
+ if (event.duration !== undefined) {
149
+ if (!durationsByType[event.type]) {
150
+ durationsByType[event.type] = [];
151
+ }
152
+ durationsByType[event.type].push(event.duration);
153
+ }
154
+ // Cache metrics
155
+ if (event.type === 'cache-hit')
156
+ cacheHits++;
157
+ if (event.type === 'cache-miss')
158
+ cacheMisses++;
159
+ // Hot-reload metrics
160
+ if (event.type === 'hot-reload-start' && event.duration !== undefined) {
161
+ hotReloads.push(event.duration);
162
+ }
163
+ }
164
+ // Calculate averages, mins, maxs
165
+ const avgDurations = {};
166
+ const maxDurations = {};
167
+ const minDurations = {};
168
+ for (const [type, durations] of Object.entries(durationsByType)) {
169
+ if (durations.length > 0) {
170
+ avgDurations[type] = durations.reduce((sum, d) => sum + d, 0) / durations.length;
171
+ maxDurations[type] = Math.max(...durations);
172
+ minDurations[type] = Math.min(...durations);
173
+ }
174
+ }
175
+ // Cache hit rate
176
+ const totalCacheEvents = cacheHits + cacheMisses;
177
+ const cacheHitRate = totalCacheEvents > 0 ? (cacheHits / totalCacheEvents) * 100 : 0;
178
+ // Hot-reload performance
179
+ const hotReloadPerformance = {
180
+ avgTime: hotReloads.length > 0 ? hotReloads.reduce((sum, d) => sum + d, 0) / hotReloads.length : 0,
181
+ maxTime: hotReloads.length > 0 ? Math.max(...hotReloads) : 0,
182
+ totalReloads: hotReloads.length
183
+ };
184
+ return {
185
+ totalEvents: this.events.length,
186
+ eventsByType,
187
+ avgDurations,
188
+ maxDurations,
189
+ minDurations,
190
+ cacheHitRate,
191
+ hotReloadPerformance,
192
+ timeRange: {
193
+ start: this.startTime,
194
+ end: now,
195
+ duration: now - this.startTime
196
+ }
197
+ };
198
+ }
199
+ /**
200
+ * Get raw events for detailed analysis
201
+ */
202
+ getEvents() {
203
+ return [...this.events];
204
+ }
205
+ /**
206
+ * Clear event history
207
+ */
208
+ clear() {
209
+ this.events.length = 0;
210
+ this.activeSpans.clear();
211
+ this.startTime = performance.now();
212
+ }
213
+ /**
214
+ * Flush events to background processing
215
+ */
216
+ flush() {
217
+ if (this.events.length === 0)
218
+ return;
219
+ // TODO: Body - integrate with ThreadPool for background processing
220
+ // For now, just maintain event history with size limit
221
+ if (this.events.length > this.config.maxEventHistory) {
222
+ const excess = this.events.length - this.config.maxEventHistory;
223
+ this.events.splice(0, excess);
224
+ }
225
+ if (this.config.enableConsoleOutput) {
226
+ console.log(`[PERF] Flushed ${this.events.length} events`);
227
+ }
228
+ }
229
+ /**
230
+ * Start automatic flush timer
231
+ */
232
+ startFlushTimer() {
233
+ this.flushTimer = setInterval(() => {
234
+ this.flush();
235
+ }, this.config.flushInterval);
236
+ }
237
+ /**
238
+ * Stop automatic flush timer
239
+ */
240
+ stopFlushTimer() {
241
+ if (this.flushTimer) {
242
+ clearInterval(this.flushTimer);
243
+ this.flushTimer = null;
244
+ }
245
+ }
246
+ /**
247
+ * Generate unique event ID
248
+ */
249
+ generateEventId() {
250
+ return `perf-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
251
+ }
252
+ }
253
+ exports.PerfReporter = PerfReporter;
254
+ // Singleton instance for global access
255
+ let globalPerfReporter = null;
256
+ function getPerfReporter(config) {
257
+ if (!globalPerfReporter) {
258
+ globalPerfReporter = new PerfReporter(config);
259
+ }
260
+ return globalPerfReporter;
261
+ }
262
+ function shutdownPerfReporter() {
263
+ if (globalPerfReporter) {
264
+ globalPerfReporter.stopFlushTimer();
265
+ globalPerfReporter.flush();
266
+ globalPerfReporter = null;
267
+ }
268
+ }
269
+ // Convenience functions for common operations
270
+ exports.perf = {
271
+ flowStart: (nodeId, metadata) => getPerfReporter().recordFlowStart(nodeId, metadata),
272
+ flowComplete: (spanId, nextNodeId, metadata) => getPerfReporter().recordFlowComplete(spanId, nextNodeId, metadata),
273
+ cacheHit: (cacheKey, metadata) => getPerfReporter().recordCacheHit(cacheKey, metadata),
274
+ cacheMiss: (cacheKey, metadata) => getPerfReporter().recordCacheMiss(cacheKey, metadata),
275
+ hotReloadStart: (deltaSize, metadata) => getPerfReporter().recordHotReloadStart(deltaSize, metadata),
276
+ hotReloadEnd: (spanId, success, metadata) => getPerfReporter().recordHotReloadEnd(spanId, success, metadata),
277
+ record: (type, metadata, category) => getPerfReporter().record(type, metadata, category),
278
+ summary: () => getPerfReporter().summary(),
279
+ clear: () => getPerfReporter().clear()
280
+ };
@@ -0,0 +1,347 @@
1
+ "use strict";
2
+ // S2-T2: Multithreaded Job Scheduler Integration
3
+ // QnceThreadPool for cache loads and telemetry writes off main thread
4
+ Object.defineProperty(exports, "__esModule", { value: true });
5
+ exports.QnceThreadPool = void 0;
6
+ exports.getThreadPool = getThreadPool;
7
+ exports.shutdownThreadPool = shutdownThreadPool;
8
+ /**
9
+ * QnceThreadPool - Background job processing for QNCE engine
10
+ * Handles cache operations, telemetry, and other non-blocking tasks
11
+ */
12
+ class QnceThreadPool {
13
+ workers = [];
14
+ jobQueue = [];
15
+ activeJobs = new Map();
16
+ config;
17
+ stats;
18
+ isShuttingDown = false;
19
+ constructor(config = {}) {
20
+ this.config = {
21
+ maxWorkers: config.maxWorkers || Math.max(1, Math.floor(navigator?.hardwareConcurrency || 4) / 2),
22
+ queueLimit: config.queueLimit || 100,
23
+ idleTimeout: config.idleTimeout || 30000,
24
+ enableProfiling: config.enableProfiling || false
25
+ };
26
+ this.stats = {
27
+ activeWorkers: 0,
28
+ queuedJobs: 0,
29
+ completedJobs: 0,
30
+ failedJobs: 0,
31
+ avgExecutionTime: 0,
32
+ workerUtilization: 0
33
+ };
34
+ this.initializeWorkers();
35
+ }
36
+ /**
37
+ * Submit job for background processing
38
+ */
39
+ async submitJob(type, payload, priority = 'normal') {
40
+ return new Promise((resolve, reject) => {
41
+ if (this.isShuttingDown) {
42
+ reject(new Error('ThreadPool is shutting down'));
43
+ return;
44
+ }
45
+ if (this.jobQueue.length >= this.config.queueLimit) {
46
+ reject(new Error('Job queue limit exceeded'));
47
+ return;
48
+ }
49
+ const job = {
50
+ id: `job-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
51
+ type,
52
+ priority,
53
+ payload,
54
+ timestamp: performance.now(),
55
+ resolve: resolve,
56
+ reject
57
+ };
58
+ // Insert based on priority (high -> normal -> low)
59
+ const insertIndex = this.findInsertionIndex(priority);
60
+ this.jobQueue.splice(insertIndex, 0, job);
61
+ this.stats.queuedJobs = this.jobQueue.length;
62
+ this.processQueue();
63
+ });
64
+ }
65
+ /**
66
+ * Cache load operation (S2-T2 primary use case)
67
+ */
68
+ async loadFromCache(cacheKey, loader) {
69
+ return this.submitJob('cache-load', { cacheKey, loader: loader.toString() }, 'normal');
70
+ }
71
+ /**
72
+ * Telemetry write operation (S2-T2 primary use case)
73
+ */
74
+ async writeTelemetry(eventData) {
75
+ return this.submitJob('telemetry-write', eventData, 'low');
76
+ }
77
+ /**
78
+ * Hot-reload preparation (integration with S2-T3)
79
+ */
80
+ async prepareHotReload(deltaData) {
81
+ return this.submitJob('hot-reload-prep', deltaData, 'high');
82
+ }
83
+ /**
84
+ * Get current thread pool statistics
85
+ */
86
+ getStats() {
87
+ return { ...this.stats };
88
+ }
89
+ /**
90
+ * Graceful shutdown of thread pool
91
+ */
92
+ async shutdown(timeoutMs = 5000) {
93
+ this.isShuttingDown = true;
94
+ const startTime = Date.now();
95
+ // Wait for active jobs to complete or timeout
96
+ while (this.activeJobs.size > 0 && (Date.now() - startTime) < timeoutMs) {
97
+ await new Promise(resolve => setTimeout(resolve, 100));
98
+ }
99
+ // Terminate all workers
100
+ for (const worker of this.workers) {
101
+ worker.terminate();
102
+ }
103
+ this.workers.length = 0;
104
+ this.stats.activeWorkers = 0;
105
+ }
106
+ /**
107
+ * Initialize worker threads based on environment
108
+ */
109
+ initializeWorkers() {
110
+ // Browser environment: Use Web Workers
111
+ if (typeof Worker !== 'undefined' && typeof window !== 'undefined') {
112
+ this.initializeWebWorkers();
113
+ }
114
+ // Node.js environment: Use worker_threads
115
+ else if (typeof require !== 'undefined') {
116
+ this.initializeNodeWorkers();
117
+ }
118
+ // Fallback: Simulate workers with setTimeout (for testing)
119
+ else {
120
+ this.initializeFallbackWorkers();
121
+ }
122
+ }
123
+ /**
124
+ * Web Workers for browser environment
125
+ */
126
+ initializeWebWorkers() {
127
+ const workerCode = this.generateWebWorkerCode();
128
+ const workerBlob = new Blob([workerCode], { type: 'application/javascript' });
129
+ const workerUrl = URL.createObjectURL(workerBlob);
130
+ for (let i = 0; i < this.config.maxWorkers; i++) {
131
+ try {
132
+ const worker = new Worker(workerUrl);
133
+ this.setupWorkerHandlers(worker, i);
134
+ this.workers.push(worker);
135
+ this.stats.activeWorkers++;
136
+ }
137
+ catch (error) {
138
+ console.warn(`Failed to create worker ${i}:`, error);
139
+ }
140
+ }
141
+ }
142
+ /**
143
+ * Node.js worker_threads (placeholder - would need actual implementation)
144
+ */
145
+ initializeNodeWorkers() {
146
+ // TODO: Implement worker_threads for Node.js environment
147
+ // For now, fall back to simulation
148
+ this.initializeFallbackWorkers();
149
+ }
150
+ /**
151
+ * Fallback simulation for testing/development
152
+ */
153
+ initializeFallbackWorkers() {
154
+ // Simulate workers with async processing
155
+ for (let i = 0; i < this.config.maxWorkers; i++) {
156
+ const mockWorker = {
157
+ postMessage: (data) => {
158
+ // Simulate async processing
159
+ setTimeout(() => {
160
+ this.handleWorkerMessage({
161
+ data: {
162
+ jobId: data.jobId,
163
+ result: `Processed: ${JSON.stringify(data.payload)}`,
164
+ success: true
165
+ }
166
+ });
167
+ }, Math.random() * 100 + 50); // 50-150ms simulation
168
+ },
169
+ terminate: () => { },
170
+ addEventListener: () => { },
171
+ removeEventListener: () => { }
172
+ };
173
+ this.workers.push(mockWorker);
174
+ this.stats.activeWorkers++;
175
+ }
176
+ }
177
+ /**
178
+ * Generate Web Worker code for browser execution
179
+ */
180
+ generateWebWorkerCode() {
181
+ return `
182
+ // QNCE Thread Pool Worker
183
+ self.addEventListener('message', function(e) {
184
+ const { jobId, type, payload } = e.data;
185
+
186
+ try {
187
+ let result;
188
+
189
+ switch (type) {
190
+ case 'cache-load':
191
+ // Simulate cache loading
192
+ result = processCache(payload);
193
+ break;
194
+
195
+ case 'telemetry-write':
196
+ // Simulate telemetry writing
197
+ result = writeTelemetryData(payload);
198
+ break;
199
+
200
+ case 'hot-reload-prep':
201
+ // Simulate hot-reload preparation
202
+ result = prepareReload(payload);
203
+ break;
204
+
205
+ case 'asset-process':
206
+ // Simulate asset processing
207
+ result = processAsset(payload);
208
+ break;
209
+
210
+ default:
211
+ throw new Error('Unknown job type: ' + type);
212
+ }
213
+
214
+ self.postMessage({
215
+ jobId,
216
+ result,
217
+ success: true
218
+ });
219
+
220
+ } catch (error) {
221
+ self.postMessage({
222
+ jobId,
223
+ error: error.message,
224
+ success: false
225
+ });
226
+ }
227
+ });
228
+
229
+ function processCache(payload) {
230
+ // Simulate cache processing work
231
+ const data = JSON.parse(JSON.stringify(payload));
232
+ return { cached: true, data, timestamp: Date.now() };
233
+ }
234
+
235
+ function writeTelemetryData(payload) {
236
+ // Simulate telemetry write
237
+ return { written: true, bytes: JSON.stringify(payload).length };
238
+ }
239
+
240
+ function prepareReload(payload) {
241
+ // Simulate hot-reload preparation
242
+ return { prepared: true, deltaSize: JSON.stringify(payload).length };
243
+ }
244
+
245
+ function processAsset(payload) {
246
+ // Simulate asset processing
247
+ return { processed: true, asset: payload };
248
+ }
249
+ `;
250
+ }
251
+ /**
252
+ * Setup worker message handlers
253
+ */
254
+ setupWorkerHandlers(worker, workerId) {
255
+ worker.addEventListener('message', (e) => this.handleWorkerMessage(e));
256
+ worker.addEventListener('error', (e) => this.handleWorkerError(e, workerId));
257
+ }
258
+ /**
259
+ * Handle worker completion messages
260
+ */
261
+ handleWorkerMessage(event) {
262
+ const { jobId, result, error, success } = event.data;
263
+ const job = this.activeJobs.get(jobId);
264
+ if (!job)
265
+ return;
266
+ this.activeJobs.delete(jobId);
267
+ if (success) {
268
+ job.resolve(result);
269
+ this.stats.completedJobs++;
270
+ }
271
+ else {
272
+ job.reject(new Error(error));
273
+ this.stats.failedJobs++;
274
+ }
275
+ // Update execution time stats
276
+ const executionTime = performance.now() - job.timestamp;
277
+ this.updateExecutionTimeStats(executionTime);
278
+ // Process next job in queue
279
+ this.processQueue();
280
+ }
281
+ /**
282
+ * Handle worker errors
283
+ */
284
+ handleWorkerError(error, workerId) {
285
+ console.error(`Worker ${workerId} error:`, error);
286
+ // TODO: Implement worker recovery/restart logic
287
+ }
288
+ /**
289
+ * Process job queue by assigning jobs to available workers
290
+ */
291
+ processQueue() {
292
+ if (this.jobQueue.length === 0)
293
+ return;
294
+ const availableWorkers = this.config.maxWorkers - this.activeJobs.size;
295
+ if (availableWorkers <= 0)
296
+ return;
297
+ const job = this.jobQueue.shift();
298
+ this.stats.queuedJobs = this.jobQueue.length;
299
+ this.activeJobs.set(job.id, job);
300
+ // Find least busy worker (for now, just use round-robin)
301
+ const workerIndex = this.stats.completedJobs % this.workers.length;
302
+ const worker = this.workers[workerIndex];
303
+ worker.postMessage({
304
+ jobId: job.id,
305
+ type: job.type,
306
+ payload: job.payload
307
+ });
308
+ }
309
+ /**
310
+ * Find insertion index for job based on priority
311
+ */
312
+ findInsertionIndex(priority) {
313
+ const priorityValues = { high: 3, normal: 2, low: 1 };
314
+ const jobPriority = priorityValues[priority];
315
+ for (let i = 0; i < this.jobQueue.length; i++) {
316
+ if (priorityValues[this.jobQueue[i].priority] < jobPriority) {
317
+ return i;
318
+ }
319
+ }
320
+ return this.jobQueue.length;
321
+ }
322
+ /**
323
+ * Update execution time statistics
324
+ */
325
+ updateExecutionTimeStats(executionTime) {
326
+ const totalJobs = this.stats.completedJobs + this.stats.failedJobs;
327
+ this.stats.avgExecutionTime = ((this.stats.avgExecutionTime * (totalJobs - 1)) + executionTime) / totalJobs;
328
+ this.stats.workerUtilization = (this.activeJobs.size / this.config.maxWorkers) * 100;
329
+ }
330
+ }
331
+ exports.QnceThreadPool = QnceThreadPool;
332
+ // Singleton instance for global access
333
+ let globalThreadPool = null;
334
+ function getThreadPool(config) {
335
+ if (!globalThreadPool) {
336
+ globalThreadPool = new QnceThreadPool(config);
337
+ }
338
+ return globalThreadPool;
339
+ }
340
+ function shutdownThreadPool() {
341
+ if (globalThreadPool) {
342
+ const shutdown = globalThreadPool.shutdown();
343
+ globalThreadPool = null;
344
+ return shutdown;
345
+ }
346
+ return Promise.resolve();
347
+ }
package/dist/index.d.ts CHANGED
@@ -1,4 +1,8 @@
1
1
  export * from './engine/core.js';
2
2
  export { DEMO_STORY } from './engine/demo-story.js';
3
+ export * from './engine/condition.js';
4
+ export * from './integrations/react.js';
5
+ export * from './ui/index.js';
3
6
  export { QNCEEngine, createQNCEEngine, loadStoryData, type Choice, type NarrativeNode, type QNCEState, type StoryData } from './engine/core.js';
7
+ export { ConditionEvaluator, ConditionEvaluationError, conditionEvaluator, type ConditionContext, type CustomEvaluatorFunction } from './engine/condition.js';
4
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAGpD,OAAO,EACL,UAAU,EACV,gBAAgB,EAChB,aAAa,EACb,KAAK,MAAM,EACX,KAAK,aAAa,EAClB,KAAK,SAAS,EACd,KAAK,SAAS,EACf,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAGpD,cAAc,uBAAuB,CAAC;AAGtC,cAAc,yBAAyB,CAAC;AAGxC,cAAc,eAAe,CAAC;AAG9B,OAAO,EACL,UAAU,EACV,gBAAgB,EAChB,aAAa,EACb,KAAK,MAAM,EACX,KAAK,aAAa,EAClB,KAAK,SAAS,EACd,KAAK,SAAS,EACf,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,kBAAkB,EAClB,wBAAwB,EACxB,kBAAkB,EAClB,KAAK,gBAAgB,EACrB,KAAK,uBAAuB,EAC7B,MAAM,uBAAuB,CAAC"}
package/dist/index.js CHANGED
@@ -1,6 +1,38 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.conditionEvaluator = exports.ConditionEvaluationError = exports.ConditionEvaluator = exports.loadStoryData = exports.createQNCEEngine = exports.QNCEEngine = exports.DEMO_STORY = void 0;
1
18
  // QNCE Engine - Main Export
2
- export * from './engine/core.js';
3
- export { DEMO_STORY } from './engine/demo-story.js';
19
+ __exportStar(require("./engine/core.js"), exports);
20
+ var demo_story_js_1 = require("./engine/demo-story.js");
21
+ Object.defineProperty(exports, "DEMO_STORY", { enumerable: true, get: function () { return demo_story_js_1.DEMO_STORY; } });
22
+ // Sprint 3.4: Conditional choice display exports
23
+ __exportStar(require("./engine/condition.js"), exports);
24
+ // Sprint 3.5: React integration for autosave/undo functionality
25
+ __exportStar(require("./integrations/react.js"), exports);
26
+ // Sprint 3.6: UI Components
27
+ __exportStar(require("./ui/index.js"), exports);
4
28
  // Re-export for convenience
5
- export { QNCEEngine, createQNCEEngine, loadStoryData } from './engine/core.js';
29
+ var core_js_1 = require("./engine/core.js");
30
+ Object.defineProperty(exports, "QNCEEngine", { enumerable: true, get: function () { return core_js_1.QNCEEngine; } });
31
+ Object.defineProperty(exports, "createQNCEEngine", { enumerable: true, get: function () { return core_js_1.createQNCEEngine; } });
32
+ Object.defineProperty(exports, "loadStoryData", { enumerable: true, get: function () { return core_js_1.loadStoryData; } });
33
+ // Sprint 3.4: Conditional choice types
34
+ var condition_js_1 = require("./engine/condition.js");
35
+ Object.defineProperty(exports, "ConditionEvaluator", { enumerable: true, get: function () { return condition_js_1.ConditionEvaluator; } });
36
+ Object.defineProperty(exports, "ConditionEvaluationError", { enumerable: true, get: function () { return condition_js_1.ConditionEvaluationError; } });
37
+ Object.defineProperty(exports, "conditionEvaluator", { enumerable: true, get: function () { return condition_js_1.conditionEvaluator; } });
6
38
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,4BAA4B;AAC5B,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEpD,4BAA4B;AAC5B,OAAO,EACL,UAAU,EACV,gBAAgB,EAChB,aAAa,EAKd,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,4BAA4B;AAC5B,mDAAiC;AACjC,wDAAoD;AAA3C,2GAAA,UAAU,OAAA;AAEnB,iDAAiD;AACjD,wDAAsC;AAEtC,gEAAgE;AAChE,0DAAwC;AAExC,4BAA4B;AAC5B,gDAA8B;AAE9B,4BAA4B;AAC5B,4CAQ0B;AAPxB,qGAAA,UAAU,OAAA;AACV,2GAAA,gBAAgB,OAAA;AAChB,wGAAA,aAAa,OAAA;AAOf,uCAAuC;AACvC,sDAM+B;AAL7B,kHAAA,kBAAkB,OAAA;AAClB,wHAAA,wBAAwB,OAAA;AACxB,kHAAA,kBAAkB,OAAA"}