@tlockney/cdp-kernel 0.12.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/package.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /** Kept equal to package.json and deno.jsonc by test/package.test.ts. */
2
- export declare const VERSION = "0.12.0";
2
+ export declare const VERSION = "0.12.1";
3
3
  /**
4
4
  * The UTC day this version was released, set with VERSION. The publish
5
5
  * workflow refuses a tag unless it's today or yesterday, so it can't go
package/dist/package.js CHANGED
@@ -14,7 +14,7 @@ import { join, sep } from "node:path";
14
14
  import process from "node:process";
15
15
  import { fileURLToPath } from "node:url";
16
16
  /** Kept equal to package.json and deno.jsonc by test/package.test.ts. */
17
- export const VERSION = "0.12.0";
17
+ export const VERSION = "0.12.1";
18
18
  /**
19
19
  * The UTC day this version was released, set with VERSION. The publish
20
20
  * workflow refuses a tag unless it's today or yesterday, so it can't go
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tlockney/cdp-kernel",
3
- "version": "0.12.0",
3
+ "version": "0.12.1",
4
4
  "description": "Deno's Jupyter kernel, extracted to run notebooks on Node, Deno, Bun, workerd and celld",
5
5
  "license": "MIT",
6
6
  "author": "Thomas Lockney <thomas@lockney.net>",
@@ -163,6 +163,9 @@ function install({ base, cdn: cdnBase, modules: moduleMap, params }) {
163
163
  globalThis.__cdpk = {
164
164
  import: (specifier, options) => import(resolveSpecifier(specifier, url, cdn, modules), options),
165
165
  meta: { url, resolve: (specifier) => resolveSpecifier(specifier, url, cdn, modules) },
166
+ // The URL `modules` maps a specifier to, or null: jupyter.sql's DuckDB
167
+ // files load from the page this way.
168
+ mapped: (specifier) => (modules && Object.hasOwn(modules, specifier) ? new URL(modules[specifier], url).href : null),
166
169
  // inspect_request (#10): by descriptor, so nothing runs.
167
170
  describeForInspect: (value, detail) => describeValue(value, { inspect: (v) => inspect(v, { depth: 2 }), detail }),
168
171
  inspectPath: (path, detail) => inspectPath(path, { inspect: (v) => inspect(v, { depth: 2 }), detail }),
@@ -462,7 +462,11 @@ export function createJupyter(host) {
462
462
 
463
463
  // jupyter.sql (#477): DuckDB, loaded on the first query; an interrupt
464
464
  // (jupyter.signal) interrupts the query.
465
- const sql = createSql({ importModule: cellImport("jupyter.sql"), signal: () => current.signal });
465
+ const sql = createSql({
466
+ importModule: cellImport("jupyter.sql"),
467
+ mapped: (specifier) => globalThis.__cdpk?.mapped?.(specifier) ?? null,
468
+ signal: () => current.signal,
469
+ });
466
470
 
467
471
  // jupyter.chart (#479): vega, vega-lite or linkedom, loaded on first use.
468
472
  const chart = createChart({ importModule: cellImport("jupyter.chart"), summarizeSpec: summarizeChart });
package/runtime/sql.d.mts CHANGED
@@ -2,6 +2,19 @@ import type { Displayable } from "./display.d.mts";
2
2
 
3
3
  export const NODE_API: string;
4
4
  export const DUCKDB_WASM: string;
5
+ export interface DuckDBBundle {
6
+ mainModule: string;
7
+ mainWorker: string;
8
+ pthreadWorker?: string | null;
9
+ }
10
+ export const DUCKDB_FILES: {
11
+ readonly mvp: { readonly mainModule: string; readonly mainWorker: string };
12
+ readonly eh: { readonly mainModule: string; readonly mainWorker: string };
13
+ };
14
+ export function duckdbBundles(
15
+ jsDelivr: Record<string, DuckDBBundle>,
16
+ mapped?: (specifier: string) => string | null | undefined,
17
+ ): Record<string, DuckDBBundle>;
5
18
 
6
19
  /** One column of a query's result. */
