@nteract/sift 0.0.1 → 0.5.4-nightly.38473320686

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 (34) hide show
  1. package/README.md +101 -7
  2. package/docs/embedding-guide.md +216 -0
  3. package/lib/LICENSE +28 -0
  4. package/lib/THIRD_PARTY_NOTICES.md +2018 -0
  5. package/lib/handoff.css +37 -0
  6. package/lib/handoff.js +26 -0
  7. package/lib/index.js +6548 -0
  8. package/lib/style.css +2 -0
  9. package/lib/types/packages/sift/src/accumulators.d.ts +61 -0
  10. package/lib/types/packages/sift/src/auto-width.d.ts +13 -0
  11. package/lib/types/packages/sift/src/column-menu.d.ts +8 -0
  12. package/lib/types/packages/sift/src/components/column-context-menu.d.ts +32 -0
  13. package/lib/types/packages/sift/src/components/image-viewer.d.ts +16 -0
  14. package/lib/types/packages/sift/src/csv.d.ts +19 -0
  15. package/lib/types/packages/sift/src/events.d.ts +1 -0
  16. package/lib/types/packages/sift/src/file-data.d.ts +40 -0
  17. package/lib/types/packages/sift/src/filter-schema.d.ts +100 -0
  18. package/lib/types/packages/sift/src/handoff.d.ts +11 -0
  19. package/lib/types/packages/sift/src/index.d.ts +29 -0
  20. package/lib/types/packages/sift/src/parquet-features.d.ts +15 -0
  21. package/lib/types/packages/sift/src/predicate.d.ts +199 -0
  22. package/lib/types/packages/sift/src/react.d.ts +97 -0
  23. package/lib/types/packages/sift/src/sparkline.d.ts +25 -0
  24. package/lib/types/packages/sift/src/table-response.d.ts +5 -0
  25. package/lib/types/packages/sift/src/table.d.ts +175 -0
  26. package/lib/types/packages/sift/src/wasm-summaries.d.ts +3 -0
  27. package/lib/types/packages/sift/src/wasm-table-data.d.ts +19 -0
  28. package/lib/types/src/components/ui/button.d.ts +11 -0
  29. package/lib/types/src/components/ui/popover.d.ts +7 -0
  30. package/lib/types/src/lib/utils.d.ts +2 -0
  31. package/lib/wasm/sift_wasm.d.ts +382 -0
  32. package/lib/wasm/sift_wasm.js +1398 -0
  33. package/lib/wasm/sift_wasm_bg.wasm +0 -0
  34. package/package.json +76 -5
package/README.md CHANGED
@@ -1,13 +1,107 @@
1
- # @nteract/sift 0.0.1
1
+ # Sift
2
2
 
3
- This version only reserves the package name so nteract's release workflow can
4
- publish through npm trusted publishing. It contains no code.
3
+ Crossfilter data explorer. [Pretext](https://github.com/chenglou/pretext) × [Arrow](https://arrow.apache.org/).
5
4
 
6
- Install a real build instead:
5
+ ![Sift — crossfilter in action](https://img.runt.run/sift/crossfilter-demo.gif)
6
+
7
+ [`@nteract/sift`](https://github.com/nteract) on npm. Try it at [rgbkrk.github.io/sift](https://rgbkrk.github.io/sift/).
8
+
9
+ ## Features
10
+
11
+ - **Local CSV/TSV, Parquet, and Arrow IPC streams** — bounded file adapters, no notebook or server needed
12
+ - **Stream hosted Arrow IPC or load Parquet URLs** — WASM-native
13
+ - **Crossfilter** — brush histograms to filter, all other summaries update instantly
14
+ - **Column profiling** — null%, distinct count shown inline in headers
15
+ - **Smart summaries** — histograms, category bars, boolean ratios, binary value bars, searchable popovers. Adapts visualization to column type × cardinality.
16
+ - **Column type casting** — right-click to treat text as numbers, dates, booleans — with full undo
17
+ - **Smart column detection** — pandas index columns, HuggingFace ClassLabel features
18
+ - **Virtual scroll** — 100k+ rows at 120fps. Pretext layout at 0.0002ms/cell.
19
+ - **Odometer status bar** — rolling digit animation for row count, visible range, FPS
20
+ - **Multi-instance** — embed multiple tables on one page (notebook demo included)
21
+ - **RxJS streaming** — cancellable dataset loading via `switchMap`
22
+ - **Mobile** — tap any row for a detail sheet with all column values
23
+ - **Dark mode**, keyboard nav, pinned columns, ARIA grid
24
+ - **8 HuggingFace datasets** built into the demo
25
+
26
+ ## Quick start
27
+
28
+ From the nteract repository root:
29
+
30
+ ```sh
31
+ pnpm install --frozen-lockfile
32
+ cargo xtask wasm sift --skip-renderer-plugins
33
+ pnpm --dir packages/sift dev
34
+ ```
35
+
36
+ ## Use as a library
37
+
38
+ The package ships JavaScript, compiled CSS, TypeScript declarations, and WASM,
39
+ with third-party notices for the bundled code in `lib/THIRD_PARTY_NOTICES.md`.
40
+ React and React DOM are required; no notebook, kernel, or daemon is needed.
41
+
42
+ ```tsx
43
+ import { parseCsv, SiftTable } from '@nteract/sift'
44
+ import '@nteract/sift/style.css'
45
+
46
+ const data = parseCsv(csvText) // Complete file; rejects malformed/over-limit data.
47
+ <SiftTable data={data} style={{ height: 600 }} />
48
+ ```
49
+
50
+ For binary files, use `loadTableFile(bytes)` through a memoized React loader
51
+ source or pass its result to imperative `createTable` (see the guide).
52
+ The default file adapters impose independent ceilings of 16 MiB,
53
+ 100,000 rows, and 256 columns; CSV also has a 1,000,000-cell ceiling, and binary
54
+ files have a conservative 128 MiB decoded-memory admission guard. Meeting the
55
+ byte, row, and column ceilings alone does not guarantee acceptance.
56
+ Virtual scrolling does not provide disk paging.
57
+
58
+ See the [embedding guide](docs/embedding-guide.md) for WASM asset setup, CSV/TSV
59
+ options, memory limits, lifecycle, themes, and host viewer/editor integration.
60
+
61
+ ## Engine API
62
+
63
+ ```ts
64
+ engine.setSort('age', 'desc')
65
+ engine.setFilter(colIndex, { kind: 'range', min: 18, max: 65 })
66
+ engine.clearAllFilters()
67
+
68
+ // Inspect the current sort, filters, and counts.
69
+ const state = engine.getState()
70
+ ```
71
+
72
+ See [`docs/embedding-guide.md`](docs/embedding-guide.md) for the full API.
73
+
74
+ ## WASM compute (`sift-wasm`)
75
+
76
+ Rust crate wrapping arrow-rs. Parquet decoding, sort, histograms, value counts, string search, column casting — all in one WASM binary. Data lives in WASM memory; JS holds handles.
7
77
 
8
78
  ```sh
9
- npm install --save-exact @nteract/sift@nightly
79
+ cargo xtask wasm sift --skip-renderer-plugins
10
80
  ```
11
81
 
12
- Stable versions publish to `latest` from the nteract release workflow. See
13
- https://github.com/nteract/nteract/tree/main/packages/sift.
82
+ ## Stack
83
+
84
+ | Layer | What |
85
+ |-------|------|
86
+ | Layout | [@chenglou/pretext](https://github.com/chenglou/pretext) — DOM-free text measurement |
87
+ | Data | [Apache Arrow](https://arrow.apache.org/) — columnar, streamed via RecordBatchReader |
88
+ | Compute | [sift-wasm](../../crates/sift-wasm/) — arrow-rs WASM kernels |
89
+ | Reactivity | [RxJS](https://rxjs.dev/) — streaming pipeline |
90
+ | Charts | SVG histograms + HTML bars |
91
+
92
+ ## Development
93
+
94
+ Run from the repository root after the setup above:
95
+
96
+ ```sh
97
+ pnpm --dir packages/sift test # Vitest unit tests
98
+ pnpm --dir packages/sift test:e2e # Playwright E2E
99
+ pnpm --dir packages/sift build # production build (demo)
100
+ pnpm --dir packages/sift build:lib # lib/ (JS, CSS, types, WASM)
101
+ pnpm --dir packages/sift test:package # packed consumer outside the workspace
102
+ ```
103
+
104
+ ## Demo pages
105
+
106
+ - `/` — main demo with dataset picker
107
+ - `/notebook.html` — multi-table notebook simulation
@@ -0,0 +1,216 @@
1
+ # Embedding Sift
2
+
3
+ `@nteract/sift` is a browser table viewer with compiled JavaScript, CSS, TypeScript
4
+ interfaces, and a matching WebAssembly decoder. It does not require a notebook,
5
+ kernel, daemon, Tauri, Electron, or an nteract runtime connection. React and React
6
+ DOM are required even for the imperative API because column controls use React.
7
+
8
+ ## Install and assets
9
+
10
+ ```sh
11
+ npm install @nteract/sift react react-dom
12
+ ```
13
+
14
+ ```tsx
15
+ import { SiftTable, setWasmUrl } from '@nteract/sift'
16
+ import '@nteract/sift/style.css'
17
+ // Vite example: emit the packaged binary through the host's asset pipeline.
18
+ import wasmUrl from '@nteract/sift/sift.wasm?url'
19
+
20
+ setWasmUrl(wasmUrl) // Configure once, before the first binary table loads.
21
+ ```
22
+
23
+ Other bundlers can copy `@nteract/sift/sift.wasm` into their local assets and pass
24
+ its served URL to `setWasmUrl`. Without an override, the shipped wasm-bindgen glue
25
+ resolves `sift_wasm_bg.wasm` relative to itself; preserve that layout when serving
26
+ unbundled modules. Serve the binary with `application/wasm`. Keep the JavaScript
27
+ and WASM from the same package version. No CDN or data upload is required.
28
+
29
+ The host owns file access and its existing sandbox/CSP policy. In an Electron
30
+ app, read selected files through the approved main/preload bridge and pass text
31
+ or bytes to the renderer; Sift does not need Node integration or filesystem
32
+ access. The package's URL API explicitly fetches URLs supplied by the host.
33
+
34
+ ## Local CSV and TSV
35
+
36
+ ```tsx
37
+ import { parseCsv, SiftTable } from '@nteract/sift'
38
+
39
+ const data = parseCsv(csvText)
40
+ // For TSV: parseCsv(tsvText, { delimiter: '\t' })
41
+
42
+ <SiftTable data={data} style={{ height: 600 }} />
43
+ ```
44
+
45
+ Parse once when the file snapshot changes, not on every React render. The host
46
+ owns text decoding: for UTF-8 files, use `new TextDecoder('utf-8', { fatal: true })`
47
+ so invalid byte sequences become an actionable error. A leading BOM is accepted.
48
+ There is no encoding or delimiter guessing. Delimiters are comma (default), tab,
49
+ semicolon, and pipe.
50
+
51
+ The first record supplies column names; blank and duplicate names are made
52
+ unique. Quoted delimiters, escaped quotes, and multiline fields are supported.
53
+ Malformed quoting and inconsistent record widths throw an error. Empty unquoted
54
+ fields become null; quoted empty fields stay empty strings. Literal `NA`, `null`,
55
+ and similar strings are preserved. Nulls are excluded from categorical counts
56
+ and never match the empty-string category. Selecting that category matches only
57
+ quoted empty strings; excluding it retains null rows. Category percentages use
58
+ all rows in the current selection as their denominator, including null rows.
59
+
60
+ A final line ending terminates the preceding record. Additional blank records
61
+ are preserved: in a one-column file each is a null row; in a wider file each
62
+ fails the record-width check. Automatically skipping blank records is an open
63
+ format-policy decision because it could silently remove valid null rows.
64
+
65
+ Default inference examines every loaded value in a column. Conservative numbers
66
+ and lowercase `true`/`false` become numeric/boolean columns; dates, leading-zero
67
+ identifiers, and unsafe integers stay text. Original spellings remain available
68
+ for display, but numeric comparisons use JavaScript numbers and can approximate
69
+ exact decimals. Use `{ typing: 'text' }` to keep every non-null field as text.
70
+
71
+ The default limits are 16 MiB of UTF-8 text, 100,000 data rows, 256 columns, and
72
+ 1,000,000 cells. Override `maxBytes`, `maxRows`, `maxColumns`, and `maxCells`
73
+ explicitly when the host can support larger files. Over-limit input fails; it is
74
+ never silently truncated. Reject oversized files before reading them as well.
75
+
76
+ ## Local Parquet and Arrow
77
+
78
+ ```ts
79
+ import { loadTableFile, createTable } from '@nteract/sift'
80
+
81
+ const data = await loadTableFile(bytes) // Uint8Array; detects Parquet magic.
82
+ const engine = createTable(container, data)
83
+ // When the document closes or changes:
84
+ engine.destroy() // Also disposes the data's WASM store.
85
+ ```
86
+
87
+ For React, use a memoized loader returning fresh data for every invocation.
88
+ This also supports React StrictMode's setup/cleanup replay:
89
+
90
+ ```tsx
91
+ import { useCallback } from 'react'
92
+ import { loadTableFile, SiftTable } from '@nteract/sift'
93
+
94
+ function ParquetView({ bytes }: { bytes: Uint8Array }) {
95
+ const load = useCallback(() => loadTableFile(bytes), [bytes])
96
+ return <SiftTable source={{ kind: 'loader', load }} style={{ height: 600 }} />
97
+ }
98
+ ```
99
+
100
+ Keep the bytes snapshot stable until the file changes. Do not capture and return
101
+ one previously loaded disposable table from the callback.
102
+
103
+ `loadTableFile` supports Parquet and uncompressed Arrow IPC streams. The optional
104
+ `format` is `'parquet'` or `'arrow-ipc'`. CSV/TSV should use `parseCsv`. Arrow file
105
+ containers and compressed IPC streams are not supported by this path.
106
+
107
+ Binary input defaults to 16 MiB, 100,000 rows, 256 columns, and a 128 MiB decoded
108
+ memory guardrail (`maxBytes`, `maxRows`, `maxColumns`, `maxDecodedBytes`). Parquet
109
+ checks footer metadata before decoding, summed across all row groups. Boolean and
110
+ fixed-width leaves (integers, floats, dates, times, timestamps, decimals,
111
+ fixed-size binary) are charged their Arrow width per value, plus validity, list
112
+ offsets, and per-batch array overhead, so compact wide tables such as 100,000
113
+ rows across 90 boolean columns fit the default guard. Variable-width leaves
114
+ (strings, binaries, dictionaries) keep a conservative allowance of twice the
115
+ uncompressed page bytes plus 16 bytes per value, so string-heavy files can still
116
+ be rejected below the byte, row, and column caps. The row, column,
117
+ input-byte, and decoded-byte limits are independent ceilings, not a promise that
118
+ every file below the first three will load. Retained Arrow allocations are also
119
+ checked after each decoded batch. That check counts decoder buffer capacity,
120
+ which can reach twice the data of a numeric column with nulls, so a file the
121
+ footer estimate admits can still be rejected. These are admission limits, not a
122
+ process memory sandbox: dictionaries, decoder batches, summaries, copies, and
123
+ temporary allocations add memory. Raising `maxDecodedBytes` relaxes both the
124
+ Parquet estimate and retained-allocation checks; hosts should do so only with an
125
+ appropriate memory budget.
126
+
127
+ This API materializes a complete file. Virtual scrolling bounds rendered DOM,
128
+ not the loaded dataset. Sorting, filtering, and summaries cover all accepted
129
+ rows. There is no disk paging or sampled preview. Larger files need a separate
130
+ bounded-preview or query architecture with explicit operation scope.
131
+
132
+ After filtering, binary numeric ratio bars retain the unfiltered file's two-value
133
+ baseline; nearby counts and null statistics describe the filtered rows.
134
+
135
+ Arrow/Parquet schemas and nulls are retained in WASM. Lists, structs, and map-like
136
+ values may display as text; known image structures have dedicated rendering.
137
+ Nested values do not become editable sub-tables, and a formatted nested column's
138
+ ordering/filtering is not a nested query language. Do not assume all nested types
139
+ have numeric or categorical semantics identical to primitive columns.
140
+
141
+ ## Lifecycle and host controls
142
+
143
+ A `TableData` instance returned by `loadTableFile` owns one WASM store. The React
144
+ loader source creates a fresh store for each setup and discards stale completions;
145
+ the mounted engine disposes the store on teardown. Avoid passing preloaded binary
146
+ data directly to the React `data` prop, because effect replay would reuse a freed
147
+ store. For the imperative API, pass each result to one `createTable` instance. If
148
+ an imperative load is stale or never mounted, call `data.dispose?.()`, using an
149
+ activation token to recognize results completed after switching or closing files.
150
+ Reopen/reload creates fresh data. Do not share disposable data between tables.
151
+
152
+ Sift is a read-only exploration surface. Sorting, filtering, and type casts do
153
+ not rewrite the file. Keep editor text and dirty state in the host; a viewer/editor
154
+ toggle should derive its preview from the current text buffer and preserve edits
155
+ on parse errors or mode changes. Save, Save As/export, reveal-in-folder, file
156
+ watching, dirty-file conflict handling, and loading/error messages remain host
157
+ responsibilities. Parquet can use a view-only mode.
158
+
159
+ For already hosted data, `<SiftTable url="/data.arrow" />` and
160
+ `<SiftTable url="/data.parquet" />` remain available. The URL/manifest paths have
161
+ different loading behavior and do not inherit `loadTableFile` admission limits.
162
+ A sampled Arrow manifest must include its `summary.sampled` and included/total
163
+ row metadata; its sorting, filtering, and summaries cover only the supplied rows.
164
+
165
+ ## Interaction and appearance
166
+
167
+ ```ts
168
+ engine.setSort('age', 'desc')
169
+ engine.setFilter(0, { kind: 'range', min: 18, max: 65 })
170
+ engine.clearAllFilters()
171
+ const state = engine.getState()
172
+ ```
173
+
174
+ The container needs a defined height. The table supplies keyboard navigation and
175
+ ARIA grid semantics; give the surrounding file view an accessible title, expose
176
+ loading/errors to assistive technology, and keep keyboard focus predictable when
177
+ switching modes. Test the host's narrow view and keyboard paths.
178
+
179
+ `style.css` is compiled: consumers do not need Tailwind or nteract source files.
180
+ It includes shared base styles and design-token defaults, so load it before host
181
+ theme overrides or use an existing isolated document boundary. Set
182
+ `data-theme="dark"`/`"light"` explicitly on the root; OS theme preference is not
183
+ a separate automatic mode. `data-color-theme="cream"` selects the optional cream
184
+ palette. Override `--sift-bg`, `--sift-panel`, `--sift-ink`, `--sift-muted`,
185
+ `--sift-rule`, and `--sift-accent` to match the host. Popovers render in portals;
186
+ theme tokens should be available at their document root too.
187
+
188
+ ## Build and verify a local package
189
+
190
+ From the nteract repository root:
191
+
192
+ ```sh
193
+ pnpm install --frozen-lockfile
194
+ cargo xtask wasm sift --skip-renderer-plugins
195
+ pnpm --dir packages/sift test:package
196
+ # Optional: production consumer smoke (install with pnpm exec playwright install chromium).
197
+ pnpm --dir packages/sift exec node scripts/test-package.mjs --browser
198
+ pnpm --dir packages/sift pack --pack-destination /tmp/sift-package
199
+ ```
200
+
201
+ `test:package` builds the library, packs it, extracts it outside the workspace,
202
+ checks its public declarations and dependency boundary, and tests the shipped
203
+ WASM glue/binary against synthetic Arrow data. With `--browser`, it builds and
204
+ previews a production consumer in Chromium, testing both explicit and default
205
+ WASM asset URLs. It reuses installed public
206
+ dependencies without registry requests. `prepack` rejects missing built assets,
207
+ checks every current runtime method against the actual WASM and JavaScript
208
+ exports, and instantiates the pair to reject incompatible glue/binary imports.
209
+ The resulting tarball can be installed via its local path; nothing is published.
210
+
211
+ `build:lib` also writes `lib/THIRD_PARTY_NOTICES.md`. It lists the Rust crates
212
+ and C sources that `cargo tree` reports for the WASM build and the npm packages
213
+ the bundle inlines, with their license texts and NOTICE files. Ship it with any
214
+ host app that redistributes Sift. A dependency under a license the generator
215
+ doesn't recognize, without its license text, or with unreviewed native sources
216
+ fails the build until the generator learns it.
package/lib/LICENSE ADDED
@@ -0,0 +1,28 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2024, runtimed
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.