@perspective-dev/client 4.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.
- package/LICENSE.md +193 -0
- package/README.md +3 -0
- package/dist/cdn/perspective-server.worker.js +2 -0
- package/dist/cdn/perspective-server.worker.js.map +7 -0
- package/dist/cdn/perspective.js +3 -0
- package/dist/cdn/perspective.js.map +7 -0
- package/dist/esm/perspective-server.worker.d.ts +1 -0
- package/dist/esm/perspective.browser.d.ts +14 -0
- package/dist/esm/perspective.inline.js +3 -0
- package/dist/esm/perspective.inline.js.map +7 -0
- package/dist/esm/perspective.js +3 -0
- package/dist/esm/perspective.js.map +7 -0
- package/dist/esm/perspective.node.d.ts +60 -0
- package/dist/esm/perspective.node.js +2431 -0
- package/dist/esm/perspective.node.js.map +7 -0
- package/dist/esm/ts-rs/Aggregate.d.ts +1 -0
- package/dist/esm/ts-rs/ColumnWindow.d.ts +4 -0
- package/dist/esm/ts-rs/DeleteOptions.d.ts +6 -0
- package/dist/esm/ts-rs/Expressions.d.ts +3 -0
- package/dist/esm/ts-rs/Filter.d.ts +2 -0
- package/dist/esm/ts-rs/FilterReducer.d.ts +1 -0
- package/dist/esm/ts-rs/FilterTerm.d.ts +2 -0
- package/dist/esm/ts-rs/OnUpdateMode.d.ts +9 -0
- package/dist/esm/ts-rs/OnUpdateOptions.d.ts +7 -0
- package/dist/esm/ts-rs/Scalar.d.ts +5 -0
- package/dist/esm/ts-rs/Sort.d.ts +2 -0
- package/dist/esm/ts-rs/SortDir.d.ts +1 -0
- package/dist/esm/ts-rs/SystemInfo.d.ts +40 -0
- package/dist/esm/ts-rs/TableInitOptions.d.ts +22 -0
- package/dist/esm/ts-rs/TableReadFormat.d.ts +7 -0
- package/dist/esm/ts-rs/UpdateOptions.d.ts +8 -0
- package/dist/esm/ts-rs/ViewConfigUpdate.d.ts +90 -0
- package/dist/esm/ts-rs/ViewOnUpdateResp.d.ts +4 -0
- package/dist/esm/ts-rs/ViewWindow.d.ts +23 -0
- package/dist/esm/wasm/browser.d.ts +21 -0
- package/dist/esm/wasm/decompress.d.ts +1 -0
- package/dist/esm/wasm/emscripten_api.d.ts +5 -0
- package/dist/esm/wasm/engine.d.ts +40 -0
- package/dist/esm/wasm/perspective-server.poly.d.ts +1 -0
- package/dist/esm/websocket.d.ts +4 -0
- package/dist/wasm/perspective-js.d.ts +712 -0
- package/dist/wasm/perspective-js.js +1934 -0
- package/dist/wasm/perspective-js.wasm +0 -0
- package/dist/wasm/perspective-js.wasm.d.ts +75 -0
- package/package.json +68 -0
- package/src/rust/client.rs +483 -0
- package/src/rust/lib.rs +70 -0
- package/src/rust/table.rs +364 -0
- package/src/rust/table_data.rs +159 -0
- package/src/rust/utils/browser.rs +39 -0
- package/src/rust/utils/console_logger.rs +236 -0
- package/src/rust/utils/errors.rs +288 -0
- package/src/rust/utils/futures.rs +174 -0
- package/src/rust/utils/json.rs +252 -0
- package/src/rust/utils/local_poll_loop.rs +63 -0
- package/src/rust/utils/mod.rs +32 -0
- package/src/rust/utils/serde.rs +46 -0
- package/src/rust/utils/trace_allocator.rs +98 -0
- package/src/rust/view.rs +355 -0
- package/src/ts/perspective-server.worker.ts +54 -0
- package/src/ts/perspective.browser.ts +132 -0
- package/src/ts/perspective.cdn.ts +22 -0
- package/src/ts/perspective.inline.ts +27 -0
- package/src/ts/perspective.node.ts +315 -0
- package/src/ts/ts-rs/Aggregate.ts +3 -0
- package/src/ts/ts-rs/ColumnWindow.ts +3 -0
- package/src/ts/ts-rs/DeleteOptions.ts +6 -0
- package/src/ts/ts-rs/Expressions.ts +3 -0
- package/src/ts/ts-rs/Filter.ts +4 -0
- package/src/ts/ts-rs/FilterReducer.ts +3 -0
- package/src/ts/ts-rs/FilterTerm.ts +4 -0
- package/src/ts/ts-rs/OnUpdateData.ts +8 -0
- package/src/ts/ts-rs/OnUpdateMode.ts +11 -0
- package/src/ts/ts-rs/OnUpdateOptions.ts +7 -0
- package/src/ts/ts-rs/Scalar.ts +7 -0
- package/src/ts/ts-rs/Sort.ts +4 -0
- package/src/ts/ts-rs/SortDir.ts +3 -0
- package/src/ts/ts-rs/SystemInfo.ts +41 -0
- package/src/ts/ts-rs/TableInitOptions.ts +21 -0
- package/src/ts/ts-rs/TableReadFormat.ts +9 -0
- package/src/ts/ts-rs/UpdateOptions.ts +7 -0
- package/src/ts/ts-rs/ViewConfigUpdate.ts +87 -0
- package/src/ts/ts-rs/ViewOnUpdateResp.ts +3 -0
- package/src/ts/ts-rs/ViewWindow.ts +17 -0
- package/src/ts/wasm/browser.ts +123 -0
- package/src/ts/wasm/decompress.ts +64 -0
- package/src/ts/wasm/emscripten_api.ts +63 -0
- package/src/ts/wasm/engine.ts +271 -0
- package/src/ts/wasm/perspective-server.poly.ts +244 -0
- package/src/ts/websocket.ts +95 -0
- package/tsconfig.json +20 -0
package/src/rust/view.rs
ADDED
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
2
|
+
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
|
3
|
+
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
|
4
|
+
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
|
5
|
+
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
|
6
|
+
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
|
7
|
+
// ┃ Copyright (c) 2017, the Perspective Authors. ┃
|
|
8
|
+
// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
|
|
9
|
+
// ┃ This file is part of the Perspective library, distributed under the terms ┃
|
|
10
|
+
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
|
|
11
|
+
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
12
|
+
|
|
13
|
+
use js_sys::{Array, ArrayBuffer, Function, Object};
|
|
14
|
+
use perspective_client::{
|
|
15
|
+
ColumnWindow, OnUpdateData, OnUpdateOptions, ViewWindow, assert_view_api,
|
|
16
|
+
};
|
|
17
|
+
use wasm_bindgen::prelude::*;
|
|
18
|
+
use wasm_bindgen_futures::spawn_local;
|
|
19
|
+
|
|
20
|
+
#[cfg(doc)]
|
|
21
|
+
use crate::table::Table;
|
|
22
|
+
use crate::utils::{ApiFuture, ApiResult, JsValueSerdeExt, LocalPollLoop};
|
|
23
|
+
|
|
24
|
+
#[wasm_bindgen]
|
|
25
|
+
unsafe extern "C" {
|
|
26
|
+
#[wasm_bindgen(typescript_type = "ViewWindow")]
|
|
27
|
+
#[derive(Clone)]
|
|
28
|
+
pub type JsViewWindow;
|
|
29
|
+
|
|
30
|
+
#[wasm_bindgen(typescript_type = "ColumnWindow")]
|
|
31
|
+
#[derive(Clone)]
|
|
32
|
+
pub type JsColumnWindow;
|
|
33
|
+
|
|
34
|
+
#[wasm_bindgen(method, setter, js_name = "formatted")]
|
|
35
|
+
pub fn set_formatted(this: &JsViewWindow, x: bool);
|
|
36
|
+
|
|
37
|
+
#[wasm_bindgen(typescript_type = "OnUpdateOptions")]
|
|
38
|
+
pub type JsOnUpdateOptions;
|
|
39
|
+
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
impl From<ViewWindow> for JsViewWindow {
|
|
43
|
+
fn from(value: ViewWindow) -> Self {
|
|
44
|
+
JsViewWindow::from_serde_ext(&value)
|
|
45
|
+
.unwrap()
|
|
46
|
+
.unchecked_into()
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/// The [`View`] struct is Perspective's query and serialization interface. It
|
|
51
|
+
/// represents a query on the `Table`'s dataset and is always created from an
|
|
52
|
+
/// existing `Table` instance via the [`Table::view`] method.
|
|
53
|
+
///
|
|
54
|
+
/// [`View`]s are immutable with respect to the arguments provided to the
|
|
55
|
+
/// [`Table::view`] method; to change these parameters, you must create a new
|
|
56
|
+
/// [`View`] on the same [`Table`]. However, each [`View`] is _live_ with
|
|
57
|
+
/// respect to the [`Table`]'s data, and will (within a conflation window)
|
|
58
|
+
/// update with the latest state as its parent [`Table`] updates, including
|
|
59
|
+
/// incrementally recalculating all aggregates, pivots, filters, etc. [`View`]
|
|
60
|
+
/// query parameters are composable, in that each parameter works independently
|
|
61
|
+
/// _and_ in conjunction with each other, and there is no limit to the number of
|
|
62
|
+
/// pivots, filters, etc. which can be applied.
|
|
63
|
+
#[wasm_bindgen]
|
|
64
|
+
#[derive(Clone)]
|
|
65
|
+
pub struct View(pub(crate) perspective_client::View);
|
|
66
|
+
|
|
67
|
+
assert_view_api!(View);
|
|
68
|
+
|
|
69
|
+
impl From<perspective_client::View> for View {
|
|
70
|
+
fn from(value: perspective_client::View) -> Self {
|
|
71
|
+
View(value)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
#[wasm_bindgen]
|
|
76
|
+
impl View {
|
|
77
|
+
#[doc(hidden)]
|
|
78
|
+
pub fn __get_model(&self) -> View {
|
|
79
|
+
self.clone()
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/// Returns an array of strings containing the column paths of the [`View`]
|
|
83
|
+
/// without any of the source columns.
|
|
84
|
+
///
|
|
85
|
+
/// A column path shows the columns that a given cell belongs to after
|
|
86
|
+
/// pivots are applied.
|
|
87
|
+
#[wasm_bindgen]
|
|
88
|
+
pub async fn column_paths(&self, window: Option<JsColumnWindow>) -> ApiResult<JsValue> {
|
|
89
|
+
let window = window.into_serde_ext::<Option<ColumnWindow>>()?;
|
|
90
|
+
let columns = self.0.column_paths(window.unwrap_or_default()).await?;
|
|
91
|
+
Ok(JsValue::from_serde_ext(&columns)?)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/// Delete this [`View`] and clean up all resources associated with it.
|
|
95
|
+
/// [`View`] objects do not stop consuming resources or processing
|
|
96
|
+
/// updates when they are garbage collected - you must call this method
|
|
97
|
+
/// to reclaim these.
|
|
98
|
+
#[wasm_bindgen]
|
|
99
|
+
pub async fn delete(self) -> ApiResult<()> {
|
|
100
|
+
self.0.delete().await?;
|
|
101
|
+
Ok(())
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/// Returns this [`View`]'s _dimensions_, row and column count, as well as
|
|
105
|
+
/// those of the [`crate::Table`] from which it was derived.
|
|
106
|
+
///
|
|
107
|
+
/// - `num_table_rows` - The number of rows in the underlying
|
|
108
|
+
/// [`crate::Table`].
|
|
109
|
+
/// - `num_table_columns` - The number of columns in the underlying
|
|
110
|
+
/// [`crate::Table`] (including the `index` column if this
|
|
111
|
+
/// [`crate::Table`] was constructed with one).
|
|
112
|
+
/// - `num_view_rows` - The number of rows in this [`View`]. If this
|
|
113
|
+
/// [`View`] has a `group_by` clause, `num_view_rows` will also include
|
|
114
|
+
/// aggregated rows.
|
|
115
|
+
/// - `num_view_columns` - The number of columns in this [`View`]. If this
|
|
116
|
+
/// [`View`] has a `split_by` clause, `num_view_columns` will include all
|
|
117
|
+
/// _column paths_, e.g. the number of `columns` clause times the number
|
|
118
|
+
/// of `split_by` groups.
|
|
119
|
+
#[wasm_bindgen]
|
|
120
|
+
pub async fn dimensions(&self) -> ApiResult<JsValue> {
|
|
121
|
+
let dimensions = self.0.dimensions().await?;
|
|
122
|
+
Ok(JsValue::from_serde_ext(&dimensions)?)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/// The expression schema of this [`View`], which contains only the
|
|
126
|
+
/// expressions created on this [`View`]. See [`View::schema`] for
|
|
127
|
+
/// details.
|
|
128
|
+
#[wasm_bindgen]
|
|
129
|
+
pub async fn expression_schema(&self) -> ApiResult<JsValue> {
|
|
130
|
+
let schema = self.0.expression_schema().await?;
|
|
131
|
+
Ok(JsValue::from_serde_ext(&schema)?)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/// A copy of the config object passed to the [`Table::view`] method which
|
|
135
|
+
/// created this [`View`].
|
|
136
|
+
#[wasm_bindgen]
|
|
137
|
+
pub async fn get_config(&self) -> ApiResult<JsValue> {
|
|
138
|
+
let config = self.0.get_config().await?;
|
|
139
|
+
Ok(JsValue::from_serde_ext(&config)?)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/// Calculates the [min, max] of the leaf nodes of a column `column_name`.
|
|
143
|
+
///
|
|
144
|
+
/// # Returns
|
|
145
|
+
///
|
|
146
|
+
/// A tuple of [min, max], whose types are column and aggregate dependent.
|
|
147
|
+
#[wasm_bindgen]
|
|
148
|
+
pub async fn get_min_max(&self, name: String) -> ApiResult<Array> {
|
|
149
|
+
let result = self.0.get_min_max(name).await?;
|
|
150
|
+
Ok([result.0, result.1]
|
|
151
|
+
.iter()
|
|
152
|
+
.map(|x| js_sys::JSON::parse(x))
|
|
153
|
+
.collect::<Result<_, _>>()?)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/// The number of aggregated rows in this [`View`]. This is affected by the
|
|
157
|
+
/// "group_by" configuration parameter supplied to this view's contructor.
|
|
158
|
+
///
|
|
159
|
+
/// # Returns
|
|
160
|
+
///
|
|
161
|
+
/// The number of aggregated rows.
|
|
162
|
+
#[wasm_bindgen]
|
|
163
|
+
pub async fn num_rows(&self) -> ApiResult<i32> {
|
|
164
|
+
let size = self.0.num_rows().await?;
|
|
165
|
+
Ok(size as i32)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/// The schema of this [`View`].
|
|
169
|
+
///
|
|
170
|
+
/// The [`View`] schema differs from the `schema` returned by
|
|
171
|
+
/// [`Table::schema`]; it may have different column names due to
|
|
172
|
+
/// `expressions` or `columns` configs, or it maye have _different
|
|
173
|
+
/// column types_ due to the application og `group_by` and `aggregates`
|
|
174
|
+
/// config. You can think of [`Table::schema`] as the _input_ schema and
|
|
175
|
+
/// [`View::schema`] as the _output_ schema of a Perspective pipeline.
|
|
176
|
+
#[wasm_bindgen]
|
|
177
|
+
pub async fn schema(&self) -> ApiResult<JsValue> {
|
|
178
|
+
let schema = self.0.schema().await?;
|
|
179
|
+
Ok(JsValue::from_serde_ext(&schema)?)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/// Serializes a [`View`] to the Apache Arrow data format.
|
|
183
|
+
#[wasm_bindgen]
|
|
184
|
+
pub async fn to_arrow(&self, window: Option<JsViewWindow>) -> ApiResult<ArrayBuffer> {
|
|
185
|
+
let window = window.into_serde_ext::<Option<ViewWindow>>()?;
|
|
186
|
+
let arrow = self.0.to_arrow(window.unwrap_or_default()).await?;
|
|
187
|
+
Ok(js_sys::Uint8Array::from(&arrow[..])
|
|
188
|
+
.buffer()
|
|
189
|
+
.unchecked_into())
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/// Serializes this [`View`] to a string of JSON data. Useful if you want to
|
|
193
|
+
/// save additional round trip serialize/deserialize cycles.
|
|
194
|
+
#[wasm_bindgen]
|
|
195
|
+
pub async fn to_columns_string(&self, window: Option<JsViewWindow>) -> ApiResult<String> {
|
|
196
|
+
let window = window.into_serde_ext::<Option<ViewWindow>>()?;
|
|
197
|
+
let json = self.0.to_columns_string(window.unwrap_or_default()).await?;
|
|
198
|
+
Ok(json)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/// Serializes this [`View`] to JavaScript objects in a column-oriented
|
|
202
|
+
/// format.
|
|
203
|
+
#[wasm_bindgen]
|
|
204
|
+
pub async fn to_columns(&self, window: Option<JsViewWindow>) -> ApiResult<Object> {
|
|
205
|
+
let json = self.to_columns_string(window).await?;
|
|
206
|
+
Ok(js_sys::JSON::parse(&json)?.unchecked_into())
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/// Render this `View` as a JSON string.
|
|
210
|
+
#[wasm_bindgen]
|
|
211
|
+
pub async fn to_json_string(&self, window: Option<JsViewWindow>) -> ApiResult<String> {
|
|
212
|
+
let window = window.into_serde_ext::<Option<ViewWindow>>()?;
|
|
213
|
+
let json = self.0.to_json_string(window.unwrap_or_default()).await?;
|
|
214
|
+
Ok(json)
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/// Serializes this [`View`] to JavaScript objects in a row-oriented
|
|
218
|
+
/// format.
|
|
219
|
+
#[wasm_bindgen]
|
|
220
|
+
pub async fn to_json(&self, window: Option<JsViewWindow>) -> ApiResult<Array> {
|
|
221
|
+
let json = self.to_json_string(window).await?;
|
|
222
|
+
Ok(js_sys::JSON::parse(&json)?.unchecked_into())
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/// Renders this [`View`] as an [NDJSON](https://github.com/ndjson/ndjson-spec)
|
|
226
|
+
/// formatted [`String`].
|
|
227
|
+
#[wasm_bindgen]
|
|
228
|
+
pub async fn to_ndjson(&self, window: Option<JsViewWindow>) -> ApiResult<String> {
|
|
229
|
+
let window = window.into_serde_ext::<Option<ViewWindow>>()?;
|
|
230
|
+
let ndjson = self.0.to_ndjson(window.unwrap_or_default()).await?;
|
|
231
|
+
Ok(ndjson)
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/// Serializes this [`View`] to CSV data in a standard format.
|
|
235
|
+
#[wasm_bindgen]
|
|
236
|
+
pub async fn to_csv(&self, window: Option<JsViewWindow>) -> ApiResult<String> {
|
|
237
|
+
let window = window.into_serde_ext::<Option<ViewWindow>>()?;
|
|
238
|
+
Ok(self.0.to_csv(window.unwrap_or_default()).await?)
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/// Register a callback with this [`View`]. Whenever the view's underlying
|
|
242
|
+
/// table emits an update, this callback will be invoked with an object
|
|
243
|
+
/// containing `port_id`, indicating which port the update fired on, and
|
|
244
|
+
/// optionally `delta`, which is the new data that was updated for each
|
|
245
|
+
/// cell or each row.
|
|
246
|
+
///
|
|
247
|
+
/// # Arguments
|
|
248
|
+
///
|
|
249
|
+
/// - `on_update` - A callback function invoked on update, which receives an
|
|
250
|
+
/// object with two keys: `port_id`, indicating which port the update was
|
|
251
|
+
/// triggered on, and `delta`, whose value is dependent on the mode
|
|
252
|
+
/// parameter.
|
|
253
|
+
/// - `options` - If this is provided as `OnUpdateOptions { mode:
|
|
254
|
+
/// Some(OnUpdateMode::Row) }`, then `delta` is an Arrow of the updated
|
|
255
|
+
/// rows. Otherwise `delta` will be [`Option::None`].
|
|
256
|
+
///
|
|
257
|
+
/// # JavaScript Examples
|
|
258
|
+
///
|
|
259
|
+
/// ```javascript
|
|
260
|
+
/// // Attach an `on_update` callback
|
|
261
|
+
/// view.on_update((updated) => console.log(updated.port_id));
|
|
262
|
+
/// ```
|
|
263
|
+
///
|
|
264
|
+
/// ```javascript
|
|
265
|
+
/// // `on_update` with row deltas
|
|
266
|
+
/// view.on_update((updated) => console.log(updated.delta), { mode: "row" });
|
|
267
|
+
/// ```
|
|
268
|
+
#[wasm_bindgen]
|
|
269
|
+
pub fn on_update(
|
|
270
|
+
&self,
|
|
271
|
+
on_update_js: Function,
|
|
272
|
+
options: Option<JsOnUpdateOptions>,
|
|
273
|
+
) -> ApiFuture<u32> {
|
|
274
|
+
let poll_loop = LocalPollLoop::new(move |args: OnUpdateData| {
|
|
275
|
+
let js_obj = JsValue::from_serde_ext(&*args)?;
|
|
276
|
+
on_update_js.call1(&JsValue::UNDEFINED, &js_obj)
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
let on_update = Box::new(move |msg| poll_loop.poll(msg));
|
|
280
|
+
let view = self.0.clone();
|
|
281
|
+
ApiFuture::new(async move {
|
|
282
|
+
let on_update_opts = options
|
|
283
|
+
.into_serde_ext::<Option<OnUpdateOptions>>()?
|
|
284
|
+
.unwrap_or_default();
|
|
285
|
+
|
|
286
|
+
let id = view.on_update(on_update, on_update_opts).await?;
|
|
287
|
+
Ok(id)
|
|
288
|
+
})
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/// Unregister a previously registered update callback with this [`View`].
|
|
292
|
+
///
|
|
293
|
+
/// # Arguments
|
|
294
|
+
///
|
|
295
|
+
/// - `id` - A callback `id` as returned by a recipricol call to
|
|
296
|
+
/// [`View::on_update`].
|
|
297
|
+
#[wasm_bindgen]
|
|
298
|
+
pub async fn remove_update(&self, callback_id: u32) -> ApiResult<()> {
|
|
299
|
+
Ok(self.0.remove_update(callback_id).await?)
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/// Register a callback with this [`View`]. Whenever the [`View`] is
|
|
303
|
+
/// deleted, this callback will be invoked.
|
|
304
|
+
#[wasm_bindgen]
|
|
305
|
+
pub fn on_delete(&self, on_delete: Function) -> ApiFuture<u32> {
|
|
306
|
+
let view = self.clone();
|
|
307
|
+
ApiFuture::new(async move {
|
|
308
|
+
let emit = LocalPollLoop::new(move |()| on_delete.call0(&JsValue::UNDEFINED));
|
|
309
|
+
let on_delete = Box::new(move || spawn_local(emit.poll(())));
|
|
310
|
+
Ok(view.0.on_delete(on_delete).await?)
|
|
311
|
+
})
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/// The number of aggregated columns in this [`View`]. This is affected by
|
|
315
|
+
/// the "split_by" configuration parameter supplied to this view's
|
|
316
|
+
/// contructor.
|
|
317
|
+
///
|
|
318
|
+
/// # Returns
|
|
319
|
+
///
|
|
320
|
+
/// The number of aggregated columns.
|
|
321
|
+
#[wasm_bindgen]
|
|
322
|
+
pub async fn num_columns(&self) -> ApiResult<u32> {
|
|
323
|
+
// TODO: This is broken because of how split by creates a
|
|
324
|
+
// cartesian product of columns * unique values.
|
|
325
|
+
Ok(self.0.dimensions().await?.num_view_columns)
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/// Unregister a previously registered [`View::on_delete`] callback.
|
|
329
|
+
#[wasm_bindgen]
|
|
330
|
+
pub fn remove_delete(&self, callback_id: u32) -> ApiFuture<()> {
|
|
331
|
+
let client = self.0.clone();
|
|
332
|
+
ApiFuture::new(async move {
|
|
333
|
+
client.remove_delete(callback_id).await?;
|
|
334
|
+
Ok(())
|
|
335
|
+
})
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/// Collapses the `group_by` row at `row_index`.
|
|
339
|
+
#[wasm_bindgen]
|
|
340
|
+
pub async fn collapse(&self, row_index: u32) -> ApiResult<u32> {
|
|
341
|
+
Ok(self.0.collapse(row_index).await?)
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/// Expand the `group_by` row at `row_index`.
|
|
345
|
+
#[wasm_bindgen]
|
|
346
|
+
pub async fn expand(&self, row_index: u32) -> ApiResult<u32> {
|
|
347
|
+
Ok(self.0.expand(row_index).await?)
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/// Set expansion `depth` of the `group_by` tree.
|
|
351
|
+
#[wasm_bindgen]
|
|
352
|
+
pub async fn set_depth(&self, depth: u32) -> ApiResult<()> {
|
|
353
|
+
Ok(self.0.set_depth(depth).await?)
|
|
354
|
+
}
|
|
355
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
2
|
+
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
|
3
|
+
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
|
4
|
+
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
|
5
|
+
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
|
6
|
+
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
|
7
|
+
// ┃ Copyright (c) 2017, the Perspective Authors. ┃
|
|
8
|
+
// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
|
|
9
|
+
// ┃ This file is part of the Perspective library, distributed under the terms ┃
|
|
10
|
+
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
|
|
11
|
+
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
PerspectiveSession,
|
|
15
|
+
PerspectiveServer,
|
|
16
|
+
PerspectivePollThread,
|
|
17
|
+
} from "./wasm/engine.ts";
|
|
18
|
+
import { compile_perspective } from "./wasm/emscripten_api.ts";
|
|
19
|
+
|
|
20
|
+
let GLOBAL_SERVER: PerspectiveServer;
|
|
21
|
+
let POLL_THREAD: PerspectivePollThread;
|
|
22
|
+
|
|
23
|
+
function bindPort(e: MessageEvent) {
|
|
24
|
+
const port = e.ports[0];
|
|
25
|
+
let session: PerspectiveSession;
|
|
26
|
+
port.addEventListener("message", async (msg) => {
|
|
27
|
+
if (msg.data.cmd === "init") {
|
|
28
|
+
const id = msg.data.id;
|
|
29
|
+
if (!GLOBAL_SERVER) {
|
|
30
|
+
const module = await compile_perspective(msg.data.args[0]);
|
|
31
|
+
GLOBAL_SERVER = new PerspectiveServer(module, {
|
|
32
|
+
on_poll_request: () => POLL_THREAD.on_poll_request(),
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
POLL_THREAD = new PerspectivePollThread(GLOBAL_SERVER);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
session = GLOBAL_SERVER.make_session(async (resp) => {
|
|
39
|
+
const f = resp.slice().buffer;
|
|
40
|
+
port.postMessage(f, { transfer: [f] });
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
port.postMessage({ id });
|
|
44
|
+
} else {
|
|
45
|
+
await session.handle_request(new Uint8Array(msg.data));
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
port.start();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// @ts-expect-error wrong scope
|
|
53
|
+
self.addEventListener("connect", bindPort);
|
|
54
|
+
self.addEventListener("message", bindPort);
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
2
|
+
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
|
3
|
+
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
|
4
|
+
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
|
5
|
+
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
|
6
|
+
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
|
7
|
+
// ┃ Copyright (c) 2017, the Perspective Authors. ┃
|
|
8
|
+
// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
|
|
9
|
+
// ┃ This file is part of the Perspective library, distributed under the terms ┃
|
|
10
|
+
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
|
|
11
|
+
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
12
|
+
|
|
13
|
+
export type * from "../../dist/wasm/perspective-js.d.ts";
|
|
14
|
+
import type * as psp from "../../dist/wasm/perspective-js.d.ts";
|
|
15
|
+
|
|
16
|
+
import * as wasm_module from "../../dist/wasm/perspective-js.js";
|
|
17
|
+
import * as api from "./wasm/browser.ts";
|
|
18
|
+
import { load_wasm_stage_0 } from "./wasm/decompress.ts";
|
|
19
|
+
|
|
20
|
+
let GLOBAL_SERVER_WASM: Promise<ArrayBuffer | WebAssembly.Module>;
|
|
21
|
+
|
|
22
|
+
export function init_server(
|
|
23
|
+
wasm:
|
|
24
|
+
| Uint8Array
|
|
25
|
+
| Promise<ArrayBuffer | Response | WebAssembly.Module>
|
|
26
|
+
| ArrayBuffer
|
|
27
|
+
| Response
|
|
28
|
+
| WebAssembly.Module,
|
|
29
|
+
disable_stage_0: boolean = false,
|
|
30
|
+
) {
|
|
31
|
+
if (wasm instanceof Uint8Array) {
|
|
32
|
+
GLOBAL_SERVER_WASM = Promise.resolve(wasm.buffer);
|
|
33
|
+
} else if (wasm instanceof Response) {
|
|
34
|
+
GLOBAL_SERVER_WASM = Promise.resolve(wasm);
|
|
35
|
+
} else if (wasm instanceof Promise) {
|
|
36
|
+
GLOBAL_SERVER_WASM = wasm;
|
|
37
|
+
} else {
|
|
38
|
+
GLOBAL_SERVER_WASM = Promise.resolve(wasm);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (!disable_stage_0) {
|
|
42
|
+
GLOBAL_SERVER_WASM = GLOBAL_SERVER_WASM.then((x) =>
|
|
43
|
+
load_wasm_stage_0(x).then((x) => x.buffer as ArrayBuffer),
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
let GLOBAL_CLIENT_WASM: Promise<typeof psp>;
|
|
49
|
+
|
|
50
|
+
async function compilerize(
|
|
51
|
+
wasm: PerspectiveWasm,
|
|
52
|
+
disable_stage_0: boolean = false,
|
|
53
|
+
) {
|
|
54
|
+
const wasm_buff = disable_stage_0 ? wasm : await load_wasm_stage_0(wasm);
|
|
55
|
+
await wasm_module.default({ module_or_path: wasm_buff });
|
|
56
|
+
await wasm_module.init();
|
|
57
|
+
return wasm_module;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export type PerspectiveWasm =
|
|
61
|
+
| ArrayBuffer
|
|
62
|
+
| Response
|
|
63
|
+
| typeof psp
|
|
64
|
+
| Promise<ArrayBuffer | Response | Object>;
|
|
65
|
+
|
|
66
|
+
export function init_client(wasm: PerspectiveWasm, disable_stage_0 = false) {
|
|
67
|
+
if (wasm instanceof Uint8Array) {
|
|
68
|
+
GLOBAL_CLIENT_WASM = compilerize(
|
|
69
|
+
wasm.buffer as ArrayBuffer,
|
|
70
|
+
disable_stage_0,
|
|
71
|
+
);
|
|
72
|
+
} else if (wasm instanceof ArrayBuffer) {
|
|
73
|
+
GLOBAL_CLIENT_WASM = compilerize(wasm, disable_stage_0);
|
|
74
|
+
} else if (wasm instanceof Response) {
|
|
75
|
+
GLOBAL_CLIENT_WASM = compilerize(wasm, disable_stage_0);
|
|
76
|
+
} else if (wasm instanceof Promise) {
|
|
77
|
+
GLOBAL_CLIENT_WASM = compilerize(wasm, disable_stage_0);
|
|
78
|
+
} else if (wasm instanceof Object) {
|
|
79
|
+
GLOBAL_CLIENT_WASM = Promise.resolve(wasm as typeof psp);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function get_client() {
|
|
84
|
+
const viewer_class: any = customElements.get("perspective-viewer");
|
|
85
|
+
if (viewer_class) {
|
|
86
|
+
GLOBAL_CLIENT_WASM = Promise.resolve(viewer_class.__wasm_module__);
|
|
87
|
+
} else if (GLOBAL_CLIENT_WASM === undefined) {
|
|
88
|
+
throw new Error("Missing perspective-client.wasm");
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return GLOBAL_CLIENT_WASM;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function get_server() {
|
|
95
|
+
if (GLOBAL_SERVER_WASM === undefined) {
|
|
96
|
+
throw new Error("Missing perspective-server.wasm");
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return GLOBAL_SERVER_WASM.then((x) =>
|
|
100
|
+
x instanceof WebAssembly.Module ? x : x.slice(0),
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
let GLOBAL_WORKER: undefined | (() => Promise<Worker>) = undefined;
|
|
105
|
+
|
|
106
|
+
// Inline the worker for now. This code will eventually allow outlining this resource
|
|
107
|
+
// @ts-ignore
|
|
108
|
+
import perspective_wasm_worker from "../../src/ts/perspective-server.worker.js";
|
|
109
|
+
|
|
110
|
+
function get_worker(): Promise<Worker> {
|
|
111
|
+
if (GLOBAL_WORKER === undefined) {
|
|
112
|
+
return perspective_wasm_worker();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return GLOBAL_WORKER();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export async function websocket(url: string | URL) {
|
|
119
|
+
return await api.websocket(get_client(), url);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export async function worker(
|
|
123
|
+
worker?: Promise<SharedWorker | ServiceWorker | Worker>,
|
|
124
|
+
) {
|
|
125
|
+
if (typeof worker === "undefined") {
|
|
126
|
+
worker = get_worker();
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return await api.worker(get_client(), get_server(), worker);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export default { websocket, worker, init_client, init_server };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
2
|
+
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
|
3
|
+
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
|
4
|
+
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
|
5
|
+
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
|
6
|
+
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
|
7
|
+
// ┃ Copyright (c) 2017, the Perspective Authors. ┃
|
|
8
|
+
// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
|
|
9
|
+
// ┃ This file is part of the Perspective library, distributed under the terms ┃
|
|
10
|
+
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
|
|
11
|
+
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
12
|
+
|
|
13
|
+
import perspective from "./perspective.browser.ts";
|
|
14
|
+
export * from "./perspective.browser.ts";
|
|
15
|
+
|
|
16
|
+
const url = new URL(
|
|
17
|
+
"../../../server/dist/wasm/perspective-server.wasm",
|
|
18
|
+
import.meta.url,
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
perspective.init_server(fetch(url));
|
|
22
|
+
export default perspective;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
2
|
+
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
|
3
|
+
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
|
4
|
+
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
|
5
|
+
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
|
6
|
+
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
|
7
|
+
// ┃ Copyright (c) 2017, the Perspective Authors. ┃
|
|
8
|
+
// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
|
|
9
|
+
// ┃ This file is part of the Perspective library, distributed under the terms ┃
|
|
10
|
+
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
|
|
11
|
+
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
12
|
+
|
|
13
|
+
import perspective, { PerspectiveWasm } from "./perspective.browser.ts";
|
|
14
|
+
export * from "./perspective.browser.ts";
|
|
15
|
+
|
|
16
|
+
// @ts-ignore;
|
|
17
|
+
import server_wasm from "@perspective-dev/server/dist/wasm/perspective-server.wasm";
|
|
18
|
+
|
|
19
|
+
// @ts-ignore;
|
|
20
|
+
import client_wasm from "../../dist/wasm/perspective-js.wasm";
|
|
21
|
+
|
|
22
|
+
await perspective.init_server(server_wasm);
|
|
23
|
+
await perspective.init_client(client_wasm as any as PerspectiveWasm);
|
|
24
|
+
|
|
25
|
+
console.warn("Perspective wasn been initialized in inline mode");
|
|
26
|
+
|
|
27
|
+
export default perspective;
|