canvasframework 0.5.48 → 0.5.50

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.
@@ -149,6 +149,213 @@ const FIXED_COMPONENT_TYPES = new Set([
149
149
  * @property {number} scrollVelocity - Vitesse de défilement
150
150
  * @property {number} scrollFriction - Friction du défilement
151
151
  */
152
+
153
+ // ========================================
154
+ // ✨ NOUVEAU: WorkerPool System
155
+ // ========================================
156
+
157
+ class WorkerPool {
158
+ constructor(options = {}) {
159
+ this.maxWorkers = options.maxWorkers || navigator.hardwareConcurrency || 4;
160
+ this.minWorkers = options.minWorkers || 1;
161
+ this.workerScript = options.workerScript || null;
162
+
163
+ this.workers = [];
164
+ this.availableWorkers = [];
165
+ this.busyWorkers = new Set();
166
+ this.taskQueue = [];
167
+ this.taskIdCounter = 0;
168
+ this.pendingTasks = new Map();
169
+
170
+ this._initializeMinWorkers();
171
+ }
172
+
173
+ _initializeMinWorkers() {
174
+ for (let i = 0; i < this.minWorkers; i++) {
175
+ this._createWorker();
176
+ }
177
+ }
178
+
179
+ _createWorker() {
180
+ const blob = new Blob([this._getDefaultWorkerCode()], { type: 'application/javascript' });
181
+ const workerUrl = URL.createObjectURL(blob);
182
+ const worker = new Worker(workerUrl);
183
+ const workerWrapper = this._wrapWorker(worker);
184
+
185
+ this.workers.push(workerWrapper);
186
+ this.availableWorkers.push(workerWrapper);
187
+ URL.revokeObjectURL(workerUrl);
188
+
189
+ return workerWrapper;
190
+ }
191
+
192
+ _wrapWorker(worker) {
193
+ const wrapper = {
194
+ worker,
195
+ id: `worker-${Date.now()}-${Math.random()}`,
196
+ currentTask: null,
197
+ lastUsed: Date.now()
198
+ };
199
+
200
+ worker.onmessage = (e) => this._handleWorkerMessage(wrapper, e);
201
+ worker.onerror = (e) => this._handleWorkerError(wrapper, e);
202
+
203
+ return wrapper;
204
+ }
205
+
206
+ _getDefaultWorkerCode() {
207
+ return `
208
+ let state = {};
209
+
210
+ self.onmessage = async function(e) {
211
+ const { taskId, type, payload } = e.data;
212
+
213
+ try {
214
+ let result;
215
+
216
+ switch(type) {
217
+ case 'SET_STATE':
218
+ state = payload;
219
+ result = { state };
220
+ break;
221
+
222
+ case 'EXECUTE':
223
+ const fn = new Function('state', 'args', payload.fnString);
224
+ result = await fn(state, payload.args);
225
+ break;
226
+
227
+ case 'COMPUTE':
228
+ result = payload.data;
229
+ break;
230
+
231
+ default:
232
+ throw new Error('Unknown task type: ' + type);
233
+ }
234
+
235
+ self.postMessage({
236
+ taskId,
237
+ type: 'SUCCESS',
238
+ result
239
+ });
240
+ } catch (error) {
241
+ self.postMessage({
242
+ taskId,
243
+ type: 'ERROR',
244
+ error: error.message
245
+ });
246
+ }
247
+ };
248
+ `;
249
+ }
250
+
251
+ _handleWorkerMessage(wrapper, e) {
252
+ const { taskId, type, result, error } = e.data;
253
+ const task = this.pendingTasks.get(taskId);
254
+
255
+ if (!task) return;
256
+
257
+ this.pendingTasks.delete(taskId);
258
+ this.busyWorkers.delete(wrapper);
259
+ this.availableWorkers.push(wrapper);
260
+ wrapper.currentTask = null;
261
+ wrapper.lastUsed = Date.now();
262
+
263
+ if (type === 'ERROR') {
264
+ task.reject(new Error(error));
265
+ } else {
266
+ task.resolve(result);
267
+ }
268
+
269
+ this._processQueue();
270
+ }
271
+
272
+ _handleWorkerError(wrapper, error) {
273
+ console.error('Worker error:', error);
274
+
275
+ if (wrapper.currentTask) {
276
+ const task = this.pendingTasks.get(wrapper.currentTask);
277
+ if (task) {
278
+ task.reject(error);
279
+ this.pendingTasks.delete(wrapper.currentTask);
280
+ }
281
+ }
282
+
283
+ this.busyWorkers.delete(wrapper);
284
+ const index = this.workers.indexOf(wrapper);
285
+ if (index > -1) {
286
+ this.workers.splice(index, 1);
287
+ }
288
+
289
+ if (this.workers.length < this.minWorkers) {
290
+ this._createWorker();
291
+ }
292
+
293
+ this._processQueue();
294
+ }
295
+
296
+ execute(type, payload) {
297
+ return new Promise((resolve, reject) => {
298
+ const taskId = ++this.taskIdCounter;
299
+
300
+ this.taskQueue.push({
301
+ taskId,
302
+ type,
303
+ payload,
304
+ resolve,
305
+ reject,
306
+ timestamp: Date.now()
307
+ });
308
+
309
+ this._processQueue();
310
+ });
311
+ }
312
+
313
+ _processQueue() {
314
+ while (this.taskQueue.length > 0 && this.availableWorkers.length > 0) {
315
+ const task = this.taskQueue.shift();
316
+ const wrapper = this.availableWorkers.shift();
317
+
318
+ this.busyWorkers.add(wrapper);
319
+ wrapper.currentTask = task.taskId;
320
+ this.pendingTasks.set(task.taskId, task);
321
+
322
+ wrapper.worker.postMessage({
323
+ taskId: task.taskId,
324
+ type: task.type,
325
+ payload: task.payload
326
+ });
327
+ }
328
+
329
+ if (this.taskQueue.length > 0 &&
330
+ this.availableWorkers.length === 0 &&
331
+ this.workers.length < this.maxWorkers) {
332
+ this._createWorker();
333
+ this._processQueue();
334
+ }
335
+ }
336
+
337
+ getStats() {
338
+ return {
339
+ totalWorkers: this.workers.length,
340
+ availableWorkers: this.availableWorkers.length,
341
+ busyWorkers: this.busyWorkers.size,
342
+ queuedTasks: this.taskQueue.length,
343
+ pendingTasks: this.pendingTasks.size
344
+ };
345
+ }
346
+
347
+ terminateAll() {
348
+ this.workers.forEach(wrapper => {
349
+ wrapper.worker.terminate();
350
+ });
351
+ this.workers = [];
352
+ this.availableWorkers = [];
353
+ this.busyWorkers.clear();
354
+ this.taskQueue = [];
355
+ this.pendingTasks.clear();
356
+ }
357
+ }
358
+
152
359
  class CanvasFramework {
153
360
  /**
154
361
  * Crée une instance de CanvasFramework
@@ -316,21 +523,14 @@ class CanvasFramework {
316
523
  }
317
524
  });
318
525
 
319
- // Logic Worker
320
- this.logicWorker = this.createLogicWorker();
321
- this.logicWorker.onmessage = this.handleLogicWorkerMessage.bind(this);
322
- this.logicWorkerState = {};
323
- this.logicWorker.postMessage({
324
- type: 'SET_STATE',
325
- payload: this.state
326
- });
327
-
328
- // Envoyer l'état initial au worker
329
- this.logicWorker.postMessage({
330
- type: 'SET_STATE',
331
- payload: this.state
332
- });
333
-
526
+ // WorkerPool pour le logic
527
+ this.workerPool = new WorkerPool({
528
+ maxWorkers: options.maxLogicWorkers || navigator.hardwareConcurrency || 4,
529
+ minWorkers: options.minLogicWorkers || 1
530
+ });
531
+
532
+ this.logicWorkerState = {};
533
+
334
534
  // Gestion des événements
335
535
  this.isDragging = false;
336
536
  this.lastTouchY = 0;
@@ -1483,37 +1683,7 @@ class CanvasFramework {
1483
1683
  return new Worker(URL.createObjectURL(blob));
1484
1684
  }
1485
1685
 
1486
- createLogicWorker() {
1487
- const workerCode = `
1488
- let state = {};
1489
1686
 
1490
- self.onmessage = async function(e) {
1491
- const { type, payload } = e.data;
1492
-
1493
- switch(type) {
1494
- case 'SET_STATE':
1495
- state = payload;
1496
- self.postMessage({ type: 'STATE_UPDATED', payload: state });
1497
- break;
1498
-
1499
- case 'EXECUTE':
1500
- try {
1501
- const fn = new Function('state', 'args', payload.fnString);
1502
- const result = await fn(state, payload.args);
1503
- self.postMessage({ type: 'EXECUTION_RESULT', payload: result });
1504
- } catch (err) {
1505
- self.postMessage({ type: 'EXECUTION_ERROR', payload: err.message });
1506
- }
1507
- break;
1508
- }
1509
- };
1510
- `;
1511
-
1512
- const blob = new Blob([workerCode], {
1513
- type: 'application/javascript'
1514
- });
1515
- return new Worker(URL.createObjectURL(blob));
1516
- }
1517
1687
 
1518
1688
  // Set Theme dynamique
1519
1689
  setTheme(theme) {
@@ -1588,49 +1758,39 @@ class CanvasFramework {
1588
1758
  });
1589
1759
  }
1590
1760
 
1591
- // ------ Logic Worker --------
1592
- handleLogicWorkerMessage(e) {
1593
- const {
1594
- type,
1595
- payload
1596
- } = e.data;
1597
- switch (type) {
1598
- case 'STATE_UPDATED':
1599
- // Le worker a renvoyé le nouvel état global
1600
- this.logicWorkerState = payload;
1601
- break;
1602
-
1603
- case 'EXECUTION_RESULT':
1604
- // Résultat d'une tâche spécifique envoyée au worker
1605
- if (this.onWorkerResult) this.onWorkerResult(payload);
1606
- break;
1607
-
1608
- case 'EXECUTION_ERROR':
1609
- console.error('Logic Worker Error:', payload);
1610
- break;
1611
- }
1612
- }
1761
+ /**
1762
+ * Exécute une tâche dans le pool de workers
1763
+ * @param {string} fnString - Code de la fonction à exécuter
1764
+ * @param {*} args - Arguments pour la fonction
1765
+ * @returns {Promise} Résultat de l'exécution
1766
+ */
1767
+ async executeTask(fnString, args = {}) {
1768
+ return this.workerPool.execute('EXECUTE', {
1769
+ fnString,
1770
+ args
1771
+ });
1772
+ }
1613
1773
 
1614
- runLogicTask(taskName, taskData) {
1615
- this.logicWorker.postMessage({
1616
- type: 'EXECUTE_TASK',
1617
- payload: {
1618
- taskName,
1619
- taskData
1620
- }
1621
- });
1622
- }
1774
+ /**
1775
+ * Met à jour l'état dans tous les workers
1776
+ * @param {Object} newState - Nouvel état
1777
+ */
1778
+ async updateWorkerState(newState) {
1779
+ this.logicWorkerState = {
1780
+ ...this.logicWorkerState,
1781
+ ...newState
1782
+ };
1783
+
1784
+ return this.workerPool.execute('SET_STATE', this.logicWorkerState);
1785
+ }
1623
1786
 
1624
- updateLogicWorkerState(newState) {
1625
- this.logicWorkerState = {
1626
- ...this.logicWorkerState,
1627
- ...newState
1628
- };
1629
- this.logicWorker.postMessage({
1630
- type: 'SET_STATE',
1631
- payload: this.logicWorkerState
1632
- });
1633
- }
1787
+ /**
1788
+ * Obtient les statistiques du pool de workers
1789
+ * @returns {Object} Statistiques
1790
+ */
1791
+ getWorkerPoolStats() {
1792
+ return this.workerPool.getStats();
1793
+ }
1634
1794
 
1635
1795
  detectPlatform() {
1636
1796
  const ua = navigator.userAgent.toLowerCase();
@@ -1846,8 +2006,24 @@ class CanvasFramework {
1846
2006
  comp.destroy();
1847
2007
  }
1848
2008
  }
2009
+
2010
+ // 2. Désactiver TOUS les gestionnaires d'événements
2011
+ comp.onClick = null;
2012
+ comp.onPress = null;
2013
+ comp.onMove = null;
2014
+ comp.onHover = null;
2015
+ comp.onFocus = null;
2016
+ comp.onBlur = null;
2017
+ comp.onChange = null;
2018
+ comp.onInput = null;
2019
+ comp.onSubmit = null;
2020
+
2021
+ if (comp._unmount && typeof comp._unmount === 'function') {
2022
+ comp._unmount();
2023
+ }
1849
2024
  });
1850
-
2025
+
2026
+
1851
2027
  // ✅ Nettoyer toutes les vidéos orphelines AVANT de créer les nouveaux composants
1852
2028
  const allVideos = document.querySelectorAll('video');
1853
2029
  allVideos.forEach(v => v.remove());
@@ -2985,12 +3161,14 @@ class CanvasFramework {
2985
3161
  if (this.scrollWorker) {
2986
3162
  this.scrollWorker.terminate();
2987
3163
  }
2988
- if (this.worker) {
3164
+
3165
+ if (this.worker) {
2989
3166
  this.worker.terminate();
2990
3167
  }
2991
- if (this.logicWorker) {
2992
- this.logicWorker.terminate();
2993
- }
3168
+
3169
+ if (this.workerPool) {
3170
+ this.workerPool.terminateAll();
3171
+ }
2994
3172
 
2995
3173
  if (this.ctx && typeof this.ctx.destroy === 'function') {
2996
3174
  this.ctx.destroy();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "canvasframework",
3
- "version": "0.5.48",
3
+ "version": "0.5.50",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/beyons/CanvasFramework.git"