@tlockney/cdp-kernel 0.10.0 → 0.11.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.
@@ -306,10 +306,10 @@ var Widget = class {
306
306
  #state;
307
307
  #listeners = /* @__PURE__ */ new Map();
308
308
  // event -> Set<listener>
309
- constructor(comm, state, $display2) {
309
+ constructor(comm, state, $display4) {
310
310
  this.#comm = comm;
311
311
  this.#state = state;
312
- this[$display2] = () => ({
312
+ this[$display4] = () => ({
313
313
  [VIEW_MIME]: { version_major: 2, version_minor: 1, model_id: this.id },
314
314
  "text/plain": `Widget(${this.id})`
315
315
  });
@@ -374,7 +374,7 @@ var Widget = class {
374
374
  }
375
375
  }
376
376
  };
377
- function createWidgetFactory(commApi, $display2) {
377
+ function createWidgetFactory(commApi, $display4) {
378
378
  const live = /* @__PURE__ */ new Map();
379
379
  commApi.registerTarget(CONTROL_TARGET, (control) => {
380
380
  control.onMsg((msg) => {
@@ -408,7 +408,7 @@ function createWidgetFactory(commApi, $display2) {
408
408
  metadata: { version: WIDGET_PROTOCOL_VERSION },
409
409
  buffers
410
410
  });
411
- const instance = new Widget(comm, { ...full }, $display2);
411
+ const instance = new Widget(comm, { ...full }, $display4);
412
412
  live.set(comm.id, instance);
413
413
  comm.onMsg((msg) => instance._receive(msg));
414
414
  comm.onClose(() => {
@@ -418,19 +418,955 @@ function createWidgetFactory(commApi, $display2) {
418
418
  };
419
419
  }
420
420
 
421
- // runtime/display.mjs
421
+ // runtime/table.mjs
422
+ var TABLE_MIME = "application/vnd.dataresource+json";
423
+ var DEFAULT_LIMIT = 1e3;
424
+ var MAX_LIMIT = 1e4;
425
+ var MAX_BYTES = 1e6;
426
+ var MAX_CELL = 1e3;
427
+ var HTML_ALL = 20;
428
+ var HTML_HEAD = 10;
429
+ var HTML_TAIL = 5;
430
+ var TEXT_HEAD = 3;
431
+ var TEXT_TAIL = 2;
432
+ var TEXT_COLUMNS = 20;
433
+ var TEXT_MAX = 4096;
434
+ var rawToEntity = { "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" };
435
+ var escapeHTML = (s) => s.replace(/[&<>"']/g, (c) => rawToEntity[c]);
436
+ function isObject(value) {
437
+ return value !== null && typeof value === "object";
438
+ }
439
+ function isArrowTable(value) {
440
+ return isObject(value) && isObject(value.schema) && Array.isArray(value.schema.fields) && typeof value.numRows === "number" && typeof value.getChild === "function";
441
+ }
442
+ function isPolarsFrame(value) {
443
+ return isObject(value) && isObject(value.schema) && typeof value.head === "function" && typeof value.toRecords === "function";
444
+ }
445
+ function isDuckDBShaped(value) {
446
+ return isObject(value) && typeof value.columnNames === "function" && typeof value.columnTypes === "function" && typeof value.getRowObjectsJS === "function";
447
+ }
448
+ function isDuckDBResult(value) {
449
+ return isDuckDBShaped(value) && typeof value.currentRowCount === "number" && typeof value.getRowsJS === "function" && value.done === true;
450
+ }
451
+ function isTableLike(value) {
452
+ return isArrowTable(value) || isPolarsFrame(value) || isDuckDBResult(value);
453
+ }
454
+ function schemaType(declared) {
455
+ const t = String(declared ?? "");
456
+ if (t === "") return void 0;
457
+ if (/^(list|large_?list|fixed_?size_?list|array)\b|\[\d*\]$/i.test(t)) return "array";
458
+ if (/^(struct|map|union|json)\b/i.test(t)) return "object";
459
+ if (/^(u?int\d*|u?(tiny|small|big|huge)?int(eger)?)\b/i.test(t)) return "integer";
460
+ if (/^(float\d*|double|real|decimal|numeric)\b/i.test(t)) return "number";
461
+ if (/^bool/i.test(t)) return "boolean";
462
+ if (/^date(32|64)?(<|$|\b(?!time))/i.test(t) && !/^datetime/i.test(t)) return "date";
463
+ if (/^(timestamp|datetime)/i.test(t)) return "datetime";
464
+ if (/^time/i.test(t)) return "time";
465
+ if (/^(interval|duration)/i.test(t)) return "duration";
466
+ if (/^(utf8|large_?utf8|string|varchar|char|text|categorical|enum|uuid|null)\b/i.test(t)) return "string";
467
+ return void 0;
468
+ }
469
+ function polarsDeclared(dtype) {
470
+ if (typeof dtype?.toJSON === "function") {
471
+ const json = dtype.toJSON();
472
+ const name = isObject(json) ? json.DataType ?? Object.keys(json)[0] : json;
473
+ if (typeof name === "string") return name;
474
+ if (isObject(name)) return Object.keys(name)[0];
475
+ }
476
+ return typeof dtype === "string" ? dtype : void 0;
477
+ }
478
+ function inferType(values) {
479
+ let type;
480
+ for (const v of values) {
481
+ if (v === null || v === void 0) continue;
482
+ let t;
483
+ if (typeof v === "bigint") t = "integer";
484
+ else if (typeof v === "number") t = Number.isInteger(v) ? "integer" : "number";
485
+ else if (typeof v === "boolean") t = "boolean";
486
+ else if (typeof v === "string") t = "string";
487
+ else if (v instanceof Date) t = v.getTime() % 864e5 === 0 ? "date" : "datetime";
488
+ else if (Array.isArray(v) || ArrayBuffer.isView(v)) t = ArrayBuffer.isView(v) && !(v instanceof DataView) && v.BYTES_PER_ELEMENT === 1 ? "string" : "array";
489
+ else t = "object";
490
+ if (type === void 0) type = t;
491
+ else if (type !== t) {
492
+ if (type === "integer" && t === "number" || type === "number" && t === "integer") type = "number";
493
+ else if (type === "date" && t === "datetime" || type === "datetime" && t === "date") type = "datetime";
494
+ else return "string";
495
+ }
496
+ }
497
+ return type ?? "string";
498
+ }
499
+ function clipString(s, max = MAX_CELL) {
500
+ return s.length > max ? `${s.slice(0, max - 1)}\u2026` : s;
501
+ }
502
+ function toDate(value) {
503
+ if (value instanceof Date) return Number.isNaN(value.getTime()) ? null : value;
504
+ if (typeof value === "number" || typeof value === "bigint") {
505
+ const d = new Date(Number(value));
506
+ return Number.isNaN(d.getTime()) ? null : d;
507
+ }
508
+ return null;
509
+ }
510
+ function plain(value, depth = 0) {
511
+ if (value === null || value === void 0) return null;
512
+ if (typeof value === "bigint") return Number.isSafeInteger(Number(value)) ? Number(value) : String(value);
513
+ if (typeof value === "number") return Number.isFinite(value) ? value : String(value);
514
+ if (typeof value === "string") return clipString(value);
515
+ if (typeof value !== "object") return value;
516
+ if (value instanceof Date) return Number.isNaN(value.getTime()) ? null : value.toISOString();
517
+ if (depth > 8) return "\u2026";
518
+ if (ArrayBuffer.isView(value) && !(value instanceof DataView) && value.BYTES_PER_ELEMENT === 1) {
519
+ return `<${value.byteLength} bytes>`;
520
+ }
521
+ if (Array.isArray(value) || ArrayBuffer.isView(value)) return Array.from(value, (v) => plain(v, depth + 1));
522
+ if (typeof value.toJSON === "function") return plain(value.toJSON(), depth + 1);
523
+ if (typeof value.toArray === "function") return plain(value.toArray(), depth + 1);
524
+ const out = {};
525
+ for (const [k, v] of Object.entries(value)) out[k] = plain(v, depth + 1);
526
+ return out;
527
+ }
528
+ function encodeValue(value, type, declared) {
529
+ if (value === null || value === void 0) return null;
530
+ if (type === "time" && (typeof value === "bigint" || typeof value === "number")) return timeText(value, declared);
531
+ if (type === "date" || type === "datetime") {
532
+ const d = toDate(value);
533
+ if (d) return type === "date" ? d.toISOString().slice(0, 10) : d.toISOString();
534
+ }
535
+ if (typeof value === "bigint") return Number.isSafeInteger(Number(value)) ? Number(value) : String(value);
536
+ if (typeof value === "number") {
537
+ if (Number.isNaN(value)) return "NaN";
538
+ if (!Number.isFinite(value)) return value > 0 ? "Infinity" : "-Infinity";
539
+ return value;
540
+ }
541
+ if (typeof value === "string") return clipString(value);
542
+ if (typeof value === "boolean") return value;
543
+ return plain(value);
544
+ }
545
+ function timeText(value, declared = "") {
546
+ const d = String(declared);
547
+ const unit = /NANOSECOND|^Time$/.test(d) || /_NS$/i.test(d) ? 1000000000n : /MILLISECOND/i.test(d) ? 1000n : /<SECOND/i.test(d) ? 1n : 1000000n;
548
+ const total = typeof value === "bigint" ? value : BigInt(Math.trunc(value));
549
+ const seconds = total / unit;
550
+ const fraction = total % unit;
551
+ const pad = (n) => String(n).padStart(2, "0");
552
+ const text = `${pad(seconds / 3600n)}:${pad(seconds / 60n % 60n)}:${pad(seconds % 60n)}`;
553
+ if (fraction === 0n) return text;
554
+ return `${text}.${String(fraction).padStart(String(unit).length - 1, "0").replace(/0+$/, "")}`;
555
+ }
556
+ function uniqueNames(names) {
557
+ const seen = /* @__PURE__ */ new Set();
558
+ return names.map((name) => {
559
+ let unique = name;
560
+ for (let i = 1; seen.has(unique); i++) unique = `${name}_${i}`;
561
+ seen.add(unique);
562
+ return unique;
563
+ });
564
+ }
565
+ function fromObjects(rows, names) {
566
+ let columns = names;
567
+ if (!columns) {
568
+ const seen = /* @__PURE__ */ new Set();
569
+ for (const row of rows.slice(0, DEFAULT_LIMIT)) {
570
+ if (isObject(row)) for (const key2 of Object.keys(row)) seen.add(key2);
571
+ }
572
+ columns = [...seen];
573
+ }
574
+ return {
575
+ columns: columns.map((name) => ({ name: String(name) })),
576
+ total: rows.length,
577
+ read: (start, end) => rows.slice(start, end).map((row) => columns.map((c) => isObject(row) ? row[c] : void 0))
578
+ };
579
+ }
580
+ function fromArrays(rows, names) {
581
+ const width = names?.length ?? Math.max(0, ...rows.slice(0, DEFAULT_LIMIT).map((r) => Array.isArray(r) ? r.length : 0));
582
+ const columns = names ?? Array.from({ length: width }, (_, i) => String(i));
583
+ return {
584
+ columns: columns.map((name) => ({ name: String(name) })),
585
+ total: rows.length,
586
+ read: (start, end) => rows.slice(start, end).map((row) => columns.map((_, i) => Array.isArray(row) ? row[i] : void 0))
587
+ };
588
+ }
589
+ function fromArrow(table, names) {
590
+ const fields = table.schema.fields.filter((f) => !names || names.includes(f.name));
591
+ const vectors = fields.map((f) => table.getChild(f.name));
592
+ return {
593
+ columns: fields.map((f) => ({ name: String(f.name), declared: String(f.type) })),
594
+ total: table.numRows,
595
+ read(start, end) {
596
+ const out = [];
597
+ for (let i = start; i < Math.min(end, table.numRows); i++) out.push(vectors.map((v) => v?.get(i)));
598
+ return out;
599
+ }
600
+ };
601
+ }
602
+ function fromPolars(df, names) {
603
+ const columns = Object.entries(df.schema).filter(([name]) => !names || names.includes(name)).map(([name, dtype]) => ({ name, declared: polarsDeclared(dtype) }));
604
+ const total = Number.isInteger(df.height) ? df.height : df.toRecords().length;
605
+ const records = (start, end) => typeof df.slice === "function" ? df.slice(start, end - start).toRecords() : df.toRecords().slice(start, end);
606
+ return {
607
+ columns,
608
+ total,
609
+ read: (start, end) => records(start, Math.min(end, total)).map((row) => columns.map((c) => row[c.name]))
610
+ };
611
+ }
612
+ function fromDuckDB(result, names) {
613
+ const types = result.columnTypes();
614
+ const all = result.columnNames().map((name, i) => ({ name, declared: String(types[i]) }));
615
+ const columns = names ? all.filter((c) => names.includes(c.name)) : all;
616
+ const at = columns.map((c) => all.indexOf(c));
617
+ let rows;
618
+ const get = () => rows ??= result.getRowsJS();
619
+ return {
620
+ columns,
621
+ total: result.currentRowCount,
622
+ read: (start, end) => get().slice(start, end).map((row) => at.map((i) => row[i]))
623
+ };
624
+ }
625
+ function tableSource(value, options = {}) {
626
+ const names = Array.isArray(options.columns) ? options.columns.map(String) : void 0;
627
+ if (isObject(value) && typeof value.toTableSource === "function") return value.toTableSource(names);
628
+ if (isArrowTable(value)) return fromArrow(value, names);
629
+ if (isPolarsFrame(value)) return fromPolars(value, names);
630
+ if (isDuckDBResult(value)) return fromDuckDB(value, names);
631
+ if (isDuckDBShaped(value)) {
632
+ throw new TypeError(typeof value.currentRowCount === "number" ? "jupyter.table: this DuckDB reader hasn't read all its rows; call readAll() first, or use runAndReadAll()" : "jupyter.table: a DuckDB run() result reads its rows asynchronously; use runAndReadAll(), or jupyter.sql");
633
+ }
634
+ if (Array.isArray(value)) {
635
+ const first = value.find((row) => row !== null && row !== void 0);
636
+ return Array.isArray(first) ? fromArrays(value, names) : fromObjects(value, names);
637
+ }
638
+ throw new TypeError(
639
+ "jupyter.table takes an array of rows, an Arrow table, a Polars DataFrame or a DuckDB result"
640
+ );
641
+ }
642
+ function fieldsOf(source, rows) {
643
+ const names = uniqueNames(source.columns.map((c) => c.name));
644
+ return source.columns.map((c, i) => ({
645
+ name: names[i],
646
+ type: schemaType(c.declared) ?? inferType(rows.map((r) => r[i]))
647
+ }));
648
+ }
649
+ function count(n, noun) {
650
+ return `${n.toLocaleString("en-US")} ${n === 1 ? noun : `${noun}s`}`;
651
+ }
652
+ function shortValue(v) {
653
+ if (v === null || v === void 0) return "";
654
+ const s = typeof v === "string" ? v : JSON.stringify(v);
655
+ return clipString(s.replace(/\s+/g, " "), 30);
656
+ }
657
+ function columnStats(rows, i, type) {
658
+ let nulls = 0;
659
+ const values = [];
660
+ for (const row of rows) {
661
+ const v = row[i];
662
+ if (v === null) nulls++;
663
+ else values.push(v);
664
+ }
665
+ let range = "";
666
+ if (values.length) {
667
+ if (type === "integer" || type === "number") {
668
+ const nums = values.map(Number).filter(Number.isFinite);
669
+ if (nums.length) {
670
+ const min = Math.min(...nums);
671
+ const max = Math.max(...nums);
672
+ const fmt = (n) => Number.isInteger(n) && Math.abs(n) >= 1e4 ? n.toLocaleString("en-US") : String(Number(n.toPrecision(6)));
673
+ range = min === max ? fmt(min) : `${fmt(min)} to ${fmt(max)}`;
674
+ }
675
+ } else if (type === "date" || type === "datetime" || type === "time") {
676
+ const sorted = values.map(String).sort();
677
+ range = sorted[0] === sorted.at(-1) ? sorted[0] : `${sorted[0]} to ${sorted.at(-1)}`;
678
+ } else if (type === "boolean") {
679
+ const t = values.filter((v) => v === true).length;
680
+ range = `${t} true, ${values.length - t} false`;
681
+ } else {
682
+ const distinct = new Set(values.map((v) => typeof v === "string" ? v : JSON.stringify(v))).size;
683
+ range = count(distinct, "value");
684
+ }
685
+ }
686
+ return [range, nulls ? count(nulls, "null") : ""].filter(Boolean).join(", ");
687
+ }
688
+ function htmlTable(fields, head, tail, total, title) {
689
+ let html = "<table>";
690
+ if (title) html += `<caption>${escapeHTML(title)}</caption>`;
691
+ html += "<thead><tr>";
692
+ for (const f of fields) {
693
+ html += `<th>${escapeHTML(f.name)}<br><small>${escapeHTML(f.type)}</small></th>`;
694
+ }
695
+ html += "</tr></thead><tbody>";
696
+ const row = (r) => {
697
+ let s = "<tr>";
698
+ fields.forEach((f, i) => {
699
+ const v = r[i];
700
+ const text = v === null ? "" : typeof v === "string" ? v : JSON.stringify(v);
701
+ const align = f.type === "integer" || f.type === "number" ? ' style="text-align:right"' : "";
702
+ s += `<td${align}>${escapeHTML(text)}</td>`;
703
+ });
704
+ return `${s}</tr>`;
705
+ };
706
+ for (const r of head) html += row(r);
707
+ const hidden = total - head.length - tail.length;
708
+ if (hidden > 0) html += `<tr><td colspan="${Math.max(1, fields.length)}">\u2026 ${count(hidden, "more row")}</td></tr>`;
709
+ for (const r of tail) html += row(r);
710
+ return `${html}</tbody></table>`;
711
+ }
712
+ function textTable(fields, numbered) {
713
+ const cells = numbered.map(([n, r]) => [String(n), ...r.map(shortValue)]);
714
+ const widths = [0, ...fields.map(() => 0)];
715
+ for (const row of cells) row.forEach((c, i) => widths[i] = Math.max(widths[i], c.length));
716
+ return cells.map((row) => ` ${row.map((c, i) => c.padEnd(widths[i])).join(" | ").trimEnd()}`);
717
+ }
718
+ function tableBundle(source, options = {}) {
719
+ const requested = options.limit ?? DEFAULT_LIMIT;
720
+ if (!Number.isInteger(requested) || requested < 0) throw new TypeError("jupyter.table: limit must be a whole number");
721
+ const limit = Math.min(requested, MAX_LIMIT);
722
+ const title = typeof options.title === "string" && options.title !== "" ? options.title : void 0;
723
+ const total = source.total;
724
+ const raw = source.read(0, Math.min(limit, total));
725
+ const fields = fieldsOf(source, raw);
726
+ const encode = (r) => fields.map((f, i) => encodeValue(r[i], f.type, source.columns[i].declared));
727
+ const utf8 = new TextEncoder();
728
+ const encoded = [];
729
+ const data = [];
730
+ let bytes = 0;
731
+ for (const r of raw) {
732
+ const values = encode(r);
733
+ const obj = {};
734
+ fields.forEach((f, i) => Object.defineProperty(obj, f.name, { value: values[i], enumerable: true, writable: true, configurable: true }));
735
+ bytes += utf8.encode(JSON.stringify(obj)).length + 1;
736
+ if (bytes > MAX_BYTES && data.length > 0) break;
737
+ encoded.push(values);
738
+ data.push(obj);
739
+ }
740
+ const shown = data.length;
741
+ const split = total > HTML_ALL;
742
+ const htmlHead = split ? encoded.slice(0, HTML_HEAD) : encoded.slice(0, total);
743
+ const htmlTail = split ? source.read(total - HTML_TAIL, total).map(encode) : [];
744
+ const textSplit = total > TEXT_HEAD + TEXT_TAIL;
745
+ const textHeadRows = encoded.slice(0, textSplit ? TEXT_HEAD : total);
746
+ const textTail = !textSplit ? [] : htmlTail.length ? htmlTail.slice(-TEXT_TAIL) : source.read(total - TEXT_TAIL, total).map(encode);
747
+ const lines = [];
748
+ const name = title ? `Table "${clipString(title, 80)}"` : "Table";
749
+ const shape = `${count(total, "row")}, ${count(fields.length, "column")}`;
750
+ lines.push(`${name} (${shape}${shown < total ? `; ${shown.toLocaleString("en-US")} shown` : ""})`);
751
+ if (fields.length) {
752
+ lines.push(shown < total ? `Columns (ranges over the first ${shown.toLocaleString("en-US")} of ${total.toLocaleString("en-US")} rows):` : "Columns:");
753
+ const nameWidth = Math.min(30, Math.max(...fields.slice(0, TEXT_COLUMNS).map((f) => f.name.length)));
754
+ const typeWidth = Math.max(...fields.slice(0, TEXT_COLUMNS).map((f) => f.type.length));
755
+ fields.slice(0, TEXT_COLUMNS).forEach((f, i) => {
756
+ const stats = columnStats(encoded, i, f.type);
757
+ lines.push(` ${clipString(f.name, 30).padEnd(nameWidth)} ${f.type.padEnd(typeWidth)} ${stats}`.trimEnd());
758
+ });
759
+ if (fields.length > TEXT_COLUMNS) lines.push(` \u2026 ${count(fields.length - TEXT_COLUMNS, "more column")}`);
760
+ const numbered = [
761
+ ...textHeadRows.map((r, i) => [i, r]),
762
+ ...textTail.map((r, i) => [total - textTail.length + i, r])
763
+ ];
764
+ if (numbered.length) {
765
+ lines.push("Rows:");
766
+ const table = textTable(fields.slice(0, TEXT_COLUMNS), numbered.map(([n, r]) => [n, r.slice(0, TEXT_COLUMNS)]));
767
+ if (textTail.length) table.splice(textHeadRows.length, 0, " \u2026");
768
+ lines.push(...table);
769
+ }
770
+ }
771
+ let text = lines.join("\n");
772
+ if (text.length > TEXT_MAX) text = `${text.slice(0, TEXT_MAX - 1)}\u2026`;
773
+ const resource = { schema: { fields }, data, ganymede: { rows: total, shown } };
774
+ if (title) resource.ganymede.title = title;
775
+ return {
776
+ [TABLE_MIME]: resource,
777
+ "text/html": htmlTable(fields, htmlHead, htmlTail, total, title),
778
+ "text/plain": text
779
+ };
780
+ }
781
+
782
+ // runtime/sql.mjs
783
+ var NODE_API = "npm:@duckdb/node-api@1.5.6-r.2";
784
+ var DUCKDB_WASM = "npm:@duckdb/duckdb-wasm@1.29.0";
422
785
  var $display = /* @__PURE__ */ Symbol.for("Jupyter.display");
423
- var rawToEntity = /* @__PURE__ */ new Map([
786
+ var RAW = /* @__PURE__ */ Symbol("cdpk.sql.raw");
787
+ function jsValue(value, declared) {
788
+ if (/^time(?!stamp)/i.test(declared ?? "") && (typeof value === "bigint" || typeof value === "number")) {
789
+ return timeText(value, declared);
790
+ }
791
+ if (typeof value === "bigint") return Number.isSafeInteger(Number(value)) ? Number(value) : value;
792
+ if (typeof value === "number" && /^(date|timestamp)/i.test(declared ?? "")) return new Date(value);
793
+ if (Array.isArray(value)) return value.map((v) => jsValue(v));
794
+ if (value !== null && typeof value === "object" && !(value instanceof Date) && !ArrayBuffer.isView(value)) {
795
+ if (typeof value.toJSON === "function" && !(Symbol.iterator in value) && Object.getPrototypeOf(value) !== Object.prototype) {
796
+ return jsValue(value.toJSON());
797
+ }
798
+ if (typeof value.toArray === "function") return jsValue(Array.from(value.toArray()));
799
+ const out = {};
800
+ for (const [k, v] of Object.entries(value)) out[k] = jsValue(v);
801
+ return out;
802
+ }
803
+ return value;
804
+ }
805
+ function decimalValue(words, scale) {
806
+ if (typeof words === "bigint" && scale === 0) return jsValue(words);
807
+ if (typeof words === "number" || typeof words === "bigint") return Number(words) / 10 ** scale;
808
+ const list2 = Array.from(words);
809
+ let big = 0n;
810
+ list2.forEach((w, i) => big |= BigInt(w >>> 0) << BigInt(32 * i));
811
+ const bits = BigInt(32 * list2.length);
812
+ if (list2.length && list2.at(-1) >>> 31) big -= 1n << bits;
813
+ return scale === 0 ? jsValue(big) : Number(big) / 10 ** scale;
814
+ }
815
+ var SqlResult = class {
816
+ /**
817
+ * @param {{ name: string, type: string }[]} columns
818
+ * @param {Record<string, unknown>[]} rows
819
+ */
820
+ constructor(columns, rows) {
821
+ this.columns = columns;
822
+ this.rows = rows;
823
+ }
824
+ /** table.mjs reads this instead of guessing at the shape. */
825
+ toTableSource(names) {
826
+ const columns = names ? this.columns.filter((c) => names.includes(c.name)) : this.columns;
827
+ return {
828
+ columns: columns.map((c) => ({ name: c.name, declared: c.type })),
829
+ total: this.rows.length,
830
+ read: (start, end) => this.rows.slice(start, end).map((row) => columns.map((c) => row[c.name]))
831
+ };
832
+ }
833
+ [$display]() {
834
+ return tableBundle(tableSource(this));
835
+ }
836
+ };
837
+ function toParam(value) {
838
+ if (value === void 0) return null;
839
+ if (value instanceof Date) return value.toISOString();
840
+ if (typeof value === "object" && value !== null && !ArrayBuffer.isView(value)) return JSON.stringify(value);
841
+ return value;
842
+ }
843
+ function compose(strings, values) {
844
+ let text = strings[0];
845
+ const params = [];
846
+ values.forEach((value, i) => {
847
+ if (value !== null && typeof value === "object" && RAW in value) text += value[RAW];
848
+ else {
849
+ text += "?";
850
+ params.push(toParam(value));
851
+ }
852
+ text += strings[i + 1];
853
+ });
854
+ return { text, params };
855
+ }
856
+ function quoteIdent(name) {
857
+ return `"${String(name).replaceAll('"', '""')}"`;
858
+ }
859
+ function quoteString(text) {
860
+ return `'${String(text).replaceAll("'", "''")}'`;
861
+ }
862
+ function sqlLiteral(value) {
863
+ const text = literalText(value);
864
+ return text.startsWith("-") ? `(${text})` : text;
865
+ }
866
+ function literalText(value) {
867
+ if (value === null || value === void 0) return "NULL";
868
+ if (typeof value === "boolean") return value ? "TRUE" : "FALSE";
869
+ if (typeof value === "bigint") return `${value}`;
870
+ if (typeof value === "number") {
871
+ if (Number.isSafeInteger(value)) return String(value);
872
+ return Number.isFinite(value) ? `${value}::DOUBLE` : `'${value}'::DOUBLE`;
873
+ }
874
+ if (value instanceof Uint8Array) {
875
+ return `'${Array.from(value, (b) => `\\x${b.toString(16).padStart(2, "0")}`).join("")}'::BLOB`;
876
+ }
877
+ if (ArrayBuffer.isView(value)) return `[${Array.from(value, (v) => sqlLiteral(v)).join(", ")}]`;
878
+ return quoteString(value);
879
+ }
880
+ function inlineParams(text, params) {
881
+ if (params.length === 0) return text;
882
+ let out = "";
883
+ let next = 0;
884
+ for (let i = 0; i < text.length; i++) {
885
+ const c = text[i];
886
+ const end = c === "'" || c === '"' ? text.indexOf(c, i + 1) : c === "-" && text[i + 1] === "-" ? text.indexOf("\n", i) : c === "/" && text[i + 1] === "*" ? text.indexOf("*/", i + 2) + 1 : void 0;
887
+ if (end !== void 0) {
888
+ const stop = end < 0 || end === 0 ? text.length : end + 1;
889
+ out += text.slice(i, stop);
890
+ i = stop - 1;
891
+ } else if (c === "?") {
892
+ if (next >= params.length) throw new RangeError("jupyter.sql: more ? placeholders than parameters");
893
+ if (out.endsWith("'") || out.endsWith('"')) out += " ";
894
+ out += sqlLiteral(params[next++]);
895
+ } else out += c;
896
+ }
897
+ if (next < params.length) throw new RangeError("jupyter.sql: more parameters than ? placeholders");
898
+ return out;
899
+ }
900
+ var DUCK_TYPES = {
901
+ integer: "BIGINT",
902
+ number: "DOUBLE",
903
+ boolean: "BOOLEAN",
904
+ string: "VARCHAR",
905
+ date: "DATE",
906
+ datetime: "TIMESTAMP",
907
+ time: "TIME",
908
+ array: "JSON",
909
+ object: "JSON",
910
+ duration: "VARCHAR"
911
+ };
912
+ function createSql({ importModule, signal, openEngine }) {
913
+ let engine = null;
914
+ const open = () => engine ??= (openEngine ?? (() => defaultEngine(importModule)))().catch((err) => {
915
+ engine = null;
916
+ throw err;
917
+ });
918
+ async function run(text, params) {
919
+ const s = signal?.();
920
+ const e = await open();
921
+ if (s?.aborted) throw s.reason;
922
+ const onAbort = () => e.interrupt?.();
923
+ s?.addEventListener("abort", onAbort, { once: true });
924
+ try {
925
+ return await e.query(text, params);
926
+ } finally {
927
+ s?.removeEventListener("abort", onAbort);
928
+ }
929
+ }
930
+ function sql(strings, ...values) {
931
+ if (!Array.isArray(strings) || !Array.isArray(strings.raw)) {
932
+ return Promise.reject(new TypeError("jupyter.sql is a tag: jupyter.sql`select ...`; for a string, use jupyter.sql.query(text, params)"));
933
+ }
934
+ const { text, params } = compose(strings, values);
935
+ return run(text, params);
936
+ }
937
+ sql.query = (text, params = []) => {
938
+ if (typeof text !== "string") return Promise.reject(new TypeError("jupyter.sql.query: text must be a string"));
939
+ return run(text, params.map(toParam));
940
+ };
941
+ sql.raw = (text) => ({ [RAW]: String(text) });
942
+ sql.load = async (name, source) => {
943
+ if (typeof name !== "string" || name === "") throw new TypeError("jupyter.sql.load: name must be a non-empty string");
944
+ const table = quoteIdent(name);
945
+ if (typeof source === "string") {
946
+ await run(`create or replace table ${table} as select * from ${quoteString(source)}`, []);
947
+ return;
948
+ }
949
+ const { structure, json } = rowsAsJson(source);
950
+ if (structure === null) {
951
+ throw new TypeError("jupyter.sql.load: the rows have no columns");
952
+ }
953
+ await run(
954
+ `create or replace table ${table} as select unnest(from_json(?, ?), recursive := true)`,
955
+ [json, structure]
956
+ );
957
+ };
958
+ sql.close = async () => {
959
+ const e = engine;
960
+ engine = null;
961
+ if (e) await (await e).close();
962
+ };
963
+ sql.connection = async () => (await open()).connection;
964
+ return sql;
965
+ }
966
+ function loadValue(value, type, depth = 0) {
967
+ if (value === null || value === void 0) return null;
968
+ if (type === "date" || type === "datetime") {
969
+ const d = value instanceof Date ? value : typeof value === "number" || typeof value === "bigint" ? new Date(Number(value)) : null;
970
+ if (d) return Number.isNaN(d.getTime()) ? null : type === "date" ? d.toISOString().slice(0, 10) : d.toISOString();
971
+ }
972
+ if (typeof value === "bigint") return Number.isSafeInteger(Number(value)) ? Number(value) : String(value);
973
+ if (typeof value === "number") return Number.isFinite(value) ? value : null;
974
+ if (typeof value !== "object") return value;
975
+ if (value instanceof Date) return Number.isNaN(value.getTime()) ? null : value.toISOString();
976
+ if (depth > 64) throw new TypeError("jupyter.sql.load: a value is nested more than 64 levels deep");
977
+ if (ArrayBuffer.isView(value) && !(value instanceof DataView) && value.BYTES_PER_ELEMENT === 1) {
978
+ let text = "";
979
+ for (const byte of value) text += String.fromCharCode(byte);
980
+ return btoa(text);
981
+ }
982
+ if (Array.isArray(value) || ArrayBuffer.isView(value)) return Array.from(value, (v) => loadValue(v, void 0, depth + 1));
983
+ if (typeof value.toJSON === "function" && Object.getPrototypeOf(value) !== Object.prototype) {
984
+ return loadValue(value.toJSON(), void 0, depth + 1);
985
+ }
986
+ const out = {};
987
+ for (const [k, v] of Object.entries(value)) out[k] = loadValue(v, void 0, depth + 1);
988
+ return out;
989
+ }
990
+ function rowsAsJson(source) {
991
+ const src = tableSource(source);
992
+ const raw = src.read(0, src.total);
993
+ const fields = fieldsOf(src, raw);
994
+ if (fields.length === 0) return { structure: null, json: "[]" };
995
+ const rows = raw.map((r) => {
996
+ const obj = {};
997
+ fields.forEach((f, i) => {
998
+ obj[f.name] = loadValue(r[i], f.type);
999
+ });
1000
+ return obj;
1001
+ });
1002
+ const structure = {};
1003
+ for (const f of fields) structure[f.name] = DUCK_TYPES[f.type] ?? "VARCHAR";
1004
+ return { structure: JSON.stringify([structure]), json: JSON.stringify(rows) };
1005
+ }
1006
+ function inWorker() {
1007
+ return typeof globalThis.WorkerGlobalScope === "function" && globalThis instanceof globalThis.WorkerGlobalScope;
1008
+ }
1009
+ function hasNativeRuntime() {
1010
+ return typeof globalThis.Deno?.version?.deno === "string" || typeof globalThis.Bun?.version === "string" || typeof globalThis.process?.versions?.node === "string" && !inWorker();
1011
+ }
1012
+ async function defaultEngine(importModule) {
1013
+ if (hasNativeRuntime()) return nativeEngine(importModule);
1014
+ if (inWorker() && typeof Worker === "function") return wasmEngine(importModule);
1015
+ throw new Error("jupyter.sql needs Node, Deno, Bun or the browser target; workerd and celld have no DuckDB");
1016
+ }
1017
+ async function nativeEngine(importModule) {
1018
+ const { DuckDBInstance, blobValue } = await importModule(NODE_API);
1019
+ const db = await DuckDBInstance.create(":memory:");
1020
+ const conn = await db.connect();
1021
+ return {
1022
+ connection: conn,
1023
+ interrupt: () => conn.interrupt?.(),
1024
+ async query(text, params) {
1025
+ const bound = params.map((p) => p instanceof Uint8Array && blobValue ? blobValue(p) : p);
1026
+ const result = await conn.runAndReadAll(text, bound);
1027
+ const types = result.columnTypes();
1028
+ const columns = uniqueNames(result.columnNames()).map((name, i) => ({ name, type: String(types[i]) }));
1029
+ const rows = result.getRowsJS().map((row) => {
1030
+ const out = {};
1031
+ columns.forEach((c, j) => out[c.name] = jsValue(row[j], c.type));
1032
+ return out;
1033
+ });
1034
+ return new SqlResult(columns, rows);
1035
+ },
1036
+ async close() {
1037
+ conn.closeSync?.();
1038
+ db.closeSync?.();
1039
+ }
1040
+ };
1041
+ }
1042
+ async function wasmEngine(importModule) {
1043
+ const duckdb = await importModule(DUCKDB_WASM);
1044
+ const bundle = await duckdb.selectBundle(duckdb.getJsDelivrBundles());
1045
+ const loader = URL.createObjectURL(new Blob([`importScripts(${JSON.stringify(bundle.mainWorker)});`], { type: "text/javascript" }));
1046
+ const worker = new Worker(loader);
1047
+ const db = new duckdb.AsyncDuckDB(new duckdb.VoidLogger(), worker);
1048
+ await db.instantiate(bundle.mainModule, bundle.pthreadWorker);
1049
+ URL.revokeObjectURL(loader);
1050
+ const conn = await db.connect();
1051
+ let cancelled = false;
1052
+ return {
1053
+ connection: conn,
1054
+ interrupt: () => {
1055
+ cancelled = true;
1056
+ void conn.cancelSent?.();
1057
+ },
1058
+ async query(text, params) {
1059
+ cancelled = false;
1060
+ const reader = await conn.send(inlineParams(text, params), true);
1061
+ const stop = () => {
1062
+ if (cancelled) throw new DOMException("The query was interrupted", "AbortError");
1063
+ };
1064
+ try {
1065
+ stop();
1066
+ const columns = [];
1067
+ const rows = [];
1068
+ let decimal = [];
1069
+ for await (const batch of reader) {
1070
+ if (columns.length === 0 && batch.schema.fields.length) {
1071
+ const fields = batch.schema.fields;
1072
+ uniqueNames(fields.map((f) => f.name)).forEach((name, j) => columns.push({ name, type: String(fields[j].type) }));
1073
+ decimal = fields.map((f) => /^decimal/i.test(String(f.type)) ? f.type.scale ?? 0 : null);
1074
+ }
1075
+ stop();
1076
+ const vectors = columns.map((_, j) => batch.getChildAt(j));
1077
+ for (let i = 0; i < batch.numRows; i++) {
1078
+ const row = {};
1079
+ columns.forEach((c, j) => {
1080
+ const v = vectors[j]?.get(i);
1081
+ row[c.name] = decimal[j] !== null && v != null ? decimalValue(v, decimal[j]) : jsValue(v, c.type);
1082
+ });
1083
+ rows.push(row);
1084
+ }
1085
+ }
1086
+ if (columns.length === 0 && reader.schema?.fields) {
1087
+ const fields = reader.schema.fields;
1088
+ uniqueNames(fields.map((f) => f.name)).forEach((name, j) => columns.push({ name, type: String(fields[j].type) }));
1089
+ }
1090
+ return new SqlResult(columns, rows);
1091
+ } catch (err) {
1092
+ await Promise.resolve((reader.cancel ?? reader.return)?.call(reader)).catch(() => {
1093
+ });
1094
+ throw err;
1095
+ }
1096
+ },
1097
+ async close() {
1098
+ await conn.close();
1099
+ await db.terminate();
1100
+ worker.terminate();
1101
+ }
1102
+ };
1103
+ }
1104
+
1105
+ // runtime/chart.mjs
1106
+ var VEGA = "npm:vega@6";
1107
+ var VEGA_LITE = "npm:vega-lite@6";
1108
+ var LINKEDOM = "npm:linkedom@0.18";
1109
+ var WARN_BYTES = 2e6;
1110
+ var $display2 = /* @__PURE__ */ Symbol.for("Jupyter.display");
1111
+ var OTHER_LEGENDS = ["opacity", "r", "symbol"];
1112
+ var FONT = "system-ui, sans-serif";
1113
+ var rawToEntity2 = { "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" };
1114
+ var esc = (s) => String(s).replace(/[&<>"']/g, (c) => rawToEntity2[c]);
1115
+ function isObject2(value) {
1116
+ return value !== null && typeof value === "object";
1117
+ }
1118
+ function isPlotModule(value) {
1119
+ return (isObject2(value) || typeof value === "function") && typeof value.plot === "function" && typeof value.barY === "function";
1120
+ }
1121
+ var SPEC_KEYS = ["$schema", "mark", "marks", "layer", "spec", "concat", "hconcat", "vconcat", "facet", "repeat"];
1122
+ function specOf(value) {
1123
+ const spec = isObject2(value) && typeof value.toSpec === "function" ? value.toSpec() : value;
1124
+ if (!isObject2(spec) || Array.isArray(spec) || !SPEC_KEYS.some((key2) => key2 in spec)) {
1125
+ throw new TypeError(
1126
+ "jupyter.chart takes a Vega-Lite or Vega spec, the Plot module and Plot options, or an SVG element"
1127
+ );
1128
+ }
1129
+ return spec;
1130
+ }
1131
+ function isSvgElement(value) {
1132
+ return isObject2(value) && typeof value.outerHTML === "string" && /^<svg\b/i.test(value.outerHTML);
1133
+ }
1134
+ function isVegaSpec(spec) {
1135
+ return typeof spec.$schema === "string" && /\/schema\/vega\/v\d/.test(spec.$schema);
1136
+ }
1137
+ function svgSize(svg) {
1138
+ const open = /<svg\b[^>]*>/i.exec(svg)?.[0] ?? "";
1139
+ const attr = (name) => {
1140
+ const m = new RegExp(`\\s${name}="([^"]*)"`, "i").exec(open);
1141
+ return m ? m[1] : void 0;
1142
+ };
1143
+ let width = parseFloat(attr("width") ?? "");
1144
+ let height = parseFloat(attr("height") ?? "");
1145
+ const box = attr("viewBox")?.trim().split(/[\s,]+/).map(Number);
1146
+ if (!Number.isFinite(width) && box?.length === 4) width = box[2];
1147
+ if (!Number.isFinite(height) && box?.length === 4) height = box[3];
1148
+ return { width: Number.isFinite(width) ? width : 640, height: Number.isFinite(height) ? height : 400 };
1149
+ }
1150
+ function withNamespace(svg) {
1151
+ return /<svg\b[^>]*\sxmlns=/i.test(svg) ? svg : svg.replace(/<svg\b/i, '<svg xmlns="http://www.w3.org/2000/svg"');
1152
+ }
1153
+ function hash(text) {
1154
+ let h = 2166136261;
1155
+ for (let i = 0; i < text.length; i++) h = Math.imul(h ^ text.charCodeAt(i), 16777619);
1156
+ return (h >>> 0).toString(36);
1157
+ }
1158
+ var textWidth = (text, size) => String(text).length * size * 0.6;
1159
+ function legendSVG(scale, width, form) {
1160
+ if (!isObject2(scale) || typeof scale.apply !== "function" || !Array.isArray(scale.domain)) return { svg: "", height: 0 };
1161
+ const label = typeof scale.label === "string" && scale.label ? scale.label : "";
1162
+ const parts = [];
1163
+ let y = 0;
1164
+ if (label) {
1165
+ parts.push(`<text x="0" y="10" font-weight="bold">${esc(label)}</text>`);
1166
+ y = 16;
1167
+ }
1168
+ const continuous = form === "ramp" || form !== "swatches" && /^(linear|log|pow|sqrt|symlog|utc|time|sequential|diverging|quantize|threshold|quantile)/.test(String(scale.type));
1169
+ if (!continuous) {
1170
+ let x = 0;
1171
+ let row = y;
1172
+ for (const value of scale.domain) {
1173
+ const text = String(value);
1174
+ const w = 14 + textWidth(text, 10) + 12;
1175
+ if (x > 0 && x + w > width) {
1176
+ x = 0;
1177
+ row += 16;
1178
+ }
1179
+ parts.push(
1180
+ `<rect x="${x}" y="${row}" width="10" height="10" fill="${esc(scale.apply(value))}"/><text x="${x + 14}" y="${row + 9}">${esc(text)}</text>`
1181
+ );
1182
+ x += w;
1183
+ }
1184
+ return { svg: parts.join(""), height: row + 14 };
1185
+ }
1186
+ const [lo, hi] = [scale.domain[0], scale.domain.at(-1)];
1187
+ const n = 16;
1188
+ const stops = [];
1189
+ const range = Array.isArray(scale.range) && scale.range.length >= 2 && scale.range.every((r) => typeof r === "number") ? [scale.range[0], scale.range.at(-1)] : void 0;
1190
+ const numeric = (v) => v instanceof Date ? v.getTime() : typeof v === "number" ? v : NaN;
1191
+ const [a, b] = [numeric(lo), numeric(hi)];
1192
+ for (let i = 0; i <= n; i++) {
1193
+ const t = i / n;
1194
+ let color;
1195
+ if (typeof scale.interpolate === "function" && range) color = scale.interpolate(range[0] + (range[1] - range[0]) * t);
1196
+ else if (Number.isFinite(a) && Number.isFinite(b)) {
1197
+ const v = a + (b - a) * t;
1198
+ color = scale.apply(lo instanceof Date ? new Date(v) : v);
1199
+ } else color = scale.apply(i === 0 ? lo : hi);
1200
+ stops.push(`<stop offset="${t * 100}%" stop-color="${esc(color)}"/>`);
1201
+ }
1202
+ const id = `ramp-${hash(stops.join(""))}`;
1203
+ const rampWidth = Math.min(240, width);
1204
+ const fmt = (v) => v instanceof Date ? v.toISOString().slice(0, 10) : typeof v === "number" ? String(Number(v.toPrecision(4))) : String(v);
1205
+ parts.push(
1206
+ `<defs><linearGradient id="${id}">${stops.join("")}</linearGradient></defs><rect x="0" y="${y}" width="${rampWidth}" height="10" fill="url(#${id})"/><text x="0" y="${y + 22}">${esc(fmt(lo))}</text><text x="${rampWidth}" y="${y + 22}" text-anchor="end">${esc(fmt(hi))}</text>`
1207
+ );
1208
+ return { svg: parts.join(""), height: y + 26 };
1209
+ }
1210
+ function composeSVG(inner, { title, subtitle, caption, legend }) {
1211
+ const { width, height } = svgSize(inner);
1212
+ const head = [];
1213
+ let y = 0;
1214
+ if (title) {
1215
+ head.push(`<text x="0" y="${y + 16}" font-size="16" font-weight="bold">${esc(title)}</text>`);
1216
+ y += 24;
1217
+ }
1218
+ if (subtitle) {
1219
+ head.push(`<text x="0" y="${y + 13}" font-size="13">${esc(subtitle)}</text>`);
1220
+ y += 20;
1221
+ }
1222
+ if (legend?.svg) {
1223
+ head.push(`<g transform="translate(0,${y})" font-size="10">${legend.svg}</g>`);
1224
+ y += legend.height + 8;
1225
+ }
1226
+ const body = withNamespace(inner).replace(/<svg\b/i, `<svg x="0" y="${y}"`);
1227
+ let total = y + height;
1228
+ let foot = "";
1229
+ if (caption) {
1230
+ foot = `<text x="0" y="${total + 16}" font-size="12">${esc(caption)}</text>`;
1231
+ total += 24;
1232
+ }
1233
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${total}" viewBox="0 0 ${width} ${total}" font-family="${FONT}" fill="currentColor">${head.join("")}${body}${foot}</svg>`;
1234
+ }
1235
+ function markName(mark) {
1236
+ if (typeof mark?.ariaLabel === "string" && mark.ariaLabel) return mark.ariaLabel;
1237
+ return mark?.constructor?.name?.toLowerCase() ?? "mark";
1238
+ }
1239
+ function describeScale(name, scale) {
1240
+ if (!isObject2(scale)) return null;
1241
+ const label = typeof scale.label === "string" && scale.label ? scale.label : "";
1242
+ const domain = Array.isArray(scale.domain) ? scale.domain : [];
1243
+ let range = "";
1244
+ if (domain.length && domain.every((v) => typeof v === "number" || v instanceof Date) && !/ordinal|band|point/.test(scale.type)) {
1245
+ const fmt = (v) => v instanceof Date ? v.toISOString().slice(0, 10) : String(Number(v.toPrecision(4)));
1246
+ range = `${fmt(domain[0])} to ${fmt(domain.at(-1))}`;
1247
+ } else if (domain.length) {
1248
+ range = `${domain.length} ${domain.length === 1 ? "value" : "values"}`;
1249
+ }
1250
+ const details = [scale.type, range].filter(Boolean).join(", ");
1251
+ return `${name}${label ? `=${label}` : ""}${details ? ` (${details})` : ""}`;
1252
+ }
1253
+ function summarizePlot(element, options) {
1254
+ const marks = (Array.isArray(options.marks) ? options.marks.flat(Infinity) : []).filter(isObject2);
1255
+ const names = [...new Set(marks.map(markName))];
1256
+ const scales = ["x", "y", "fx", "fy", "color", "r", "opacity", "symbol"].map((name) => typeof element?.scale === "function" ? describeScale(name, element.scale(name)) : null).filter(Boolean);
1257
+ const rows = marks.map((m) => Array.isArray(m.data) ? m.data.length : typeof m.data?.length === "number" ? m.data.length : 0);
1258
+ const most = rows.length ? Math.max(...rows) : 0;
1259
+ const title = typeof options.title === "string" && options.title ? ` "${options.title}"` : "";
1260
+ const parts = [...scales];
1261
+ if (most) parts.push(`${most} ${most === 1 ? "row" : "rows"}`);
1262
+ return `Plot ${names.length ? `${names.join(", ")} ` : ""}chart${title}${parts.length ? `: ${parts.join(", ")}` : ""}`;
1263
+ }
1264
+ var INK = "currentColor";
1265
+ var VEGA_CONFIG = {
1266
+ background: "transparent",
1267
+ axis: { labelColor: INK, titleColor: INK, domainColor: INK, tickColor: INK, gridColor: INK, gridOpacity: 0.15 },
1268
+ legend: { labelColor: INK, titleColor: INK },
1269
+ title: { color: INK, subtitleColor: INK },
1270
+ header: { labelColor: INK, titleColor: INK },
1271
+ view: { stroke: null }
1272
+ };
1273
+ function mergeConfig(base, own) {
1274
+ if (!isObject2(own)) return base;
1275
+ const out = { ...base };
1276
+ for (const [key2, value] of Object.entries(own)) {
1277
+ out[key2] = isObject2(value) && isObject2(base[key2]) && !Array.isArray(value) ? mergeConfig(base[key2], value) : value;
1278
+ }
1279
+ return out;
1280
+ }
1281
+ function createChart({ importModule, summarizeSpec, warn = (m) => console.warn(m) }) {
1282
+ let dom;
1283
+ async function document() {
1284
+ if (globalThis.document && typeof globalThis.document.createElementNS === "function") return globalThis.document;
1285
+ dom ??= importModule(LINKEDOM).then(({ parseHTML }) => parseHTML("<!doctype html><html><body></body></html>").document).catch((err) => {
1286
+ dom = void 0;
1287
+ throw err;
1288
+ });
1289
+ return dom;
1290
+ }
1291
+ async function renderVega(spec, options) {
1292
+ const keepSpec = isObject2(options) && options.spec === true;
1293
+ const [vega, vl] = await Promise.all([importModule(VEGA), isVegaSpec(spec) ? null : importModule(VEGA_LITE)]);
1294
+ const config = mergeConfig(VEGA_CONFIG, spec.config);
1295
+ const vgSpec = vl ? vl.compile({ ...spec, config }).spec : { ...spec, config };
1296
+ const view = new vega.View(vega.parse(vgSpec), { renderer: "none" });
1297
+ let svg;
1298
+ try {
1299
+ svg = await view.toSVG();
1300
+ } finally {
1301
+ view.finalize();
1302
+ }
1303
+ const bundle = { "image/svg+xml": withNamespace(svg) };
1304
+ const summary = summarizeSpec?.({ $schema: "https://vega.github.io/schema/vega-lite/v5.json", ...spec });
1305
+ if (summary) bundle["text/plain"] = summary;
1306
+ if (keepSpec) {
1307
+ const v4 = /\/schema\/vega-lite\/v4/.test(String(spec.$schema));
1308
+ bundle[vl ? v4 ? "application/vnd.vegalite.v4+json" : "application/vnd.vegalite.v5+json" : "application/vnd.vega.v5+json"] = spec;
1309
+ }
1310
+ return bundle;
1311
+ }
1312
+ async function renderPlot(Plot, options) {
1313
+ if (!isObject2(options)) throw new TypeError("jupyter.chart(Plot, options): options must be Plot's options object");
1314
+ const { title, subtitle, caption, ...rest } = options;
1315
+ const color = isObject2(rest.color) ? rest.color : void 0;
1316
+ const wantLegend = Boolean(color?.legend);
1317
+ const plotOptions = { ...rest, document: await document(), figure: false };
1318
+ if (color) plotOptions.color = { ...color, legend: false };
1319
+ const others = OTHER_LEGENDS.filter((name) => isObject2(rest[name]) && rest[name].legend);
1320
+ if (others.length) {
1321
+ warn(`jupyter.chart draws only a color legend; the ${others.join(", ")} legend is left out`);
1322
+ }
1323
+ const element = Plot.plot(plotOptions);
1324
+ const inner = element.outerHTML;
1325
+ const { width } = svgSize(inner);
1326
+ const scale = wantLegend && typeof element.scale === "function" ? element.scale("color") : void 0;
1327
+ const form = color?.legend === "ramp" || color?.legend === "swatches" ? color.legend : void 0;
1328
+ const legend = scale ? legendSVG({ ...scale, label: scale.label ?? color.label }, width, form) : void 0;
1329
+ const svg = composeSVG(inner, { title, subtitle, caption, legend });
1330
+ return { "image/svg+xml": withNamespace(svg), "text/plain": summarizePlot(element, options) };
1331
+ }
1332
+ function chart(first, second) {
1333
+ const spec = isPlotModule(first) || isSvgElement(first) ? void 0 : specOf(first);
1334
+ if (isPlotModule(first) && !isObject2(second)) {
1335
+ throw new TypeError("jupyter.chart(Plot, options): options must be Plot's options object");
1336
+ }
1337
+ if (isSvgElement(first)) {
1338
+ const bundle = { "image/svg+xml": withNamespace(first.outerHTML) };
1339
+ return { [$display2]: () => bundle };
1340
+ }
1341
+ let pending;
1342
+ const render = () => pending ??= (async () => {
1343
+ const bundle = spec ? await renderVega(spec, second) : await renderPlot(first, second);
1344
+ const size = bundle["image/svg+xml"].length;
1345
+ if (size > WARN_BYTES) {
1346
+ warn(
1347
+ `jupyter.chart: the SVG is ${(size / 1e6).toFixed(1)} MB, which makes the notebook large; aggregate or sample the data first`
1348
+ );
1349
+ }
1350
+ return bundle;
1351
+ })().catch((err) => {
1352
+ pending = void 0;
1353
+ throw err;
1354
+ });
1355
+ return { [$display2]: render };
1356
+ }
1357
+ return chart;
1358
+ }
1359
+
1360
+ // runtime/display.mjs
1361
+ var $display3 = /* @__PURE__ */ Symbol.for("Jupyter.display");
1362
+ var rawToEntity3 = /* @__PURE__ */ new Map([
424
1363
  ["&", "&amp;"],
425
1364
  ["<", "&lt;"],
426
1365
  [">", "&gt;"],
427
1366
  ['"', "&quot;"],
428
1367
  ["'", "&#39;"]
429
1368
  ]);
430
- var rawRe = new RegExp(`[${[...rawToEntity.keys()].join("")}]`, "g");
431
- function escapeHTML(str) {
432
- return str.replaceAll(rawRe, (m) => rawToEntity.get(m) ?? m);
433
- }
1369
+ var rawRe = new RegExp(`[${[...rawToEntity3.keys()].join("")}]`, "g");
434
1370
  function isVegaLike(obj) {
435
1371
  return obj !== null && typeof obj === "object" && "toSpec" in obj;
436
1372
  }
@@ -617,59 +1553,6 @@ function summarizeChart(spec) {
617
1553
  return void 0;
618
1554
  }
619
1555
  }
620
- function isDataFrameLike(obj) {
621
- return obj !== null && typeof obj === "object" && obj.schema !== void 0 && typeof obj.schema === "object" && typeof obj.head === "function" && typeof obj.toRecords === "function";
622
- }
623
- function mapPolarsTypeToJSONSchema(colType) {
624
- const typeMapping = {
625
- Null: "null",
626
- Bool: "boolean",
627
- Int8: "integer",
628
- Int16: "integer",
629
- Int32: "integer",
630
- Int64: "integer",
631
- UInt8: "integer",
632
- UInt16: "integer",
633
- UInt32: "integer",
634
- UInt64: "integer",
635
- Float32: "number",
636
- Float64: "number",
637
- Date: "string",
638
- Datetime: "string",
639
- Utf8: "string",
640
- Categorical: "string",
641
- List: "array",
642
- Struct: "object"
643
- };
644
- const dataType = typeof colType?.toJSON === "function" ? colType.toJSON()["DataType"] : void 0;
645
- return typeMapping[dataType] || "string";
646
- }
647
- function extractDataFrame(df) {
648
- const fields = [];
649
- for (const [name, colType] of Object.entries(df.schema)) {
650
- fields.push({ name, type: mapPolarsTypeToJSONSchema(colType) });
651
- }
652
- const data = df.head(50).toRecords();
653
- let html = "<table><thead><tr>";
654
- for (const field of fields) html += `<th>${escapeHTML(String(field.name))}</th>`;
655
- html += "</tr></thead><tbody>";
656
- for (const row of df.head(10).toRecords()) {
657
- html += "<tr>";
658
- for (const field of fields) html += `<td>${escapeHTML(String(row[field.name]))}</td>`;
659
- html += "</tr>";
660
- }
661
- html += "</tbody></table>";
662
- const size = [];
663
- if (Number.isInteger(df.height)) size.push(`${df.height} ${df.height === 1 ? "row" : "rows"}`);
664
- size.push(`${fields.length} ${fields.length === 1 ? "column" : "columns"}`);
665
- const columns = fields.slice(0, 20).map((field) => `${clip(String(field.name), 40)} (${field.type})`);
666
- if (fields.length > 20) columns.push(`\u2026 ${fields.length - 20} more`);
667
- return {
668
- "application/vnd.dataresource+json": { data, schema: { fields } },
669
- "text/html": html,
670
- "text/plain": `DataFrame (${size.join(", ")})${columns.length ? `: ${columns.join(", ")}` : ""}`
671
- };
672
- }
673
1556
  function isCanvasLike(obj) {
674
1557
  return obj !== null && typeof obj === "object" && "toDataURL" in obj;
675
1558
  }
@@ -687,7 +1570,7 @@ function isHTMLElementLike(obj) {
687
1570
  return obj !== null && typeof obj === "object" && typeof obj.outerHTML === "string";
688
1571
  }
689
1572
  function hasDisplaySymbol(obj) {
690
- return obj !== null && typeof obj === "object" && typeof obj[$display] === "function";
1573
+ return obj !== null && typeof obj === "object" && typeof obj[$display3] === "function";
691
1574
  }
692
1575
  function isMediaBundle(obj) {
693
1576
  return obj !== null && typeof obj === "object" && !Array.isArray(obj);
@@ -705,7 +1588,7 @@ function createJupyter(host2) {
705
1588
  return await formatNow(obj);
706
1589
  }
707
1590
  function formatNow(obj) {
708
- if (hasDisplaySymbol(obj)) return obj[$display]();
1591
+ if (hasDisplaySymbol(obj)) return obj[$display3]();
709
1592
  if (typeof obj !== "object" || obj === null) return { "text/plain": host2.inspect(obj) };
710
1593
  if (isCanvasLike(obj)) {
711
1594
  const [prefix, data] = obj.toDataURL().split(",");
@@ -715,7 +1598,7 @@ function createJupyter(host2) {
715
1598
  const vega = extractVega(obj);
716
1599
  if (vega) return vega;
717
1600
  }
718
- if (isDataFrameLike(obj)) return extractDataFrame(obj);
1601
+ if (isTableLike(obj)) return tableBundle(tableSource(obj));
719
1602
  if (isJpg(obj)) return { "image/jpeg": host2.base64(obj) };
720
1603
  if (isPng(obj)) return { "image/png": host2.base64(obj) };
721
1604
  if (isSVGElementLike(obj)) return { "image/svg+xml": obj.outerHTML };
@@ -725,7 +1608,7 @@ function createJupyter(host2) {
725
1608
  function taggedTemplate(mediatype) {
726
1609
  return (strings, ...values) => {
727
1610
  const payload = strings.reduce((acc, string, i) => acc + string + (values[i] !== void 0 ? values[i] : ""), "");
728
- return { [$display]: () => ({ [mediatype]: payload }) };
1611
+ return { [$display3]: () => ({ [mediatype]: payload }) };
729
1612
  };
730
1613
  }
731
1614
  function image(obj) {
@@ -735,12 +1618,16 @@ function createJupyter(host2) {
735
1618
  } catch {
736
1619
  }
737
1620
  }
738
- if (isJpg(obj)) return { [$display]: () => ({ "image/jpeg": host2.base64(obj) }) };
739
- if (isPng(obj)) return { [$display]: () => ({ "image/png": host2.base64(obj) }) };
1621
+ if (isJpg(obj)) return { [$display3]: () => ({ "image/jpeg": host2.base64(obj) }) };
1622
+ if (isPng(obj)) return { [$display3]: () => ({ "image/png": host2.base64(obj) }) };
740
1623
  throw new TypeError(
741
1624
  "Object is not a valid image or a path to an image. `jupyter.image` supports displaying JPG or PNG images."
742
1625
  );
743
1626
  }
1627
+ function table(value, options = {}) {
1628
+ const bundle = tableBundle(tableSource(value, options), options);
1629
+ return { [$display3]: () => bundle };
1630
+ }
744
1631
  function broadcast(msgType, content, { metadata = {}, buffers = [] } = {}) {
745
1632
  host2.send(msgType, content, metadata, buffers);
746
1633
  }
@@ -811,6 +1698,13 @@ function createJupyter(host2) {
811
1698
  transient: {}
812
1699
  });
813
1700
  }
1701
+ const cellImport = (what) => (specifier) => {
1702
+ const load = globalThis.__cdpk?.import;
1703
+ if (typeof load !== "function") return Promise.reject(new Error(`${what}: no module loader in this runtime`));
1704
+ return load(specifier);
1705
+ };
1706
+ const sql = createSql({ importModule: cellImport("jupyter.sql"), signal: () => current.signal });
1707
+ const chart = createChart({ importModule: cellImport("jupyter.chart"), summarizeSpec: summarizeChart });
814
1708
  const comms = createComms((msgType, content, metadata, buffers) => host2.send(msgType, content, metadata, buffers));
815
1709
  let cell = new AbortController();
816
1710
  let current = cell;
@@ -848,15 +1742,18 @@ function createJupyter(host2) {
848
1742
  return readParams();
849
1743
  },
850
1744
  result,
1745
+ table,
1746
+ sql,
1747
+ chart,
851
1748
  format: format2,
852
1749
  md: taggedTemplate("text/markdown"),
853
1750
  html: taggedTemplate("text/html"),
854
1751
  svg: taggedTemplate("image/svg+xml"),
855
1752
  image,
856
- $display,
1753
+ $display: $display3,
857
1754
  comm: comms.api,
858
1755
  host: hostApi.host,
859
- widget: createWidgetFactory(comms.api, $display)
1756
+ widget: createWidgetFactory(comms.api, $display3)
860
1757
  },
861
1758
  broadcastResult,
862
1759
  formatNow,