7
20
  export interface SqlColumn {
@@ -142,6 +155,7 @@ export interface SqlEngine {
142
155
 
143
156
  export function createSql(options: {
144
157
  importModule: (specifier: string) => Promise<any>;
158
+ mapped?: (specifier: string) => string | null | undefined;
145
159
  signal?: () => AbortSignal | undefined;
146
160
  openEngine?: () => Promise<SqlEngine>;
147
161
  }): Sql;
package/runtime/sql.mjs CHANGED
@@ -21,6 +21,35 @@ export { timeText, uniqueNames };
21
21
  export const NODE_API = "npm:@duckdb/node-api@1.5.6-r.2";
22
22
  export const DUCKDB_WASM = "npm:@duckdb/duckdb-wasm@1.29.0";
23
23
 
24
+ /**
25
+ * The files duckdb-wasm runs from besides its module, by the specifier a
26
+ * browser kernel's `modules` option maps to a URL the page serves. A
27
+ * variant whose wasm and worker are both mapped loads from those URLs;
28
+ * the others load from jsDelivr, as duckdb-wasm's own list has them.
29
+ */
30
+ export const DUCKDB_FILES = {
31
+ mvp: { mainModule: `${DUCKDB_WASM}/dist/duckdb-mvp.wasm`, mainWorker: `${DUCKDB_WASM}/dist/duckdb-browser-mvp.worker.js` },
32
+ eh: { mainModule: `${DUCKDB_WASM}/dist/duckdb-eh.wasm`, mainWorker: `${DUCKDB_WASM}/dist/duckdb-browser-eh.worker.js` },
33
+ };
34
+
35
+ /**
36
+ * duckdb-wasm's bundles: jsDelivr's, with each variant `mapped` gives
37
+ * both files for in their place.
38
+ *
39
+ * @param {Record<string, { mainModule: string, mainWorker: string, pthreadWorker?: string | null }>} jsDelivr
40
+ * @param {(specifier: string) => string | null | undefined} [mapped]
41
+ */
42
+ export function duckdbBundles(jsDelivr, mapped) {
43
+ const bundles = { ...jsDelivr };
44
+ if (!mapped) return bundles;
45
+ for (const [variant, files] of Object.entries(DUCKDB_FILES)) {
46
+ const mainModule = mapped(files.mainModule);
47
+ const mainWorker = mapped(files.mainWorker);
48
+ if (mainModule && mainWorker) bundles[variant] = { mainModule, mainWorker, pthreadWorker: null };
49
+ }
50
+ return bundles;
51
+ }
52
+
24
53
  const $display = Symbol.for("Jupyter.display");
25
54
  const RAW = Symbol("cdpk.sql.raw");
26
55
 
@@ -190,6 +219,7 @@ const DUCK_TYPES = {
190
219
  /**
191
220
  * @param {{
192
221
  * importModule: (specifier: string) => Promise<any>,
222
+ * mapped?: (specifier: string) => string | null | undefined,
193
223
  * signal?: () => AbortSignal | undefined,
194
224
  * openEngine?: () => Promise<Engine>,
195
225
  * }} options
@@ -201,9 +231,9 @@ const DUCK_TYPES = {
201
231
  * connection: unknown,
202
232
  * }} Engine
203
233
  */
204
- export function createSql({ importModule, signal, openEngine }) {
234
+ export function createSql({ importModule, mapped, signal, openEngine }) {
205
235
  let engine = null;
206
- const open = () => (engine ??= (openEngine ?? (() => defaultEngine(importModule)))().catch((err) => {
236
+ const open = () => (engine ??= (openEngine ?? (() => defaultEngine(importModule, mapped)))().catch((err) => {
207
237
  engine = null;
208
238
  throw err;
209
239
  }));
@@ -321,9 +351,9 @@ function hasNativeRuntime() {
321
351
  typeof globalThis.process?.versions?.node === "string" && !inWorker();
322
352
  }
323
353
 
324
- async function defaultEngine(importModule) {
354
+ async function defaultEngine(importModule, mapped) {
325
355
  if (hasNativeRuntime()) return nativeEngine(importModule);
326
- if (inWorker() && typeof Worker === "function") return wasmEngine(importModule);
356
+ if (inWorker() && typeof Worker === "function") return wasmEngine(importModule, mapped);
327
357
  throw new Error("jupyter.sql needs Node, Deno, Bun or the browser target; workerd and celld have no DuckDB");
328
358
  }
329
359
 
@@ -355,9 +385,10 @@ async function nativeEngine(importModule) {
355
385
  };
356
386
  }
357
387
 
358
- async function wasmEngine(importModule) {
388
+ async function wasmEngine(importModule, mapped) {
359
389
  const duckdb = await importModule(DUCKDB_WASM);
360
- const bundle = await duckdb.selectBundle(duckdb.getJsDelivrBundles());
390
+ // An app that serves the files (DUCKDB_FILES) runs SQL offline.
391
+ const bundle = await duckdb.selectBundle(duckdbBundles(duckdb.getJsDelivrBundles(), mapped));
361
392
  // A Worker can only start from its own origin's scripts, so a Blob
362
393
  // script loads DuckDB's.
363
394
  const loader = URL.createObjectURL(new Blob([`importScripts(${JSON.stringify(bundle.mainWorker)});`], { type: "text/javascript" }));