visualifyjs 2.5.3 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (252) hide show
  1. package/.claude/mem/TIMELINE.md +36 -0
  2. package/.claude/mem/notes/2026-02-11-3d-visualization-docs-fix-external-script-solution.md +24 -0
  3. package/.claude/mem/notes/2026-02-11-3d-visualization-docs-fix-session-summary.md +43 -0
  4. package/.claude/mem/notes/2026-02-11-cli-fix-editor-command-alias.md +26 -0
  5. package/.claude/mem/notes/2026-02-11-phase-3-developer-experience-completed.md +51 -0
  6. package/.claude/mem/notes/2026-02-11-phase-4-web-workers-implementation-complete.md +59 -0
  7. package/.claude/mem/notes/2026-02-11-visualify-phase-2-3d-visualization-complete.md +50 -0
  8. package/.claude/mem/notes/2026-02-11-visualify-phase-2-committed-ready-for-phase-3.md +33 -0
  9. package/.claude/mem/notes/2026-02-11-visualify-phase-3-complete-developer-experience.md +52 -0
  10. package/.claude/mem/notes/2026-02-11-visualify-repository-cleanup-complete.md +28 -0
  11. package/.claude/mem/notes/2026-02-18-codebase-cleanup-docsify-plugin-documentation.md +37 -0
  12. package/.claude/mem/notes/2026-02-19-css-grid-layout-fix-displaycontents-on-vcontroller.md +18 -0
  13. package/.claude/mem/notes/2026-02-19-docsify-plugin-fixes-latex-and-visualify-code-bloc.md +26 -0
  14. package/.claude/mem/notes/2026-02-19-page-mode-docs-update-decisions.md +23 -0
  15. package/.claude/mem/notes/2026-02-19-react-context-infinite-re-render-loop-fix-pattern.md +31 -0
  16. package/.claude/mem/notes/2026-02-19-version-300-bump-and-build-fixes.md +32 -0
  17. package/.claude/mem/notes/2026-02-19-visualify-build-deployment-architecture-bug-fixes.md +25 -0
  18. package/.claude/mem/notes/2026-02-19-visualify-dist-iife-self-contained-build-config.md +30 -0
  19. package/.claude/mem/notes/2026-02-19-visualify-infinite-loop-i18n-fixes.md +31 -0
  20. package/.claude/mem/notes/2026-02-19-visualify-v3-bundle-splitting-docs-restructuring.md +32 -0
  21. package/.claude/mem/notes/2026-02-20-bundle-externalization-final-architecture.md +29 -0
  22. package/.claude/mem/notes/2026-02-20-chromium-page-fix-unstable-keys-and-double-event-b.md +27 -0
  23. package/.claude/mem/notes/2026-02-20-console-cleanup-bundle-optimization-commit.md +20 -0
  24. package/.claude/mem/notes/2026-02-20-dotbio-dot-plot-fix-useeffect-dependency.md +21 -0
  25. package/.claude/mem/notes/2026-02-20-public-folder-cleanup-and-readme-rewrite.md +25 -0
  26. package/.claude/mem/notes/2026-02-20-v300-release-and-beta-channel-strategy.md +29 -0
  27. package/.claude/mem/notes/2026-02-20-visium-background-image-unknown-legend-fix.md +19 -0
  28. package/.claude/mem/notes/2026-02-20-visualify-cdn-loader-bundle-externalization.md +34 -0
  29. package/.claude/mem/sessions/session-2026-02-20-031524.md +54 -0
  30. package/.claude/settings.local.json +21 -0
  31. package/.github/workflows/static.yml.bak +51 -51
  32. package/.sisyphus/boulder.json +65 -0
  33. package/.sisyphus/plans/phase-4-advanced-optimizations.md +217 -0
  34. package/LICENSE +674 -674
  35. package/README.md +94 -59
  36. package/config-overrides.js +31 -31
  37. package/dist/stats.html +4949 -0
  38. package/dist/visualify-3d.esm.js +1 -0
  39. package/dist/visualify-3d.js +1 -0
  40. package/dist/visualify-core.esm.js +1 -0
  41. package/dist/visualify-core.js +1 -0
  42. package/dist/visualify-docs.esm.js +1 -0
  43. package/dist/visualify-docs.js +1 -0
  44. package/dist/visualify-loader.js +1 -0
  45. package/dist/visualify-pages.esm.js +1 -0
  46. package/dist/visualify-pages.js +1 -0
  47. package/dist/visualify-portal.esm.js +1 -0
  48. package/dist/visualify-portal.js +1 -0
  49. package/dist/visualify-shared.js +26571 -0
  50. package/dist/visualify.js +1 -188
  51. package/docs/CHANGELOG.md +148 -0
  52. package/docs/cli/commands.md +513 -0
  53. package/docs/configuration/visualify-json.md +474 -0
  54. package/docs/docs/3d-visualization.md +374 -0
  55. package/docs/docs/CLI.md +303 -34
  56. package/docs/docs/README.md +65 -65
  57. package/docs/docs/Rechart/bar.md +190 -190
  58. package/docs/docs/Rechart/funnel.md +241 -193
  59. package/docs/docs/Rechart/line.md +355 -355
  60. package/docs/docs/Rechart/pie.md +225 -225
  61. package/docs/docs/Rechart/radar.md +253 -253
  62. package/docs/docs/Rechart/scatter.md +298 -0
  63. package/docs/docs/_404.md +51 -51
  64. package/docs/docs/_coverpage.md +11 -11
  65. package/docs/docs/_sidebar.md +54 -43
  66. package/docs/docs/components/dotBio.md +87 -34
  67. package/docs/docs/components/echart.md +171 -82
  68. package/docs/docs/components/html.md +61 -34
  69. package/docs/docs/components/macaron.md +156 -145
  70. package/docs/docs/components/markdown.md +42 -0
  71. package/docs/docs/components/more.md +183 -142
  72. package/docs/docs/components/plotly.md +132 -62
  73. package/docs/docs/components/scatterL.md +171 -70
  74. package/docs/docs/components/visium.md +112 -57
  75. package/docs/docs/configuration.md +121 -123
  76. package/docs/docs/deploy.md +31 -31
  77. package/docs/docs/docsify-plugin.md +655 -0
  78. package/docs/docs/hmr.md +165 -0
  79. package/docs/docs/i18n.md +332 -0
  80. package/docs/docs/log.md +30 -1
  81. package/docs/docs/more-pages.md +23 -23
  82. package/docs/docs/quickstart.md +148 -119
  83. package/docs/docs/rechart-attributes.md +74 -74
  84. package/docs/docs/rechart-basic-usage.md +160 -162
  85. package/docs/docs/theme.md +5 -5
  86. package/docs/docs/typescript.md +306 -0
  87. package/docs/docs/visual-editor.md +359 -0
  88. package/docs/index.html +85 -71
  89. package/docs/manifest.json +23 -23
  90. package/docs/migration/v3-migration.md +392 -0
  91. package/docs/static/css/fluff-stuff.css +169 -169
  92. package/docs/static/css/font-awesome.min.css +4 -4
  93. package/docs/static/css/visualify.css +6 -25
  94. package/docs/static/js/3d-viz-examples.js +181 -0
  95. package/docs/static/js/configuration.js +630 -448
  96. package/docs/static/js/visualify.js +1 -188
  97. package/package.json +106 -84
  98. package/rollup.config.mjs +766 -76
  99. package/src/_css/404.css +115 -115
  100. package/src/_css/App.css +37 -37
  101. package/src/_css/autoSuggestion.css +26 -26
  102. package/src/_css/circular-progress.css +32 -32
  103. package/src/_css/index.css +36 -36
  104. package/src/_css/modern.css +350 -25
  105. package/src/_media/corner.svg +8 -8
  106. package/src/_media/download.svg +3 -3
  107. package/src/_media/logo.svg +14 -14
  108. package/src/_test/App.test.js +15 -15
  109. package/src/_utils/reportWebVitals.js +13 -13
  110. package/src/a11y/README.md +177 -0
  111. package/src/a11y/aria-labels.js +339 -0
  112. package/src/a11y/color-contrast.js +535 -0
  113. package/src/a11y/index.js +197 -0
  114. package/src/a11y/keyboard-nav.js +523 -0
  115. package/src/a11y/styles.css +165 -0
  116. package/src/cli/commands/dev.js +214 -0
  117. package/src/cli/commands/docs.js +521 -0
  118. package/src/cli/commands/edit.js +379 -0
  119. package/src/cli/commands/init.js +213 -0
  120. package/src/cli/commands/portal.js +236 -0
  121. package/src/cli/dev-server.js +530 -0
  122. package/src/cli/hmr.js +456 -0
  123. package/src/cli/index.js +180 -0
  124. package/src/cli/utils/config.js +207 -0
  125. package/src/cli/utils/logger.js +241 -0
  126. package/src/config/defaults.ts +122 -0
  127. package/src/config/index.ts +72 -0
  128. package/src/config/loader.ts +478 -0
  129. package/src/config/schema.ts +227 -0
  130. package/src/config/validator.ts +337 -0
  131. package/src/core/appContext.js +34 -27
  132. package/src/core/components/Bar.js +383 -0
  133. package/src/core/components/Bar3D.js +473 -0
  134. package/src/core/components/LargeDatasetChart.js +296 -0
  135. package/src/core/components/Line3D.js +310 -0
  136. package/src/core/components/Scatter.js +392 -188
  137. package/src/core/components/Scatter3D.js +455 -0
  138. package/src/core/components/ScatterBio.js +601 -572
  139. package/src/core/components/Surface3D.js +326 -0
  140. package/src/core/components/ThreeCustom.js +648 -0
  141. package/src/core/components/ThreeScene.js +459 -0
  142. package/src/core/components/VisiumPlot.js +191 -165
  143. package/src/core/components/browser.js +42 -42
  144. package/src/core/components/dotplot.js +413 -413
  145. package/src/core/components/html.js +29 -29
  146. package/src/core/components/list.js +178 -178
  147. package/src/core/components/macaron.js +206 -201
  148. package/src/core/components/markdown.js +56 -56
  149. package/src/core/components/parser.scatterBio.js +582 -579
  150. package/src/core/components/ratio.js +82 -80
  151. package/src/core/components/scatterL.js +206 -173
  152. package/src/core/components/searchbar.js +156 -131
  153. package/src/core/components/selection.js +310 -193
  154. package/src/core/components/timeline.js +236 -281
  155. package/src/core/components/visium.js +114 -97
  156. package/src/core/data-processor.js +413 -0
  157. package/src/core/fetch/condfetch.js +82 -82
  158. package/src/core/fetch/fetch.js +92 -92
  159. package/src/core/fetch/json.js +29 -29
  160. package/src/core/fetch/vfetch.js +42 -42
  161. package/src/core/hmr-client.js +724 -0
  162. package/src/core/liveEditor.js +44 -44
  163. package/src/core/modules/codeEditorWithPreview.js +104 -104
  164. package/src/core/modules/echarts/common.js +20 -20
  165. package/src/core/modules/echarts/gl.js +228 -0
  166. package/src/core/modules/echarts/presetHandler.js +41 -41
  167. package/src/core/modules/echarts/presets/esodev.chromium.js +172 -172
  168. package/src/core/modules/echarts/presets/esodev.codex.js +130 -130
  169. package/src/core/modules/echarts/presets/esodev.visium.js +123 -123
  170. package/src/core/modules/echarts/presets/mmtrbc.js +186 -186
  171. package/src/core/modules/echarts.js +70 -71
  172. package/src/core/modules/echartsUtils.js +43 -43
  173. package/src/core/modules/echartswitcher.js +227 -152
  174. package/src/core/modules/replotly/presetHandler.js +24 -24
  175. package/src/core/modules/replotly/presets/minimum.js +18 -18
  176. package/src/core/modules/replotly/presets/mmtrbc.dot.js +114 -114
  177. package/src/core/modules/replotly/presets/mmtrbc.violin.js +100 -100
  178. package/src/core/modules/replotly.js +74 -71
  179. package/src/core/modules/threejs/Camera.js +373 -0
  180. package/src/core/modules/threejs/Lighting.js +459 -0
  181. package/src/core/modules/threejs/Renderer.js +364 -0
  182. package/src/core/modules/threejs/Scene.js +266 -0
  183. package/src/core/modules/threejs/index.js +155 -0
  184. package/src/core/pages/404.js +50 -50
  185. package/src/core/pages/error.js +27 -27
  186. package/src/core/pages/jsonPage.js +62 -62
  187. package/src/core/pages/loading.js +44 -44
  188. package/src/core/parser/echart.data.js +204 -183
  189. package/src/core/parser/echart.features.js +125 -125
  190. package/src/core/parser/echart.general.js +147 -143
  191. package/src/core/parser/echart.hilbert.js +57 -57
  192. package/src/core/parser/echart.parser.js +210 -210
  193. package/src/core/parser/echart.series.js +67 -67
  194. package/src/core/parser/echart.types.js +76 -76
  195. package/src/core/parser/plotly.config.js +10 -10
  196. package/src/core/parser/plotly.data.js +132 -132
  197. package/src/core/parser/plotly.layout.js +9 -9
  198. package/src/core/parser/plotly.violin.js +18 -18
  199. package/src/core/recharts.js +361 -62
  200. package/src/core/router/alias.js +49 -49
  201. package/src/core/router/jsonRouter.js +31 -31
  202. package/src/core/themes/modern.js +32 -32
  203. package/src/core/themes/themeSelector.js +33 -33
  204. package/src/core/visualify.js +213 -47
  205. package/src/core/widgets/circularProgress.js +23 -23
  206. package/src/core/widgets/controller.js +116 -83
  207. package/src/core/widgets/errorBoundary.js +36 -36
  208. package/src/core/widgets/footer.js +185 -177
  209. package/src/core/widgets/header.js +238 -234
  210. package/src/core/widgets/layout/Grid.js +31 -31
  211. package/src/core/widgets/layout.js +36 -36
  212. package/src/core/widgets/mapping.js +56 -42
  213. package/src/core/workers/data-worker.js +349 -0
  214. package/src/core/workers/worker-pool.js +396 -0
  215. package/src/docsify/bundle.js +215 -0
  216. package/src/docsify/markdown.js +271 -0
  217. package/src/docsify/plugin.js +268 -0
  218. package/src/editor/README.md +172 -0
  219. package/src/editor/components/ChartBuilder.jsx +341 -0
  220. package/src/editor/components/ChartTypeSidebar.jsx +91 -0
  221. package/src/editor/components/Editor.jsx +367 -0
  222. package/src/editor/components/Preview.jsx +446 -0
  223. package/src/editor/components/PropertyPanel.jsx +468 -0
  224. package/src/editor/components/StatusBar.jsx +85 -0
  225. package/src/editor/context/EditorContext.js +248 -0
  226. package/src/editor/hooks/useDebounce.js +32 -0
  227. package/src/editor/index.js +315 -0
  228. package/src/editor/styles/editor.css +637 -0
  229. package/src/editor/utils/chartValidator.js +263 -0
  230. package/src/entries/charts3d.js +70 -0
  231. package/src/entries/core.js +78 -0
  232. package/src/entries/docs.js +154 -0
  233. package/src/entries/pages.js +93 -0
  234. package/src/entries/portal.js +204 -0
  235. package/src/entries/shared.js +50 -0
  236. package/src/i18n/formatters.js +455 -0
  237. package/src/i18n/index.js +169 -0
  238. package/src/i18n/locales/ar.json +137 -0
  239. package/src/i18n/locales/de.json +137 -0
  240. package/src/i18n/locales/en.json +137 -0
  241. package/src/i18n/locales/es.json +137 -0
  242. package/src/i18n/locales/he.json +137 -0
  243. package/src/i18n/locales/zh.json +137 -0
  244. package/src/i18n/rtl.css +183 -0
  245. package/src/index.js +82 -62
  246. package/src/loader.js +103 -0
  247. package/src/setupTests.js +5 -5
  248. package/tsconfig.json +51 -0
  249. package/types/charts.d.ts +569 -0
  250. package/types/components.d.ts +441 -0
  251. package/types/config.d.ts +199 -0
  252. package/types/index.d.ts +353 -0
