@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.
Files changed (91) hide show
  1. package/LICENSE.md +193 -0
  2. package/README.md +3 -0
  3. package/dist/cdn/perspective-server.worker.js +2 -0
  4. package/dist/cdn/perspective-server.worker.js.map +7 -0
  5. package/dist/cdn/perspective.js +3 -0
  6. package/dist/cdn/perspective.js.map +7 -0
  7. package/dist/esm/perspective-server.worker.d.ts +1 -0
  8. package/dist/esm/perspective.browser.d.ts +14 -0
  9. package/dist/esm/perspective.inline.js +3 -0
  10. package/dist/esm/perspective.inline.js.map +7 -0
  11. package/dist/esm/perspective.js +3 -0
  12. package/dist/esm/perspective.js.map +7 -0
  13. package/dist/esm/perspective.node.d.ts +60 -0
  14. package/dist/esm/perspective.node.js +2431 -0
  15. package/dist/esm/perspective.node.js.map +7 -0
  16. package/dist/esm/ts-rs/Aggregate.d.ts +1 -0
  17. package/dist/esm/ts-rs/ColumnWindow.d.ts +4 -0
  18. package/dist/esm/ts-rs/DeleteOptions.d.ts +6 -0
  19. package/dist/esm/ts-rs/Expressions.d.ts +3 -0
  20. package/dist/esm/ts-rs/Filter.d.ts +2 -0
  21. package/dist/esm/ts-rs/FilterReducer.d.ts +1 -0
  22. package/dist/esm/ts-rs/FilterTerm.d.ts +2 -0
  23. package/dist/esm/ts-rs/OnUpdateMode.d.ts +9 -0
  24. package/dist/esm/ts-rs/OnUpdateOptions.d.ts +7 -0
  25. package/dist/esm/ts-rs/Scalar.d.ts +5 -0
  26. package/dist/esm/ts-rs/Sort.d.ts +2 -0
  27. package/dist/esm/ts-rs/SortDir.d.ts +1 -0
  28. package/dist/esm/ts-rs/SystemInfo.d.ts +40 -0
  29. package/dist/esm/ts-rs/TableInitOptions.d.ts +22 -0
  30. package/dist/esm/ts-rs/TableReadFormat.d.ts +7 -0
  31. package/dist/esm/ts-rs/UpdateOptions.d.ts +8 -0
  32. package/dist/esm/ts-rs/ViewConfigUpdate.d.ts +90 -0
  33. package/dist/esm/ts-rs/ViewOnUpdateResp.d.ts +4 -0
  34. package/dist/esm/ts-rs/ViewWindow.d.ts +23 -0
  35. package/dist/esm/wasm/browser.d.ts +21 -0
  36. package/dist/esm/wasm/decompress.d.ts +1 -0
  37. package/dist/esm/wasm/emscripten_api.d.ts +5 -0
  38. package/dist/esm/wasm/engine.d.ts +40 -0
  39. package/dist/esm/wasm/perspective-server.poly.d.ts +1 -0
  40. package/dist/esm/websocket.d.ts +4 -0
  41. package/dist/wasm/perspective-js.d.ts +712 -0
  42. package/dist/wasm/perspective-js.js +1934 -0
  43. package/dist/wasm/perspective-js.wasm +0 -0
  44. package/dist/wasm/perspective-js.wasm.d.ts +75 -0
  45. package/package.json +68 -0
  46. package/src/rust/client.rs +483 -0
  47. package/src/rust/lib.rs +70 -0
  48. package/src/rust/table.rs +364 -0
  49. package/src/rust/table_data.rs +159 -0
  50. package/src/rust/utils/browser.rs +39 -0
  51. package/src/rust/utils/console_logger.rs +236 -0
  52. package/src/rust/utils/errors.rs +288 -0
  53. package/src/rust/utils/futures.rs +174 -0
  54. package/src/rust/utils/json.rs +252 -0
  55. package/src/rust/utils/local_poll_loop.rs +63 -0
  56. package/src/rust/utils/mod.rs +32 -0
  57. package/src/rust/utils/serde.rs +46 -0
  58. package/src/rust/utils/trace_allocator.rs +98 -0
  59. package/src/rust/view.rs +355 -0
  60. package/src/ts/perspective-server.worker.ts +54 -0
  61. package/src/ts/perspective.browser.ts +132 -0
  62. package/src/ts/perspective.cdn.ts +22 -0
  63. package/src/ts/perspective.inline.ts +27 -0
  64. package/src/ts/perspective.node.ts +315 -0
  65. package/src/ts/ts-rs/Aggregate.ts +3 -0
  66. package/src/ts/ts-rs/ColumnWindow.ts +3 -0
  67. package/src/ts/ts-rs/DeleteOptions.ts +6 -0
  68. package/src/ts/ts-rs/Expressions.ts +3 -0
  69. package/src/ts/ts-rs/Filter.ts +4 -0
  70. package/src/ts/ts-rs/FilterReducer.ts +3 -0
  71. package/src/ts/ts-rs/FilterTerm.ts +4 -0
  72. package/src/ts/ts-rs/OnUpdateData.ts +8 -0
  73. package/src/ts/ts-rs/OnUpdateMode.ts +11 -0
  74. package/src/ts/ts-rs/OnUpdateOptions.ts +7 -0
  75. package/src/ts/ts-rs/Scalar.ts +7 -0
  76. package/src/ts/ts-rs/Sort.ts +4 -0
  77. package/src/ts/ts-rs/SortDir.ts +3 -0
  78. package/src/ts/ts-rs/SystemInfo.ts +41 -0
  79. package/src/ts/ts-rs/TableInitOptions.ts +21 -0
  80. package/src/ts/ts-rs/TableReadFormat.ts +9 -0
  81. package/src/ts/ts-rs/UpdateOptions.ts +7 -0
  82. package/src/ts/ts-rs/ViewConfigUpdate.ts +87 -0
  83. package/src/ts/ts-rs/ViewOnUpdateResp.ts +3 -0
  84. package/src/ts/ts-rs/ViewWindow.ts +17 -0
  85. package/src/ts/wasm/browser.ts +123 -0
  86. package/src/ts/wasm/decompress.ts +64 -0
  87. package/src/ts/wasm/emscripten_api.ts +63 -0
  88. package/src/ts/wasm/engine.ts +271 -0
  89. package/src/ts/wasm/perspective-server.poly.ts +244 -0
  90. package/src/ts/websocket.ts +95 -0
  91. package/tsconfig.json +20 -0
@@ -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;