@tlockney/cdp-kernel 0.8.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -2
- package/dist/browser/kernel.js +592 -426
- package/dist/browser/kernel.js.map +3 -3
- package/dist/browser/target.d.ts +7 -0
- package/dist/browser/target.js +2 -1
- package/dist/browser/worker.js +224 -6
- package/dist/browser/worker.js.map +4 -4
- package/dist/celld-target.d.ts +2 -1
- package/dist/celld-target.js +5 -3
- package/dist/globals.d.ts +35 -4
- package/dist/kernel-core.js +11 -0
- package/dist/package.d.ts +3 -3
- package/dist/package.js +3 -2
- package/dist/transform.d.ts +10 -1
- package/dist/transform.js +20 -2
- package/dist/workerd-target.js +6 -2
- package/dist/workers-project.js +1 -1
- package/package.json +3 -3
- package/runtime/bootstrap.mjs +2 -0
- package/runtime/browser-worker.mjs +5 -1
- package/runtime/comm.d.mts +87 -1
- package/runtime/display.d.mts +315 -6
- package/runtime/display.mjs +63 -0
- package/runtime/host.d.mts +246 -0
- package/runtime/host.mjs +215 -0
- package/runtime/widget.d.mts +205 -6
- package/runtime/workerd-worker.mjs +5 -1
package/dist/browser/target.d.ts
CHANGED
|
@@ -28,6 +28,13 @@ export interface BrowserTargetOptions {
|
|
|
28
28
|
* called with `{ host, jupyter }` (#384).
|
|
29
29
|
*/
|
|
30
30
|
preload?: string[];
|
|
31
|
+
/**
|
|
32
|
+
* The run's parameters, which cells read as `jupyter.params` (#456). The
|
|
33
|
+
* other targets read them from `GANYMEDE_PARAMS`.
|
|
34
|
+
*/
|
|
35
|
+
params?: {
|
|
36
|
+
[name: string]: unknown;
|
|
37
|
+
};
|
|
31
38
|
/** Answers the Worker's `host.request` calls; without it they reject (#384). */
|
|
32
39
|
host?: HostHandler;
|
|
33
40
|
}
|
package/dist/browser/target.js
CHANGED
|
@@ -48,7 +48,8 @@ export class BrowserTarget {
|
|
|
48
48
|
const target = new BrowserTarget(options.createWorker(), events, options.host);
|
|
49
49
|
const base = options.base ?? globalThis.location?.href ?? "file:///";
|
|
50
50
|
try {
|
|
51
|
-
const
|
|
51
|
+
const params = options.params === undefined ? undefined : JSON.stringify(options.params);
|
|
52
|
+
const { userAgent } = await target.#request("info", { base, cdn: options.cdn, preload: options.preload ?? [], params });
|
|
52
53
|
if (typeof userAgent === "string")
|
|
53
54
|
target.versions.runtime = browserName(userAgent);
|
|
54
55
|
target.versions.engine = engineName(typeof userAgent === "string" ? userAgent : "");
|
package/dist/browser/worker.js
CHANGED
|
@@ -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";
|
|
@@ -251,7 +419,7 @@ function createWidgetFactory(commApi, $display2) {
|
|
|
251
419
|
}
|
|
252
420
|
|
|
253
421
|
// runtime/display.mjs
|
|
254
|
-
var $display = Symbol.for("Jupyter.display");
|
|
422
|
+
var $display = /* @__PURE__ */ Symbol.for("Jupyter.display");
|
|
255
423
|
var rawToEntity = /* @__PURE__ */ new Map([
|
|
256
424
|
["&", "&"],
|
|
257
425
|
["<", "<"],
|
|
@@ -524,6 +692,14 @@ function hasDisplaySymbol(obj) {
|
|
|
524
692
|
function isMediaBundle(obj) {
|
|
525
693
|
return obj !== null && typeof obj === "object" && !Array.isArray(obj);
|
|
526
694
|
}
|
|
695
|
+
var RESULT_MIME = "application/vnd.ganymede.result+json";
|
|
696
|
+
function deepFreeze(value) {
|
|
697
|
+
if (typeof value === "object" && value !== null) {
|
|
698
|
+
for (const v of Object.values(value)) deepFreeze(v);
|
|
699
|
+
Object.freeze(value);
|
|
700
|
+
}
|
|
701
|
+
return value;
|
|
702
|
+
}
|
|
527
703
|
function createJupyter(host2) {
|
|
528
704
|
async function format2(obj) {
|
|
529
705
|
return await formatNow(obj);
|
|
@@ -576,12 +752,12 @@ function createJupyter(host2) {
|
|
|
576
752
|
transient: options.display_id ? { display_id: options.display_id } : {}
|
|
577
753
|
});
|
|
578
754
|
}
|
|
579
|
-
async function broadcastResult(executionCount,
|
|
580
|
-
if (
|
|
755
|
+
async function broadcastResult(executionCount, result2) {
|
|
756
|
+
if (result2 === void 0) return;
|
|
581
757
|
try {
|
|
582
758
|
broadcast("execute_result", {
|
|
583
759
|
execution_count: executionCount,
|
|
584
|
-
data: await format2(
|
|
760
|
+
data: await format2(result2),
|
|
585
761
|
metadata: {},
|
|
586
762
|
// nbclient crashes on a null `transient`; Deno learned this too.
|
|
587
763
|
transient: {}
|
|
@@ -603,6 +779,38 @@ function createJupyter(host2) {
|
|
|
603
779
|
const answer2 = host2.input(message, password);
|
|
604
780
|
return answer2 === "" ? defaultValue : answer2;
|
|
605
781
|
}
|
|
782
|
+
let params;
|
|
783
|
+
function readParams() {
|
|
784
|
+
if (params !== void 0) return params;
|
|
785
|
+
const text = host2.params?.();
|
|
786
|
+
if (text === void 0 || text === "") return params = Object.freeze({});
|
|
787
|
+
let value;
|
|
788
|
+
try {
|
|
789
|
+
value = JSON.parse(text);
|
|
790
|
+
} catch (err) {
|
|
791
|
+
throw new TypeError(`GANYMEDE_PARAMS is not JSON: ${err instanceof Error ? err.message : err}`);
|
|
792
|
+
}
|
|
793
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
794
|
+
throw new TypeError('GANYMEDE_PARAMS must be a JSON object, such as {"counter": "Fremont"}');
|
|
795
|
+
}
|
|
796
|
+
return params = deepFreeze(value);
|
|
797
|
+
}
|
|
798
|
+
function result(name, value) {
|
|
799
|
+
if (typeof name !== "string" || name === "") throw new TypeError("jupyter.result: name must be a non-empty string");
|
|
800
|
+
let text;
|
|
801
|
+
try {
|
|
802
|
+
text = JSON.stringify(value);
|
|
803
|
+
} catch (err) {
|
|
804
|
+
throw new TypeError(`jupyter.result: ${name} is not JSON: ${err instanceof Error ? err.message : err}`);
|
|
805
|
+
}
|
|
806
|
+
if (text === void 0) throw new TypeError(`jupyter.result: ${name} is not JSON (${typeof value})`);
|
|
807
|
+
const json = JSON.parse(text);
|
|
808
|
+
broadcast("display_data", {
|
|
809
|
+
data: { [RESULT_MIME]: { name, value: json }, "text/plain": `${name} = ${text}` },
|
|
810
|
+
metadata: {},
|
|
811
|
+
transient: {}
|
|
812
|
+
});
|
|
813
|
+
}
|
|
606
814
|
const comms = createComms((msgType, content, metadata, buffers) => host2.send(msgType, content, metadata, buffers));
|
|
607
815
|
let cell = new AbortController();
|
|
608
816
|
let current = cell;
|
|
@@ -616,6 +824,7 @@ function createJupyter(host2) {
|
|
|
616
824
|
for (const controller of handlers.keys()) controller.abort(reason);
|
|
617
825
|
handlers.clear();
|
|
618
826
|
}
|
|
827
|
+
const hostApi = createHost(comms.api, () => current.signal);
|
|
619
828
|
async function commDispatch(msgType, content, metadata, buffers, ownSignal = false) {
|
|
620
829
|
const controller = ownSignal ? new AbortController() : cell;
|
|
621
830
|
if (ownSignal) current = controller;
|
|
@@ -635,6 +844,10 @@ function createJupyter(host2) {
|
|
|
635
844
|
},
|
|
636
845
|
broadcast,
|
|
637
846
|
display,
|
|
847
|
+
get params() {
|
|
848
|
+
return readParams();
|
|
849
|
+
},
|
|
850
|
+
result,
|
|
638
851
|
format: format2,
|
|
639
852
|
md: taggedTemplate("text/markdown"),
|
|
640
853
|
html: taggedTemplate("text/html"),
|
|
@@ -642,6 +855,7 @@ function createJupyter(host2) {
|
|
|
642
855
|
image,
|
|
643
856
|
$display,
|
|
644
857
|
comm: comms.api,
|
|
858
|
+
host: hostApi.host,
|
|
645
859
|
widget: createWidgetFactory(comms.api, $display)
|
|
646
860
|
},
|
|
647
861
|
broadcastResult,
|
|
@@ -990,8 +1204,11 @@ var jupyter = createJupyter({
|
|
|
990
1204
|
},
|
|
991
1205
|
// A synchronous prompt would need SharedArrayBuffer and Atomics.wait, so
|
|
992
1206
|
// a cross-origin isolated page; until then, as with no stdin.
|
|
993
|
-
input: () => null
|
|
1207
|
+
input: () => null,
|
|
1208
|
+
// jupyter.params (#456): the target's `params` option, sent with info.
|
|
1209
|
+
params: () => paramsText
|
|
994
1210
|
});
|
|
1211
|
+
var paramsText;
|
|
995
1212
|
var cdn = DEFAULT_CDN;
|
|
996
1213
|
var hostCalls = /* @__PURE__ */ new Map();
|
|
997
1214
|
var hostListeners = /* @__PURE__ */ new Set();
|
|
@@ -1059,7 +1276,8 @@ async function preload(urls, base) {
|
|
|
1059
1276
|
}
|
|
1060
1277
|
}
|
|
1061
1278
|
var handle = null;
|
|
1062
|
-
function install({ base, cdn: cdnBase }) {
|
|
1279
|
+
function install({ base, cdn: cdnBase, params }) {
|
|
1280
|
+
if (typeof params === "string") paramsText = params;
|
|
1063
1281
|
if (typeof cdnBase === "string") cdn = cdnBase.endsWith("/") ? cdnBase : cdnBase + "/";
|
|
1064
1282
|
const url = new URL("$cdpk$repl.mjs", base).href;
|
|
1065
1283
|
globalThis.__cdpk = {
|