@@ -1,97 +1,114 @@
1
- /*
2
- * @Author : Lihao leolihao@arizona.edu
3
- * @Date : 2024-01-08 16:34:20
4
- * @FilePath : /visualifyjs/src/core/components/Visium.js
5
- * @Description :
6
- * Copyright (c) 2024 by Lihao (leolihao@arizona.edu), All Rights Reserved.
7
- */
8
- import React, { useEffect, useState } from 'react';
9
- import { useAppContext } from '../appContext';
10
- import conditionalFetch from '../fetch/condfetch';
11
- import EChartSwitcher from '../modules/echartswitcher';
12
- import Loading from '../pages/loading';
13
- import { isEmpty } from 'lodash';
14
-
15
- const Visium = ({ props, style }) => {
16
- const [loading, setLoading] = useState({
17
- active: true,
18
- message: 'Please Select the Section',
19
- });
20
- const { sharedData } = useAppContext();
21
- const [Options, setOptions] = useState(props);
22
-
23
- useEffect(() => {
24
- const { cellfrac, metadata, gene, image } = props.settings;
25
- if (!metadata || !gene || !image)
26
- throw new Error('missing metadata, gene, or image');
27
- let trigger = {
28
- metadata: metadata.trigger,
29
- gene: gene.trigger,
30
- image: image.trigger,
31
- };
32
-
33
- const updatePlot = async () => {
34
- try {
35
- const cellval = isEmpty(sharedData[cellfrac])
36
- ? 'All'
37
- : sharedData[cellfrac][0];
38
-
39
- if (sharedData?.[trigger.image]) {
40
- console.log('image trigger', sharedData?.[trigger.image]);
41
- const imageBuffer = await conditionalFetch(
42
- image,
43
- sharedData,
44
- {},
45
- );
46
-
47
- const blob = new Blob([new Uint8Array(imageBuffer.data)], {
48
- type: 'image/png',
49
- });
50
- const dataURL = URL.createObjectURL(blob);
51
-
52
- console.log('image', dataURL);
53
- setOptions((prev) => ({
54
- ...prev,
55
- config: {
56
- ...prev.config,
57
- overrides: {
58
- ...prev.config.overrides,
59
- backgroundColor: {
60
- image: dataURL,
61
- },
62
- },
63
- },
64
- }));
65
- }
66
-
67
- console.log('update plot', cellval, trigger);
68
- } catch (e) {
69
- setLoading({ active: true, message: e.message });
70
- }
71
- };
72
-
73
- updatePlot();
74
- }, [props, sharedData]);
75
-
76
- return (
77
- <div
78
- id={props.id}
79
- style={{ ...style, position: 'relative' }}>
80
- {loading.active && (
81
- <Loading
82
- message={loading.message}
83
- style={loading.style}
84
- />
85
- )}
86
- <EChartSwitcher
87
- props={Options}
88
- style={{
89
- opacity: loading.active ? 0 : 1,
90
- ...style,
91
- }}
92
- />
93
- </div>
94
- );
95
- };
96
-
97
- export default Visium;
1
+ /*
2
+ * @Author : Lihao leolihao@arizona.edu
3
+ * @Date : 2024-01-08 16:34:20
4
+ * @FilePath : /visualifyjs/src/core/components/Visium.js
5
+ * @Description :
6
+ * Copyright (c) 2024 by Lihao (leolihao@arizona.edu), All Rights Reserved.
7
+ */
8
+ import React, { useEffect, useState, useRef } from 'react';
9
+ import { useAppContext } from '../appContext';
10
+ import conditionalFetch from '../fetch/condfetch';
11
+ import EChartSwitcher from '../modules/echartswitcher';
12
+ import Loading from '../pages/loading';
13
+ import { isEmpty } from 'lodash';
14
+
15
+ const Visium = ({ props, style }) => {
16
+ const [loading, setLoading] = useState({
17
+ active: true,
18
+ message: 'Please Select the Section',
19
+ });
20
+ const { sharedData } = useAppContext();
21
+ const [Options, setOptions] = useState(props);
22
+
23
+ // Use ref for sharedData to access latest without depending on it
24
+ const sharedDataRef = useRef(sharedData);
25
+ sharedDataRef.current = sharedData;
26
+
27
+ // Extract stable trigger keys from props.settings
28
+ const { cellfrac, metadata, gene, image } = props.settings || {};
29
+ const imageTrigger = image?.trigger;
30
+ // Build a snapshot of only the sharedData keys we depend on
31
+ const relevantKeys = [imageTrigger, cellfrac].filter(Boolean);
32
+ const sharedDataSnapshot = JSON.stringify(
33
+ relevantKeys.reduce((acc, key) => {
34
+ acc[key] = sharedData[key];
35
+ return acc;
36
+ }, {}),
37
+ );
38
+
39
+ useEffect(() => {
40
+ if (!metadata || !gene || !image)
41
+ throw new Error('missing metadata, gene, or image');
42
+ const currentSharedData = sharedDataRef.current;
43
+ let trigger = {
44
+ metadata: metadata.trigger,
45
+ gene: gene.trigger,
46
+ image: image.trigger,
47
+ };
48
+
49
+ const updatePlot = async () => {
50
+ try {
51
+ const cellval = isEmpty(currentSharedData[cellfrac])
52
+ ? 'All'
53
+ : currentSharedData[cellfrac][0];
54
+
55
+ if (currentSharedData?.[trigger.image]) {
56
+ console.log('image trigger', currentSharedData?.[trigger.image]);
57
+ const imageBuffer = await conditionalFetch(
58
+ image,
59
+ currentSharedData,
60
+ {},
61
+ );
62
+
63
+ const blob = new Blob([new Uint8Array(imageBuffer.data)], {
64
+ type: 'image/png',
65
+ });
66
+ const dataURL = URL.createObjectURL(blob);
67
+
68
+ console.log('image', dataURL);
69
+ setOptions((prev) => ({
70
+ ...prev,
71
+ config: {
72
+ ...prev.config,
73
+ overrides: {
74
+ ...prev.config.overrides,
75
+ backgroundColor: {
76
+ image: dataURL,
77
+ },
78
+ },
79
+ },
80
+ }));
81
+ }
82
+
83
+ console.log('update plot', cellval, trigger);
84
+ } catch (e) {
85
+ setLoading({ active: true, message: e.message });
86
+ }
87
+ };
88
+
89
+ updatePlot();
90
+ // eslint-disable-next-line react-hooks/exhaustive-deps
91
+ }, [sharedDataSnapshot, cellfrac, imageTrigger]);
92
+
93
+ return (
94
+ <div
95
+ id={props.id}
96
+ style={{ ...style, position: 'relative' }}>
97
+ {loading.active && (
98
+ <Loading
99
+ message={loading.message}
100
+ style={loading.style}
101
+ />
102
+ )}
103
+ <EChartSwitcher
104
+ props={Options}
105
+ style={{
106
+ opacity: loading.active ? 0 : 1,
107
+ ...style,
108
+ }}
109
+ />
110
+ </div>
111
+ );
112
+ };
113
+
114
+ export default Visium;
@@ -0,0 +1,413 @@
1
+ /**
2
+ * @fileoverview Data Processor - Main Thread Interface
3
+ * @module core/data-processor
4
+ *
5
+ * Provides a high-level API for offloading data processing to Web Workers.
6
+ * Handles worker pool management, task distribution, and result aggregation.
7
+ */
8
+
9
+ import { getWorkerPool, terminateWorkerPool } from './workers/worker-pool';
10
+
11
+ /**
12
+ * Default configuration for data processor
13
+ * @readonly
14
+ */
15
+ const DEFAULT_CONFIG = {
16
+ workerScript: new URL('./workers/data-worker.js', import.meta.url).href,
17
+ minWorkers: 2,
18
+ maxWorkers: 4,
19
+ chunkSize: 10000,
20
+ enableChunking: true,
21
+ };
22
+
23
+ /**
24
+ * Data Processor class
25
+ * Manages data processing operations using Web Workers
26
+ */
27
+ class DataProcessor {
28
+ /**
29
+ * Create a new data processor
30
+ * @param {Object} config - Processor configuration
31
+ */
32
+ constructor(config = {}) {
33
+ this.config = { ...DEFAULT_CONFIG, ...config };
34
+ this.pool = getWorkerPool({
35
+ workerScript: this.config.workerScript,
36
+ minWorkers: this.config.minWorkers,
37
+ maxWorkers: this.config.maxWorkers,
38
+ });
39
+ }
40
+
41
+ /**
42
+ * Check if data should be chunked for processing
43
+ * @private
44
+ * @param {Array} data - Data array
45
+ * @returns {boolean} True if data should be chunked
46
+ */
47
+ _shouldChunk(data) {
48
+ return this.config.enableChunking && data.length > this.config.chunkSize;
49
+ }
50
+
51
+ /**
52
+ * Split data into chunks for parallel processing
53
+ * @private
54
+ * @param {Array} data - Data array
55
+ * @param {number} chunkSize - Size of each chunk
56
+ * @returns {Array<Array>} Array of chunks
57
+ */
58
+ _chunkData(data, chunkSize) {
59
+ const chunks = [];
60
+ for (let i = 0; i < data.length; i += chunkSize) {
61
+ chunks.push(data.slice(i, i + chunkSize));
62
+ }
63
+ return chunks;
64
+ }
65
+
66
+ /**
67
+ * Aggregate data using specified method
68
+ * @param {Array} data - Input data
69
+ * @param {Object} config - Aggregation config
70
+ * @returns {Promise<number>} Aggregated value
71
+ */
72
+ async aggregate(data, config) {
73
+ if (this._shouldChunk(data)) {
74
+ // Process in chunks and combine results
75
+ const chunks = this._chunkData(data, this.config.chunkSize);
76
+ const chunkResults = await this.pool.executeAll(
77
+ chunks.map((chunk) => ({
78
+ operation: 'aggregate',
79
+ data: chunk,
80
+ config: { ...config, method: 'sum' },
81
+ }))
82
+ );
83
+
84
+ // Combine chunk results
85
+ const total = chunkResults.reduce((a, b) => a + b, 0);
86
+
87
+ if (config.method === 'avg' || config.method === 'mean') {
88
+ return total / data.length;
89
+ }
90
+ if (config.method === 'count') {
91
+ return data.length;
92
+ }
93
+ return total;
94
+ }
95
+
96
+ return this.pool.execute({
97
+ operation: 'aggregate',
98
+ data,
99
+ config,
100
+ });
101
+ }
102
+
103
+ /**
104
+ * Filter data based on criteria
105
+ * @param {Array} data - Input data
106
+ * @param {Object} criteria - Filter criteria
107
+ * @returns {Promise<Array>} Filtered data
108
+ */
109
+ async filter(data, criteria) {
110
+ if (this._shouldChunk(data)) {
111
+ const chunks = this._chunkData(data, this.config.chunkSize);
112
+ const results = await this.pool.executeAll(
113
+ chunks.map((chunk) => ({
114
+ operation: 'filter',
115
+ data: chunk,
116
+ config: criteria,
117
+ }))
118
+ );
119
+ return results.flat();
120
+ }
121
+
122
+ return this.pool.execute({
123
+ operation: 'filter',
124
+ data,
125
+ config: criteria,
126
+ });
127
+ }
128
+
129
+ /**
130
+ * Sort data by fields
131
+ * @param {Array} data - Input data
132
+ * @param {Array|Object} sortConfig - Sort configuration
133
+ * @returns {Promise<Array>} Sorted data
134
+ */
135
+ async sort(data, sortConfig) {
136
+ // Sorting is typically done on full dataset
137
+ return this.pool.execute({
138
+ operation: 'sort',
139
+ data,
140
+ config: sortConfig,
141
+ });
142
+ }
143
+
144
+ /**
145
+ * Group data by field(s)
146
+ * @param {Array} data - Input data
147
+ * @param {string|Array} fields - Field(s) to group by
148
+ * @returns {Promise<Object>} Grouped data
149
+ */
150
+ async groupBy(data, fields) {
151
+ return this.pool.execute({
152
+ operation: 'groupBy',
153
+ data,
154
+ config: fields,
155
+ });
156
+ }
157
+
158
+ /**
159
+ * Sample data for preview
160
+ * @param {Array} data - Input data
161
+ * @param {number} sampleSize - Sample size
162
+ * @returns {Promise<Array>} Sampled data
163
+ */
164
+ async sample(data, sampleSize) {
165
+ return this.pool.execute({
166
+ operation: 'sample',
167
+ data,
168
+ config: { size: sampleSize },
169
+ });
170
+ }
171
+
172
+ /**
173
+ * Calculate statistics for a field
174
+ * @param {Array} data - Input data
175
+ * @param {string} field - Field name
176
+ * @returns {Promise<Object>} Statistics
177
+ */
178
+ async statistics(data, field) {
179
+ if (this._shouldChunk(data)) {
180
+ // Process statistics in chunks and combine
181
+ const chunks = this._chunkData(data, this.config.chunkSize);
182
+ const chunkStats = await this.pool.executeAll(
183
+ chunks.map((chunk) => ({
184
+ operation: 'statistics',
185
+ data: chunk,
186
+ config: { field },
187
+ }))
188
+ );
189
+
190
+ // Combine statistics from chunks
191
+ return this._combineStatistics(chunkStats);
192
+ }
193
+
194
+ return this.pool.execute({
195
+ operation: 'statistics',
196
+ data,
197
+ config: { field },
198
+ });
199
+ }
200
+
201
+ /**
202
+ * Combine statistics from multiple chunks
203
+ * @private
204
+ * @param {Array<Object>} statsArray - Array of statistics
205
+ * @returns {Object} Combined statistics
206
+ */
207
+ _combineStatistics(statsArray) {
208
+ const validStats = statsArray.filter((s) => s !== null);
209
+ if (validStats.length === 0) return null;
210
+
211
+ const totalCount = validStats.reduce((sum, s) => sum + s.count, 0);
212
+ const totalSum = validStats.reduce((sum, s) => sum + s.sum, 0);
213
+ const mean = totalSum / totalCount;
214
+
215
+ // Combine variance (using parallel algorithm)
216
+ let totalVariance = 0;
217
+ for (const s of validStats) {
218
+ totalVariance += s.variance * s.count + Math.pow(s.mean - mean, 2) * s.count;
219
+ }
220
+ totalVariance /= totalCount;
221
+
222
+ return {
223
+ count: totalCount,
224
+ sum: totalSum,
225
+ mean,
226
+ min: Math.min(...validStats.map((s) => s.min)),
227
+ max: Math.max(...validStats.map((s) => s.max)),
228
+ variance: totalVariance,
229
+ std: Math.sqrt(totalVariance),
230
+ };
231
+ }
232
+
233
+ /**
234
+ * Parse CSV data
235
+ * @param {string} csv - CSV content
236
+ * @param {Object} options - Parse options
237
+ * @returns {Promise<Array>} Parsed data
238
+ */
239
+ async parseCSV(csv, options = {}) {
240
+ return this.pool.execute({
241
+ operation: 'parseCSV',
242
+ data: csv,
243
+ config: options,
244
+ });
245
+ }
246
+
247
+ /**
248
+ * Execute a pipeline of operations
249
+ * @param {Array} data - Input data
250
+ * @param {Array} operations - Array of operations
251
+ * @returns {Promise<Array>} Result data
252
+ */
253
+ async pipeline(data, operations) {
254
+ return this.pool.execute({
255
+ operation: 'pipeline',
256
+ data,
257
+ config: operations,
258
+ });
259
+ }
260
+
261
+ /**
262
+ * Process large dataset with progress callbacks
263
+ * @param {Array} data - Input data
264
+ * @param {Object} config - Processing config
265
+ * @param {Function} onProgress - Progress callback
266
+ * @returns {Promise<Object>} Processing result
267
+ */
268
+ async processLargeDataset(data, config, onProgress) {
269
+ const { operation, operationConfig } = config;
270
+ const chunks = this._chunkData(data, this.config.chunkSize);
271
+ const totalChunks = chunks.length;
272
+ const results = [];
273
+
274
+ // Process chunks in batches to avoid overwhelming the worker pool
275
+ const batchSize = this.config.maxWorkers;
276
+ for (let i = 0; i < chunks.length; i += batchSize) {
277
+ const batch = chunks.slice(i, i + batchSize);
278
+ const batchResults = await this.pool.executeAll(
279
+ batch.map((chunk) => ({
280
+ operation,
281
+ data: chunk,
282
+ config: operationConfig,
283
+ }))
284
+ );
285
+ results.push(...batchResults);
286
+
287
+ if (onProgress) {
288
+ onProgress({
289
+ processed: Math.min(i + batchSize, totalChunks),
290
+ total: totalChunks,
291
+ percent: Math.round((Math.min(i + batchSize, totalChunks) / totalChunks) * 100),
292
+ });
293
+ }
294
+ }
295
+
296
+ return this._combineResults(results, operation);
297
+ }
298
+
299
+ /**
300
+ * Combine results from chunked processing
301
+ * @private
302
+ * @param {Array} results - Chunk results
303
+ * @param {string} operation - Operation type
304
+ * @returns {any} Combined result
305
+ */
306
+ _combineResults(results, operation) {
307
+ switch (operation) {
308
+ case 'filter':
309
+ return results.flat();
310
+ case 'aggregate':
311
+ return results.reduce((a, b) => a + b, 0);
312
+ case 'statistics':
313
+ return this._combineStatistics(results);
314
+ default:
315
+ return results;
316
+ }
317
+ }
318
+
319
+ /**
320
+ * Get processor statistics
321
+ * @returns {Object} Statistics
322
+ */
323
+ getStats() {
324
+ return this.pool.getStats();
325
+ }
326
+
327
+ /**
328
+ * Terminate the processor and clean up resources
329
+ */
330
+ terminate() {
331
+ terminateWorkerPool();
332
+ }
333
+ }
334
+
335
+ // Singleton instance
336
+ let globalProcessor = null;
337
+
338
+ /**
339
+ * Get or create global data processor
340
+ * @param {Object} config - Processor configuration
341
+ * @returns {DataProcessor} Global processor instance
342
+ */
343
+ export function getDataProcessor(config = {}) {
344
+ if (!globalProcessor) {
345
+ globalProcessor = new DataProcessor(config);
346
+ }
347
+ return globalProcessor;
348
+ }
349
+
350
+ /**
351
+ * Terminate global data processor
352
+ */
353
+ export function terminateDataProcessor() {
354
+ if (globalProcessor) {
355
+ globalProcessor.terminate();
356
+ globalProcessor = null;
357
+ }
358
+ }
359
+
360
+ // Convenience functions for common operations
361
+
362
+ /**
363
+ * Aggregate data using workers
364
+ * @param {Array} data - Input data
365
+ * @param {Object} config - Aggregation config
366
+ * @returns {Promise<number>} Aggregated value
367
+ */
368
+ export function aggregateData(data, config) {
369
+ return getDataProcessor().aggregate(data, config);
370
+ }
371
+
372
+ /**
373
+ * Filter data using workers
374
+ * @param {Array} data - Input data
375
+ * @param {Object} criteria - Filter criteria
376
+ * @returns {Promise<Array>} Filtered data
377
+ */
378
+ export function filterData(data, criteria) {
379
+ return getDataProcessor().filter(data, criteria);
380
+ }
381
+
382
+ /**
383
+ * Sort data using workers
384
+ * @param {Array} data - Input data
385
+ * @param {Object} sortConfig - Sort config
386
+ * @returns {Promise<Array>} Sorted data
387
+ */
388
+ export function sortData(data, sortConfig) {
389
+ return getDataProcessor().sort(data, sortConfig);
390
+ }
391
+
392
+ /**
393
+ * Sample data for preview
394
+ * @param {Array} data - Input data
395
+ * @param {number} sampleSize - Sample size
396
+ * @returns {Promise<Array>} Sampled data
397
+ */
398
+ export function sampleData(data, sampleSize) {
399
+ return getDataProcessor().sample(data, sampleSize);
400
+ }
401
+
402
+ /**
403
+ * Calculate statistics
404
+ * @param {Array} data - Input data
405
+ * @param {string} field - Field name
406
+ * @returns {Promise<Object>} Statistics
407
+ */
408
+ export function calculateStatistics(data, field) {
409
+ return getDataProcessor().statistics(data, field);
410
+ }
411
+
412
+ export { DataProcessor };
413
+ export default DataProcessor;