@tlockney/cdp-kernel 0.9.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.
@@ -86,6 +86,174 @@ function createComms(send, newId = () => crypto.randomUUID()) {
86
86
  return { api, dispatch, list: list2 };
87
87
  }
88
88
 
89
+ // runtime/host.mjs
90
+ var HOST_TARGET = "ganymede.host.v1";
91
+ var CODES = /* @__PURE__ */ new Set(["unavailable", "denied", "cancelled", "timeout", "failed", "disconnected"]);
92
+ var HostError = class _HostError extends Error {
93
+ constructor(code, message) {
94
+ super(message);
95
+ this.name = "HostError";
96
+ this.code = code;
97
+ }
98
+ /** An abort becomes `cancelled`, a timeout signal's abort `timeout`, anything else `failed`. */
99
+ static from(err) {
100
+ if (err instanceof _HostError) return err;
101
+ const message = typeof err?.message === "string" ? err.message : String(err);
102
+ if (err?.name === "AbortError") return new _HostError("cancelled", message);
103
+ if (err?.name === "TimeoutError") return new _HostError("timeout", message);
104
+ if (typeof err?.code === "string" && CODES.has(err.code)) return new _HostError(err.code, message);
105
+ return new _HostError("failed", message);
106
+ }
107
+ };
108
+ function abortError(reason) {
109
+ const message = typeof reason?.message === "string" ? reason.message : String(reason ?? "aborted");
110
+ return new HostError(reason?.name === "TimeoutError" ? "timeout" : "cancelled", message);
111
+ }
112
+ function helloOf(data) {
113
+ if (data === null || typeof data !== "object" || typeof data.protocol !== "number") return null;
114
+ return {
115
+ protocol: data.protocol,
116
+ server: typeof data.server === "string" ? data.server : "",
117
+ notebook: typeof data.notebook === "string" ? data.notebook : null,
118
+ methods: Array.isArray(data.methods) ? data.methods.filter((m) => typeof m === "string") : []
119
+ };
120
+ }
121
+ function createHost(comms, defaultSignal = () => void 0) {
122
+ let link = null;
123
+ const waiters = /* @__PURE__ */ new Set();
124
+ let noServer = false;
125
+ let nextId = 0;
126
+ function settle() {
127
+ const host3 = link?.host ?? null;
128
+ if (host3 === null && link !== null) return;
129
+ for (const waiter of [...waiters]) waiter(host3);
130
+ }
131
+ function adopt(comm, data, ours = false) {
132
+ const old = link;
133
+ const l = { comm, host: null, pending: /* @__PURE__ */ new Map(), ours };
134
+ if (!ours) noServer = false;
135
+ link = l;
136
+ if (old !== null && !old.comm.closed) old.comm.close();
137
+ const hello = helloOf(data);
138
+ if (hello !== null) l.host = makeHost(l, hello);
139
+ comm.onMsg(({ data: data2 }) => {
140
+ if (data2.hello !== void 0) {
141
+ const hello2 = helloOf(data2.hello);
142
+ if (hello2 !== null) {
143
+ l.host = makeHost(l, hello2);
144
+ if (link === l) settle();
145
+ }
146
+ return;
147
+ }
148
+ const call = l.pending.get(data2.id);
149
+ if (call === void 0) return;
150
+ if (data2.error !== void 0) {
151
+ const { code, message } = data2.error ?? {};
152
+ call.reject(new HostError(CODES.has(code) ? code : "failed", typeof message === "string" ? message : String(code)));
153
+ } else {
154
+ call.resolve(data2.result);
155
+ }
156
+ });
157
+ comm.onClose(({ data: data2 }) => {
158
+ for (const call of l.pending.values()) {
159
+ call.reject(new HostError("disconnected", typeof data2?.error === "string" ? `host comm closed: ${data2.error}` : "host comm closed"));
160
+ }
161
+ l.pending.clear();
162
+ if (link === l) {
163
+ link = null;
164
+ if (l.ours && l.host === null) noServer = true;
165
+ settle();
166
+ }
167
+ });
168
+ if (l.host !== null) settle();
169
+ }
170
+ function makeHost(l, hello) {
171
+ const methods = Object.freeze([...hello.methods]);
172
+ function call(method, params = {}, { signal = defaultSignal() } = {}) {
173
+ if (signal?.aborted) return Promise.reject(abortError(signal.reason));
174
+ if (l.comm.closed) return Promise.reject(new HostError("disconnected", "host comm closed"));
175
+ const id = nextId++;
176
+ return new Promise((resolve, reject) => {
177
+ const onAbort = () => {
178
+ l.pending.delete(id);
179
+ if (!l.comm.closed) l.comm.send({ id, cancel: true });
180
+ reject(abortError(signal.reason));
181
+ };
182
+ const done = () => signal?.removeEventListener("abort", onAbort);
183
+ const call2 = {
184
+ resolve: (value) => (l.pending.delete(id), done(), resolve(value)),
185
+ reject: (error) => (l.pending.delete(id), done(), reject(error))
186
+ };
187
+ l.pending.set(id, call2);
188
+ signal?.addEventListener("abort", onAbort, { once: true });
189
+ try {
190
+ l.comm.send({ id, method, params });
191
+ } catch (err) {
192
+ call2.reject(new HostError("failed", `cannot send ${method}: ${err instanceof Error ? err.message : err}`));
193
+ }
194
+ });
195
+ }
196
+ const bind = (method) => (params, options) => call(method, params, options);
197
+ return Object.freeze({
198
+ protocol: hello.protocol,
199
+ server: hello.server,
200
+ notebook: hello.notebook,
201
+ methods,
202
+ has: (method) => methods.includes(method),
203
+ call,
204
+ notebooks: Object.freeze({
205
+ list: bind("notebooks.list"),
206
+ read: bind("notebooks.read"),
207
+ search: bind("notebooks.search"),
208
+ results: bind("notebooks.results"),
209
+ run: bind("notebooks.run")
210
+ }),
211
+ self: Object.freeze({
212
+ add: bind("self.add"),
213
+ edit: bind("self.edit"),
214
+ delete: bind("self.delete")
215
+ })
216
+ });
217
+ }
218
+ comms.registerTarget(HOST_TARGET, (comm, msg) => adopt(comm, msg.data));
219
+ async function host2({ timeout = 1e3 } = {}) {
220
+ if (link?.host) return link.host;
221
+ const signal = defaultSignal();
222
+ if (signal?.aborted) throw abortError(signal.reason);
223
+ if (noServer) return null;
224
+ if (link === null) adopt(comms.open(HOST_TARGET), null, true);
225
+ const mine = link;
226
+ return new Promise((resolve, reject) => {
227
+ let timer;
228
+ const onAbort = () => {
229
+ done();
230
+ reject(abortError(signal.reason));
231
+ };
232
+ const done = () => {
233
+ waiters.delete(waiter);
234
+ clearTimeout(timer);
235
+ signal?.removeEventListener("abort", onAbort);
236
+ };
237
+ const waiter = (host3) => {
238
+ done();
239
+ resolve(host3);
240
+ };
241
+ waiters.add(waiter);
242
+ signal?.addEventListener("abort", onAbort, { once: true });
243
+ timer = setTimeout(() => {
244
+ done();
245
+ if (waiters.size === 0 && link === mine && mine.host === null && mine.ours) {
246
+ link = null;
247
+ noServer = true;
248
+ mine.comm.close();
249
+ }
250
+ resolve(null);
251
+ }, timeout);
252
+ });
253
+ }
254
+ return { host: host2 };
255
+ }
256
+
89
257
  // runtime/widget.mjs
