juxscript 1.1.232 → 1.1.234

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.
@@ -0,0 +1,165 @@
1
+ import { DataFrame } from '../storage/DataFrame.js';
2
+ export interface PipelineOptions {
3
+ dbName?: string;
4
+ storeName?: string;
5
+ }
6
+ export interface FetchOptions {
7
+ delimiter?: string;
8
+ hasHeader?: boolean;
9
+ headerRow?: number;
10
+ mimeType?: 'csv' | 'tsv' | 'json' | 'auto';
11
+ }
12
+ /**
13
+ * DataPipeline — Headless, promise-based data loading and transformation.
14
+ *
15
+ * Usage:
16
+ * const df = await data.fromUrl('https://example.com/report.csv').result();
17
+ * const df = await data.fromStorage('patients').result();
18
+ * const df = await data.fromJson(apiResponse).result();
19
+ *
20
+ * // Transform and distribute:
21
+ * await data.fromUrl(url)
22
+ * .transform(df => df.select('name', 'age').filter(r => r.age > 30))
23
+ * .into(myTable);
24
+ *
25
+ * await data.fromStorage('encounters')
26
+ * .transform(df => df.distinct('provider'))
27
+ * .into(myDropdown, { labelKey: 'provider', valueKey: 'provider' });
28
+ */
29
+ export declare class DataPipeline {
30
+ private _driver;
31
+ private _pending;
32
+ private _transforms;
33
+ constructor(options?: PipelineOptions);
34
+ /**
35
+ * Load from IndexedDB by key
36
+ */
37
+ fromStorage(key: string): this;
38
+ /**
39
+ * Load from a URL (CSV, TSV, or JSON)
40
+ */
41
+ fromUrl(url: string, options?: FetchOptions): this;
42
+ /**
43
+ * Load from a File object directly
44
+ */
45
+ fromFile(file: File, options?: FetchOptions): this;
46
+ /**
47
+ * Load from an array of row objects
48
+ */
49
+ fromRows(rows: Record<string, any>[], columns?: string[]): this;
50
+ /**
51
+ * Load from column-oriented data
52
+ */
53
+ fromColumns(columns: Record<string, any[]>): this;
54
+ /**
55
+ * Load from a JSON response (array of objects or column-oriented)
56
+ */
57
+ fromJson(json: Record<string, any>[] | Record<string, any[]> | string): this;
58
+ /**
59
+ * Load from a FileUpload component's cached storage key.
60
+ * Requires .cache() enabled on the FileUpload and a file already uploaded.
61
+ *
62
+ * Usage:
63
+ * await data().fromUpload(myUpload).select('name').into(myTable);
64
+ */
65
+ fromUpload(upload: any): this;
66
+ /**
67
+ * Start from an existing DataFrame
68
+ */
69
+ from(df: DataFrame): this;
70
+ /**
71
+ * Apply an arbitrary transform
72
+ */
73
+ transform(fn: (df: DataFrame) => DataFrame): this;
74
+ /**
75
+ * Select columns
76
+ */
77
+ select(...cols: string[]): this;
78
+ /**
79
+ * Filter rows
80
+ */
81
+ filter(predicate: (row: Record<string, any>, index: number) => boolean): this;
82
+ /**
83
+ * Sort by column
84
+ */
85
+ sort(column: string, descending?: boolean): this;
86
+ /**
87
+ * Take first N rows
88
+ */
89
+ head(n?: number): this;
90
+ /**
91
+ * Take last N rows
92
+ */
93
+ tail(n?: number): this;
94
+ /**
95
+ * Rename columns
96
+ */
97
+ rename(mapping: Record<string, string>): this;
98
+ /**
99
+ * Add a computed column
100
+ */
101
+ withColumn(name: string, fn: (row: Record<string, any>, index: number) => any): this;
102
+ /**
103
+ * Drop nulls
104
+ */
105
+ dropna(columns?: string[]): this;
106
+ /**
107
+ * Drop duplicates
108
+ */
109
+ dropDuplicates(columns?: string[]): this;
110
+ /**
111
+ * Execute the pipeline and return the DataFrame
112
+ */
113
+ result(): Promise<DataFrame>;
114
+ /**
115
+ * Execute and return rows as plain objects
116
+ */
117
+ toRows(): Promise<Record<string, any>[]>;
118
+ /**
119
+ * Execute and return a single column as an array
120
+ */
121
+ toArray(column: string): Promise<any[]>;
122
+ /**
123
+ * Execute and return distinct values from a column
124
+ */
125
+ toDistinct(column: string): Promise<any[]>;
126
+ /**
127
+ * Execute and push results into a jux component.
128
+ *
129
+ * Supported targets:
130
+ * - Table: sets columns + rows
131
+ * - Dropdown/Select: sets options from labelKey/valueKey
132
+ * - List: sets items
133
+ * - DataFrameComponent: loads data
134
+ * - Any object with a .rows() or .options() or .items() method
135
+ */
136
+ into(target: any, options?: IntoOptions): Promise<DataFrame>;
137
+ /**
138
+ * Execute and persist to IndexedDB
139
+ */
140
+ store(key: string, metadata?: Record<string, any>): Promise<DataFrame>;
141
+ private _loadFromUrl;
142
+ private _loadFromFile;
143
+ private _detectMimeType;
144
+ }
145
+ export interface IntoOptions {
146
+ /** Override which columns to use (for Table targets) */
147
+ columns?: string[];
148
+ /** Column name → display label mapping */
149
+ columnLabels?: Record<string, string>;
150
+ /** Key for option labels (Dropdown/Select/List targets) */
151
+ labelKey?: string;
152
+ /** Key for option values (Dropdown/Select targets) */
153
+ valueKey?: string;
154
+ }
155
+ /**
156
+ * Factory function — the primary entry point
157
+ *
158
+ * Usage:
159
+ * import { data } from 'juxscript';
160
+ *
161
+ * const df = await data().fromStorage('patients').select('name', 'dob').result();
162
+ * await data().fromUrl(presignedUrl).filter(r => r.status === 'active').into(myTable);
163
+ */
164
+ export declare function data(options?: PipelineOptions): DataPipeline;
165
+ //# sourceMappingURL=DataPipeline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DataPipeline.d.ts","sourceRoot":"","sources":["DataPipeline.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAGpD,MAAM,WAAW,eAAe;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,KAAK,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;CAC9C;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,YAAY;IACrB,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,QAAQ,CAAmC;IACnD,OAAO,CAAC,WAAW,CAAwC;gBAE/C,OAAO,GAAE,eAAoB;IAWzC;;OAEG;IACH,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAS9B;;OAEG;IACH,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,GAAG,IAAI;IAMtD;;OAEG;IACH,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,GAAE,YAAiB,GAAG,IAAI;IAMtD;;OAEG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI;IAM/D;;OAEG;IACH,WAAW,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,GAAG,IAAI;IAMjD;;OAEG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,GAAG,MAAM,GAAG,IAAI;IAO5E;;;;;;OAMG;IACH,UAAU,CAAC,MAAM,EAAE,GAAG,GAAG,IAAI;IAgB7B;;OAEG;IACH,IAAI,CAAC,EAAE,EAAE,SAAS,GAAG,IAAI;IAUzB;;OAEG;IACH,SAAS,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE,SAAS,KAAK,SAAS,GAAG,IAAI;IAKjD;;OAEG;IACH,MAAM,CAAC,GAAG,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI;IAI/B;;OAEG;IACH,MAAM,CAAC,SAAS,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,GAAG,IAAI;IAI7E;;OAEG;IACH,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,OAAO,GAAG,IAAI;IAIhD;;OAEG;IACH,IAAI,CAAC,CAAC,GAAE,MAAU,GAAG,IAAI;IAIzB;;OAEG;IACH,IAAI,CAAC,CAAC,GAAE,MAAU,GAAG,IAAI;IAIzB;;OAEG;IACH,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI;IAI7C;;OAEG;IACH,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,GAAG,GAAG,IAAI;IAIpF;;OAEG;IACH,MAAM,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI;IAIhC;;OAEG;IACH,cAAc,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI;IAQxC;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC,SAAS,CAAC;IAUlC;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CAAC;IAI9C;;OAEG;IACG,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAI7C;;OAEG;IACG,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAIhD;;;;;;;;;OASG;IACG,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,SAAS,CAAC;IA0CtE;;OAEG;IACG,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,OAAO,CAAC,SAAS,CAAC;YAU9D,YAAY;YAmBZ,aAAa;IAqB3B,OAAO,CAAC,eAAe;CAO1B;AAED,MAAM,WAAW,WAAW;IACxB,wDAAwD;IACxD,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,0CAA0C;IAC1C,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sDAAsD;IACtD,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;GAQG;AACH,wBAAgB,IAAI,CAAC,OAAO,GAAE,eAAoB,GAAG,YAAY,CAEhE"}
@@ -0,0 +1,324 @@
1
+ import { DataFrame } from '../storage/DataFrame.js';
2
+ import { TabularDriver } from '../storage/TabularDriver.js';
3
+ /**
4
+ * DataPipeline — Headless, promise-based data loading and transformation.
5
+ *
6
+ * Usage:
7
+ * const df = await data.fromUrl('https://example.com/report.csv').result();
8
+ * const df = await data.fromStorage('patients').result();
9
+ * const df = await data.fromJson(apiResponse).result();
10
+ *
11
+ * // Transform and distribute:
12
+ * await data.fromUrl(url)
13
+ * .transform(df => df.select('name', 'age').filter(r => r.age > 30))
14
+ * .into(myTable);
15
+ *
16
+ * await data.fromStorage('encounters')
17
+ * .transform(df => df.distinct('provider'))
18
+ * .into(myDropdown, { labelKey: 'provider', valueKey: 'provider' });
19
+ */
20
+ export class DataPipeline {
21
+ constructor(options = {}) {
22
+ this._pending = null;
23
+ this._transforms = [];
24
+ this._driver = new TabularDriver(options.dbName ?? 'jux-dataframes', options.storeName ?? 'frames');
25
+ }
26
+ /* ═══════════════════════════════════════════════════
27
+ * SOURCES
28
+ * ═══════════════════════════════════════════════════ */
29
+ /**
30
+ * Load from IndexedDB by key
31
+ */
32
+ fromStorage(key) {
33
+ this._transforms = [];
34
+ this._pending = this._driver.loadByName(key).then(df => {
35
+ if (!df)
36
+ throw new Error(`No data found in storage with key: "${key}"`);
37
+ return df;
38
+ });
39
+ return this;
40
+ }
41
+ /**
42
+ * Load from a URL (CSV, TSV, or JSON)
43
+ */
44
+ fromUrl(url, options = {}) {
45
+ this._transforms = [];
46
+ this._pending = this._loadFromUrl(url, options);
47
+ return this;
48
+ }
49
+ /**
50
+ * Load from a File object directly
51
+ */
52
+ fromFile(file, options = {}) {
53
+ this._transforms = [];
54
+ this._pending = this._loadFromFile(file, options);
55
+ return this;
56
+ }
57
+ /**
58
+ * Load from an array of row objects
59
+ */
60
+ fromRows(rows, columns) {
61
+ this._transforms = [];
62
+ this._pending = Promise.resolve(new DataFrame(rows, { columns }));
63
+ return this;
64
+ }
65
+ /**
66
+ * Load from column-oriented data
67
+ */
68
+ fromColumns(columns) {
69
+ this._transforms = [];
70
+ this._pending = Promise.resolve(new DataFrame(columns));
71
+ return this;
72
+ }
73
+ /**
74
+ * Load from a JSON response (array of objects or column-oriented)
75
+ */
76
+ fromJson(json) {
77
+ this._transforms = [];
78
+ const parsed = typeof json === 'string' ? JSON.parse(json) : json;
79
+ this._pending = Promise.resolve(new DataFrame(parsed));
80
+ return this;
81
+ }
82
+ /**
83
+ * Load from a FileUpload component's cached storage key.
84
+ * Requires .cache() enabled on the FileUpload and a file already uploaded.
85
+ *
86
+ * Usage:
87
+ * await data().fromUpload(myUpload).select('name').into(myTable);
88
+ */
89
+ fromUpload(upload) {
90
+ this._transforms = [];
91
+ const key = upload.storageKey;
92
+ if (!key) {
93
+ this._pending = Promise.reject(new Error('FileUpload has no cached storage key. Ensure .cache() is enabled and a file has been uploaded.'));
94
+ }
95
+ else {
96
+ this._pending = this._driver.loadByName(key).then((df) => {
97
+ if (!df)
98
+ throw new Error(`No cached data found for key: "${key}"`);
99
+ return df;
100
+ });
101
+ }
102
+ return this;
103
+ }
104
+ /**
105
+ * Start from an existing DataFrame
106
+ */
107
+ from(df) {
108
+ this._transforms = [];
109
+ this._pending = Promise.resolve(df.clone());
110
+ return this;
111
+ }
112
+ /* ═══════════════════════════════════════════════════
113
+ * TRANSFORMS (chained, lazy — applied on .result())
114
+ * ═══════════════════════════════════════════════════ */
115
+ /**
116
+ * Apply an arbitrary transform
117
+ */
118
+ transform(fn) {
119
+ this._transforms.push(fn);
120
+ return this;
121
+ }
122
+ /**
123
+ * Select columns
124
+ */
125
+ select(...cols) {
126
+ return this.transform(df => df.select(...cols));
127
+ }
128
+ /**
129
+ * Filter rows
130
+ */
131
+ filter(predicate) {
132
+ return this.transform(df => df.filter(predicate));
133
+ }
134
+ /**
135
+ * Sort by column
136
+ */
137
+ sort(column, descending) {
138
+ return this.transform(df => df.sort(column, descending));
139
+ }
140
+ /**
141
+ * Take first N rows
142
+ */
143
+ head(n = 5) {
144
+ return this.transform(df => df.head(n));
145
+ }
146
+ /**
147
+ * Take last N rows
148
+ */
149
+ tail(n = 5) {
150
+ return this.transform(df => df.tail(n));
151
+ }
152
+ /**
153
+ * Rename columns
154
+ */
155
+ rename(mapping) {
156
+ return this.transform(df => df.rename(mapping));
157
+ }
158
+ /**
159
+ * Add a computed column
160
+ */
161
+ withColumn(name, fn) {
162
+ return this.transform(df => df.withColumn(name, fn));
163
+ }
164
+ /**
165
+ * Drop nulls
166
+ */
167
+ dropna(columns) {
168
+ return this.transform(df => df.dropna(columns));
169
+ }
170
+ /**
171
+ * Drop duplicates
172
+ */
173
+ dropDuplicates(columns) {
174
+ return this.transform(df => df.dropDuplicates(columns));
175
+ }
176
+ /* ═══════════════════════════════════════════════════
177
+ * TERMINAL OPERATIONS
178
+ * ═══════════════════════════════════════════════════ */
179
+ /**
180
+ * Execute the pipeline and return the DataFrame
181
+ */
182
+ async result() {
183
+ if (!this._pending)
184
+ throw new Error('No data source specified. Call fromStorage(), fromUrl(), etc. first.');
185
+ let df = await this._pending;
186
+ for (const fn of this._transforms) {
187
+ df = fn(df);
188
+ }
189
+ return df;
190
+ }
191
+ /**
192
+ * Execute and return rows as plain objects
193
+ */
194
+ async toRows() {
195
+ return (await this.result()).toRows();
196
+ }
197
+ /**
198
+ * Execute and return a single column as an array
199
+ */
200
+ async toArray(column) {
201
+ return (await this.result()).col(column);
202
+ }
203
+ /**
204
+ * Execute and return distinct values from a column
205
+ */
206
+ async toDistinct(column) {
207
+ return (await this.result()).distinct(column);
208
+ }
209
+ /**
210
+ * Execute and push results into a jux component.
211
+ *
212
+ * Supported targets:
213
+ * - Table: sets columns + rows
214
+ * - Dropdown/Select: sets options from labelKey/valueKey
215
+ * - List: sets items
216
+ * - DataFrameComponent: loads data
217
+ * - Any object with a .rows() or .options() or .items() method
218
+ */
219
+ async into(target, options = {}) {
220
+ const df = await this.result();
221
+ const rows = df.toRows();
222
+ // Table-like: has .columns() and .rows()
223
+ if (typeof target.columns === 'function' && typeof target.rows === 'function') {
224
+ const columnDefs = (options.columns ?? df.columns).map((col) => {
225
+ const label = options.columnLabels?.[col] ?? col;
226
+ return { key: col, label };
227
+ });
228
+ target.columns(columnDefs).rows(rows);
229
+ return df;
230
+ }
231
+ // DataFrameComponent: has .fromData()
232
+ if (typeof target.fromData === 'function') {
233
+ target.fromData(rows);
234
+ return df;
235
+ }
236
+ // Dropdown/Select-like: has .options()
237
+ if (typeof target.options === 'function') {
238
+ const labelKey = options.labelKey ?? df.columns[0];
239
+ const valueKey = options.valueKey ?? (df.columns[1] ?? df.columns[0]);
240
+ const opts = rows.map(row => ({
241
+ label: String(row[labelKey] ?? ''),
242
+ value: row[valueKey]
243
+ }));
244
+ target.options(opts);
245
+ return df;
246
+ }
247
+ // List-like: has .items()
248
+ if (typeof target.items === 'function') {
249
+ const key = options.labelKey ?? df.columns[0];
250
+ target.items(rows.map(row => String(row[key] ?? '')));
251
+ return df;
252
+ }
253
+ throw new Error(`Target does not have a recognized API (.columns/.rows, .fromData, .options, or .items)`);
254
+ }
255
+ /**
256
+ * Execute and persist to IndexedDB
257
+ */
258
+ async store(key, metadata) {
259
+ const df = await this.result();
260
+ await this._driver.store(key, df, metadata);
261
+ return df;
262
+ }
263
+ /* ═══════════════════════════════════════════════════
264
+ * PRIVATE
265
+ * ═══════════════════════════════════════════════════ */
266
+ async _loadFromUrl(url, options) {
267
+ const mimeType = options.mimeType ?? this._detectMimeType(url);
268
+ if (mimeType === 'json') {
269
+ const response = await fetch(url);
270
+ if (!response.ok)
271
+ throw new Error(`Fetch failed: ${response.status} ${response.statusText}`);
272
+ const json = await response.json();
273
+ return new DataFrame(json);
274
+ }
275
+ // CSV/TSV — delegate to driver
276
+ return this._driver.fetch(url, {
277
+ autoDetectDelimiter: !options.delimiter,
278
+ delimiter: options.delimiter,
279
+ hasHeader: options.hasHeader ?? true,
280
+ headerRow: options.headerRow
281
+ });
282
+ }
283
+ async _loadFromFile(file, options) {
284
+ const isExcel = /\.(xlsx?|xls)$/i.test(file.name);
285
+ if (isExcel) {
286
+ const sheets = await this._driver.streamFileMultiSheet(file, {
287
+ headerRow: options.headerRow
288
+ });
289
+ const sheetNames = Object.keys(sheets);
290
+ if (sheetNames.length === 0)
291
+ throw new Error('No data found in file');
292
+ return sheets[sheetNames[0]];
293
+ }
294
+ const text = await file.text();
295
+ return this._driver.parseCSV(text, {
296
+ autoDetectDelimiter: !options.delimiter,
297
+ delimiter: options.delimiter,
298
+ hasHeader: options.hasHeader ?? true,
299
+ headerRow: options.headerRow
300
+ });
301
+ }
302
+ _detectMimeType(url) {
303
+ const lower = url.toLowerCase().split('?')[0];
304
+ if (lower.endsWith('.json'))
305
+ return 'json';
306
+ if (lower.endsWith('.tsv'))
307
+ return 'tsv';
308
+ if (lower.endsWith('.csv'))
309
+ return 'csv';
310
+ return 'auto';
311
+ }
312
+ }
313
+ /**
314
+ * Factory function — the primary entry point
315
+ *
316
+ * Usage:
317
+ * import { data } from 'juxscript';
318
+ *
319
+ * const df = await data().fromStorage('patients').select('name', 'dob').result();
320
+ * await data().fromUrl(presignedUrl).filter(r => r.status === 'active').into(myTable);
321
+ */
322
+ export function data(options = {}) {
323
+ return new DataPipeline(options);
324
+ }