@tlockney/cdp-kernel 0.11.0 → 0.12.1
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/browser/kernel.js +16 -2
- package/dist/browser/kernel.js.map +2 -2
- package/dist/browser/mod.d.ts +27 -0
- package/dist/browser/mod.js +27 -0
- package/dist/browser/target.d.ts +10 -0
- package/dist/browser/target.js +1 -1
- package/dist/browser/worker.js +182 -14
- package/dist/browser/worker.js.map +4 -4
- package/dist/package.d.ts +3 -3
- package/dist/package.js +3 -2
- package/dist/workerd-target.js +1 -1
- package/package.json +1 -1
- package/runtime/browser-worker.mjs +9 -3
- package/runtime/cdn.d.mts +6 -1
- package/runtime/cdn.mjs +7 -2
- package/runtime/display.d.mts +136 -135
- package/runtime/display.mjs +15 -4
- package/runtime/sql.d.mts +14 -0
- package/runtime/sql.mjs +37 -6
- package/runtime/table-paging.d.mts +19 -0
- package/runtime/table-paging.mjs +183 -0
- package/runtime/table.d.mts +14 -0
- package/runtime/table.mjs +31 -2
- package/runtime/widget.d.mts +3 -3
package/dist/browser/mod.d.ts
CHANGED
|
@@ -4,6 +4,33 @@ import { type Channel, type JupyterMessage, type MessageListener } from "./chann
|
|
|
4
4
|
import { type BrowserTargetOptions } from "./target.ts";
|
|
5
5
|
export type { Channel, JupyterMessage, MessageListener } from "./channels.ts";
|
|
6
6
|
export { BrowserTarget, type BrowserTargetOptions, type HostHandler, type HostRequest, type WorkerLike } from "./target.ts";
|
|
7
|
+
/**
|
|
8
|
+
* The packages `jupyter.chart` imports, as it writes them: map them in
|
|
9
|
+
* `modules` to copies the page serves, and charts draw offline. The page
|
|
10
|
+
* serves ES modules that export what the packages do. Same as
|
|
11
|
+
* runtime/chart.mjs's VEGA, VEGA_LITE and LINKEDOM.
|
|
12
|
+
*/
|
|
13
|
+
export declare const CHART_MODULES: {
|
|
14
|
+
readonly vega: "npm:vega@6";
|
|
15
|
+
readonly vegaLite: "npm:vega-lite@6";
|
|
16
|
+
readonly linkedom: "npm:linkedom@0.18";
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* What `jupyter.sql` loads, as it writes them: map them in `modules` to
|
|
20
|
+
* copies the page serves, and SQL runs offline. `duckdb` is an ES module
|
|
21
|
+
* that exports what @duckdb/duckdb-wasm does; the others are that
|
|
22
|
+
* package's own dist files, served as they are. A page may map only the
|
|
23
|
+
* `eh` pair (every current browser runs it); a variant left unmapped
|
|
24
|
+
* loads from jsDelivr. Same as runtime/sql.mjs's DUCKDB_WASM and
|
|
25
|
+
* DUCKDB_FILES.
|
|
26
|
+
*/
|
|
27
|
+
export declare const SQL_MODULES: {
|
|
28
|
+
readonly duckdb: "npm:@duckdb/duckdb-wasm@1.29.0";
|
|
29
|
+
readonly ehWasm: "npm:@duckdb/duckdb-wasm@1.29.0/dist/duckdb-eh.wasm";
|
|
30
|
+
readonly ehWorker: "npm:@duckdb/duckdb-wasm@1.29.0/dist/duckdb-browser-eh.worker.js";
|
|
31
|
+
readonly mvpWasm: "npm:@duckdb/duckdb-wasm@1.29.0/dist/duckdb-mvp.wasm";
|
|
32
|
+
readonly mvpWorker: "npm:@duckdb/duckdb-wasm@1.29.0/dist/duckdb-browser-mvp.worker.js";
|
|
33
|
+
};
|
|
7
34
|
export interface BrowserKernelOptions extends BrowserTargetOptions {
|
|
8
35
|
/** What strips cells' types; sucrase by default, since esbuild's native binary can't run in a page. */
|
|
9
36
|
typeStrip?: TypeStripperName | TypeStripper;
|
package/dist/browser/mod.js
CHANGED
|
@@ -15,6 +15,33 @@ import { VERSION } from "../package.js";
|
|
|
15
15
|
import { PageChannels } from "./channels.js";
|
|
16
16
|
import { BrowserTarget } from "./target.js";
|
|
17
17
|
export { BrowserTarget } from "./target.js";
|
|
18
|
+
/**
|
|
19
|
+
* The packages `jupyter.chart` imports, as it writes them: map them in
|
|
20
|
+
* `modules` to copies the page serves, and charts draw offline. The page
|
|
21
|
+
* serves ES modules that export what the packages do. Same as
|
|
22
|
+
* runtime/chart.mjs's VEGA, VEGA_LITE and LINKEDOM.
|
|
23
|
+
*/
|
|
24
|
+
export const CHART_MODULES = {
|
|
25
|
+
vega: "npm:vega@6",
|
|
26
|
+
vegaLite: "npm:vega-lite@6",
|
|
27
|
+
linkedom: "npm:linkedom@0.18",
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* What `jupyter.sql` loads, as it writes them: map them in `modules` to
|
|
31
|
+
* copies the page serves, and SQL runs offline. `duckdb` is an ES module
|
|
32
|
+
* that exports what @duckdb/duckdb-wasm does; the others are that
|
|
33
|
+
* package's own dist files, served as they are. A page may map only the
|
|
34
|
+
* `eh` pair (every current browser runs it); a variant left unmapped
|
|
35
|
+
* loads from jsDelivr. Same as runtime/sql.mjs's DUCKDB_WASM and
|
|
36
|
+
* DUCKDB_FILES.
|
|
37
|
+
*/
|
|
38
|
+
export const SQL_MODULES = {
|
|
39
|
+
duckdb: "npm:@duckdb/duckdb-wasm@1.29.0",
|
|
40
|
+
ehWasm: "npm:@duckdb/duckdb-wasm@1.29.0/dist/duckdb-eh.wasm",
|
|
41
|
+
ehWorker: "npm:@duckdb/duckdb-wasm@1.29.0/dist/duckdb-browser-eh.worker.js",
|
|
42
|
+
mvpWasm: "npm:@duckdb/duckdb-wasm@1.29.0/dist/duckdb-mvp.wasm",
|
|
43
|
+
mvpWorker: "npm:@duckdb/duckdb-wasm@1.29.0/dist/duckdb-browser-mvp.worker.js",
|
|
44
|
+
};
|
|
18
45
|
export function startBrowserKernel(options) {
|
|
19
46
|
const channels = new PageChannels();
|
|
20
47
|
const log = options.log ?? ((text) => console.debug(`[cdp-kernel] ${text}`));
|
package/dist/browser/target.d.ts
CHANGED
|
@@ -22,6 +22,16 @@ export interface BrowserTargetOptions {
|
|
|
22
22
|
base?: string;
|
|
23
23
|
/** Where npm:, jsr: and bare imports load from; esm.sh by default. */
|
|
24
24
|
cdn?: string;
|
|
25
|
+
/**
|
|
26
|
+
* Specifiers, as written, mapped to URLs the page serves (resolved
|
|
27
|
+
* against `base`), which win over `cdn`: a cell's imports and the
|
|
28
|
+
* libraries `jupyter.chart` and `jupyter.sql` import load from there.
|
|
29
|
+
* Mapping `CHART_MODULES` makes charts draw offline, and mapping
|
|
30
|
+
* `SQL_MODULES` does the same for `jupyter.sql`.
|
|
31
|
+
*/
|
|
32
|
+
modules?: {
|
|
33
|
+
readonly [specifier: string]: string;
|
|
34
|
+
};
|
|
25
35
|
/**
|
|
26
36
|
* Modules the Worker imports before the first cell, each time it starts,
|
|
27
37
|
* resolved against `base`. A module's default export, if a function, is
|
package/dist/browser/target.js
CHANGED
|
@@ -49,7 +49,7 @@ export class BrowserTarget {
|
|
|
49
49
|
const base = options.base ?? globalThis.location?.href ?? "file:///";
|
|
50
50
|
try {
|
|
51
51
|
const params = options.params === undefined ? undefined : JSON.stringify(options.params);
|
|
52
|
-
const { userAgent } = await target.#request("info", { base, cdn: options.cdn, preload: options.preload ?? [], params });
|
|
52
|
+
const { userAgent } = await target.#request("info", { base, cdn: options.cdn, modules: options.modules, preload: options.preload ?? [], params });
|
|
53
53
|
if (typeof userAgent === "string")
|
|
54
54
|
target.versions.runtime = browserName(userAgent);
|
|
55
55
|
target.versions.engine = engineName(typeof userAgent === "string" ? userAgent : "");
|
package/dist/browser/worker.js
CHANGED
|
@@ -715,6 +715,17 @@ function textTable(fields, numbered) {
|
|
|
715
715
|
for (const row of cells) row.forEach((c, i) => widths[i] = Math.max(widths[i], c.length));
|
|
716
716
|
return cells.map((row) => ` ${row.map((c, i) => c.padEnd(widths[i])).join(" | ").trimEnd()}`);
|
|
717
717
|
}
|
|
718
|
+
var PAGEABLE = /* @__PURE__ */ Symbol("cdpk.table.pageable");
|
|
719
|
+
function encodeRow(source, fields, row) {
|
|
720
|
+
return fields.map((f, i) => encodeValue(row[i], f.type, source.columns[i].declared));
|
|
721
|
+
}
|
|
722
|
+
function pageTable(bundle, pager, key2) {
|
|
723
|
+
const resource = bundle?.[TABLE_MIME];
|
|
724
|
+
const pending = resource?.[PAGEABLE];
|
|
725
|
+
if (!pending || !pager || resource.ganymede?.page !== void 0) return bundle;
|
|
726
|
+
resource.ganymede.page = pager.register(pending.source, pending.fields, key2);
|
|
727
|
+
return bundle;
|
|
728
|
+
}
|
|
718
729
|
function tableBundle(source, options = {}) {
|
|
719
730
|
const requested = options.limit ?? DEFAULT_LIMIT;
|
|
720
731
|
if (!Number.isInteger(requested) || requested < 0) throw new TypeError("jupyter.table: limit must be a whole number");
|
|
@@ -723,7 +734,7 @@ function tableBundle(source, options = {}) {
|
|
|
723
734
|
const total = source.total;
|
|
724
735
|
const raw = source.read(0, Math.min(limit, total));
|
|
725
736
|
const fields = fieldsOf(source, raw);
|
|
726
|
-
const encode = (r) =>
|
|
737
|
+
const encode = (r) => encodeRow(source, fields, r);
|
|
727
738
|
const utf8 = new TextEncoder();
|
|
728
739
|
const encoded = [];
|
|
729
740
|
const data = [];
|
|
@@ -772,6 +783,10 @@ function tableBundle(source, options = {}) {
|
|
|
772
783
|
if (text.length > TEXT_MAX) text = `${text.slice(0, TEXT_MAX - 1)}\u2026`;
|
|
773
784
|
const resource = { schema: { fields }, data, ganymede: { rows: total, shown } };
|
|
774
785
|
if (title) resource.ganymede.title = title;
|
|
786
|
+
if (shown < total) {
|
|
787
|
+
Object.defineProperty(resource, PAGEABLE, { value: { source, fields } });
|
|
788
|
+
if (options.pager) pageTable({ [TABLE_MIME]: resource }, options.pager);
|
|
789
|
+
}
|
|
775
790
|
return {
|
|
776
791
|
[TABLE_MIME]: resource,
|
|
777
792
|
"text/html": htmlTable(fields, htmlHead, htmlTail, total, title),
|
|
@@ -782,6 +797,20 @@ function tableBundle(source, options = {}) {
|
|
|
782
797
|
// runtime/sql.mjs
|
|
783
798
|
var NODE_API = "npm:@duckdb/node-api@1.5.6-r.2";
|
|
784
799
|
var DUCKDB_WASM = "npm:@duckdb/duckdb-wasm@1.29.0";
|
|
800
|
+
var DUCKDB_FILES = {
|
|
801
|
+
mvp: { mainModule: `${DUCKDB_WASM}/dist/duckdb-mvp.wasm`, mainWorker: `${DUCKDB_WASM}/dist/duckdb-browser-mvp.worker.js` },
|
|
802
|
+
eh: { mainModule: `${DUCKDB_WASM}/dist/duckdb-eh.wasm`, mainWorker: `${DUCKDB_WASM}/dist/duckdb-browser-eh.worker.js` }
|
|
803
|
+
};
|
|
804
|
+
function duckdbBundles(jsDelivr, mapped) {
|
|
805
|
+
const bundles = { ...jsDelivr };
|
|
806
|
+
if (!mapped) return bundles;
|
|
807
|
+
for (const [variant, files] of Object.entries(DUCKDB_FILES)) {
|
|
808
|
+
const mainModule = mapped(files.mainModule);
|
|
809
|
+
const mainWorker = mapped(files.mainWorker);
|
|
810
|
+
if (mainModule && mainWorker) bundles[variant] = { mainModule, mainWorker, pthreadWorker: null };
|
|
811
|
+
}
|
|
812
|
+
return bundles;
|
|
813
|
+
}
|
|
785
814
|
var $display = /* @__PURE__ */ Symbol.for("Jupyter.display");
|
|
786
815
|
var RAW = /* @__PURE__ */ Symbol("cdpk.sql.raw");
|
|
787
816
|
function jsValue(value, declared) {
|
|
@@ -909,9 +938,9 @@ var DUCK_TYPES = {
|
|
|
909
938
|
object: "JSON",
|
|
910
939
|
duration: "VARCHAR"
|
|
911
940
|
};
|
|
912
|
-
function createSql({ importModule, signal, openEngine }) {
|
|
941
|
+
function createSql({ importModule, mapped, signal, openEngine }) {
|
|
913
942
|
let engine = null;
|
|
914
|
-
const open = () => engine ??= (openEngine ?? (() => defaultEngine(importModule)))().catch((err) => {
|
|
943
|
+
const open = () => engine ??= (openEngine ?? (() => defaultEngine(importModule, mapped)))().catch((err) => {
|
|
915
944
|
engine = null;
|
|
916
945
|
throw err;
|
|
917
946
|
});
|
|
@@ -1009,9 +1038,9 @@ function inWorker() {
|
|
|
1009
1038
|
function hasNativeRuntime() {
|
|
1010
1039
|
return typeof globalThis.Deno?.version?.deno === "string" || typeof globalThis.Bun?.version === "string" || typeof globalThis.process?.versions?.node === "string" && !inWorker();
|
|
1011
1040
|
}
|
|
1012
|
-
async function defaultEngine(importModule) {
|
|
1041
|
+
async function defaultEngine(importModule, mapped) {
|
|
1013
1042
|
if (hasNativeRuntime()) return nativeEngine(importModule);
|
|
1014
|
-
if (inWorker() && typeof Worker === "function") return wasmEngine(importModule);
|
|
1043
|
+
if (inWorker() && typeof Worker === "function") return wasmEngine(importModule, mapped);
|
|
1015
1044
|
throw new Error("jupyter.sql needs Node, Deno, Bun or the browser target; workerd and celld have no DuckDB");
|
|
1016
1045
|
}
|
|
1017
1046
|
async function nativeEngine(importModule) {
|
|
@@ -1039,9 +1068,9 @@ async function nativeEngine(importModule) {
|
|
|
1039
1068
|
}
|
|
1040
1069
|
};
|
|
1041
1070
|
}
|
|
1042
|
-
async function wasmEngine(importModule) {
|
|
1071
|
+
async function wasmEngine(importModule, mapped) {
|
|
1043
1072
|
const duckdb = await importModule(DUCKDB_WASM);
|
|
1044
|
-
const bundle = await duckdb.selectBundle(duckdb.getJsDelivrBundles());
|
|
1073
|
+
const bundle = await duckdb.selectBundle(duckdbBundles(duckdb.getJsDelivrBundles(), mapped));
|
|
1045
1074
|
const loader = URL.createObjectURL(new Blob([`importScripts(${JSON.stringify(bundle.mainWorker)});`], { type: "text/javascript" }));
|
|
1046
1075
|
const worker = new Worker(loader);
|
|
1047
1076
|
const db = new duckdb.AsyncDuckDB(new duckdb.VoidLogger(), worker);
|
|
@@ -1357,6 +1386,133 @@ function createChart({ importModule, summarizeSpec, warn = (m) => console.warn(m
|
|
|
1357
1386
|
return chart;
|
|
1358
1387
|
}
|
|
1359
1388
|
|
|
1389
|
+
// runtime/table-paging.mjs
|
|
1390
|
+
var PAGE_TARGET = "ganymede.table.v1";
|
|
1391
|
+
var KEEP = 16;
|
|
1392
|
+
var MAX_PAGE = 1e3;
|
|
1393
|
+
var MAX_SORT = 1e6;
|
|
1394
|
+
var CHUNK = 1e4;
|
|
1395
|
+
var SORTS = 2;
|
|
1396
|
+
var NUMERIC = /* @__PURE__ */ new Set(["integer", "number"]);
|
|
1397
|
+
var INTEGER_TEXT = /^-?\d+$/;
|
|
1398
|
+
function sortNumber(value) {
|
|
1399
|
+
if (typeof value === "number") return value;
|
|
1400
|
+
if (typeof value === "string" && value.trim() !== "") return Number(value);
|
|
1401
|
+
return Number.NaN;
|
|
1402
|
+
}
|
|
1403
|
+
function textOf(value) {
|
|
1404
|
+
if (value === null || value === void 0) return "null";
|
|
1405
|
+
if (typeof value === "string") return value;
|
|
1406
|
+
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
1407
|
+
return JSON.stringify(value);
|
|
1408
|
+
}
|
|
1409
|
+
var collator = new Intl.Collator(void 0, { numeric: true });
|
|
1410
|
+
function compareEncoded(a, b, type, descending = false) {
|
|
1411
|
+
const numeric = NUMERIC.has(type) || typeof a === "number" && typeof b === "number";
|
|
1412
|
+
const last = (v) => v === null || v === void 0 || numeric && Number.isNaN(sortNumber(v));
|
|
1413
|
+
const aLast = last(a);
|
|
1414
|
+
const bLast = last(b);
|
|
1415
|
+
if (aLast || bLast) return aLast === bLast ? 0 : aLast ? 1 : -1;
|
|
1416
|
+
const sign = descending ? -1 : 1;
|
|
1417
|
+
if (numeric) {
|
|
1418
|
+
if (typeof a === "string" && typeof b === "string" && INTEGER_TEXT.test(a) && INTEGER_TEXT.test(b)) {
|
|
1419
|
+
const x = BigInt(a);
|
|
1420
|
+
const y = BigInt(b);
|
|
1421
|
+
return sign * (x < y ? -1 : x > y ? 1 : 0);
|
|
1422
|
+
}
|
|
1423
|
+
return sign * (sortNumber(a) - sortNumber(b));
|
|
1424
|
+
}
|
|
1425
|
+
if (typeof a === "boolean" && typeof b === "boolean") return sign * (Number(a) - Number(b));
|
|
1426
|
+
return sign * collator.compare(textOf(a), textOf(b));
|
|
1427
|
+
}
|
|
1428
|
+
function createTablePager(commApi, { keep = KEEP, newId = () => crypto.randomUUID() } = {}) {
|
|
1429
|
+
const tables = /* @__PURE__ */ new Map();
|
|
1430
|
+
const keyed = /* @__PURE__ */ new Map();
|
|
1431
|
+
function register(source, fields, key2) {
|
|
1432
|
+
let id = key2 === void 0 ? void 0 : keyed.get(key2);
|
|
1433
|
+
if (id === void 0 || !tables.has(id)) id = newId();
|
|
1434
|
+
if (key2 !== void 0) keyed.set(key2, id);
|
|
1435
|
+
tables.delete(id);
|
|
1436
|
+
tables.set(id, { source, fields, key: key2, orders: /* @__PURE__ */ new Map() });
|
|
1437
|
+
while (tables.size > keep) {
|
|
1438
|
+
const [oldest, entry] = tables.entries().next().value;
|
|
1439
|
+
tables.delete(oldest);
|
|
1440
|
+
if (entry.key !== void 0 && keyed.get(entry.key) === oldest) keyed.delete(entry.key);
|
|
1441
|
+
}
|
|
1442
|
+
return id;
|
|
1443
|
+
}
|
|
1444
|
+
function encodeRows(entry, raw) {
|
|
1445
|
+
return raw.map((r) => encodeRow(entry.source, entry.fields, r));
|
|
1446
|
+
}
|
|
1447
|
+
function orderFor(entry, column, descending) {
|
|
1448
|
+
const key2 = `${column}\0${descending}`;
|
|
1449
|
+
const kept = entry.orders.get(key2);
|
|
1450
|
+
if (kept) return kept;
|
|
1451
|
+
const { source, fields } = entry;
|
|
1452
|
+
const c = fields.findIndex((f) => f.name === column);
|
|
1453
|
+
if (c < 0) throw new Error(`no column ${JSON.stringify(column)}`);
|
|
1454
|
+
if (source.total > MAX_SORT) throw new Error(`sorting is offered up to ${MAX_SORT.toLocaleString("en-US")} rows`);
|
|
1455
|
+
const type = fields[c].type;
|
|
1456
|
+
const declared = source.columns[c].declared;
|
|
1457
|
+
const values = new Array(source.total);
|
|
1458
|
+
for (let start = 0; start < source.total; start += CHUNK) {
|
|
1459
|
+
const raw = source.read(start, Math.min(start + CHUNK, source.total));
|
|
1460
|
+
raw.forEach((r, i) => values[start + i] = encodeValue(r[c], type, declared));
|
|
1461
|
+
}
|
|
1462
|
+
const index = Array.from({ length: source.total }, (_, i) => i);
|
|
1463
|
+
index.sort((i, j) => compareEncoded(values[i], values[j], type, descending));
|
|
1464
|
+
entry.orders.set(key2, index);
|
|
1465
|
+
while (entry.orders.size > SORTS) entry.orders.delete(entry.orders.keys().next().value);
|
|
1466
|
+
return index;
|
|
1467
|
+
}
|
|
1468
|
+
function page(entry, { offset, limit, sort }) {
|
|
1469
|
+
const { source } = entry;
|
|
1470
|
+
const start = Math.max(0, Math.min(offset, source.total));
|
|
1471
|
+
const end = Math.min(source.total, start + Math.min(limit, MAX_PAGE));
|
|
1472
|
+
if (!sort) return encodeRows(entry, source.read(start, end));
|
|
1473
|
+
const index = orderFor(entry, String(sort.column), sort.descending === true).slice(start, end);
|
|
1474
|
+
const rows = [];
|
|
1475
|
+
for (let i = 0; i < index.length; ) {
|
|
1476
|
+
let j = i + 1;
|
|
1477
|
+
while (j < index.length && index[j] === index[j - 1] + 1) j++;
|
|
1478
|
+
rows.push(...source.read(index[i], index[j - 1] + 1));
|
|
1479
|
+
i = j;
|
|
1480
|
+
}
|
|
1481
|
+
return encodeRows(entry, rows);
|
|
1482
|
+
}
|
|
1483
|
+
const gone = "The kernel no longer has this table: run the cell again to page through it.";
|
|
1484
|
+
commApi.registerTarget(PAGE_TARGET, (comm, msg) => {
|
|
1485
|
+
const id = msg.data?.id;
|
|
1486
|
+
if (typeof id !== "string" || !tables.has(id)) {
|
|
1487
|
+
comm.send({ error: gone });
|
|
1488
|
+
comm.close();
|
|
1489
|
+
return;
|
|
1490
|
+
}
|
|
1491
|
+
comm.onMsg(({ data }) => {
|
|
1492
|
+
const request = data?.request;
|
|
1493
|
+
const entry = tables.get(id);
|
|
1494
|
+
if (!entry) {
|
|
1495
|
+
comm.send({ request, error: gone });
|
|
1496
|
+
comm.close();
|
|
1497
|
+
return;
|
|
1498
|
+
}
|
|
1499
|
+
const offset = data?.offset;
|
|
1500
|
+
const limit = data?.limit ?? MAX_PAGE;
|
|
1501
|
+
if (!Number.isInteger(offset) || offset < 0 || !Number.isInteger(limit) || limit < 1) {
|
|
1502
|
+
comm.send({ request, error: "offset and limit must be whole numbers" });
|
|
1503
|
+
return;
|
|
1504
|
+
}
|
|
1505
|
+
try {
|
|
1506
|
+
const rows = page(entry, { offset, limit, sort: data?.sort ?? void 0 });
|
|
1507
|
+
comm.send({ request, offset, total: entry.source.total, rows });
|
|
1508
|
+
} catch (err) {
|
|
1509
|
+
comm.send({ request, error: err instanceof Error ? err.message : String(err) });
|
|
1510
|
+
}
|
|
1511
|
+
});
|
|
1512
|
+
});
|
|
1513
|
+
return { register, size: () => tables.size };
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1360
1516
|
// runtime/display.mjs
|
|
1361
1517
|
var $display3 = /* @__PURE__ */ Symbol.for("Jupyter.display");
|
|
1362
1518
|
var rawToEntity3 = /* @__PURE__ */ new Map([
|
|
@@ -1631,10 +1787,11 @@ function createJupyter(host2) {
|
|
|
1631
1787
|
function broadcast(msgType, content, { metadata = {}, buffers = [] } = {}) {
|
|
1632
1788
|
host2.send(msgType, content, metadata, buffers);
|
|
1633
1789
|
}
|
|
1790
|
+
let pager;
|
|
1634
1791
|
async function display(obj, options = { raw: false, update: false }) {
|
|
1635
1792
|
const bundle = options.raw && isMediaBundle(obj) ? obj : await format2(obj);
|
|
1636
1793
|
broadcast(options.update ? "update_display_data" : "display_data", {
|
|
1637
|
-
data: bundle,
|
|
1794
|
+
data: pageTable(bundle, pager, options.display_id),
|
|
1638
1795
|
metadata: {},
|
|
1639
1796
|
transient: options.display_id ? { display_id: options.display_id } : {}
|
|
1640
1797
|
});
|
|
@@ -1644,7 +1801,7 @@ function createJupyter(host2) {
|
|
|
1644
1801
|
try {
|
|
1645
1802
|
broadcast("execute_result", {
|
|
1646
1803
|
execution_count: executionCount,
|
|
1647
|
-
data: await format2(result2),
|
|
1804
|
+
data: pageTable(await format2(result2), pager),
|
|
1648
1805
|
metadata: {},
|
|
1649
1806
|
// nbclient crashes on a null `transient`; Deno learned this too.
|
|
1650
1807
|
transient: {}
|
|
@@ -1703,9 +1860,14 @@ function createJupyter(host2) {
|
|
|
1703
1860
|
if (typeof load !== "function") return Promise.reject(new Error(`${what}: no module loader in this runtime`));
|
|
1704
1861
|
return load(specifier);
|
|
1705
1862
|
};
|
|
1706
|
-
const sql = createSql({
|
|
1863
|
+
const sql = createSql({
|
|
1864
|
+
importModule: cellImport("jupyter.sql"),
|
|
1865
|
+
mapped: (specifier) => globalThis.__cdpk?.mapped?.(specifier) ?? null,
|
|
1866
|
+
signal: () => current.signal
|
|
1867
|
+
});
|
|
1707
1868
|
const chart = createChart({ importModule: cellImport("jupyter.chart"), summarizeSpec: summarizeChart });
|
|
1708
1869
|
const comms = createComms((msgType, content, metadata, buffers) => host2.send(msgType, content, metadata, buffers));
|
|
1870
|
+
pager = createTablePager(comms.api);
|
|
1709
1871
|
let cell = new AbortController();
|
|
1710
1872
|
let current = cell;
|
|
1711
1873
|
const handlers = /* @__PURE__ */ new Map();
|
|
@@ -2052,7 +2214,8 @@ ${resolved.name} has a getter, which isn't called for inspection: it could have
|
|
|
2052
2214
|
|
|
2053
2215
|
// runtime/cdn.mjs
|
|
2054
2216
|
var DEFAULT_CDN = "https://esm.sh/";
|
|
2055
|
-
function resolveSpecifier(specifier, base, cdn2 = DEFAULT_CDN) {
|
|
2217
|
+
function resolveSpecifier(specifier, base, cdn2 = DEFAULT_CDN, modules2 = void 0) {
|
|
2218
|
+
if (modules2 && Object.hasOwn(modules2, specifier)) return new URL(modules2[specifier], base).href;
|
|
2056
2219
|
if (/^(?:https?|data|blob):/.test(specifier)) return specifier;
|
|
2057
2220
|
if (/^\.{0,2}\//.test(specifier)) return new URL(specifier, base).href;
|
|
2058
2221
|
if (specifier.startsWith("npm:")) return cdn2 + specifier.slice(4);
|
|
@@ -2107,6 +2270,7 @@ var jupyter = createJupyter({
|
|
|
2107
2270
|
});
|
|
2108
2271
|
var paramsText;
|
|
2109
2272
|
var cdn = DEFAULT_CDN;
|
|
2273
|
+
var modules;
|
|
2110
2274
|
var hostCalls = /* @__PURE__ */ new Map();
|
|
2111
2275
|
var hostListeners = /* @__PURE__ */ new Set();
|
|
2112
2276
|
var nextHostId = 0;
|
|
@@ -2173,13 +2337,17 @@ async function preload(urls, base) {
|
|
|
2173
2337
|
}
|
|
2174
2338
|
}
|
|
2175
2339
|
var handle = null;
|
|
2176
|
-
function install({ base, cdn: cdnBase, params }) {
|
|
2340
|
+
function install({ base, cdn: cdnBase, modules: moduleMap, params }) {
|
|
2177
2341
|
if (typeof params === "string") paramsText = params;
|
|
2178
2342
|
if (typeof cdnBase === "string") cdn = cdnBase.endsWith("/") ? cdnBase : cdnBase + "/";
|
|
2343
|
+
if (moduleMap !== null && typeof moduleMap === "object") modules = { ...moduleMap };
|
|
2179
2344
|
const url = new URL("$cdpk$repl.mjs", base).href;
|
|
2180
2345
|
globalThis.__cdpk = {
|
|
2181
|
-
import: (specifier, options) => import(resolveSpecifier(specifier, url, cdn)),
|
|
2182
|
-
meta: { url, resolve: (specifier) => resolveSpecifier(specifier, url, cdn) },
|
|
2346
|
+
import: (specifier, options) => import(resolveSpecifier(specifier, url, cdn, modules)),
|
|
2347
|
+
meta: { url, resolve: (specifier) => resolveSpecifier(specifier, url, cdn, modules) },
|
|
2348
|
+
// The URL `modules` maps a specifier to, or null: jupyter.sql's DuckDB
|
|
2349
|
+
// files load from the page this way.
|
|
2350
|
+
mapped: (specifier) => modules && Object.hasOwn(modules, specifier) ? new URL(modules[specifier], url).href : null,
|
|
2183
2351
|
// inspect_request (#10): by descriptor, so nothing runs.
|
|
2184
2352
|
describeForInspect: (value, detail) => describeValue(value, { inspect: (v) => inspect(v, { depth: 2 }), detail }),
|
|
2185
2353
|
inspectPath: (path, detail) => inspectPath(path, { inspect: (v) => inspect(v, { depth: 2 }), detail }),
|