90
258
  var WIDGET_PROTOCOL_VERSION = "2.1.0";
91
259
  var WIDGET_TARGET = "jupyter.widget";
@@ -138,10 +306,10 @@ var Widget = class {
138
306
  #state;
139
307
  #listeners = /* @__PURE__ */ new Map();
140
308
  // event -> Set<listener>
141
- constructor(comm, state, $display2) {
309
+ constructor(comm, state, $display4) {
142
310
  this.#comm = comm;
143
311
  this.#state = state;
144
- this[$display2] = () => ({
312
+ this[$display4] = () => ({
145
313
  [VIEW_MIME]: { version_major: 2, version_minor: 1, model_id: this.id },
146
314
  "text/plain": `Widget(${this.id})`
147
315
  });
@@ -206,7 +374,7 @@ var Widget = class {
206
374
  }
207
375
  }
208
376
  };
209
- function createWidgetFactory(commApi, $display2) {
377
+ function createWidgetFactory(commApi, $display4) {
210
378
  const live = /* @__PURE__ */ new Map();
211
379
  commApi.registerTarget(CONTROL_TARGET, (control) => {
212
380
  control.onMsg((msg) => {
@@ -240,7 +408,7 @@ function createWidgetFactory(commApi, $display2) {
240
408
  metadata: { version: WIDGET_PROTOCOL_VERSION },
241
409
  buffers
242
410
  });
243
- const instance = new Widget(comm, { ...full }, $display2);
411
+ const instance = new Widget(comm, { ...full }, $display4);
244
412
  live.set(comm.id, instance);
245
413
  comm.onMsg((msg) => instance._receive(msg));
246
414
  comm.onClose(() => {
@@ -250,19 +418,955 @@ function createWidgetFactory(commApi, $display2) {
250
418
  };
251
419
  }
252
420
 
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";
785
+ var $display = /* @__PURE__ */ Symbol.for("Jupyter.display");
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
+
253
1360
  // runtime/display.mjs
254
- var $display = Symbol.for("Jupyter.display");
255
- var rawToEntity = /* @__PURE__ */ new Map([
1361
+ var $display3 = /* @__PURE__ */ Symbol.for("Jupyter.display");
1362
+ var rawToEntity3 = /* @__PURE__ */ new Map([
256
1363
  ["&", "&amp;"],
257
1364
  ["<", "&lt;"],
258
1365
  [">", "&gt;"],
259
1366
  ['"', "&quot;"],
260
1367
  ["'", "&#39;"]
261
1368
  ]);
262
- var rawRe = new RegExp(`[${[...rawToEntity.keys()].join("")}]`, "g");
263
- function escapeHTML(str) {
264
- return str.replaceAll(rawRe, (m) => rawToEntity.get(m) ?? m);
265
- }
1369
+ var rawRe = new RegExp(`[${[...rawToEntity3.keys()].join("")}]`, "g");
266
1370
  function isVegaLike(obj) {
267
1371
  return obj !== null && typeof obj === "object" && "toSpec" in obj;
268
1372
  }
@@ -449,59 +1553,6 @@ function summarizeChart(spec) {
449
1553
  return void 0;
450
1554
  }
451
1555
  }
452
- function isDataFrameLike(obj) {
453
- return obj !== null && typeof obj === "object" && obj.schema !== void 0 && typeof obj.schema === "object" && typeof obj.head === "function" && typeof obj.toRecords === "function";
454
- }
455
- function mapPolarsTypeToJSONSchema(colType) {
456
- const typeMapping = {
457
- Null: "null",
458
- Bool: "boolean",
459
- Int8: "integer",
460
- Int16: "integer",
461
- Int32: "integer",
462
- Int64: "integer",
463
- UInt8: "integer",
464
- UInt16: "integer",
465
- UInt32: "integer",
466
- UInt64: "integer",
467
- Float32: "number",
468
- Float64: "number",
469
- Date: "string",
470
- Datetime: "string",
471
- Utf8: "string",
472
- Categorical: "string",
473
- List: "array",
474
- Struct: "object"
475
- };
476
- const dataType = typeof colType?.toJSON === "function" ? colType.toJSON()["DataType"] : void 0;
477
- return typeMapping[dataType] || "string";
478
- }
479
- function extractDataFrame(df) {
480
- const fields = [];
481
- for (const [name, colType] of Object.entries(df.schema)) {
482
- fields.push({ name, type: mapPolarsTypeToJSONSchema(colType) });
483
- }
484
- const data = df.head(50).toRecords();
485
- let html = "<table><thead><tr>";
486
- for (const field of fields) html += `<th>${escapeHTML(String(field.name))}</th>`;
487
- html += "</tr></thead><tbody>";
488
- for (const row of df.head(10).toRecords()) {
489
- html += "<tr>";
490
- for (const field of fields) html += `<td>${escapeHTML(String(row[field.name]))}</td>`;
491
- html += "</tr>";
492
- }
493
- html += "</tbody></table>";
494
- const size = [];
495
- if (Number.isInteger(df.height)) size.push(`${df.height} ${df.height === 1 ? "row" : "rows"}`);
496
- size.push(`${fields.length} ${fields.length === 1 ? "column" : "columns"}`);
497
- const columns = fields.slice(0, 20).map((field) => `${clip(String(field.name), 40)} (${field.type})`);
498
- if (fields.length > 20) columns.push(`\u2026 ${fields.length - 20} more`);
499
- return {
500
- "application/vnd.dataresource+json": { data, schema: { fields } },
501
- "text/html": html,
502
- "text/plain": `DataFrame (${size.join(", ")})${columns.length ? `: ${columns.join(", ")}` : ""}`
503
- };
504
- }
505
1556
  function isCanvasLike(obj) {
506
1557
  return obj !== null && typeof obj === "object" && "toDataURL" in obj;
507
1558
  }
@@ -519,17 +1570,25 @@ function isHTMLElementLike(obj) {
519
1570
  return obj !== null && typeof obj === "object" && typeof obj.outerHTML === "string";
520
1571
  }
521
1572
  function hasDisplaySymbol(obj) {
522
- return obj !== null && typeof obj === "object" && typeof obj[$display] === "function";
1573
+ return obj !== null && typeof obj === "object" && typeof obj[$display3] === "function";
523
1574
  }
524
1575
  function isMediaBundle(obj) {
525
1576
  return obj !== null && typeof obj === "object" && !Array.isArray(obj);
526
1577
  }
1578
+ var RESULT_MIME = "application/vnd.ganymede.result+json";
1579
+ function deepFreeze(value) {
1580
+ if (typeof value === "object" && value !== null) {
1581
+ for (const v of Object.values(value)) deepFreeze(v);
1582
+ Object.freeze(value);
1583
+ }
1584
+ return value;
1585
+ }
527
1586
  function createJupyter(host2) {
528
1587
  async function format2(obj) {
529
1588
  return await formatNow(obj);
530
1589
  }
531
1590
  function formatNow(obj) {
532
- if (hasDisplaySymbol(obj)) return obj[$display]();
1591
+ if (hasDisplaySymbol(obj)) return obj[$display3]();
533
1592
  if (typeof obj !== "object" || obj === null) return { "text/plain": host2.inspect(obj) };
534
1593
  if (isCanvasLike(obj)) {
535
1594
  const [prefix, data] = obj.toDataURL().split(",");
@@ -539,7 +1598,7 @@ function createJupyter(host2) {
539
1598
  const vega = extractVega(obj);
540
1599
  if (vega) return vega;
541
1600
  }
542
- if (isDataFrameLike(obj)) return extractDataFrame(obj);
1601
+ if (isTableLike(obj)) return tableBundle(tableSource(obj));
543
1602
  if (isJpg(obj)) return { "image/jpeg": host2.base64(obj) };
544
1603
  if (isPng(obj)) return { "image/png": host2.base64(obj) };
545
1604
  if (isSVGElementLike(obj)) return { "image/svg+xml": obj.outerHTML };
@@ -549,7 +1608,7 @@ function createJupyter(host2) {
549
1608
  function taggedTemplate(mediatype) {
550
1609
  return (strings, ...values) => {
551
1610
  const payload = strings.reduce((acc, string, i) => acc + string + (values[i] !== void 0 ? values[i] : ""), "");
552
- return { [$display]: () => ({ [mediatype]: payload }) };
1611
+ return { [$display3]: () => ({ [mediatype]: payload }) };
553
1612
  };
554
1613
  }
555
1614
  function image(obj) {
@@ -559,12 +1618,16 @@ function createJupyter(host2) {
559
1618
  } catch {
560
1619
  }
561
1620
  }
562
- if (isJpg(obj)) return { [$display]: () => ({ "image/jpeg": host2.base64(obj) }) };
563
- 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) }) };
564
1623
  throw new TypeError(
565
1624
  "Object is not a valid image or a path to an image. `jupyter.image` supports displaying JPG or PNG images."
566
1625
  );
567
1626
  }
1627
+ function table(value, options = {}) {
1628
+ const bundle = tableBundle(tableSource(value, options), options);
1629
+ return { [$display3]: () => bundle };
1630
+ }
568
1631
  function broadcast(msgType, content, { metadata = {}, buffers = [] } = {}) {
569
1632
  host2.send(msgType, content, metadata, buffers);
570
1633
  }
@@ -576,12 +1639,12 @@ function createJupyter(host2) {
576
1639
  transient: options.display_id ? { display_id: options.display_id } : {}
577
1640
  });
578
1641
  }
579
- async function broadcastResult(executionCount, result) {
580
- if (result === void 0) return;
1642
+ async function broadcastResult(executionCount, result2) {
1643
+ if (result2 === void 0) return;
581
1644
  try {
582
1645
  broadcast("execute_result", {
583
1646
  execution_count: executionCount,
584
- data: await format2(result),
1647
+ data: await format2(result2),
585
1648
  metadata: {},
586
1649
  // nbclient crashes on a null `transient`; Deno learned this too.
587
1650
  transient: {}
@@ -603,6 +1666,45 @@ function createJupyter(host2) {
603
1666
  const answer2 = host2.input(message, password);
604
1667
  return answer2 === "" ? defaultValue : answer2;
605
1668
  }
1669
+ let params;
1670
+ function readParams() {
1671
+ if (params !== void 0) return params;
1672
+ const text = host2.params?.();
1673
+ if (text === void 0 || text === "") return params = Object.freeze({});
1674
+ let value;
1675
+ try {
1676
+ value = JSON.parse(text);
1677
+ } catch (err) {
1678
+ throw new TypeError(`GANYMEDE_PARAMS is not JSON: ${err instanceof Error ? err.message : err}`);
1679
+ }
1680
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
1681
+ throw new TypeError('GANYMEDE_PARAMS must be a JSON object, such as {"counter": "Fremont"}');
1682
+ }
1683
+ return params = deepFreeze(value);
1684
+ }
1685
+ function result(name, value) {
1686
+ if (typeof name !== "string" || name === "") throw new TypeError("jupyter.result: name must be a non-empty string");
1687
+ let text;
1688
+ try {
1689
+ text = JSON.stringify(value);
1690
+ } catch (err) {
1691
+ throw new TypeError(`jupyter.result: ${name} is not JSON: ${err instanceof Error ? err.message : err}`);
1692
+ }
1693
+ if (text === void 0) throw new TypeError(`jupyter.result: ${name} is not JSON (${typeof value})`);
1694
+ const json = JSON.parse(text);
1695
+ broadcast("display_data", {
1696
+ data: { [RESULT_MIME]: { name, value: json }, "text/plain": `${name} = ${text}` },
1697
+ metadata: {},
1698
+ transient: {}
1699
+ });
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 });
606
1708
  const comms = createComms((msgType, content, metadata, buffers) => host2.send(msgType, content, metadata, buffers));
607
1709
  let cell = new AbortController();
608
1710
  let current = cell;
@@ -616,6 +1718,7 @@ function createJupyter(host2) {
616
1718
  for (const controller of handlers.keys()) controller.abort(reason);
617
1719
  handlers.clear();
618
1720
  }
1721
+ const hostApi = createHost(comms.api, () => current.signal);
619
1722
  async function commDispatch(msgType, content, metadata, buffers, ownSignal = false) {
620
1723
  const controller = ownSignal ? new AbortController() : cell;
621
1724
  if (ownSignal) current = controller;
@@ -635,14 +1738,22 @@ function createJupyter(host2) {
635
1738
  },
636
1739
  broadcast,
637
1740
  display,
1741
+ get params() {
1742
+ return readParams();
1743
+ },
1744
+ result,
1745
+ table,
1746
+ sql,
1747
+ chart,
638
1748
  format: format2,
639
1749
  md: taggedTemplate("text/markdown"),
640
1750
  html: taggedTemplate("text/html"),
641
1751
  svg: taggedTemplate("image/svg+xml"),
642
1752
  image,
643
- $display,
1753
+ $display: $display3,
644
1754
  comm: comms.api,
645
- widget: createWidgetFactory(comms.api, $display)
1755
+ host: hostApi.host,
1756
+ widget: createWidgetFactory(comms.api, $display3)
646
1757
  },
647
1758
  broadcastResult,
648
1759
  formatNow,
@@ -990,8 +2101,11 @@ var jupyter = createJupyter({
990
2101
  },
991
2102
  // A synchronous prompt would need SharedArrayBuffer and Atomics.wait, so
992
2103
  // a cross-origin isolated page; until then, as with no stdin.
993
- input: () => null
2104
+ input: () => null,
2105
+ // jupyter.params (#456): the target's `params` option, sent with info.
2106
+ params: () => paramsText
994
2107
  });
2108
+ var paramsText;
995
2109
  var cdn = DEFAULT_CDN;
996
2110
  var hostCalls = /* @__PURE__ */ new Map();
997
2111
  var hostListeners = /* @__PURE__ */ new Set();
@@ -1059,7 +2173,8 @@ async function preload(urls, base) {
1059
2173
  }
1060
2174
  }
1061
2175
  var handle = null;
1062
- function install({ base, cdn: cdnBase }) {
2176
+ function install({ base, cdn: cdnBase, params }) {
2177
+ if (typeof params === "string") paramsText = params;
1063
2178
  if (typeof cdnBase === "string") cdn = cdnBase.endsWith("/") ? cdnBase : cdnBase + "/";
1064
2179
  const url = new URL("$cdpk$repl.mjs", base).href;
1065
2180
  globalThis.__cdpk = {