@hrforte/react-pivottable 2.0.0 → 2.1.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.
@@ -301,27 +301,14 @@ module.exports = Function.call.bind(Object.prototype.hasOwnProperty);
301
301
  "use strict";
302
302
  Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
303
303
  /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__pivotCompute__ = __webpack_require__(5);
304
- /* eslint-disable valid-jsdoc */
305
- /*
306
- * Web Worker entry point for off-main-thread pivot computation.
307
- *
308
- * Message protocol (plain, structured-cloneable objects):
309
- * main -> worker {type: 'pivot:setData', id, dataKey, data}
310
- * main -> worker {type: 'pivot:compute', id, dataKey, config}
311
- * worker -> main {type: 'pivot:progress', id, processed, total, phase}
312
- * worker -> main {type: 'pivot:result', id, result}
313
- * worker -> main {type: 'pivot:error', id, message}
314
- *
315
- * The materialized dataset is cached per dataKey, so repeated computations on
316
- * the same data (e.g. dragging fields around) do not pay the cost of
317
- * re-scanning the raw records.
318
- */
319
304
 
320
305
 
321
306
  var MAX_CACHED_DATASETS = 4;
322
307
 
323
308
  var datasetCache = new Map();
324
309
 
310
+ var debugEnabled = false;
311
+
325
312
  var cacheDataset = function cacheDataset(dataKey, dataset) {
326
313
  datasetCache.delete(dataKey);
327
314
  datasetCache.set(dataKey, dataset);
@@ -336,6 +323,12 @@ var postToMain = function postToMain(message) {
336
323
  self.postMessage(message);
337
324
  };
338
325
 
326
+ var log = function log(message, detail) {
327
+ if (debugEnabled) {
328
+ postToMain({ type: 'pivot:log', message: message, detail: detail });
329
+ }
330
+ };
331
+
339
332
  var toErrorMessage = function toErrorMessage(error) {
340
333
  return String(error && error.message || error);
341
334
  };
@@ -343,9 +336,17 @@ var toErrorMessage = function toErrorMessage(error) {
343
336
  self.addEventListener('message', function (event) {
344
337
  var message = event.data || {};
345
338
 
339
+ if (message.type === 'pivot:debug') {
340
+ debugEnabled = Boolean(message.enabled);
341
+ log('debug logging ' + (debugEnabled ? 'enabled' : 'disabled') + ' in worker');
342
+ return;
343
+ }
344
+
346
345
  if (message.type === 'pivot:setData') {
346
+ var startedAt = Date.now();
347
+ log('#' + message.id + ' setData received: ' + (message.data || []).length + ' records (dataKey=' + message.dataKey + ')');
347
348
  try {
348
- cacheDataset(message.dataKey, Object(__WEBPACK_IMPORTED_MODULE_0__pivotCompute__["b" /* materializeData */])(message.data, {}, function (processed, total) {
349
+ var dataset = cacheDataset(message.dataKey, Object(__WEBPACK_IMPORTED_MODULE_0__pivotCompute__["b" /* materializeData */])(message.data, {}, function (processed, total) {
349
350
  postToMain({
350
351
  type: 'pivot:progress',
351
352
  id: message.id,
@@ -354,7 +355,9 @@ self.addEventListener('message', function (event) {
354
355
  phase: 'materialize'
355
356
  });
356
357
  }));
358
+ log('#' + message.id + ' materialized ' + dataset.totalRecords + ' records in ' + (Date.now() - startedAt + 'ms (attributes: ' + Object.keys(dataset.attrValues).join(', ') + ')'));
357
359
  } catch (error) {
360
+ log('#' + message.id + ' setData failed: ' + toErrorMessage(error));
358
361
  postToMain({
359
362
  type: 'pivot:error',
360
363
  id: message.id,
@@ -365,12 +368,15 @@ self.addEventListener('message', function (event) {
365
368
  }
366
369
 
367
370
  if (message.type === 'pivot:compute') {
371
+ var _startedAt = Date.now();
372
+ var config = message.config || {};
368
373
  try {
369
- var dataset = datasetCache.get(message.dataKey);
370
- if (!dataset) {
374
+ var _dataset = datasetCache.get(message.dataKey);
375
+ if (!_dataset) {
371
376
  throw new Error('no dataset staged for key ' + message.dataKey);
372
377
  }
373
- var result = Object(__WEBPACK_IMPORTED_MODULE_0__pivotCompute__["a" /* computePivotState */])(dataset, message.config, function (processed, total, phase) {
378
+ log('#' + message.id + ' compute received (rows=[' + (config.rows || []).join(', ') + '] cols=[' + (config.cols || []).join(', ') + '])');
379
+ var result = Object(__WEBPACK_IMPORTED_MODULE_0__pivotCompute__["a" /* computePivotState */])(_dataset, message.config, function (processed, total, phase) {
374
380
  postToMain({
375
381
  type: 'pivot:progress',
376
382
  id: message.id,
@@ -379,8 +385,12 @@ self.addEventListener('message', function (event) {
379
385
  phase: phase
380
386
  });
381
387
  });
388
+ log('#' + message.id + ' computed in ' + (Date.now() - _startedAt) + 'ms (rowKeys: ' + result.rowKeys.length + ', colKeys: ' + result.colKeys.length + ', aggregations: ' + result.aggregations.map(function (agg) {
389
+ return agg.key;
390
+ }).join(', ') + ')');
382
391
  postToMain({ type: 'pivot:result', id: message.id, result: result });
383
392
  } catch (error) {
393
+ log('#' + message.id + ' compute failed: ' + toErrorMessage(error));
384
394
  postToMain({
385
395
  type: 'pivot:error',
386
396
  id: message.id,
@@ -409,6 +419,7 @@ self.addEventListener('message', function (event) {
409
419
  /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__Utilities__ = __webpack_require__(6);
410
420
  var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
411
421
 
422
+ /* eslint-disable no-console */
412
423
  /* eslint-disable valid-jsdoc */
413
424
  /*
414
425
  * Shared pivot-computation logic.
@@ -424,11 +435,11 @@ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol
424
435
 
425
436
  // A progress callback fires every N processed records so the UI can render
426
437
  // progress without adding measurable overhead to the per-record loop.
427
- var PROGRESS_BATCH_SIZE = 20000;
438
+ var PROGRESS_BATCH_SIZE = 200;
428
439
 
429
440
  // Datasets smaller than this are computed synchronously on the main thread;
430
441
  // larger ones are handed off to the Web Worker.
431
- var DEFAULT_WORKER_THRESHOLD = 10000;
442
+ var DEFAULT_WORKER_THRESHOLD = 200;
432
443
 
433
444
  // Trailing debounce before a worker computation is dispatched, so rapid
434
445
  // interactions do not queue one full computation per single change.
@@ -652,16 +663,11 @@ function canComputeInWorker(props) {
652
663
  return false;
653
664
  }
654
665
  var threshold = typeof options.threshold === 'number' ? options.threshold : DEFAULT_WORKER_THRESHOLD;
666
+
655
667
  var data = props.data;
656
668
  if (!Array.isArray(data) || data.length < threshold) {
657
669
  return false;
658
670
  }
659
- if (props.derivedAttributes && Object.keys(props.derivedAttributes).length > 0) {
660
- return false;
661
- }
662
- if (props.aggregators !== __WEBPACK_IMPORTED_MODULE_0__Utilities__["a" /* PivotData */].defaultProps.aggregators) {
663
- return false;
664
- }
665
671
  return true;
666
672
  }
667
673
 
@@ -1069,7 +1075,7 @@ var usFmtPct = numberFormat({
1069
1075
 
1070
1076
  // Emit a progress callback every N processed records without adding
1071
1077
  // measurable overhead to the per-record loop.
1072
- var RECORD_PROGRESS_BATCH_SIZE = 25000;
1078
+ var RECORD_PROGRESS_BATCH_SIZE = 200;
1073
1079
 
1074
1080
  // Returns true when the sorters prop itself, or any of its entries, is a
1075
1081
  // function. Function-based sorters cannot be transferred to a Web Worker, so
@@ -5,7 +5,8 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.WORKER_DEBOUNCE_MS = exports.DEFAULT_WORKER_THRESHOLD = exports.PROGRESS_BATCH_SIZE = undefined;
7
7
 
8
- var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; /* eslint-disable valid-jsdoc */
8
+ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; /* eslint-disable no-console */
9
+ /* eslint-disable valid-jsdoc */
9
10
  /*
10
11
  * Shared pivot-computation logic.
11
12
  *
@@ -31,11 +32,11 @@ var _Utilities = require('./Utilities');
31
32
 
32
33
  // A progress callback fires every N processed records so the UI can render
33
34
  // progress without adding measurable overhead to the per-record loop.
34
- var PROGRESS_BATCH_SIZE = exports.PROGRESS_BATCH_SIZE = 20000;
35
+ var PROGRESS_BATCH_SIZE = exports.PROGRESS_BATCH_SIZE = 200;
35
36
 
36
37
  // Datasets smaller than this are computed synchronously on the main thread;
37
38
  // larger ones are handed off to the Web Worker.
38
- var DEFAULT_WORKER_THRESHOLD = exports.DEFAULT_WORKER_THRESHOLD = 10000;
39
+ var DEFAULT_WORKER_THRESHOLD = exports.DEFAULT_WORKER_THRESHOLD = 200;
39
40
 
40
41
  // Trailing debounce before a worker computation is dispatched, so rapid
41
42
  // interactions do not queue one full computation per single change.
@@ -259,16 +260,11 @@ function canComputeInWorker(props) {
259
260
  return false;
260
261
  }
261
262
  var threshold = typeof options.threshold === 'number' ? options.threshold : DEFAULT_WORKER_THRESHOLD;
263
+
262
264
  var data = props.data;
263
265
  if (!Array.isArray(data) || data.length < threshold) {
264
266
  return false;
265
267
  }
266
- if (props.derivedAttributes && Object.keys(props.derivedAttributes).length > 0) {
267
- return false;
268
- }
269
- if (props.aggregators !== _Utilities.PivotData.defaultProps.aggregators) {
270
- return false;
271
- }
272
268
  return true;
273
269
  }
274
270
  //# sourceMappingURL=pivotCompute.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/pivotCompute.js"],"names":["materializeData","computePivotState","serializePivotData","sortersHaveFunctions","sanitizeSorters","createWorkerConfig","createComputeSignature","canComputeInWorker","PROGRESS_BATCH_SIZE","DEFAULT_WORKER_THRESHOLD","WORKER_DEBOUNCE_MS","data","derivedAttributes","onProgress","attrValues","materializedInput","total","Array","isArray","length","recordsProcessed","PivotData","forEachRecord","record","push","Object","keys","attr","null","value","totalRecords","materialized","config","pivotData","assign","onRecordProcessed","processed","getRowKeys","getColKeys","serializeCell","values","aggregations","forEach","instance","cell","getInstance","agg","key","tree","rowBranch","flatRowKey","serializedRow","flatColKey","serializeTotals","serialized","totals","flatKey","rowKeys","map","slice","colKeys","rowTotals","colTotals","allTotal","sorters","some","clean","sorter","props","rows","cols","aggregatorName","vals","valueFilter","rowOrder","colOrder","rowSorts","colSorts","JSON","stringify","options","enabled","Worker","threshold","aggregators","defaultProps"],"mappings":"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","file":"pivotCompute.js","sourcesContent":["/* eslint-disable valid-jsdoc */\n/*\n * Shared pivot-computation logic.\n *\n * This module contains the pure, DOM-free parts of the pivot computation so\n * that the exact same code paths can run:\n * - on the main thread (classic synchronous rendering path), and\n * - inside a Web Worker (src/pivotWorker.js) for large datasets.\n *\n * It must stay free of React and of any `window`/`document` references.\n */\nimport {PivotData} from './Utilities';\n\n// A progress callback fires every N processed records so the UI can render\n// progress without adding measurable overhead to the per-record loop.\nexport const PROGRESS_BATCH_SIZE = 20000;\n\n// Datasets smaller than this are computed synchronously on the main thread;\n// larger ones are handed off to the Web Worker.\nexport const DEFAULT_WORKER_THRESHOLD = 10000;\n\n// Trailing debounce before a worker computation is dispatched, so rapid\n// interactions do not queue one full computation per single change.\nexport const WORKER_DEBOUNCE_MS = 200;\n\n/*\n * Walks the raw input once and computes the attribute-value inventory plus a\n * \"materialized\" copy of the records (with derived attributes applied).\n * This is an exact extraction of the historical PivotTableUI behavior.\n *\n * onProgress receives (processed, total, phase); total is null when it is\n * unknown upfront (function-based data sources).\n */\nexport function materializeData(data, derivedAttributes, onProgress) {\n const attrValues = {};\n const materializedInput = [];\n const total = Array.isArray(data) ? data.length : null;\n let recordsProcessed = 0;\n PivotData.forEachRecord(data, derivedAttributes || {}, function(record) {\n materializedInput.push(record);\n for (const attr of Object.keys(record)) {\n if (!(attr in attrValues)) {\n attrValues[attr] = {};\n if (recordsProcessed > 0) {\n attrValues[attr].null = recordsProcessed;\n }\n }\n }\n for (const attr in attrValues) {\n const value = attr in record ? record[attr] : 'null';\n if (!(value in attrValues[attr])) {\n attrValues[attr][value] = 0;\n }\n attrValues[attr][value]++;\n }\n recordsProcessed++;\n if (onProgress && recordsProcessed % PROGRESS_BATCH_SIZE === 0) {\n onProgress(recordsProcessed, total, 'materialize');\n }\n });\n return {attrValues, materializedInput, totalRecords: recordsProcessed};\n}\n\n/*\n * Aggregates an already-materialized dataset into a PivotData instance and\n * serializes the result into a plain (structured-cloneable) object that can\n * cross the worker boundary and later hydrate PivotData on the main thread.\n */\nexport function computePivotState(materialized, config, onProgress) {\n const total = materialized.totalRecords;\n const pivotData = new PivotData(\n Object.assign({}, config, {\n data: materialized.materializedInput,\n derivedAttributes: {},\n onRecordProcessed: processed => {\n if (onProgress) {\n onProgress(processed, total, 'aggregate');\n }\n },\n })\n );\n // Force key sorting to happen while we are (possibly) inside the worker.\n pivotData.getRowKeys();\n pivotData.getColKeys();\n return serializePivotData(pivotData, materialized);\n}\n\n/*\n * Serializes a fully-built PivotData instance into plain data. Only the final\n * aggregated value of every cell is kept; `value()` and `format()` behavior\n * are reconstructed on the main thread by the PivotData hydration step.\n */\nexport function serializePivotData(pivotData, materialized) {\n const serializeCell = cell => {\n const values = {};\n pivotData.aggregations.forEach(agg => {\n const instance = cell.getInstance(agg.key);\n if (instance) {\n values[agg.key] = instance.value();\n }\n });\n return {values};\n };\n\n const tree = {};\n Object.keys(pivotData.tree).forEach(flatRowKey => {\n const rowBranch = pivotData.tree[flatRowKey];\n const serializedRow = {};\n Object.keys(rowBranch).forEach(flatColKey => {\n serializedRow[flatColKey] = serializeCell(rowBranch[flatColKey]);\n });\n tree[flatRowKey] = serializedRow;\n });\n\n const serializeTotals = totals => {\n const serialized = {};\n Object.keys(totals).forEach(flatKey => {\n serialized[flatKey] = serializeCell(totals[flatKey]);\n });\n return serialized;\n };\n\n return {\n rowKeys: pivotData.rowKeys.map(key => key.slice()),\n colKeys: pivotData.colKeys.map(key => key.slice()),\n tree,\n rowTotals: serializeTotals(pivotData.rowTotals),\n colTotals: serializeTotals(pivotData.colTotals),\n allTotal: serializeCell(pivotData.allTotal),\n aggregations: pivotData.aggregations.map(agg => Object.assign({}, agg)),\n attrValues: materialized.attrValues,\n materializedInput: materialized.materializedInput,\n totalRecords: materialized.totalRecords,\n };\n}\n\n/*\n * Sorters containing functions cannot be structured-cloned into a worker.\n * Returns true when the sorters prop itself, or any of its entries, is a\n * function. In that case PivotData re-sorts on the main thread after\n * hydration (distinct keys are few, so this stays cheap).\n */\nexport function sortersHaveFunctions(sorters) {\n if (typeof sorters === 'function') {\n return true;\n }\n if (!sorters || typeof sorters !== 'object') {\n return false;\n }\n return Object.keys(sorters).some(attr => typeof sorters[attr] === 'function');\n}\n\n/*\n * Returns a structured-cloneable subset of the sorters prop: data-type names\n * and sample-value arrays survive the worker boundary, functions do not.\n */\nexport function sanitizeSorters(sorters) {\n if (\n !sorters ||\n typeof sorters !== 'object' ||\n typeof sorters === 'function'\n ) {\n return {};\n }\n const clean = {};\n Object.keys(sorters).forEach(attr => {\n const sorter = sorters[attr];\n if (typeof sorter === 'string' || Array.isArray(sorter)) {\n clean[attr] = sorter;\n }\n });\n return clean;\n}\n\n/*\n * Extracts exactly the props PivotData needs to compute results, with\n * everything non-serializable stripped, so the object can be posted to the\n * worker and fed straight into PivotData.\n */\nexport function createWorkerConfig(props) {\n return {\n rows: props.rows,\n cols: props.cols,\n aggregatorName: props.aggregatorName,\n aggregations: props.aggregations,\n vals: props.vals,\n valueFilter: props.valueFilter,\n sorters: sanitizeSorters(props.sorters),\n rowOrder: props.rowOrder,\n colOrder: props.colOrder,\n rowSorts: props.rowSorts,\n colSorts: props.colSorts,\n };\n}\n\n/*\n * Stable string signature of everything that influences a worker computation.\n * Data identity is NOT part of it; callers append their own data version.\n */\nexport function createComputeSignature(props) {\n return JSON.stringify(createWorkerConfig(props));\n}\n\n/*\n * Decides whether the current props can be computed inside the Web Worker.\n * Anything that cannot be structured-cloned (function data sources, custom\n * derivedAttributes, custom aggregator maps) forces the classic path.\n * Function-based sorters are allowed: they are re-applied on the main thread.\n */\nexport function canComputeInWorker(props, options = {}) {\n if (options.enabled === false) {\n return false;\n }\n if (typeof Worker === 'undefined') {\n return false;\n }\n const threshold =\n typeof options.threshold === 'number'\n ? options.threshold\n : DEFAULT_WORKER_THRESHOLD;\n const data = props.data;\n if (!Array.isArray(data) || data.length < threshold) {\n return false;\n }\n if (\n props.derivedAttributes &&\n Object.keys(props.derivedAttributes).length > 0\n ) {\n return false;\n }\n if (props.aggregators !== PivotData.defaultProps.aggregators) {\n return false;\n }\n return true;\n}\n"]}
1
+ {"version":3,"sources":["../src/pivotCompute.js"],"names":["materializeData","computePivotState","serializePivotData","sortersHaveFunctions","sanitizeSorters","createWorkerConfig","createComputeSignature","canComputeInWorker","PROGRESS_BATCH_SIZE","DEFAULT_WORKER_THRESHOLD","WORKER_DEBOUNCE_MS","data","derivedAttributes","onProgress","attrValues","materializedInput","total","Array","isArray","length","recordsProcessed","PivotData","forEachRecord","record","push","Object","keys","attr","null","value","totalRecords","materialized","config","pivotData","assign","onRecordProcessed","processed","getRowKeys","getColKeys","serializeCell","values","aggregations","forEach","instance","cell","getInstance","agg","key","tree","rowBranch","flatRowKey","serializedRow","flatColKey","serializeTotals","serialized","totals","flatKey","rowKeys","map","slice","colKeys","rowTotals","colTotals","allTotal","sorters","some","clean","sorter","props","rows","cols","aggregatorName","vals","valueFilter","rowOrder","colOrder","rowSorts","colSorts","JSON","stringify","options","enabled","Worker","threshold"],"mappings":";;;;;;;8QAAA;AACA;AACA;;;;;;;;;;;;QAgCgBA,e,GAAAA,e;QAmCAC,iB,GAAAA,iB;QAwBAC,kB,GAAAA,kB;QAkDAC,oB,GAAAA,oB;QAcAC,e,GAAAA,e;QAuBAC,kB,GAAAA,kB;QAoBAC,sB,GAAAA,sB;QAUAC,kB,GAAAA,kB;;AAtMhB;;AAEA;AACA;AACO,IAAMC,oDAAsB,GAA5B;;AAEP;AACA;AACO,IAAMC,8DAA2B,GAAjC;;AAEP;AACA;AACO,IAAMC,kDAAqB,GAA3B;;AAEP;;;;;;;;AAQO,SAASV,eAAT,CAAyBW,IAAzB,EAA+BC,iBAA/B,EAAkDC,UAAlD,EAA8D;AACnE,MAAMC,aAAa,EAAnB;AACA,MAAMC,oBAAoB,EAA1B;AACA,MAAMC,QAAQC,MAAMC,OAAN,CAAcP,IAAd,IAAsBA,KAAKQ,MAA3B,GAAoC,IAAlD;AACA,MAAIC,mBAAmB,CAAvB;AACAC,uBAAUC,aAAV,CAAwBX,IAAxB,EAA8BC,qBAAqB,EAAnD,EAAuD,UAASW,MAAT,EAAiB;AACtER,sBAAkBS,IAAlB,CAAuBD,MAAvB;AADsE;AAAA;AAAA;;AAAA;AAEtE,2BAAmBE,OAAOC,IAAP,CAAYH,MAAZ,CAAnB,8HAAwC;AAAA,YAA7BI,IAA6B;;AACtC,YAAI,EAAEA,QAAQb,UAAV,CAAJ,EAA2B;AACzBA,qBAAWa,IAAX,IAAmB,EAAnB;AACA,cAAIP,mBAAmB,CAAvB,EAA0B;AACxBN,uBAAWa,IAAX,EAAiBC,IAAjB,GAAwBR,gBAAxB;AACD;AACF;AACF;AATqE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAUtE,SAAK,IAAMO,KAAX,IAAmBb,UAAnB,EAA+B;AAC7B,UAAMe,QAAQF,SAAQJ,MAAR,GAAiBA,OAAOI,KAAP,CAAjB,GAAgC,MAA9C;AACA,UAAI,EAAEE,SAASf,WAAWa,KAAX,CAAX,CAAJ,EAAkC;AAChCb,mBAAWa,KAAX,EAAiBE,KAAjB,IAA0B,CAA1B;AACD;AACDf,iBAAWa,KAAX,EAAiBE,KAAjB;AACD;AACDT;AACA,QAAIP,cAAcO,mBAAmBZ,mBAAnB,KAA2C,CAA7D,EAAgE;AAC9DK,iBAAWO,gBAAX,EAA6BJ,KAA7B,EAAoC,aAApC;AACD;AACF,GArBD;AAsBA,SAAO,EAACF,sBAAD,EAAaC,oCAAb,EAAgCe,cAAcV,gBAA9C,EAAP;AACD;;AAED;;;;;AAKO,SAASnB,iBAAT,CAA2B8B,YAA3B,EAAyCC,MAAzC,EAAiDnB,UAAjD,EAA6D;AAClE,MAAMG,QAAQe,aAAaD,YAA3B;AACA,MAAMG,YAAY,IAAIZ,oBAAJ,CAChBI,OAAOS,MAAP,CAAc,EAAd,EAAkBF,MAAlB,EAA0B;AACxBrB,UAAMoB,aAAahB,iBADK;AAExBH,uBAAmB,EAFK;AAGxBuB,uBAAmB,sCAAa;AAC9B,UAAItB,UAAJ,EAAgB;AACdA,mBAAWuB,SAAX,EAAsBpB,KAAtB,EAA6B,WAA7B;AACD;AACF;AAPuB,GAA1B,CADgB,CAAlB;AAWA;AACAiB,YAAUI,UAAV;AACAJ,YAAUK,UAAV;AACA,SAAOpC,mBAAmB+B,SAAnB,EAA8BF,YAA9B,CAAP;AACD;;AAED;;;;;AAKO,SAAS7B,kBAAT,CAA4B+B,SAA5B,EAAuCF,YAAvC,EAAqD;AAC1D,MAAMQ,gBAAgB,SAAhBA,aAAgB,OAAQ;AAC5B,QAAMC,SAAS,EAAf;AACAP,cAAUQ,YAAV,CAAuBC,OAAvB,CAA+B,eAAO;AACpC,UAAMC,WAAWC,KAAKC,WAAL,CAAiBC,IAAIC,GAArB,CAAjB;AACA,UAAIJ,QAAJ,EAAc;AACZH,eAAOM,IAAIC,GAAX,IAAkBJ,SAASd,KAAT,EAAlB;AACD;AACF,KALD;AAMA,WAAO,EAACW,cAAD,EAAP;AACD,GATD;;AAWA,MAAMQ,OAAO,EAAb;AACAvB,SAAOC,IAAP,CAAYO,UAAUe,IAAtB,EAA4BN,OAA5B,CAAoC,sBAAc;AAChD,QAAMO,YAAYhB,UAAUe,IAAV,CAAeE,UAAf,CAAlB;AACA,QAAMC,gBAAgB,EAAtB;AACA1B,WAAOC,IAAP,CAAYuB,SAAZ,EAAuBP,OAAvB,CAA+B,sBAAc;AAC3CS,oBAAcC,UAAd,IAA4Bb,cAAcU,UAAUG,UAAV,CAAd,CAA5B;AACD,KAFD;AAGAJ,SAAKE,UAAL,IAAmBC,aAAnB;AACD,GAPD;;AASA,MAAME,kBAAkB,SAAlBA,eAAkB,SAAU;AAChC,QAAMC,aAAa,EAAnB;AACA7B,WAAOC,IAAP,CAAY6B,MAAZ,EAAoBb,OAApB,CAA4B,mBAAW;AACrCY,iBAAWE,OAAX,IAAsBjB,cAAcgB,OAAOC,OAAP,CAAd,CAAtB;AACD,KAFD;AAGA,WAAOF,UAAP;AACD,GAND;;AAQA,SAAO;AACLG,aAASxB,UAAUwB,OAAV,CAAkBC,GAAlB,CAAsB;AAAA,aAAOX,IAAIY,KAAJ,EAAP;AAAA,KAAtB,CADJ;AAELC,aAAS3B,UAAU2B,OAAV,CAAkBF,GAAlB,CAAsB;AAAA,aAAOX,IAAIY,KAAJ,EAAP;AAAA,KAAtB,CAFJ;AAGLX,cAHK;AAILa,eAAWR,gBAAgBpB,UAAU4B,SAA1B,CAJN;AAKLC,eAAWT,gBAAgBpB,UAAU6B,SAA1B,CALN;AAMLC,cAAUxB,cAAcN,UAAU8B,QAAxB,CANL;AAOLtB,kBAAcR,UAAUQ,YAAV,CAAuBiB,GAAvB,CAA2B;AAAA,aAAOjC,OAAOS,MAAP,CAAc,EAAd,EAAkBY,GAAlB,CAAP;AAAA,KAA3B,CAPT;AAQLhC,gBAAYiB,aAAajB,UARpB;AASLC,uBAAmBgB,aAAahB,iBAT3B;AAULe,kBAAcC,aAAaD;AAVtB,GAAP;AAYD;;AAED;;;;;;AAMO,SAAS3B,oBAAT,CAA8B6D,OAA9B,EAAuC;AAC5C,MAAI,OAAOA,OAAP,KAAmB,UAAvB,EAAmC;AACjC,WAAO,IAAP;AACD;AACD,MAAI,CAACA,OAAD,IAAY,QAAOA,OAAP,yCAAOA,OAAP,OAAmB,QAAnC,EAA6C;AAC3C,WAAO,KAAP;AACD;AACD,SAAOvC,OAAOC,IAAP,CAAYsC,OAAZ,EAAqBC,IAArB,CAA0B;AAAA,WAAQ,OAAOD,QAAQrC,IAAR,CAAP,KAAyB,UAAjC;AAAA,GAA1B,CAAP;AACD;;AAED;;;;AAIO,SAASvB,eAAT,CAAyB4D,OAAzB,EAAkC;AACvC,MACE,CAACA,OAAD,IACA,QAAOA,OAAP,yCAAOA,OAAP,OAAmB,QADnB,IAEA,OAAOA,OAAP,KAAmB,UAHrB,EAIE;AACA,WAAO,EAAP;AACD;AACD,MAAME,QAAQ,EAAd;AACAzC,SAAOC,IAAP,CAAYsC,OAAZ,EAAqBtB,OAArB,CAA6B,gBAAQ;AACnC,QAAMyB,SAASH,QAAQrC,IAAR,CAAf;AACA,QAAI,OAAOwC,MAAP,KAAkB,QAAlB,IAA8BlD,MAAMC,OAAN,CAAciD,MAAd,CAAlC,EAAyD;AACvDD,YAAMvC,IAAN,IAAcwC,MAAd;AACD;AACF,GALD;AAMA,SAAOD,KAAP;AACD;;AAED;;;;;AAKO,SAAS7D,kBAAT,CAA4B+D,KAA5B,EAAmC;AACxC,SAAO;AACLC,UAAMD,MAAMC,IADP;AAELC,UAAMF,MAAME,IAFP;AAGLC,oBAAgBH,MAAMG,cAHjB;AAIL9B,kBAAc2B,MAAM3B,YAJf;AAKL+B,UAAMJ,MAAMI,IALP;AAMLC,iBAAaL,MAAMK,WANd;AAOLT,aAAS5D,gBAAgBgE,MAAMJ,OAAtB,CAPJ;AAQLU,cAAUN,MAAMM,QARX;AASLC,cAAUP,MAAMO,QATX;AAULC,cAAUR,MAAMQ,QAVX;AAWLC,cAAUT,MAAMS;AAXX,GAAP;AAaD;;AAED;;;;AAIO,SAASvE,sBAAT,CAAgC8D,KAAhC,EAAuC;AAC5C,SAAOU,KAAKC,SAAL,CAAe1E,mBAAmB+D,KAAnB,CAAf,CAAP;AACD;;AAED;;;;;;AAMO,SAAS7D,kBAAT,CAA4B6D,KAA5B,EAAiD;AAAA,MAAdY,OAAc,uEAAJ,EAAI;;AACtD,MAAIA,QAAQC,OAAR,KAAoB,KAAxB,EAA+B;AAC7B,WAAO,KAAP;AACD;AACD,MAAI,OAAOC,MAAP,KAAkB,WAAtB,EAAmC;AACjC,WAAO,KAAP;AACD;AACD,MAAMC,YACJ,OAAOH,QAAQG,SAAf,KAA6B,QAA7B,GACIH,QAAQG,SADZ,GAEI1E,wBAHN;;AAKA,MAAME,OAAOyD,MAAMzD,IAAnB;AACA,MAAI,CAACM,MAAMC,OAAN,CAAcP,IAAd,CAAD,IAAwBA,KAAKQ,MAAL,GAAcgE,SAA1C,EAAqD;AACnD,WAAO,KAAP;AACD;AACD,SAAO,IAAP;AACD","file":"pivotCompute.js","sourcesContent":["/* eslint-disable no-console */\n/* eslint-disable valid-jsdoc */\n/*\n * Shared pivot-computation logic.\n *\n * This module contains the pure, DOM-free parts of the pivot computation so\n * that the exact same code paths can run:\n * - on the main thread (classic synchronous rendering path), and\n * - inside a Web Worker (src/pivotWorker.js) for large datasets.\n *\n * It must stay free of React and of any `window`/`document` references.\n */\nimport {PivotData} from './Utilities';\n\n// A progress callback fires every N processed records so the UI can render\n// progress without adding measurable overhead to the per-record loop.\nexport const PROGRESS_BATCH_SIZE = 200;\n\n// Datasets smaller than this are computed synchronously on the main thread;\n// larger ones are handed off to the Web Worker.\nexport const DEFAULT_WORKER_THRESHOLD = 200;\n\n// Trailing debounce before a worker computation is dispatched, so rapid\n// interactions do not queue one full computation per single change.\nexport const WORKER_DEBOUNCE_MS = 200;\n\n/*\n * Walks the raw input once and computes the attribute-value inventory plus a\n * \"materialized\" copy of the records (with derived attributes applied).\n * This is an exact extraction of the historical PivotTableUI behavior.\n *\n * onProgress receives (processed, total, phase); total is null when it is\n * unknown upfront (function-based data sources).\n */\nexport function materializeData(data, derivedAttributes, onProgress) {\n const attrValues = {};\n const materializedInput = [];\n const total = Array.isArray(data) ? data.length : null;\n let recordsProcessed = 0;\n PivotData.forEachRecord(data, derivedAttributes || {}, function(record) {\n materializedInput.push(record);\n for (const attr of Object.keys(record)) {\n if (!(attr in attrValues)) {\n attrValues[attr] = {};\n if (recordsProcessed > 0) {\n attrValues[attr].null = recordsProcessed;\n }\n }\n }\n for (const attr in attrValues) {\n const value = attr in record ? record[attr] : 'null';\n if (!(value in attrValues[attr])) {\n attrValues[attr][value] = 0;\n }\n attrValues[attr][value]++;\n }\n recordsProcessed++;\n if (onProgress && recordsProcessed % PROGRESS_BATCH_SIZE === 0) {\n onProgress(recordsProcessed, total, 'materialize');\n }\n });\n return {attrValues, materializedInput, totalRecords: recordsProcessed};\n}\n\n/*\n * Aggregates an already-materialized dataset into a PivotData instance and\n * serializes the result into a plain (structured-cloneable) object that can\n * cross the worker boundary and later hydrate PivotData on the main thread.\n */\nexport function computePivotState(materialized, config, onProgress) {\n const total = materialized.totalRecords;\n const pivotData = new PivotData(\n Object.assign({}, config, {\n data: materialized.materializedInput,\n derivedAttributes: {},\n onRecordProcessed: processed => {\n if (onProgress) {\n onProgress(processed, total, 'aggregate');\n }\n },\n })\n );\n // Force key sorting to happen while we are (possibly) inside the worker.\n pivotData.getRowKeys();\n pivotData.getColKeys();\n return serializePivotData(pivotData, materialized);\n}\n\n/*\n * Serializes a fully-built PivotData instance into plain data. Only the final\n * aggregated value of every cell is kept; `value()` and `format()` behavior\n * are reconstructed on the main thread by the PivotData hydration step.\n */\nexport function serializePivotData(pivotData, materialized) {\n const serializeCell = cell => {\n const values = {};\n pivotData.aggregations.forEach(agg => {\n const instance = cell.getInstance(agg.key);\n if (instance) {\n values[agg.key] = instance.value();\n }\n });\n return {values};\n };\n\n const tree = {};\n Object.keys(pivotData.tree).forEach(flatRowKey => {\n const rowBranch = pivotData.tree[flatRowKey];\n const serializedRow = {};\n Object.keys(rowBranch).forEach(flatColKey => {\n serializedRow[flatColKey] = serializeCell(rowBranch[flatColKey]);\n });\n tree[flatRowKey] = serializedRow;\n });\n\n const serializeTotals = totals => {\n const serialized = {};\n Object.keys(totals).forEach(flatKey => {\n serialized[flatKey] = serializeCell(totals[flatKey]);\n });\n return serialized;\n };\n\n return {\n rowKeys: pivotData.rowKeys.map(key => key.slice()),\n colKeys: pivotData.colKeys.map(key => key.slice()),\n tree,\n rowTotals: serializeTotals(pivotData.rowTotals),\n colTotals: serializeTotals(pivotData.colTotals),\n allTotal: serializeCell(pivotData.allTotal),\n aggregations: pivotData.aggregations.map(agg => Object.assign({}, agg)),\n attrValues: materialized.attrValues,\n materializedInput: materialized.materializedInput,\n totalRecords: materialized.totalRecords,\n };\n}\n\n/*\n * Sorters containing functions cannot be structured-cloned into a worker.\n * Returns true when the sorters prop itself, or any of its entries, is a\n * function. In that case PivotData re-sorts on the main thread after\n * hydration (distinct keys are few, so this stays cheap).\n */\nexport function sortersHaveFunctions(sorters) {\n if (typeof sorters === 'function') {\n return true;\n }\n if (!sorters || typeof sorters !== 'object') {\n return false;\n }\n return Object.keys(sorters).some(attr => typeof sorters[attr] === 'function');\n}\n\n/*\n * Returns a structured-cloneable subset of the sorters prop: data-type names\n * and sample-value arrays survive the worker boundary, functions do not.\n */\nexport function sanitizeSorters(sorters) {\n if (\n !sorters ||\n typeof sorters !== 'object' ||\n typeof sorters === 'function'\n ) {\n return {};\n }\n const clean = {};\n Object.keys(sorters).forEach(attr => {\n const sorter = sorters[attr];\n if (typeof sorter === 'string' || Array.isArray(sorter)) {\n clean[attr] = sorter;\n }\n });\n return clean;\n}\n\n/*\n * Extracts exactly the props PivotData needs to compute results, with\n * everything non-serializable stripped, so the object can be posted to the\n * worker and fed straight into PivotData.\n */\nexport function createWorkerConfig(props) {\n return {\n rows: props.rows,\n cols: props.cols,\n aggregatorName: props.aggregatorName,\n aggregations: props.aggregations,\n vals: props.vals,\n valueFilter: props.valueFilter,\n sorters: sanitizeSorters(props.sorters),\n rowOrder: props.rowOrder,\n colOrder: props.colOrder,\n rowSorts: props.rowSorts,\n colSorts: props.colSorts,\n };\n}\n\n/*\n * Stable string signature of everything that influences a worker computation.\n * Data identity is NOT part of it; callers append their own data version.\n */\nexport function createComputeSignature(props) {\n return JSON.stringify(createWorkerConfig(props));\n}\n\n/*\n * Decides whether the current props can be computed inside the Web Worker.\n * Anything that cannot be structured-cloned (function data sources, custom\n * derivedAttributes, custom aggregator maps) forces the classic path.\n * Function-based sorters are allowed: they are re-applied on the main thread.\n */\nexport function canComputeInWorker(props, options = {}) {\n if (options.enabled === false) {\n return false;\n }\n if (typeof Worker === 'undefined') {\n return false;\n }\n const threshold =\n typeof options.threshold === 'number'\n ? options.threshold\n : DEFAULT_WORKER_THRESHOLD;\n \n const data = props.data;\n if (!Array.isArray(data) || data.length < threshold) {\n return false;\n }\n return true;\n}\n"]}
@@ -2,25 +2,12 @@
2
2
 
3
3
  var _pivotCompute = require('./pivotCompute');
4
4
 
5
- var MAX_CACHED_DATASETS = 4; /* eslint-disable valid-jsdoc */
6
- /*
7
- * Web Worker entry point for off-main-thread pivot computation.
8
- *
9
- * Message protocol (plain, structured-cloneable objects):
10
- * main -> worker {type: 'pivot:setData', id, dataKey, data}
11
- * main -> worker {type: 'pivot:compute', id, dataKey, config}
12
- * worker -> main {type: 'pivot:progress', id, processed, total, phase}
13
- * worker -> main {type: 'pivot:result', id, result}
14
- * worker -> main {type: 'pivot:error', id, message}
15
- *
16
- * The materialized dataset is cached per dataKey, so repeated computations on
17
- * the same data (e.g. dragging fields around) do not pay the cost of
18
- * re-scanning the raw records.
19
- */
20
-
5
+ var MAX_CACHED_DATASETS = 4;
21
6
 
22
7
  var datasetCache = new Map();
23
8
 
9
+ var debugEnabled = false;
10
+
24
11
  var cacheDataset = function cacheDataset(dataKey, dataset) {
25
12
  datasetCache.delete(dataKey);
26
13
  datasetCache.set(dataKey, dataset);
@@ -35,6 +22,12 @@ var postToMain = function postToMain(message) {
35
22
  self.postMessage(message);
36
23
  };
37
24
 
25
+ var log = function log(message, detail) {
26
+ if (debugEnabled) {
27
+ postToMain({ type: 'pivot:log', message: message, detail: detail });
28
+ }
29
+ };
30
+
38
31
  var toErrorMessage = function toErrorMessage(error) {
39
32
  return String(error && error.message || error);
40
33
  };
@@ -42,9 +35,17 @@ var toErrorMessage = function toErrorMessage(error) {
42
35
  self.addEventListener('message', function (event) {
43
36
  var message = event.data || {};
44
37
 
38
+ if (message.type === 'pivot:debug') {
39
+ debugEnabled = Boolean(message.enabled);
40
+ log('debug logging ' + (debugEnabled ? 'enabled' : 'disabled') + ' in worker');
41
+ return;
42
+ }
43
+
45
44
  if (message.type === 'pivot:setData') {
45
+ var startedAt = Date.now();
46
+ log('#' + message.id + ' setData received: ' + (message.data || []).length + ' records (dataKey=' + message.dataKey + ')');
46
47
  try {
47
- cacheDataset(message.dataKey, (0, _pivotCompute.materializeData)(message.data, {}, function (processed, total) {
48
+ var dataset = cacheDataset(message.dataKey, (0, _pivotCompute.materializeData)(message.data, {}, function (processed, total) {
48
49
  postToMain({
49
50
  type: 'pivot:progress',
50
51
  id: message.id,
@@ -53,7 +54,9 @@ self.addEventListener('message', function (event) {
53
54
  phase: 'materialize'
54
55
  });
55
56
  }));
57
+ log('#' + message.id + ' materialized ' + dataset.totalRecords + ' records in ' + (Date.now() - startedAt + 'ms (attributes: ' + Object.keys(dataset.attrValues).join(', ') + ')'));
56
58
  } catch (error) {
59
+ log('#' + message.id + ' setData failed: ' + toErrorMessage(error));
57
60
  postToMain({
58
61
  type: 'pivot:error',
59
62
  id: message.id,
@@ -64,12 +67,15 @@ self.addEventListener('message', function (event) {
64
67
  }
65
68
 
66
69
  if (message.type === 'pivot:compute') {
70
+ var _startedAt = Date.now();
71
+ var config = message.config || {};
67
72
  try {
68
- var dataset = datasetCache.get(message.dataKey);
69
- if (!dataset) {
73
+ var _dataset = datasetCache.get(message.dataKey);
74
+ if (!_dataset) {
70
75
  throw new Error('no dataset staged for key ' + message.dataKey);
71
76
  }
72
- var result = (0, _pivotCompute.computePivotState)(dataset, message.config, function (processed, total, phase) {
77
+ log('#' + message.id + ' compute received (rows=[' + (config.rows || []).join(', ') + '] cols=[' + (config.cols || []).join(', ') + '])');
78
+ var result = (0, _pivotCompute.computePivotState)(_dataset, message.config, function (processed, total, phase) {
73
79
  postToMain({
74
80
  type: 'pivot:progress',
75
81
  id: message.id,
@@ -78,8 +84,12 @@ self.addEventListener('message', function (event) {
78
84
  phase: phase
79
85
  });
80
86
  });
87
+ log('#' + message.id + ' computed in ' + (Date.now() - _startedAt) + 'ms (rowKeys: ' + result.rowKeys.length + ', colKeys: ' + result.colKeys.length + ', aggregations: ' + result.aggregations.map(function (agg) {
88
+ return agg.key;
89
+ }).join(', ') + ')');
81
90
  postToMain({ type: 'pivot:result', id: message.id, result: result });
82
91
  } catch (error) {
92
+ log('#' + message.id + ' compute failed: ' + toErrorMessage(error));
83
93
  postToMain({
84
94
  type: 'pivot:error',
85
95
  id: message.id,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/pivotWorker.js"],"names":["MAX_CACHED_DATASETS","datasetCache","Map","cacheDataset","dataKey","dataset","delete","set","size","oldestKey","keys","next","value","postToMain","self","postMessage","message","toErrorMessage","String","error","addEventListener","event","data","type","processed","total","id","phase","get","Error","result","config"],"mappings":";;AAeA;;AAEA,IAAMA,sBAAsB,CAA5B,C,CAjBA;AACA;;;;;;;;;;;;;;;;AAkBA,IAAMC,eAAe,IAAIC,GAAJ,EAArB;;AAEA,IAAMC,eAAe,SAAfA,YAAe,CAACC,OAAD,EAAUC,OAAV,EAAsB;AACzCJ,eAAaK,MAAb,CAAoBF,OAApB;AACAH,eAAaM,GAAb,CAAiBH,OAAjB,EAA0BC,OAA1B;AACA,SAAOJ,aAAaO,IAAb,GAAoBR,mBAA3B,EAAgD;AAC9C,QAAMS,YAAYR,aAAaS,IAAb,GAAoBC,IAApB,GAA2BC,KAA7C;AACAX,iBAAaK,MAAb,CAAoBG,SAApB;AACD;AACD,SAAOJ,OAAP;AACD,CARD;;AAUA,IAAMQ,aAAa,SAAbA,UAAa,UAAW;AAC5BC,OAAKC,WAAL,CAAiBC,OAAjB;AACD,CAFD;;AAIA,IAAMC,iBAAiB,SAAjBA,cAAiB;AAAA,SAASC,OAAQC,SAASA,MAAMH,OAAhB,IAA4BG,KAAnC,CAAT;AAAA,CAAvB;;AAEAL,KAAKM,gBAAL,CAAsB,SAAtB,EAAiC,iBAAS;AACxC,MAAMJ,UAAUK,MAAMC,IAAN,IAAc,EAA9B;;AAEA,MAAIN,QAAQO,IAAR,KAAiB,eAArB,EAAsC;AACpC,QAAI;AACFpB,mBACEa,QAAQZ,OADV,EAEE,mCAAgBY,QAAQM,IAAxB,EAA8B,EAA9B,EAAkC,UAACE,SAAD,EAAYC,KAAZ,EAAsB;AACtDZ,mBAAW;AACTU,gBAAM,gBADG;AAETG,cAAIV,QAAQU,EAFH;AAGTF,8BAHS;AAITC,sBAJS;AAKTE,iBAAO;AALE,SAAX;AAOD,OARD,CAFF;AAYD,KAbD,CAaE,OAAOR,KAAP,EAAc;AACdN,iBAAW;AACTU,cAAM,aADG;AAETG,YAAIV,QAAQU,EAFH;AAGTV,iBAASC,eAAeE,KAAf;AAHA,OAAX;AAKD;AACD;AACD;;AAED,MAAIH,QAAQO,IAAR,KAAiB,eAArB,EAAsC;AACpC,QAAI;AACF,UAAMlB,UAAUJ,aAAa2B,GAAb,CAAiBZ,QAAQZ,OAAzB,CAAhB;AACA,UAAI,CAACC,OAAL,EAAc;AACZ,cAAM,IAAIwB,KAAJ,gCAAuCb,QAAQZ,OAA/C,CAAN;AACD;AACD,UAAM0B,SAAS,qCACbzB,OADa,EAEbW,QAAQe,MAFK,EAGb,UAACP,SAAD,EAAYC,KAAZ,EAAmBE,KAAnB,EAA6B;AAC3Bd,mBAAW;AACTU,gBAAM,gBADG;AAETG,cAAIV,QAAQU,EAFH;AAGTF,8BAHS;AAITC,sBAJS;AAKTE;AALS,SAAX;AAOD,OAXY,CAAf;AAaAd,iBAAW,EAACU,MAAM,cAAP,EAAuBG,IAAIV,QAAQU,EAAnC,EAAuCI,cAAvC,EAAX;AACD,KAnBD,CAmBE,OAAOX,KAAP,EAAc;AACdN,iBAAW;AACTU,cAAM,aADG;AAETG,YAAIV,QAAQU,EAFH;AAGTV,iBAASC,eAAeE,KAAf;AAHA,OAAX;AAKD;AACF;AACF,CAvDD","file":"pivotWorker.js","sourcesContent":["/* eslint-disable valid-jsdoc */\n/*\n * Web Worker entry point for off-main-thread pivot computation.\n *\n * Message protocol (plain, structured-cloneable objects):\n * main -> worker {type: 'pivot:setData', id, dataKey, data}\n * main -> worker {type: 'pivot:compute', id, dataKey, config}\n * worker -> main {type: 'pivot:progress', id, processed, total, phase}\n * worker -> main {type: 'pivot:result', id, result}\n * worker -> main {type: 'pivot:error', id, message}\n *\n * The materialized dataset is cached per dataKey, so repeated computations on\n * the same data (e.g. dragging fields around) do not pay the cost of\n * re-scanning the raw records.\n */\nimport {materializeData, computePivotState} from './pivotCompute';\n\nconst MAX_CACHED_DATASETS = 4;\n\nconst datasetCache = new Map();\n\nconst cacheDataset = (dataKey, dataset) => {\n datasetCache.delete(dataKey);\n datasetCache.set(dataKey, dataset);\n while (datasetCache.size > MAX_CACHED_DATASETS) {\n const oldestKey = datasetCache.keys().next().value;\n datasetCache.delete(oldestKey);\n }\n return dataset;\n};\n\nconst postToMain = message => {\n self.postMessage(message);\n};\n\nconst toErrorMessage = error => String((error && error.message) || error);\n\nself.addEventListener('message', event => {\n const message = event.data || {};\n\n if (message.type === 'pivot:setData') {\n try {\n cacheDataset(\n message.dataKey,\n materializeData(message.data, {}, (processed, total) => {\n postToMain({\n type: 'pivot:progress',\n id: message.id,\n processed,\n total,\n phase: 'materialize',\n });\n })\n );\n } catch (error) {\n postToMain({\n type: 'pivot:error',\n id: message.id,\n message: toErrorMessage(error),\n });\n }\n return;\n }\n\n if (message.type === 'pivot:compute') {\n try {\n const dataset = datasetCache.get(message.dataKey);\n if (!dataset) {\n throw new Error(`no dataset staged for key ${message.dataKey}`);\n }\n const result = computePivotState(\n dataset,\n message.config,\n (processed, total, phase) => {\n postToMain({\n type: 'pivot:progress',\n id: message.id,\n processed,\n total,\n phase,\n });\n }\n );\n postToMain({type: 'pivot:result', id: message.id, result});\n } catch (error) {\n postToMain({\n type: 'pivot:error',\n id: message.id,\n message: toErrorMessage(error),\n });\n }\n }\n});\n"]}
1
+ {"version":3,"sources":["../src/pivotWorker.js"],"names":["MAX_CACHED_DATASETS","datasetCache","Map","debugEnabled","cacheDataset","dataKey","dataset","delete","set","size","oldestKey","keys","next","value","postToMain","self","postMessage","message","log","detail","type","toErrorMessage","String","error","addEventListener","event","data","Boolean","enabled","startedAt","Date","now","id","length","processed","total","phase","totalRecords","Object","attrValues","join","config","get","Error","rows","cols","result","rowKeys","colKeys","aggregations","map","agg","key"],"mappings":"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","file":"pivotWorker.js","sourcesContent":["import {materializeData, computePivotState} from './pivotCompute';\n\nconst MAX_CACHED_DATASETS = 4;\n\nconst datasetCache = new Map();\n\nlet debugEnabled = false;\n\nconst cacheDataset = (dataKey, dataset) => {\n datasetCache.delete(dataKey);\n datasetCache.set(dataKey, dataset);\n while (datasetCache.size > MAX_CACHED_DATASETS) {\n const oldestKey = datasetCache.keys().next().value;\n datasetCache.delete(oldestKey);\n }\n return dataset;\n};\n\nconst postToMain = message => {\n self.postMessage(message);\n};\n\nconst log = (message, detail) => {\n if (debugEnabled) {\n postToMain({type: 'pivot:log', message, detail});\n }\n};\n\nconst toErrorMessage = error => String((error && error.message) || error);\n\nself.addEventListener('message', event => {\n const message = event.data || {};\n\n if (message.type === 'pivot:debug') {\n debugEnabled = Boolean(message.enabled);\n log(`debug logging ${debugEnabled ? 'enabled' : 'disabled'} in worker`);\n return;\n }\n\n if (message.type === 'pivot:setData') {\n const startedAt = Date.now();\n log(\n `#${message.id} setData received: ${\n (message.data || []).length\n } records (dataKey=${message.dataKey})`\n );\n try {\n const dataset = cacheDataset(\n message.dataKey,\n materializeData(message.data, {}, (processed, total) => {\n postToMain({\n type: 'pivot:progress',\n id: message.id,\n processed,\n total,\n phase: 'materialize',\n });\n })\n );\n log(\n `#${message.id} materialized ${dataset.totalRecords} records in ` +\n `${Date.now() - startedAt}ms (attributes: ${Object.keys(\n dataset.attrValues\n ).join(', ')})`\n );\n } catch (error) {\n log(`#${message.id} setData failed: ${toErrorMessage(error)}`);\n postToMain({\n type: 'pivot:error',\n id: message.id,\n message: toErrorMessage(error),\n });\n }\n return;\n }\n\n if (message.type === 'pivot:compute') {\n const startedAt = Date.now();\n const config = message.config || {};\n try {\n const dataset = datasetCache.get(message.dataKey);\n if (!dataset) {\n throw new Error(`no dataset staged for key ${message.dataKey}`);\n }\n log(\n `#${message.id} compute received (rows=[${(config.rows || []).join(\n ', '\n )}] cols=[${(config.cols || []).join(', ')}])`\n );\n const result = computePivotState(\n dataset,\n message.config,\n (processed, total, phase) => {\n postToMain({\n type: 'pivot:progress',\n id: message.id,\n processed,\n total,\n phase,\n });\n }\n );\n log(\n `#${message.id} computed in ${Date.now() - startedAt}ms (rowKeys: ${\n result.rowKeys.length\n }, colKeys: ${\n result.colKeys.length\n }, aggregations: ${result.aggregations.map(agg => agg.key).join(', ')})`\n );\n postToMain({type: 'pivot:result', id: message.id, result});\n } catch (error) {\n log(`#${message.id} compute failed: ${toErrorMessage(error)}`);\n postToMain({\n type: 'pivot:error',\n id: message.id,\n message: toErrorMessage(error),\n });\n }\n }\n});\n"]}
@@ -435,7 +435,7 @@ table.pvtTable tbody tr td {
435
435
  .pvtLoadingBar {
436
436
  height: 100%;
437
437
  background: #ff6600;
438
- transition: width 0.2s ease;
438
+ transition: width 0.1s ease;
439
439
  }
440
440
 
441
441
  .pvtLoadingPercent {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hrforte/react-pivottable",
3
- "version": "2.0.0",
3
+ "version": "2.1.1",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },