@perspective-dev/client 4.0.0 → 4.1.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/dist/cdn/perspective-server.worker.js +1 -1
- package/dist/cdn/perspective-server.worker.js.map +3 -3
- package/dist/cdn/perspective.js +2 -2
- package/dist/cdn/perspective.js.map +4 -4
- package/dist/esm/perspective.browser.d.ts +5 -1
- package/dist/esm/perspective.inline.js +2 -2
- package/dist/esm/perspective.inline.js.map +4 -4
- package/dist/esm/perspective.js +2 -2
- package/dist/esm/perspective.js.map +4 -4
- package/dist/esm/perspective.node.d.ts +6 -1
- package/dist/esm/perspective.node.js +722 -353
- package/dist/esm/perspective.node.js.map +4 -4
- package/dist/esm/ts-rs/ColumnType.d.ts +4 -0
- package/dist/esm/ts-rs/ViewConfig.d.ts +18 -0
- package/dist/esm/ts-rs/ViewWindow.d.ts +9 -9
- package/dist/esm/virtual_server.d.ts +47 -0
- package/dist/esm/virtual_servers/duckdb.d.ts +39 -0
- package/dist/esm/virtual_servers/duckdb.js +12 -0
- package/dist/esm/virtual_servers/duckdb.js.map +7 -0
- package/dist/esm/wasm/browser.d.ts +1 -1
- package/dist/wasm/perspective-js.d.ts +52 -13
- package/dist/wasm/perspective-js.js +680 -399
- package/dist/wasm/perspective-js.wasm +0 -0
- package/dist/wasm/perspective-js.wasm.d.ts +20 -8
- package/package.json +4 -1
- package/src/rust/client.rs +14 -5
- package/src/rust/lib.rs +11 -1
- package/src/rust/table.rs +3 -2
- package/src/rust/table_data.rs +19 -14
- package/src/rust/utils/browser.rs +0 -4
- package/src/rust/utils/console_logger.rs +3 -2
- package/src/rust/utils/errors.rs +10 -28
- package/src/rust/utils/futures.rs +3 -3
- package/src/rust/utils/json.rs +4 -4
- package/src/rust/virtual_server.rs +746 -0
- package/src/ts/perspective-server.worker.ts +33 -23
- package/src/ts/perspective.browser.ts +17 -2
- package/src/ts/perspective.node.ts +46 -11
- package/src/ts/ts-rs/ColumnType.ts +6 -0
- package/src/ts/ts-rs/ViewConfig.ts +8 -0
- package/src/ts/ts-rs/ViewWindow.ts +3 -3
- package/src/ts/virtual_server.ts +126 -0
- package/src/ts/virtual_servers/duckdb.ts +511 -0
- package/src/ts/wasm/browser.ts +17 -9
- package/tsconfig.json +1 -0
|
@@ -20,35 +20,45 @@ import { compile_perspective } from "./wasm/emscripten_api.ts";
|
|
|
20
20
|
let GLOBAL_SERVER: PerspectiveServer;
|
|
21
21
|
let POLL_THREAD: PerspectivePollThread;
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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] });
|
|
23
|
+
let SESSION: PerspectiveSession | undefined;
|
|
24
|
+
|
|
25
|
+
async function handleMessage(this: MessagePort, msg: MessageEvent) {
|
|
26
|
+
if (msg.data.cmd === "init") {
|
|
27
|
+
const id = msg.data.id;
|
|
28
|
+
if (!GLOBAL_SERVER) {
|
|
29
|
+
const module = await compile_perspective(msg.data.args[0]);
|
|
30
|
+
|
|
31
|
+
GLOBAL_SERVER = new PerspectiveServer(module, {
|
|
32
|
+
on_poll_request: () => POLL_THREAD.on_poll_request(),
|
|
41
33
|
});
|
|
42
34
|
|
|
43
|
-
|
|
35
|
+
POLL_THREAD = new PerspectivePollThread(GLOBAL_SERVER);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
SESSION = GLOBAL_SERVER.make_session(async (resp) => {
|
|
39
|
+
const f = resp.slice().buffer;
|
|
40
|
+
this.postMessage(f, { transfer: [f] });
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
this.postMessage({ id });
|
|
44
|
+
} else {
|
|
45
|
+
if (SESSION) {
|
|
46
|
+
await SESSION?.handle_request(new Uint8Array(msg.data));
|
|
44
47
|
} else {
|
|
45
|
-
|
|
48
|
+
throw new Error("No session");
|
|
46
49
|
}
|
|
47
|
-
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
48
52
|
|
|
53
|
+
function bindPortSharedWorker(msg: MessageEvent) {
|
|
54
|
+
const port = msg.ports[0];
|
|
55
|
+
port.addEventListener("message", handleMessage.bind(port));
|
|
49
56
|
port.start();
|
|
50
57
|
}
|
|
51
58
|
|
|
52
59
|
// @ts-expect-error wrong scope
|
|
53
|
-
self.addEventListener("connect",
|
|
54
|
-
self.addEventListener(
|
|
60
|
+
self.addEventListener("connect", bindPortSharedWorker);
|
|
61
|
+
self.addEventListener(
|
|
62
|
+
"message",
|
|
63
|
+
handleMessage.bind(self as unknown as MessagePort),
|
|
64
|
+
);
|
|
@@ -12,6 +12,9 @@
|
|
|
12
12
|
|
|
13
13
|
export type * from "../../dist/wasm/perspective-js.d.ts";
|
|
14
14
|
import type * as psp from "../../dist/wasm/perspective-js.d.ts";
|
|
15
|
+
export type * from "./virtual_server.ts";
|
|
16
|
+
|
|
17
|
+
import * as psp_virtual from "./virtual_server.ts";
|
|
15
18
|
|
|
16
19
|
import * as wasm_module from "../../dist/wasm/perspective-js.js";
|
|
17
20
|
import * as api from "./wasm/browser.ts";
|
|
@@ -19,6 +22,12 @@ import { load_wasm_stage_0 } from "./wasm/decompress.ts";
|
|
|
19
22
|
|
|
20
23
|
let GLOBAL_SERVER_WASM: Promise<ArrayBuffer | WebAssembly.Module>;
|
|
21
24
|
|
|
25
|
+
export async function createMessageHandler(
|
|
26
|
+
handler: psp_virtual.VirtualServerHandler,
|
|
27
|
+
) {
|
|
28
|
+
return psp_virtual.createMessageHandler(await get_client(), handler);
|
|
29
|
+
}
|
|
30
|
+
|
|
22
31
|
export function init_server(
|
|
23
32
|
wasm:
|
|
24
33
|
| Uint8Array
|
|
@@ -120,7 +129,7 @@ export async function websocket(url: string | URL) {
|
|
|
120
129
|
}
|
|
121
130
|
|
|
122
131
|
export async function worker(
|
|
123
|
-
worker?: Promise<SharedWorker | ServiceWorker | Worker>,
|
|
132
|
+
worker?: Promise<SharedWorker | ServiceWorker | Worker | MessagePort>,
|
|
124
133
|
) {
|
|
125
134
|
if (typeof worker === "undefined") {
|
|
126
135
|
worker = get_worker();
|
|
@@ -129,4 +138,10 @@ export async function worker(
|
|
|
129
138
|
return await api.worker(get_client(), get_server(), worker);
|
|
130
139
|
}
|
|
131
140
|
|
|
132
|
-
export default {
|
|
141
|
+
export default {
|
|
142
|
+
websocket,
|
|
143
|
+
worker,
|
|
144
|
+
init_client,
|
|
145
|
+
init_server,
|
|
146
|
+
createMessageHandler,
|
|
147
|
+
};
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
12
12
|
|
|
13
13
|
export type * from "../../dist/wasm/perspective-js.d.ts";
|
|
14
|
+
export type * from "./virtual_server.ts";
|
|
14
15
|
|
|
15
16
|
import WebSocket, { WebSocketServer as HttpWebSocketServer } from "ws";
|
|
16
17
|
import stoppable from "stoppable";
|
|
@@ -27,6 +28,9 @@ import { load_wasm_stage_0 } from "./wasm/decompress.js";
|
|
|
27
28
|
import * as engine from "./wasm/engine.ts";
|
|
28
29
|
import { compile_perspective } from "./wasm/emscripten_api.ts";
|
|
29
30
|
import * as psp_websocket from "./websocket.ts";
|
|
31
|
+
import * as api from "./wasm/browser.ts";
|
|
32
|
+
|
|
33
|
+
import * as virtual_server from "./virtual_server.ts";
|
|
30
34
|
|
|
31
35
|
const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
|
|
32
36
|
|
|
@@ -177,17 +181,6 @@ function buffer_to_arraybuffer(
|
|
|
177
181
|
}
|
|
178
182
|
}
|
|
179
183
|
|
|
180
|
-
function invert_promise<T>(): [(t: T) => void, Promise<T>, (t: any) => void] {
|
|
181
|
-
let sender: ((t: T) => void) | undefined = undefined,
|
|
182
|
-
reject = undefined;
|
|
183
|
-
let receiver: Promise<T> = new Promise((x, u) => {
|
|
184
|
-
sender = x;
|
|
185
|
-
reject = u;
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
return [sender!, receiver, reject!];
|
|
189
|
-
}
|
|
190
|
-
|
|
191
184
|
export class WebSocketServer {
|
|
192
185
|
_server: http.Server | any; // stoppable has no type ...
|
|
193
186
|
_wss: HttpWebSocketServer;
|
|
@@ -303,9 +296,51 @@ export async function websocket(
|
|
|
303
296
|
);
|
|
304
297
|
}
|
|
305
298
|
|
|
299
|
+
export async function worker(worker: Promise<MessagePort>) {
|
|
300
|
+
const port = await worker;
|
|
301
|
+
const client = new perspective_client.Client(
|
|
302
|
+
async (proto: Uint8Array) => {
|
|
303
|
+
const f = proto.slice().buffer;
|
|
304
|
+
port.postMessage(f, { transfer: [f] });
|
|
305
|
+
},
|
|
306
|
+
async () => {
|
|
307
|
+
console.debug("Closing WebWorker");
|
|
308
|
+
port.close();
|
|
309
|
+
},
|
|
310
|
+
);
|
|
311
|
+
|
|
312
|
+
const { promise, resolve, reject } = Promise.withResolvers();
|
|
313
|
+
port.onmessage = function listener(resp) {
|
|
314
|
+
port.onmessage = null;
|
|
315
|
+
resolve(null);
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
port.onmessageerror = function (...args) {
|
|
319
|
+
port.onmessage = null;
|
|
320
|
+
console.error(...args);
|
|
321
|
+
reject(args);
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
port.postMessage({ cmd: "init", args: [] });
|
|
325
|
+
await promise;
|
|
326
|
+
port.addEventListener("message", (json: MessageEvent<Uint8Array>) => {
|
|
327
|
+
client.handle_response(json.data);
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
console.log(client);
|
|
331
|
+
return client;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
export function createMessageHandler(
|
|
335
|
+
handler: virtual_server.VirtualServerHandler,
|
|
336
|
+
) {
|
|
337
|
+
return virtual_server.createMessageHandler(perspective_client, handler);
|
|
338
|
+
}
|
|
339
|
+
|
|
306
340
|
export default {
|
|
307
341
|
table,
|
|
308
342
|
websocket,
|
|
343
|
+
worker,
|
|
309
344
|
get_hosted_table_names,
|
|
310
345
|
on_hosted_tables_update,
|
|
311
346
|
remove_hosted_tables_update,
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
|
2
|
+
import type { Aggregate } from "./Aggregate.js";
|
|
3
|
+
import type { Expressions } from "./Expressions.js";
|
|
4
|
+
import type { Filter } from "./Filter.js";
|
|
5
|
+
import type { FilterReducer } from "./FilterReducer.js";
|
|
6
|
+
import type { Sort } from "./Sort.js";
|
|
7
|
+
|
|
8
|
+
export type ViewConfig = { group_by: Array<string>, split_by: Array<string>, sort: Array<Sort>, filter: Array<Filter>, filter_op?: FilterReducer, expressions: Expressions, columns: Array<string | null>, aggregates: { [key in string]?: Aggregate }, group_by_depth?: number | null, };
|
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
* Some fields of [`ViewWindow`] are only applicable to specific methods of
|
|
7
7
|
* [`View`].
|
|
8
8
|
*/
|
|
9
|
-
export type ViewWindow = { start_row
|
|
9
|
+
export type ViewWindow = { start_row?: number, start_col?: number, end_row?: number, end_col?: number, id?: boolean, index?: boolean, leaves_only?: boolean,
|
|
10
10
|
/**
|
|
11
11
|
* Only impacts [`View::to_csv`]
|
|
12
12
|
*/
|
|
13
|
-
formatted
|
|
13
|
+
formatted?: boolean,
|
|
14
14
|
/**
|
|
15
15
|
* Only impacts [`View::to_arrow`]
|
|
16
16
|
*/
|
|
17
|
-
compression
|
|
17
|
+
compression?: string, };
|
|
@@ -0,0 +1,126 @@
|
|
|
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 { ColumnType } from "./ts-rs/ColumnType.ts";
|
|
14
|
+
import { ViewConfig } from "./ts-rs/ViewConfig.ts";
|
|
15
|
+
import { ViewWindow } from "./ts-rs/ViewWindow.ts";
|
|
16
|
+
|
|
17
|
+
import type * as perspective from "../../dist/wasm/perspective-js.js";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* VirtualServer API for implementing custom data sources in JavaScript/WASM.
|
|
21
|
+
*
|
|
22
|
+
* The VirtualServer pattern allows you to create custom data sources that
|
|
23
|
+
* integrate with Perspective's protocol. This is useful for:
|
|
24
|
+
* - Connecting to external databases (DuckDB, PostgreSQL, etc.)
|
|
25
|
+
* - Streaming data from APIs or message queues
|
|
26
|
+
* - Implementing custom aggregation or transformation logic
|
|
27
|
+
* - Creating data adapters without copying data into Perspective tables
|
|
28
|
+
*
|
|
29
|
+
* @module virtual_server
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
export interface ServerFeatures {
|
|
33
|
+
expressions?: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Handler interface that you implement to provide custom data sources.
|
|
38
|
+
*
|
|
39
|
+
* All methods will be called by the VirtualServer when handling protocol
|
|
40
|
+
* messages from Perspective clients. Methods can return values directly or
|
|
41
|
+
* return Promises for asynchronous operations (e.g., database queries).
|
|
42
|
+
*/
|
|
43
|
+
export interface VirtualServerHandler {
|
|
44
|
+
getHostedTables(): string[] | Promise<string[]>;
|
|
45
|
+
tableSchema(
|
|
46
|
+
tableId: string,
|
|
47
|
+
): Record<string, ColumnType> | Promise<Record<string, ColumnType>>;
|
|
48
|
+
tableSize(tableId: string): number | Promise<number>;
|
|
49
|
+
tableMakeView(
|
|
50
|
+
tableId: string,
|
|
51
|
+
viewId: string,
|
|
52
|
+
config: ViewConfig,
|
|
53
|
+
): void | Promise<void>;
|
|
54
|
+
viewDelete(viewId: string): void | Promise<void>;
|
|
55
|
+
viewGetData(
|
|
56
|
+
viewId: string,
|
|
57
|
+
config: ViewConfig,
|
|
58
|
+
viewport: ViewWindow,
|
|
59
|
+
dataSlice: perspective.JsVirtualDataSlice,
|
|
60
|
+
): void | Promise<void>;
|
|
61
|
+
viewSchema?(
|
|
62
|
+
viewId: string,
|
|
63
|
+
config?: ViewConfig,
|
|
64
|
+
): Record<string, ColumnType> | Promise<Record<string, ColumnType>>;
|
|
65
|
+
viewSize?(viewId: string): number | Promise<number>;
|
|
66
|
+
tableValidateExpression?(
|
|
67
|
+
tableId: string,
|
|
68
|
+
expression: string,
|
|
69
|
+
): ColumnType | Promise<ColumnType>;
|
|
70
|
+
getFeatures?(): ServerFeatures | Promise<ServerFeatures>;
|
|
71
|
+
makeTable?(
|
|
72
|
+
tableId: string,
|
|
73
|
+
data: string | Uint8Array,
|
|
74
|
+
): void | Promise<void>;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function createMessageHandler(
|
|
78
|
+
mod: typeof perspective,
|
|
79
|
+
handler: VirtualServerHandler,
|
|
80
|
+
) {
|
|
81
|
+
let virtualServer: perspective.JsVirtualServer;
|
|
82
|
+
async function postMessage(port: MessagePort, msg: MessageEvent) {
|
|
83
|
+
if (msg.data.cmd === "init") {
|
|
84
|
+
try {
|
|
85
|
+
virtualServer = new mod.JsVirtualServer(handler);
|
|
86
|
+
if (msg.data.id !== undefined) {
|
|
87
|
+
port.postMessage({ id: msg.data.id });
|
|
88
|
+
} else {
|
|
89
|
+
port.postMessage(null);
|
|
90
|
+
}
|
|
91
|
+
} catch (error) {
|
|
92
|
+
console.error("Error initializing worker:", error);
|
|
93
|
+
throw error;
|
|
94
|
+
}
|
|
95
|
+
} else {
|
|
96
|
+
try {
|
|
97
|
+
const requestBytes = new Uint8Array(msg.data);
|
|
98
|
+
const responseBytes =
|
|
99
|
+
await virtualServer.handleRequest(requestBytes);
|
|
100
|
+
const buffer = responseBytes.slice().buffer;
|
|
101
|
+
port.postMessage(buffer, { transfer: [buffer] });
|
|
102
|
+
} catch (error) {
|
|
103
|
+
console.error("Error handling request in worker:", error);
|
|
104
|
+
throw error;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const channel = new MessageChannel();
|
|
110
|
+
channel.port1.onmessage = (message) => {
|
|
111
|
+
postMessage(channel.port1, message);
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
return channel.port2;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Re-export the WASM VirtualServer and VirtualDataSlice classes with better names.
|
|
119
|
+
*
|
|
120
|
+
* VirtualServer: Handles Perspective protocol messages using your custom handler
|
|
121
|
+
* VirtualDataSlice: Used to fill data in viewGetData callbacks
|
|
122
|
+
*/
|
|
123
|
+
export {
|
|
124
|
+
JsVirtualServer as VirtualServer,
|
|
125
|
+
JsVirtualDataSlice as VirtualDataSlice,
|
|
126
|
+
} from "../../dist/wasm/perspective-js.js";
|