juxscript 1.1.159 → 1.1.161
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 +4 -0
- package/index.d.ts.map +1 -1
- package/index.js +5 -0
- package/lib/components/checkbox.js +4 -4
- package/lib/components/checkbox.ts +5 -5
- package/lib/components/datepicker.d.ts.map +1 -1
- package/lib/components/datepicker.js +5 -4
- package/lib/components/datepicker.ts +5 -4
- package/lib/components/dialog.d.ts.map +1 -1
- package/lib/components/dialog.js +2 -0
- package/lib/components/dialog.ts +2 -0
- package/lib/components/dropdown.d.ts +1 -0
- package/lib/components/dropdown.d.ts.map +1 -1
- package/lib/components/dropdown.js +5 -6
- package/lib/components/dropdown.ts +6 -9
- package/lib/components/fileupload.d.ts +6 -0
- package/lib/components/fileupload.d.ts.map +1 -1
- package/lib/components/fileupload.js +31 -61
- package/lib/components/fileupload.ts +38 -67
- package/lib/components/input.js +6 -6
- package/lib/components/input.ts +6 -6
- package/lib/components/modal.d.ts.map +1 -1
- package/lib/components/modal.js +2 -0
- package/lib/components/modal.ts +2 -0
- package/lib/components/select.d.ts.map +1 -1
- package/lib/components/select.js +5 -0
- package/lib/components/select.ts +6 -0
- package/lib/components/switch.js +4 -4
- package/lib/components/switch.ts +5 -5
- package/lib/components/tabs.js +4 -4
- package/lib/components/tabs.ts +4 -4
- package/lib/storage/DataFrame.d.ts +59 -0
- package/lib/storage/DataFrame.d.ts.map +1 -0
- package/lib/storage/DataFrame.js +443 -0
- package/lib/storage/DataFrame.ts +472 -0
- package/lib/storage/FileStorage.d.ts +53 -0
- package/lib/storage/FileStorage.d.ts.map +1 -0
- package/lib/storage/FileStorage.js +80 -0
- package/lib/storage/FileStorage.ts +95 -0
- package/lib/storage/IndexedDBDriver.d.ts +75 -0
- package/lib/storage/IndexedDBDriver.d.ts.map +1 -0
- package/lib/storage/IndexedDBDriver.js +177 -0
- package/lib/storage/IndexedDBDriver.ts +226 -0
- package/lib/storage/TabularDriver.d.ts +75 -0
- package/lib/storage/TabularDriver.d.ts.map +1 -0
- package/lib/storage/TabularDriver.js +399 -0
- package/lib/storage/TabularDriver.ts +491 -0
- package/package.json +1 -1
|
@@ -0,0 +1,491 @@
|
|
|
1
|
+
import { DataFrame } from './DataFrame.js';
|
|
2
|
+
|
|
3
|
+
export interface StoredTable {
|
|
4
|
+
id: string;
|
|
5
|
+
name: string;
|
|
6
|
+
columns: string[];
|
|
7
|
+
rows: any[][];
|
|
8
|
+
rowCount: number;
|
|
9
|
+
delimiter: string;
|
|
10
|
+
timestamp: number;
|
|
11
|
+
metadata?: Record<string, any>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface ParseOptions {
|
|
15
|
+
delimiter?: string;
|
|
16
|
+
hasHeader?: boolean;
|
|
17
|
+
chunkSize?: number;
|
|
18
|
+
onProgress?: (parsed: number, total: number | null) => void;
|
|
19
|
+
maxRows?: number;
|
|
20
|
+
skipRows?: number;
|
|
21
|
+
columns?: string[];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export class TabularDriver {
|
|
25
|
+
private _dbName: string;
|
|
26
|
+
private _storeName: string;
|
|
27
|
+
private _db: IDBDatabase | null = null;
|
|
28
|
+
|
|
29
|
+
constructor(dbName: string = 'jux-tabular', storeName: string = 'tables') {
|
|
30
|
+
this._dbName = dbName;
|
|
31
|
+
this._storeName = storeName;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async open(): Promise<IDBDatabase> {
|
|
35
|
+
if (this._db) return this._db;
|
|
36
|
+
|
|
37
|
+
return new Promise((resolve, reject) => {
|
|
38
|
+
const request = indexedDB.open(this._dbName, 1);
|
|
39
|
+
|
|
40
|
+
request.onupgradeneeded = (e) => {
|
|
41
|
+
const db = (e.target as IDBOpenDBRequest).result;
|
|
42
|
+
if (!db.objectStoreNames.contains(this._storeName)) {
|
|
43
|
+
const store = db.createObjectStore(this._storeName, { keyPath: 'id' });
|
|
44
|
+
store.createIndex('name', 'name', { unique: false });
|
|
45
|
+
store.createIndex('timestamp', 'timestamp', { unique: false });
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
request.onsuccess = (e) => {
|
|
50
|
+
this._db = (e.target as IDBOpenDBRequest).result;
|
|
51
|
+
resolve(this._db);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
request.onerror = () => reject(request.error);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/* ═══════════════════════════════════════════════════
|
|
59
|
+
* CSV / TSV PARSING
|
|
60
|
+
* ═══════════════════════════════════════════════════ */
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Parse a CSV/TSV string into a DataFrame
|
|
64
|
+
*/
|
|
65
|
+
parseCSV(text: string, options: ParseOptions = {}): DataFrame {
|
|
66
|
+
const {
|
|
67
|
+
delimiter = ',',
|
|
68
|
+
hasHeader = true,
|
|
69
|
+
maxRows,
|
|
70
|
+
skipRows = 0,
|
|
71
|
+
columns: selectCols
|
|
72
|
+
} = options;
|
|
73
|
+
|
|
74
|
+
const lines = this._splitLines(text);
|
|
75
|
+
let startIdx = skipRows;
|
|
76
|
+
let headers: string[];
|
|
77
|
+
|
|
78
|
+
if (hasHeader && lines.length > startIdx) {
|
|
79
|
+
headers = this._parseLine(lines[startIdx], delimiter);
|
|
80
|
+
startIdx++;
|
|
81
|
+
} else {
|
|
82
|
+
const firstLine = this._parseLine(lines[startIdx] || '', delimiter);
|
|
83
|
+
headers = firstLine.map((_, i) => `col_${i}`);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const rows: Record<string, any>[] = [];
|
|
87
|
+
const end = maxRows ? Math.min(startIdx + maxRows, lines.length) : lines.length;
|
|
88
|
+
|
|
89
|
+
for (let i = startIdx; i < end; i++) {
|
|
90
|
+
if (!lines[i].trim()) continue;
|
|
91
|
+
const values = this._parseLine(lines[i], delimiter);
|
|
92
|
+
const row: Record<string, any> = {};
|
|
93
|
+
|
|
94
|
+
headers.forEach((h, j) => {
|
|
95
|
+
row[h] = this._autoType(values[j]);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
rows.push(row);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
let df = new DataFrame(rows);
|
|
102
|
+
|
|
103
|
+
if (selectCols) {
|
|
104
|
+
df = df.select(...selectCols);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return df;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Stream-parse a File into a DataFrame, with progress callback
|
|
112
|
+
*/
|
|
113
|
+
async streamFile(file: File, options: ParseOptions = {}): Promise<DataFrame> {
|
|
114
|
+
const {
|
|
115
|
+
delimiter = file.name.endsWith('.tsv') ? '\t' : ',',
|
|
116
|
+
hasHeader = true,
|
|
117
|
+
chunkSize = 64 * 1024,
|
|
118
|
+
onProgress,
|
|
119
|
+
maxRows,
|
|
120
|
+
skipRows = 0,
|
|
121
|
+
columns: selectCols
|
|
122
|
+
} = options;
|
|
123
|
+
|
|
124
|
+
const totalSize = file.size;
|
|
125
|
+
let bytesRead = 0;
|
|
126
|
+
let headers: string[] | null = null;
|
|
127
|
+
let lineBuffer = '';
|
|
128
|
+
let rowCount = 0;
|
|
129
|
+
let skipped = 0;
|
|
130
|
+
const rows: Record<string, any>[] = [];
|
|
131
|
+
|
|
132
|
+
const reader = file.stream().getReader();
|
|
133
|
+
const decoder = new TextDecoder();
|
|
134
|
+
|
|
135
|
+
while (true) {
|
|
136
|
+
const { done, value } = await reader.read();
|
|
137
|
+
if (done) break;
|
|
138
|
+
|
|
139
|
+
bytesRead += value.byteLength;
|
|
140
|
+
lineBuffer += decoder.decode(value, { stream: !done });
|
|
141
|
+
|
|
142
|
+
const lines = lineBuffer.split('\n');
|
|
143
|
+
lineBuffer = lines.pop() || '';
|
|
144
|
+
|
|
145
|
+
for (const line of lines) {
|
|
146
|
+
if (!line.trim()) continue;
|
|
147
|
+
|
|
148
|
+
if (!headers && hasHeader) {
|
|
149
|
+
headers = this._parseLine(line, delimiter);
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (!headers) {
|
|
154
|
+
const vals = this._parseLine(line, delimiter);
|
|
155
|
+
headers = vals.map((_, i) => `col_${i}`);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (skipped < skipRows) {
|
|
159
|
+
skipped++;
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (maxRows && rowCount >= maxRows) break;
|
|
164
|
+
|
|
165
|
+
const values = this._parseLine(line, delimiter);
|
|
166
|
+
const row: Record<string, any> = {};
|
|
167
|
+
headers.forEach((h, j) => {
|
|
168
|
+
row[h] = this._autoType(values[j]);
|
|
169
|
+
});
|
|
170
|
+
rows.push(row);
|
|
171
|
+
rowCount++;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (onProgress) {
|
|
175
|
+
onProgress(bytesRead, totalSize);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (maxRows && rowCount >= maxRows) break;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Handle remaining buffer
|
|
182
|
+
if (lineBuffer.trim() && (!maxRows || rowCount < maxRows)) {
|
|
183
|
+
if (!headers) {
|
|
184
|
+
headers = this._parseLine(lineBuffer, delimiter).map((_, i) => `col_${i}`);
|
|
185
|
+
} else {
|
|
186
|
+
const values = this._parseLine(lineBuffer, delimiter);
|
|
187
|
+
const row: Record<string, any> = {};
|
|
188
|
+
headers.forEach((h, j) => {
|
|
189
|
+
row[h] = this._autoType(values[j]);
|
|
190
|
+
});
|
|
191
|
+
rows.push(row);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (onProgress) onProgress(totalSize, totalSize);
|
|
196
|
+
|
|
197
|
+
let df = new DataFrame(rows);
|
|
198
|
+
if (selectCols) df = df.select(...selectCols);
|
|
199
|
+
|
|
200
|
+
return df;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/* ═══════════════════════════════════════════════════
|
|
204
|
+
* INDEXEDDB PERSISTENCE
|
|
205
|
+
* ═══════════════════════════════════════════════════ */
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Store a DataFrame to IndexedDB
|
|
209
|
+
*/
|
|
210
|
+
async store(name: string, df: DataFrame, metadata?: Record<string, any>): Promise<string> {
|
|
211
|
+
const db = await this.open();
|
|
212
|
+
|
|
213
|
+
const table: StoredTable = {
|
|
214
|
+
id: `${name}-${Date.now()}`,
|
|
215
|
+
name,
|
|
216
|
+
columns: df.columns,
|
|
217
|
+
rows: df.toRows().map(row => df.columns.map(c => row[c])),
|
|
218
|
+
rowCount: df.height,
|
|
219
|
+
delimiter: ',',
|
|
220
|
+
timestamp: Date.now(),
|
|
221
|
+
metadata
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
return new Promise((resolve, reject) => {
|
|
225
|
+
const tx = db.transaction(this._storeName, 'readwrite');
|
|
226
|
+
const store = tx.objectStore(this._storeName);
|
|
227
|
+
const request = store.put(table);
|
|
228
|
+
request.onsuccess = () => resolve(table.id);
|
|
229
|
+
request.onerror = () => reject(request.error);
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Load a DataFrame from IndexedDB by ID
|
|
235
|
+
*/
|
|
236
|
+
async load(id: string): Promise<DataFrame | null> {
|
|
237
|
+
const db = await this.open();
|
|
238
|
+
|
|
239
|
+
return new Promise((resolve, reject) => {
|
|
240
|
+
const tx = db.transaction(this._storeName, 'readonly');
|
|
241
|
+
const store = tx.objectStore(this._storeName);
|
|
242
|
+
const request = store.get(id);
|
|
243
|
+
|
|
244
|
+
request.onsuccess = () => {
|
|
245
|
+
const table: StoredTable = request.result;
|
|
246
|
+
if (!table) return resolve(null);
|
|
247
|
+
|
|
248
|
+
const rows = table.rows.map(row => {
|
|
249
|
+
const obj: Record<string, any> = {};
|
|
250
|
+
table.columns.forEach((col, i) => { obj[col] = row[i]; });
|
|
251
|
+
return obj;
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
resolve(new DataFrame(rows));
|
|
255
|
+
};
|
|
256
|
+
request.onerror = () => reject(request.error);
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Load the most recent table by name
|
|
262
|
+
*/
|
|
263
|
+
async loadByName(name: string): Promise<DataFrame | null> {
|
|
264
|
+
const db = await this.open();
|
|
265
|
+
|
|
266
|
+
return new Promise((resolve, reject) => {
|
|
267
|
+
const tx = db.transaction(this._storeName, 'readonly');
|
|
268
|
+
const store = tx.objectStore(this._storeName);
|
|
269
|
+
const index = store.index('name');
|
|
270
|
+
const request = index.getAll(name);
|
|
271
|
+
|
|
272
|
+
request.onsuccess = () => {
|
|
273
|
+
const tables: StoredTable[] = request.result || [];
|
|
274
|
+
if (tables.length === 0) return resolve(null);
|
|
275
|
+
|
|
276
|
+
tables.sort((a, b) => b.timestamp - a.timestamp);
|
|
277
|
+
const table = tables[0];
|
|
278
|
+
|
|
279
|
+
const rows = table.rows.map(row => {
|
|
280
|
+
const obj: Record<string, any> = {};
|
|
281
|
+
table.columns.forEach((col, i) => { obj[col] = row[i]; });
|
|
282
|
+
return obj;
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
resolve(new DataFrame(rows));
|
|
286
|
+
};
|
|
287
|
+
request.onerror = () => reject(request.error);
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* List all stored tables (metadata only)
|
|
293
|
+
*/
|
|
294
|
+
async list(): Promise<Array<{ id: string; name: string; columns: string[]; rowCount: number; timestamp: number }>> {
|
|
295
|
+
const db = await this.open();
|
|
296
|
+
|
|
297
|
+
return new Promise((resolve, reject) => {
|
|
298
|
+
const tx = db.transaction(this._storeName, 'readonly');
|
|
299
|
+
const store = tx.objectStore(this._storeName);
|
|
300
|
+
const request = store.getAll();
|
|
301
|
+
|
|
302
|
+
request.onsuccess = () => {
|
|
303
|
+
resolve((request.result || []).map((t: StoredTable) => ({
|
|
304
|
+
id: t.id,
|
|
305
|
+
name: t.name,
|
|
306
|
+
columns: t.columns,
|
|
307
|
+
rowCount: t.rowCount,
|
|
308
|
+
timestamp: t.timestamp
|
|
309
|
+
})));
|
|
310
|
+
};
|
|
311
|
+
request.onerror = () => reject(request.error);
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Delete a stored table
|
|
317
|
+
*/
|
|
318
|
+
async delete(id: string): Promise<void> {
|
|
319
|
+
const db = await this.open();
|
|
320
|
+
|
|
321
|
+
return new Promise((resolve, reject) => {
|
|
322
|
+
const tx = db.transaction(this._storeName, 'readwrite');
|
|
323
|
+
const store = tx.objectStore(this._storeName);
|
|
324
|
+
const request = store.delete(id);
|
|
325
|
+
request.onsuccess = () => resolve();
|
|
326
|
+
request.onerror = () => reject(request.error);
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
async clear(): Promise<void> {
|
|
331
|
+
const db = await this.open();
|
|
332
|
+
|
|
333
|
+
return new Promise((resolve, reject) => {
|
|
334
|
+
const tx = db.transaction(this._storeName, 'readwrite');
|
|
335
|
+
const store = tx.objectStore(this._storeName);
|
|
336
|
+
const request = store.clear();
|
|
337
|
+
request.onsuccess = () => resolve();
|
|
338
|
+
request.onerror = () => reject(request.error);
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/* ═══════════════════════════════════════════════════
|
|
343
|
+
* STREAM FROM URL
|
|
344
|
+
* ═══════════════════════════════════════════════════ */
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Fetch and stream-parse a remote CSV/TSV file
|
|
348
|
+
*/
|
|
349
|
+
async fetch(url: string, options: ParseOptions = {}): Promise<DataFrame> {
|
|
350
|
+
const response = await globalThis.fetch(url);
|
|
351
|
+
if (!response.ok) throw new Error(`Failed to fetch: ${response.status}`);
|
|
352
|
+
if (!response.body) throw new Error('No response body');
|
|
353
|
+
|
|
354
|
+
const contentLength = Number(response.headers.get('content-length')) || null;
|
|
355
|
+
const ext = url.split('.').pop()?.toLowerCase();
|
|
356
|
+
const delimiter = options.delimiter ?? (ext === 'tsv' ? '\t' : ',');
|
|
357
|
+
|
|
358
|
+
const { hasHeader = true, chunkSize, onProgress, maxRows, skipRows = 0, columns: selectCols } = options;
|
|
359
|
+
|
|
360
|
+
const reader = response.body.getReader();
|
|
361
|
+
const decoder = new TextDecoder();
|
|
362
|
+
|
|
363
|
+
let headers: string[] | null = null;
|
|
364
|
+
let lineBuffer = '';
|
|
365
|
+
let rowCount = 0;
|
|
366
|
+
let skipped = 0;
|
|
367
|
+
let bytesRead = 0;
|
|
368
|
+
const rows: Record<string, any>[] = [];
|
|
369
|
+
|
|
370
|
+
while (true) {
|
|
371
|
+
const { done, value } = await reader.read();
|
|
372
|
+
if (done) break;
|
|
373
|
+
|
|
374
|
+
bytesRead += value.byteLength;
|
|
375
|
+
lineBuffer += decoder.decode(value, { stream: true });
|
|
376
|
+
|
|
377
|
+
const lines = lineBuffer.split('\n');
|
|
378
|
+
lineBuffer = lines.pop() || '';
|
|
379
|
+
|
|
380
|
+
for (const line of lines) {
|
|
381
|
+
if (!line.trim()) continue;
|
|
382
|
+
|
|
383
|
+
if (!headers && hasHeader) {
|
|
384
|
+
headers = this._parseLine(line, delimiter);
|
|
385
|
+
continue;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
if (!headers) {
|
|
389
|
+
const vals = this._parseLine(line, delimiter);
|
|
390
|
+
headers = vals.map((_, i) => `col_${i}`);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
if (skipped < skipRows) { skipped++; continue; }
|
|
394
|
+
if (maxRows && rowCount >= maxRows) break;
|
|
395
|
+
|
|
396
|
+
const values = this._parseLine(line, delimiter);
|
|
397
|
+
const row: Record<string, any> = {};
|
|
398
|
+
headers.forEach((h, j) => { row[h] = this._autoType(values[j]); });
|
|
399
|
+
rows.push(row);
|
|
400
|
+
rowCount++;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
if (onProgress) onProgress(bytesRead, contentLength);
|
|
404
|
+
if (maxRows && rowCount >= maxRows) break;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
if (lineBuffer.trim() && (!maxRows || rowCount < maxRows)) {
|
|
408
|
+
if (headers) {
|
|
409
|
+
const values = this._parseLine(lineBuffer, delimiter);
|
|
410
|
+
const row: Record<string, any> = {};
|
|
411
|
+
headers.forEach((h, j) => { row[h] = this._autoType(values[j]); });
|
|
412
|
+
rows.push(row);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
if (onProgress) onProgress(bytesRead, bytesRead);
|
|
417
|
+
|
|
418
|
+
let df = new DataFrame(rows);
|
|
419
|
+
if (selectCols) df = df.select(...selectCols);
|
|
420
|
+
return df;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/* ═══════════════════════════════════════════════════
|
|
424
|
+
* INTERNAL PARSING HELPERS
|
|
425
|
+
* ═══════════════════════════════════════════════════ */
|
|
426
|
+
|
|
427
|
+
private _splitLines(text: string): string[] {
|
|
428
|
+
return text.replace(/\r\n/g, '\n').replace(/\r/g, '\n').split('\n');
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
private _parseLine(line: string, delimiter: string): string[] {
|
|
432
|
+
const result: string[] = [];
|
|
433
|
+
let current = '';
|
|
434
|
+
let inQuotes = false;
|
|
435
|
+
|
|
436
|
+
for (let i = 0; i < line.length; i++) {
|
|
437
|
+
const ch = line[i];
|
|
438
|
+
|
|
439
|
+
if (inQuotes) {
|
|
440
|
+
if (ch === '"') {
|
|
441
|
+
if (i + 1 < line.length && line[i + 1] === '"') {
|
|
442
|
+
current += '"';
|
|
443
|
+
i++;
|
|
444
|
+
} else {
|
|
445
|
+
inQuotes = false;
|
|
446
|
+
}
|
|
447
|
+
} else {
|
|
448
|
+
current += ch;
|
|
449
|
+
}
|
|
450
|
+
} else {
|
|
451
|
+
if (ch === '"') {
|
|
452
|
+
inQuotes = true;
|
|
453
|
+
} else if (ch === delimiter) {
|
|
454
|
+
result.push(current.trim());
|
|
455
|
+
current = '';
|
|
456
|
+
} else {
|
|
457
|
+
current += ch;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
result.push(current.trim());
|
|
463
|
+
return result;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
private _autoType(value: string | undefined): any {
|
|
467
|
+
if (value === undefined || value === '' || value === null) return null;
|
|
468
|
+
|
|
469
|
+
if (value === 'true') return true;
|
|
470
|
+
if (value === 'false') return false;
|
|
471
|
+
|
|
472
|
+
const num = Number(value);
|
|
473
|
+
if (!isNaN(num) && value.trim() !== '') return num;
|
|
474
|
+
|
|
475
|
+
return value;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
close(): void {
|
|
479
|
+
if (this._db) {
|
|
480
|
+
this._db.close();
|
|
481
|
+
this._db = null;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* Factory
|
|
488
|
+
*/
|
|
489
|
+
export function tabularDriver(dbName?: string, storeName?: string): TabularDriver {
|
|
490
|
+
return new TabularDriver(dbName, storeName);
|
|
491
|
+
}
|