juxscript 1.1.230 → 1.1.232
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.
- package/index.d.ts +5 -2
- package/index.d.ts.map +1 -1
- package/index.js +4 -2
- package/lib/components/dataframe/DataFrameSource.d.ts +118 -0
- package/lib/components/dataframe/DataFrameSource.d.ts.map +1 -0
- package/lib/components/dataframe/DataFrameSource.js +421 -0
- package/lib/components/dataframe/DataFrameSource.ts +532 -0
- package/lib/components/dataframe.d.ts +46 -52
- package/lib/components/dataframe.d.ts.map +1 -1
- package/lib/components/dataframe.js +177 -400
- package/lib/components/dataframe.ts +192 -458
- package/lib/storage/DataFrame.d.ts +262 -37
- package/lib/storage/DataFrame.d.ts.map +1 -1
- package/lib/storage/DataFrame.js +919 -340
- package/lib/storage/DataFrame.ts +1031 -308
- package/lib/storage/DataFrameSource.d.ts +158 -0
- package/lib/storage/DataFrameSource.d.ts.map +1 -0
- package/lib/storage/DataFrameSource.js +409 -0
- package/lib/storage/DataFrameSource.ts +556 -0
- package/package.json +1 -1
package/index.d.ts
CHANGED
|
@@ -50,8 +50,11 @@ import { registry } from './lib/components/registry.js';
|
|
|
50
50
|
import { stateHistory } from './lib/components/history/StateHistory.js';
|
|
51
51
|
import { watcher } from './lib/components/watcher.js';
|
|
52
52
|
export { state, registry, stateHistory };
|
|
53
|
-
export { tabularDriver } from './lib/storage/TabularDriver.js';
|
|
54
|
-
export { DataFrame } from './lib/storage/DataFrame.js';
|
|
53
|
+
export { tabularDriver, TabularDriver } from './lib/storage/TabularDriver.js';
|
|
54
|
+
export { DataFrame, GroupedDataFrame } from './lib/storage/DataFrame.js';
|
|
55
|
+
export type { DataType, ColumnSchema, DataFrameOptions } from './lib/storage/DataFrame.js';
|
|
56
|
+
export { DataFrameSource, dataFrameSource } from './lib/storage/DataFrameSource.js';
|
|
57
|
+
export type { SourceOptions, APIOptions, StreamOptions, MultiSheetResult } from './lib/storage/DataFrameSource.js';
|
|
55
58
|
export { indexedDBDriver } from './lib/storage/IndexedDBDriver.js';
|
|
56
59
|
export { fileStorage } from './lib/storage/FileStorage.js';
|
|
57
60
|
export { dataframe } from './lib/components/dataframe.js';
|
package/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"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"}
|
package/index.js
CHANGED
|
@@ -53,10 +53,12 @@ import { stateHistory } from './lib/components/history/StateHistory.js';
|
|
|
53
53
|
import { watcher } from './lib/components/watcher.js';
|
|
54
54
|
export { state, registry, stateHistory };
|
|
55
55
|
// Storage
|
|
56
|
-
export { tabularDriver } from './lib/storage/TabularDriver.js';
|
|
57
|
-
export { DataFrame } from './lib/storage/DataFrame.js';
|
|
56
|
+
export { tabularDriver, TabularDriver } from './lib/storage/TabularDriver.js';
|
|
57
|
+
export { DataFrame, GroupedDataFrame } from './lib/storage/DataFrame.js';
|
|
58
|
+
export { DataFrameSource, dataFrameSource } from './lib/storage/DataFrameSource.js';
|
|
58
59
|
export { indexedDBDriver } from './lib/storage/IndexedDBDriver.js';
|
|
59
60
|
export { fileStorage } from './lib/storage/FileStorage.js';
|
|
61
|
+
// DataFrame UI Component (separate from data layer)
|
|
60
62
|
export { dataframe } from './lib/components/dataframe.js';
|
|
61
63
|
// Import Stack components (already added earlier)
|
|
62
64
|
import { VStack, vstack } from './lib/components/stack/VStack.js';
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { DataFrame } from '../../storage/DataFrame.js';
|
|
2
|
+
import { TabularDriver } from '../../storage/TabularDriver.js';
|
|
3
|
+
import { FileUpload } from '../fileupload.js';
|
|
4
|
+
export interface DataFrameSourceOptions {
|
|
5
|
+
maxFileSize?: number;
|
|
6
|
+
maxSheetSize?: number;
|
|
7
|
+
sheetChunkSize?: number;
|
|
8
|
+
persistToIndexedDB?: boolean;
|
|
9
|
+
dbName?: string;
|
|
10
|
+
storeName?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface LoadResult {
|
|
13
|
+
df: DataFrame | null;
|
|
14
|
+
sheets?: Record<string, DataFrame>;
|
|
15
|
+
sourceName: string;
|
|
16
|
+
isMultiSheet: boolean;
|
|
17
|
+
error?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface RawFileData {
|
|
20
|
+
file: File;
|
|
21
|
+
text?: string;
|
|
22
|
+
isExcel?: boolean;
|
|
23
|
+
}
|
|
24
|
+
type SourceCallback = (result: LoadResult) => void;
|
|
25
|
+
type ProgressCallback = (loaded: number, total: number) => void;
|
|
26
|
+
type ErrorCallback = (message: string) => void;
|
|
27
|
+
/**
|
|
28
|
+
* DataFrameSource - Handles data acquisition from various sources
|
|
29
|
+
*
|
|
30
|
+
* Responsibilities:
|
|
31
|
+
* - Load data from files (CSV, Excel)
|
|
32
|
+
* - Load data from URLs
|
|
33
|
+
* - Load data from IndexedDB storage
|
|
34
|
+
* - Load data from inline arrays/objects
|
|
35
|
+
* - Validate and detect malformed data
|
|
36
|
+
* - Persist to storage if configured
|
|
37
|
+
*
|
|
38
|
+
* Does NOT handle:
|
|
39
|
+
* - UI rendering
|
|
40
|
+
* - User interactions
|
|
41
|
+
* - Modals/dialogs
|
|
42
|
+
*/
|
|
43
|
+
export declare class DataFrameSource {
|
|
44
|
+
private _driver;
|
|
45
|
+
private _options;
|
|
46
|
+
private _df;
|
|
47
|
+
private _sheets;
|
|
48
|
+
private _rawFileData;
|
|
49
|
+
private _sourceName;
|
|
50
|
+
private _onLoad;
|
|
51
|
+
private _onProgress;
|
|
52
|
+
private _onError;
|
|
53
|
+
constructor(options?: DataFrameSourceOptions);
|
|
54
|
+
onLoad(callback: SourceCallback): this;
|
|
55
|
+
onProgress(callback: ProgressCallback): this;
|
|
56
|
+
onError(callback: ErrorCallback): this;
|
|
57
|
+
/**
|
|
58
|
+
* Load from a FileUpload component
|
|
59
|
+
*/
|
|
60
|
+
fromUpload(upload: FileUpload): this;
|
|
61
|
+
/**
|
|
62
|
+
* Load from a File object directly
|
|
63
|
+
*/
|
|
64
|
+
fromFile(file: File): Promise<LoadResult>;
|
|
65
|
+
/**
|
|
66
|
+
* Load from a URL
|
|
67
|
+
*/
|
|
68
|
+
fromUrl(url: string): Promise<LoadResult>;
|
|
69
|
+
/**
|
|
70
|
+
* Load from IndexedDB storage by key
|
|
71
|
+
*/
|
|
72
|
+
fromStorage(key: string): Promise<LoadResult>;
|
|
73
|
+
/**
|
|
74
|
+
* Load from inline data (array of objects or columnar format)
|
|
75
|
+
*/
|
|
76
|
+
fromData(data: Record<string, any>[] | Record<string, any[]>, sourceName?: string): LoadResult;
|
|
77
|
+
/**
|
|
78
|
+
* Re-parse with different options (header row, delimiter)
|
|
79
|
+
*/
|
|
80
|
+
reimport(options: {
|
|
81
|
+
headerRow?: number;
|
|
82
|
+
delimiter?: string;
|
|
83
|
+
}): Promise<LoadResult>;
|
|
84
|
+
get df(): DataFrame | null;
|
|
85
|
+
get sheets(): Map<string, DataFrame>;
|
|
86
|
+
get sourceName(): string;
|
|
87
|
+
get rawFileData(): RawFileData | null;
|
|
88
|
+
get driver(): TabularDriver;
|
|
89
|
+
get shape(): [number, number];
|
|
90
|
+
get columns(): string[];
|
|
91
|
+
get height(): number;
|
|
92
|
+
get width(): number;
|
|
93
|
+
/**
|
|
94
|
+
* Detect if the data appears malformed (bad headers, metadata rows, etc.)
|
|
95
|
+
*/
|
|
96
|
+
detectMalformed(): boolean;
|
|
97
|
+
/**
|
|
98
|
+
* Get raw preview rows for header selection UI
|
|
99
|
+
*/
|
|
100
|
+
getPreviewRows(maxRows?: number): Promise<{
|
|
101
|
+
sheetRow: number;
|
|
102
|
+
values: any[];
|
|
103
|
+
}[]>;
|
|
104
|
+
save(key?: string): Promise<string | null>;
|
|
105
|
+
clearStorage(): Promise<void>;
|
|
106
|
+
clear(): void;
|
|
107
|
+
private _loadExcel;
|
|
108
|
+
private _loadCSV;
|
|
109
|
+
private _processSheets;
|
|
110
|
+
private _stripEmptyColumns;
|
|
111
|
+
private _parseCSVPreview;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Factory function
|
|
115
|
+
*/
|
|
116
|
+
export declare function dataFrameSource(options?: DataFrameSourceOptions): DataFrameSource;
|
|
117
|
+
export {};
|
|
118
|
+
//# sourceMappingURL=DataFrameSource.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DataFrameSource.d.ts","sourceRoot":"","sources":["DataFrameSource.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
import { DataFrame } from '../../storage/DataFrame.js';
|
|
2
|
+
import { TabularDriver } from '../../storage/TabularDriver.js';
|
|
3
|
+
/**
|
|
4
|
+
* DataFrameSource - Handles data acquisition from various sources
|
|
5
|
+
*
|
|
6
|
+
* Responsibilities:
|
|
7
|
+
* - Load data from files (CSV, Excel)
|
|
8
|
+
* - Load data from URLs
|
|
9
|
+
* - Load data from IndexedDB storage
|
|
10
|
+
* - Load data from inline arrays/objects
|
|
11
|
+
* - Validate and detect malformed data
|
|
12
|
+
* - Persist to storage if configured
|
|
13
|
+
*
|
|
14
|
+
* Does NOT handle:
|
|
15
|
+
* - UI rendering
|
|
16
|
+
* - User interactions
|
|
17
|
+
* - Modals/dialogs
|
|
18
|
+
*/
|
|
19
|
+
export class DataFrameSource {
|
|
20
|
+
constructor(options = {}) {
|
|
21
|
+
this._df = null;
|
|
22
|
+
this._sheets = new Map();
|
|
23
|
+
this._rawFileData = null;
|
|
24
|
+
this._sourceName = '';
|
|
25
|
+
this._onLoad = null;
|
|
26
|
+
this._onProgress = null;
|
|
27
|
+
this._onError = null;
|
|
28
|
+
this._options = {
|
|
29
|
+
maxFileSize: options.maxFileSize ?? 50,
|
|
30
|
+
maxSheetSize: options.maxSheetSize ?? 100000,
|
|
31
|
+
sheetChunkSize: options.sheetChunkSize ?? 10000,
|
|
32
|
+
persistToIndexedDB: options.persistToIndexedDB ?? false,
|
|
33
|
+
dbName: options.dbName ?? 'jux-dataframes',
|
|
34
|
+
storeName: options.storeName ?? 'frames'
|
|
35
|
+
};
|
|
36
|
+
this._driver = new TabularDriver(this._options.dbName, this._options.storeName);
|
|
37
|
+
}
|
|
38
|
+
/* ═══════════════════════════════════════════════════
|
|
39
|
+
* CALLBACKS
|
|
40
|
+
* ═══════════════════════════════════════════════════ */
|
|
41
|
+
onLoad(callback) {
|
|
42
|
+
this._onLoad = callback;
|
|
43
|
+
return this;
|
|
44
|
+
}
|
|
45
|
+
onProgress(callback) {
|
|
46
|
+
this._onProgress = callback;
|
|
47
|
+
return this;
|
|
48
|
+
}
|
|
49
|
+
onError(callback) {
|
|
50
|
+
this._onError = callback;
|
|
51
|
+
return this;
|
|
52
|
+
}
|
|
53
|
+
/* ═══════════════════════════════════════════════════
|
|
54
|
+
* DATA SOURCES
|
|
55
|
+
* ═══════════════════════════════════════════════════ */
|
|
56
|
+
/**
|
|
57
|
+
* Load from a FileUpload component
|
|
58
|
+
*/
|
|
59
|
+
fromUpload(upload) {
|
|
60
|
+
upload.bind('change', async (files) => {
|
|
61
|
+
if (!files || files.length === 0) {
|
|
62
|
+
this.clear();
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
await this.fromFile(files[0]);
|
|
66
|
+
});
|
|
67
|
+
return this;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Load from a File object directly
|
|
71
|
+
*/
|
|
72
|
+
async fromFile(file) {
|
|
73
|
+
const fileSizeMB = file.size / (1024 * 1024);
|
|
74
|
+
if (fileSizeMB > this._options.maxFileSize) {
|
|
75
|
+
const error = `File too large (${fileSizeMB.toFixed(1)}MB). Max: ${this._options.maxFileSize}MB`;
|
|
76
|
+
this._onError?.(error);
|
|
77
|
+
return { df: null, sourceName: file.name, isMultiSheet: false, error };
|
|
78
|
+
}
|
|
79
|
+
try {
|
|
80
|
+
const isExcel = /\.(xlsx?|xls)$/i.test(file.name);
|
|
81
|
+
if (isExcel) {
|
|
82
|
+
return await this._loadExcel(file);
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
return await this._loadCSV(file);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
const error = `Error parsing ${file.name}: ${err.message}`;
|
|
90
|
+
this._onError?.(error);
|
|
91
|
+
return { df: null, sourceName: file.name, isMultiSheet: false, error };
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Load from a URL
|
|
96
|
+
*/
|
|
97
|
+
async fromUrl(url) {
|
|
98
|
+
try {
|
|
99
|
+
this._onProgress?.(0, 100);
|
|
100
|
+
const df = await this._driver.fetch(url, {
|
|
101
|
+
autoDetectDelimiter: true,
|
|
102
|
+
hasHeader: true,
|
|
103
|
+
onProgress: (loaded, total) => {
|
|
104
|
+
this._onProgress?.(loaded, total ?? 100);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
this._df = df;
|
|
108
|
+
this._sourceName = url.split('/').pop() || url;
|
|
109
|
+
const result = {
|
|
110
|
+
df,
|
|
111
|
+
sourceName: this._sourceName,
|
|
112
|
+
isMultiSheet: false
|
|
113
|
+
};
|
|
114
|
+
if (this._options.persistToIndexedDB) {
|
|
115
|
+
await this._driver.store(this._sourceName, df, { source: url });
|
|
116
|
+
}
|
|
117
|
+
this._onLoad?.(result);
|
|
118
|
+
return result;
|
|
119
|
+
}
|
|
120
|
+
catch (err) {
|
|
121
|
+
const error = `Error fetching ${url}: ${err.message}`;
|
|
122
|
+
this._onError?.(error);
|
|
123
|
+
return { df: null, sourceName: url, isMultiSheet: false, error };
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Load from IndexedDB storage by key
|
|
128
|
+
*/
|
|
129
|
+
async fromStorage(key) {
|
|
130
|
+
try {
|
|
131
|
+
const df = await this._driver.loadByName(key);
|
|
132
|
+
if (!df) {
|
|
133
|
+
const error = `No data found with key: ${key}`;
|
|
134
|
+
this._onError?.(error);
|
|
135
|
+
return { df: null, sourceName: key, isMultiSheet: false, error };
|
|
136
|
+
}
|
|
137
|
+
this._df = df;
|
|
138
|
+
this._sourceName = key;
|
|
139
|
+
const result = {
|
|
140
|
+
df,
|
|
141
|
+
sourceName: key,
|
|
142
|
+
isMultiSheet: false
|
|
143
|
+
};
|
|
144
|
+
this._onLoad?.(result);
|
|
145
|
+
return result;
|
|
146
|
+
}
|
|
147
|
+
catch (err) {
|
|
148
|
+
const error = `Error loading from storage: ${err.message}`;
|
|
149
|
+
this._onError?.(error);
|
|
150
|
+
return { df: null, sourceName: key, isMultiSheet: false, error };
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Load from inline data (array of objects or columnar format)
|
|
155
|
+
*/
|
|
156
|
+
fromData(data, sourceName = 'inline data') {
|
|
157
|
+
try {
|
|
158
|
+
const df = new DataFrame(data);
|
|
159
|
+
this._df = df;
|
|
160
|
+
this._sourceName = sourceName;
|
|
161
|
+
const result = {
|
|
162
|
+
df,
|
|
163
|
+
sourceName,
|
|
164
|
+
isMultiSheet: false
|
|
165
|
+
};
|
|
166
|
+
this._onLoad?.(result);
|
|
167
|
+
return result;
|
|
168
|
+
}
|
|
169
|
+
catch (err) {
|
|
170
|
+
const error = `Error creating DataFrame: ${err.message}`;
|
|
171
|
+
this._onError?.(error);
|
|
172
|
+
return { df: null, sourceName, isMultiSheet: false, error };
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Re-parse with different options (header row, delimiter)
|
|
177
|
+
*/
|
|
178
|
+
async reimport(options) {
|
|
179
|
+
if (!this._rawFileData) {
|
|
180
|
+
const error = 'No raw file data available for reimport';
|
|
181
|
+
this._onError?.(error);
|
|
182
|
+
return { df: null, sourceName: this._sourceName, isMultiSheet: false, error };
|
|
183
|
+
}
|
|
184
|
+
try {
|
|
185
|
+
if (this._rawFileData.isExcel) {
|
|
186
|
+
const sheets = await this._driver.streamFileMultiSheet(this._rawFileData.file, {
|
|
187
|
+
headerRow: options.headerRow ?? 0,
|
|
188
|
+
maxSheetSize: this._options.maxSheetSize,
|
|
189
|
+
sheetChunkSize: this._options.sheetChunkSize
|
|
190
|
+
});
|
|
191
|
+
return this._processSheets(sheets, this._rawFileData.file.name);
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
const df = this._driver.parseCSV(this._rawFileData.text, {
|
|
195
|
+
delimiter: options.delimiter,
|
|
196
|
+
headerRow: options.headerRow ?? 0,
|
|
197
|
+
hasHeader: true
|
|
198
|
+
});
|
|
199
|
+
this._df = df;
|
|
200
|
+
if (this._options.persistToIndexedDB) {
|
|
201
|
+
await this._driver.store(this._rawFileData.file.name, df);
|
|
202
|
+
}
|
|
203
|
+
const result = {
|
|
204
|
+
df,
|
|
205
|
+
sourceName: this._rawFileData.file.name,
|
|
206
|
+
isMultiSheet: false
|
|
207
|
+
};
|
|
208
|
+
this._onLoad?.(result);
|
|
209
|
+
return result;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
catch (err) {
|
|
213
|
+
const error = `Error reimporting: ${err.message}`;
|
|
214
|
+
this._onError?.(error);
|
|
215
|
+
return { df: null, sourceName: this._sourceName, isMultiSheet: false, error };
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
/* ═══════════════════════════════════════════════════
|
|
219
|
+
* ACCESSORS
|
|
220
|
+
* ═══════════════════════════════════════════════════ */
|
|
221
|
+
get df() {
|
|
222
|
+
return this._df;
|
|
223
|
+
}
|
|
224
|
+
get sheets() {
|
|
225
|
+
return this._sheets;
|
|
226
|
+
}
|
|
227
|
+
get sourceName() {
|
|
228
|
+
return this._sourceName;
|
|
229
|
+
}
|
|
230
|
+
get rawFileData() {
|
|
231
|
+
return this._rawFileData;
|
|
232
|
+
}
|
|
233
|
+
get driver() {
|
|
234
|
+
return this._driver;
|
|
235
|
+
}
|
|
236
|
+
get shape() {
|
|
237
|
+
return this._df?.shape ?? [0, 0];
|
|
238
|
+
}
|
|
239
|
+
get columns() {
|
|
240
|
+
return this._df?.columns ?? [];
|
|
241
|
+
}
|
|
242
|
+
get height() {
|
|
243
|
+
return this._df?.height ?? 0;
|
|
244
|
+
}
|
|
245
|
+
get width() {
|
|
246
|
+
return this._df?.width ?? 0;
|
|
247
|
+
}
|
|
248
|
+
/* ═══════════════════════════════════════════════════
|
|
249
|
+
* DATA QUALITY
|
|
250
|
+
* ═══════════════════════════════════════════════════ */
|
|
251
|
+
/**
|
|
252
|
+
* Detect if the data appears malformed (bad headers, metadata rows, etc.)
|
|
253
|
+
*/
|
|
254
|
+
detectMalformed() {
|
|
255
|
+
if (!this._df)
|
|
256
|
+
return false;
|
|
257
|
+
const columns = this._df.columns;
|
|
258
|
+
const rows = this._df.toRows();
|
|
259
|
+
// Check for generic/empty column names
|
|
260
|
+
const hasGenericColumns = columns.some(col => col.startsWith('__EMPTY') ||
|
|
261
|
+
/^_\d+$/.test(col) ||
|
|
262
|
+
/^col_\d+$/.test(col));
|
|
263
|
+
if (hasGenericColumns)
|
|
264
|
+
return true;
|
|
265
|
+
// Check if first row looks like metadata
|
|
266
|
+
if (rows.length > 0) {
|
|
267
|
+
const firstRow = rows[0];
|
|
268
|
+
const values = Object.values(firstRow);
|
|
269
|
+
const nonEmpty = values.filter(v => v !== null && v !== undefined && String(v).trim() !== '');
|
|
270
|
+
// Sparse first row suggests header issues
|
|
271
|
+
if (nonEmpty.length < columns.length * 0.5)
|
|
272
|
+
return true;
|
|
273
|
+
// Common metadata patterns
|
|
274
|
+
const hasMetadata = values.some(v => String(v).includes('Exported') ||
|
|
275
|
+
String(v).includes('Generated') ||
|
|
276
|
+
String(v).includes('Report'));
|
|
277
|
+
if (hasMetadata)
|
|
278
|
+
return true;
|
|
279
|
+
}
|
|
280
|
+
return false;
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Get raw preview rows for header selection UI
|
|
284
|
+
*/
|
|
285
|
+
async getPreviewRows(maxRows = 15) {
|
|
286
|
+
if (!this._rawFileData?.file)
|
|
287
|
+
return [];
|
|
288
|
+
if (this._rawFileData.isExcel) {
|
|
289
|
+
return this._driver.readRawExcelRows(this._rawFileData.file, maxRows);
|
|
290
|
+
}
|
|
291
|
+
else if (this._rawFileData.text) {
|
|
292
|
+
return this._parseCSVPreview(this._rawFileData.text, maxRows);
|
|
293
|
+
}
|
|
294
|
+
return [];
|
|
295
|
+
}
|
|
296
|
+
/* ═══════════════════════════════════════════════════
|
|
297
|
+
* PERSISTENCE
|
|
298
|
+
* ═══════════════════════════════════════════════════ */
|
|
299
|
+
async save(key) {
|
|
300
|
+
if (!this._df)
|
|
301
|
+
return null;
|
|
302
|
+
const name = key ?? this._sourceName;
|
|
303
|
+
return this._driver.store(name, this._df);
|
|
304
|
+
}
|
|
305
|
+
async clearStorage() {
|
|
306
|
+
if (this._rawFileData?.file) {
|
|
307
|
+
const tables = await this._driver.list();
|
|
308
|
+
const matching = tables.filter(t => t.name === this._rawFileData.file.name);
|
|
309
|
+
for (const table of matching) {
|
|
310
|
+
await this._driver.delete(table.id);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
clear() {
|
|
315
|
+
this._df = null;
|
|
316
|
+
this._sheets.clear();
|
|
317
|
+
this._rawFileData = null;
|
|
318
|
+
this._sourceName = '';
|
|
319
|
+
}
|
|
320
|
+
/* ═══════════════════════════════════════════════════
|
|
321
|
+
* PRIVATE HELPERS
|
|
322
|
+
* ═══════════════════════════════════════════════════ */
|
|
323
|
+
async _loadExcel(file) {
|
|
324
|
+
this._rawFileData = { file, isExcel: true };
|
|
325
|
+
const sheets = await this._driver.streamFileMultiSheet(file, {
|
|
326
|
+
maxSheetSize: this._options.maxSheetSize,
|
|
327
|
+
sheetChunkSize: this._options.sheetChunkSize,
|
|
328
|
+
onProgress: (loaded, total) => {
|
|
329
|
+
this._onProgress?.(loaded, total ?? 100);
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
return this._processSheets(sheets, file.name);
|
|
333
|
+
}
|
|
334
|
+
async _loadCSV(file) {
|
|
335
|
+
const text = await file.text();
|
|
336
|
+
this._rawFileData = { file, text, isExcel: false };
|
|
337
|
+
const df = this._driver.parseCSV(text, {
|
|
338
|
+
autoDetectDelimiter: true,
|
|
339
|
+
hasHeader: true
|
|
340
|
+
});
|
|
341
|
+
// Strip fully empty __EMPTY columns
|
|
342
|
+
const cleanDf = this._stripEmptyColumns(df);
|
|
343
|
+
this._df = cleanDf;
|
|
344
|
+
this._sourceName = file.name;
|
|
345
|
+
if (this._options.persistToIndexedDB) {
|
|
346
|
+
await this._driver.store(file.name, cleanDf, { source: file.name });
|
|
347
|
+
}
|
|
348
|
+
const result = {
|
|
349
|
+
df: cleanDf,
|
|
350
|
+
sourceName: file.name,
|
|
351
|
+
isMultiSheet: false
|
|
352
|
+
};
|
|
353
|
+
this._onLoad?.(result);
|
|
354
|
+
return result;
|
|
355
|
+
}
|
|
356
|
+
async _processSheets(sheets, sourceName) {
|
|
357
|
+
const sheetNames = Object.keys(sheets);
|
|
358
|
+
if (sheetNames.length === 0) {
|
|
359
|
+
const error = 'No data found in file';
|
|
360
|
+
this._onError?.(error);
|
|
361
|
+
return { df: null, sourceName, isMultiSheet: false, error };
|
|
362
|
+
}
|
|
363
|
+
this._sheets.clear();
|
|
364
|
+
sheetNames.forEach(name => {
|
|
365
|
+
this._sheets.set(name, this._stripEmptyColumns(sheets[name]));
|
|
366
|
+
});
|
|
367
|
+
const firstSheet = this._sheets.get(sheetNames[0]);
|
|
368
|
+
this._df = firstSheet;
|
|
369
|
+
this._sourceName = sourceName;
|
|
370
|
+
if (this._options.persistToIndexedDB) {
|
|
371
|
+
await this._driver.store(sourceName, firstSheet, { source: sourceName });
|
|
372
|
+
}
|
|
373
|
+
const result = {
|
|
374
|
+
df: firstSheet,
|
|
375
|
+
sheets: Object.fromEntries(this._sheets),
|
|
376
|
+
sourceName,
|
|
377
|
+
isMultiSheet: sheetNames.length > 1
|
|
378
|
+
};
|
|
379
|
+
this._onLoad?.(result);
|
|
380
|
+
return result;
|
|
381
|
+
}
|
|
382
|
+
_stripEmptyColumns(df) {
|
|
383
|
+
const cols = df.columns;
|
|
384
|
+
const rows = df.toRows();
|
|
385
|
+
const emptyColumns = cols.filter(c => {
|
|
386
|
+
if (!c.startsWith('__EMPTY'))
|
|
387
|
+
return false;
|
|
388
|
+
return rows.every(row => {
|
|
389
|
+
const val = row[c];
|
|
390
|
+
return val === null || val === undefined || String(val).trim() === '';
|
|
391
|
+
});
|
|
392
|
+
});
|
|
393
|
+
if (emptyColumns.length > 0) {
|
|
394
|
+
const keepCols = cols.filter(c => !emptyColumns.includes(c));
|
|
395
|
+
if (keepCols.length > 0) {
|
|
396
|
+
return df.select(...keepCols);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
return df;
|
|
400
|
+
}
|
|
401
|
+
_parseCSVPreview(text, maxRows) {
|
|
402
|
+
const lines = text.replace(/\r\n/g, '\n').replace(/\r/g, '\n').split('\n');
|
|
403
|
+
const delimiter = this._driver._detectDelimiter(text);
|
|
404
|
+
const rows = [];
|
|
405
|
+
for (let i = 0; i < Math.min(lines.length, maxRows); i++) {
|
|
406
|
+
if (!lines[i]) {
|
|
407
|
+
rows.push({ sheetRow: i, values: [''] });
|
|
408
|
+
continue;
|
|
409
|
+
}
|
|
410
|
+
const values = this._driver._parseLine(lines[i], delimiter);
|
|
411
|
+
rows.push({ sheetRow: i, values });
|
|
412
|
+
}
|
|
413
|
+
return rows;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* Factory function
|
|
418
|
+
*/
|
|
419
|
+
export function dataFrameSource(options = {}) {
|
|
420
|
+
return new DataFrameSource(options);
|
|
421
|
+
}
|