@tlockney/cdp-kernel 0.7.1 → 0.8.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/dist/browser/kernel.js +216 -43
- package/dist/browser/kernel.js.map +2 -2
- package/dist/browser/target.d.ts +5 -3
- package/dist/browser/target.js +42 -9
- package/dist/browser/worker.js +47 -4
- package/dist/browser/worker.js.map +2 -2
- package/dist/bun-target.d.ts +5 -1
- package/dist/bun-target.js +32 -7
- package/dist/crash-reports.d.ts +47 -0
- package/dist/crash-reports.js +155 -0
- package/dist/kernel-core.d.ts +7 -0
- package/dist/kernel-core.js +212 -35
- package/dist/kernel.js +10 -0
- package/dist/main.js +31 -14
- package/dist/message.d.ts +1 -1
- package/dist/message.js +1 -1
- package/dist/package.d.ts +2 -2
- package/dist/package.js +2 -2
- package/dist/target-api.d.ts +11 -0
- package/dist/target.d.ts +9 -2
- package/dist/target.js +66 -12
- package/dist/transform.js +3 -1
- package/dist/usage.d.ts +25 -0
- package/dist/usage.js +197 -0
- package/dist/workerd-target.d.ts +23 -2
- package/dist/workerd-target.js +84 -10
- package/dist/workers-project.js +2 -0
- package/package.json +1 -1
- package/runtime/bootstrap.mjs +65 -7
- package/runtime/browser-worker.mjs +6 -2
- package/runtime/display.d.mts +24 -2
- package/runtime/display.mjs +42 -1
- package/runtime/evaluator.d.mts +5 -1
- package/runtime/evaluator.mjs +12 -1
- package/runtime/workerd-worker.mjs +6 -2
package/dist/browser/target.d.ts
CHANGED
|
@@ -58,9 +58,11 @@ export declare class BrowserTarget implements KernelTarget {
|
|
|
58
58
|
evaluate(script: string): Promise<EvaluateResult>;
|
|
59
59
|
/**
|
|
60
60
|
* A cell that's waiting on something leaves the Worker free: it answers a
|
|
61
|
-
* ping, and the cell is abandoned with its state intact
|
|
62
|
-
*
|
|
63
|
-
*
|
|
61
|
+
* ping, and the cell is abandoned with its state intact; host requests
|
|
62
|
+
* in flight are cancelled on the page, and reject in the Worker with an
|
|
63
|
+
* AbortError (#388), and jupyter.signal is aborted (#418). A busy cell
|
|
64
|
+
* can't be reached, so the Worker is
|
|
65
|
+
* terminated, and a new one starts on the next cell.
|
|
64
66
|
*/
|
|
65
67
|
interrupt(): Promise<void>;
|
|
66
68
|
/** Output arrives before the reply it precedes. */
|
package/dist/browser/target.js
CHANGED
|
@@ -27,7 +27,10 @@ export class BrowserTarget {
|
|
|
27
27
|
#hostCalls = new Map();
|
|
28
28
|
#host;
|
|
29
29
|
#nextId = 0;
|
|
30
|
-
|
|
30
|
+
/** Counts interrupt() calls: a call that saw one start during it was interrupted (#417). */
|
|
31
|
+
#interrupts = 0;
|
|
32
|
+
/** Cells running: a comm handler that starts with none gets its own jupyter.signal (#421). */
|
|
33
|
+
#cells = 0;
|
|
31
34
|
#stopNote;
|
|
32
35
|
#exited = false;
|
|
33
36
|
constructor(worker, events, host) {
|
|
@@ -107,6 +110,15 @@ export class BrowserTarget {
|
|
|
107
110
|
resolve(host({ method: String(msg.method), params: msg.params }, controller.signal));
|
|
108
111
|
}).then((result) => answer({ result }), (err) => answer({ error: describeHostError(err) }));
|
|
109
112
|
}
|
|
113
|
+
/** Aborts each host request in flight and rejects it in the Worker. */
|
|
114
|
+
#cancelHostCalls() {
|
|
115
|
+
const calls = [...this.#hostCalls];
|
|
116
|
+
this.#hostCalls.clear();
|
|
117
|
+
for (const [id, controller] of calls) {
|
|
118
|
+
controller.abort();
|
|
119
|
+
this.#worker.postMessage({ op: "host", id, error: { name: "AbortError", message: "interrupted" } });
|
|
120
|
+
}
|
|
121
|
+
}
|
|
110
122
|
/** Sends `host.onEvent` listeners in the Worker an event (#384). */
|
|
111
123
|
notifyHost(event, value) {
|
|
112
124
|
if (!this.#exited)
|
|
@@ -139,27 +151,37 @@ export class BrowserTarget {
|
|
|
139
151
|
});
|
|
140
152
|
}
|
|
141
153
|
async #interruptibly(op, params, what) {
|
|
142
|
-
|
|
154
|
+
const interrupts = this.#interrupts;
|
|
143
155
|
try {
|
|
144
156
|
return await this.#request(op, params, true);
|
|
145
157
|
}
|
|
146
158
|
catch (err) {
|
|
147
|
-
if (err instanceof Abandoned || this.#
|
|
159
|
+
if (err instanceof Abandoned || this.#interrupts !== interrupts)
|
|
148
160
|
throw new Interrupted(`${what} interrupted`);
|
|
149
161
|
throw err;
|
|
150
162
|
}
|
|
151
163
|
}
|
|
152
164
|
async evaluate(script) {
|
|
153
|
-
|
|
165
|
+
this.#cells++;
|
|
166
|
+
try {
|
|
167
|
+
return asEvaluateResult(await this.#interruptibly("eval", { code: script }, "Execution"));
|
|
168
|
+
}
|
|
169
|
+
finally {
|
|
170
|
+
this.#cells--;
|
|
171
|
+
}
|
|
154
172
|
}
|
|
155
173
|
/**
|
|
156
174
|
* A cell that's waiting on something leaves the Worker free: it answers a
|
|
157
|
-
* ping, and the cell is abandoned with its state intact
|
|
158
|
-
*
|
|
159
|
-
*
|
|
175
|
+
* ping, and the cell is abandoned with its state intact; host requests
|
|
176
|
+
* in flight are cancelled on the page, and reject in the Worker with an
|
|
177
|
+
* AbortError (#388), and jupyter.signal is aborted (#418). A busy cell
|
|
178
|
+
* can't be reached, so the Worker is
|
|
179
|
+
* terminated, and a new one starts on the next cell.
|
|
160
180
|
*/
|
|
161
181
|
async interrupt() {
|
|
162
|
-
this.#
|
|
182
|
+
this.#interrupts++;
|
|
183
|
+
// Before abandoning the cell's request, which ends the count.
|
|
184
|
+
const cellRunning = this.#cells > 0;
|
|
163
185
|
const running = [...this.#interruptible];
|
|
164
186
|
const answered = await Promise.race([
|
|
165
187
|
this.#request("ping").then(() => true, () => false),
|
|
@@ -171,6 +193,11 @@ export class BrowserTarget {
|
|
|
171
193
|
this.#pending.delete(id);
|
|
172
194
|
this.#interruptible.delete(id);
|
|
173
195
|
}
|
|
196
|
+
this.#cancelHostCalls();
|
|
197
|
+
// After the cell is abandoned, so its reply can't end it first
|
|
198
|
+
// (#418). Not awaited: requests are answered in order, and an abort
|
|
199
|
+
// listener that never returns mustn't hold up the interrupt.
|
|
200
|
+
void this.#request("abort", { cell: cellRunning }).catch(() => { });
|
|
174
201
|
return;
|
|
175
202
|
}
|
|
176
203
|
this.#stopNote = RESTART_NOTE;
|
|
@@ -186,7 +213,13 @@ export class BrowserTarget {
|
|
|
186
213
|
await this.#request("broadcast", { count: executionCount, handle: result.objectId });
|
|
187
214
|
}
|
|
188
215
|
async dispatchComm(msgType, content, metadata, buffers) {
|
|
189
|
-
const reply = await this.#interruptibly("comm", {
|
|
216
|
+
const reply = await this.#interruptibly("comm", {
|
|
217
|
+
msgType,
|
|
218
|
+
content,
|
|
219
|
+
metadata,
|
|
220
|
+
buffers: buffers.map(toBase64),
|
|
221
|
+
ownSignal: this.#cells === 0,
|
|
222
|
+
}, "Comm handler");
|
|
190
223
|
if (isRecord(reply.error)) {
|
|
191
224
|
const { name, message, stack } = reply.error;
|
|
192
225
|
return { error: { name: String(name), message: String(message), stack: String(stack) } };
|
package/dist/browser/worker.js
CHANGED
|
@@ -604,8 +604,35 @@ function createJupyter(host2) {
|
|
|
604
604
|
return answer2 === "" ? defaultValue : answer2;
|
|
605
605
|
}
|
|
606
606
|
const comms = createComms((msgType, content, metadata, buffers) => host2.send(msgType, content, metadata, buffers));
|
|
607
|
+
let cell = new AbortController();
|
|
608
|
+
let current = cell;
|
|
609
|
+
const handlers = /* @__PURE__ */ new Map();
|
|
610
|
+
function startCell() {
|
|
611
|
+
current = cell = new AbortController();
|
|
612
|
+
}
|
|
613
|
+
function abortSignal(cellRunning = true) {
|
|
614
|
+
const reason = new DOMException("Execution interrupted", "AbortError");
|
|
615
|
+
if (cellRunning) cell.abort(reason);
|
|
616
|
+
for (const controller of handlers.keys()) controller.abort(reason);
|
|
617
|
+
handlers.clear();
|
|
618
|
+
}
|
|
619
|
+
async function commDispatch(msgType, content, metadata, buffers, ownSignal = false) {
|
|
620
|
+
const controller = ownSignal ? new AbortController() : cell;
|
|
621
|
+
if (ownSignal) current = controller;
|
|
622
|
+
handlers.set(controller, (handlers.get(controller) ?? 0) + 1);
|
|
623
|
+
try {
|
|
624
|
+
return await comms.dispatch(msgType, content, metadata, buffers);
|
|
625
|
+
} finally {
|
|
626
|
+
const left = (handlers.get(controller) ?? 0) - 1;
|
|
627
|
+
if (left > 0) handlers.set(controller, left);
|
|
628
|
+
else handlers.delete(controller);
|
|
629
|
+
}
|
|
630
|
+
}
|
|
607
631
|
return {
|
|
608
632
|
namespace: {
|
|
633
|
+
get signal() {
|
|
634
|
+
return current.signal;
|
|
635
|
+
},
|
|
609
636
|
broadcast,
|
|
610
637
|
display,
|
|
611
638
|
format: format2,
|
|
@@ -621,7 +648,9 @@ function createJupyter(host2) {
|
|
|
621
648
|
formatNow,
|
|
622
649
|
confirm,
|
|
623
650
|
prompt,
|
|
624
|
-
commDispatch
|
|
651
|
+
commDispatch,
|
|
652
|
+
startCell,
|
|
653
|
+
abortSignal
|
|
625
654
|
};
|
|
626
655
|
}
|
|
627
656
|
|
|
@@ -653,6 +682,7 @@ function createHandler(shim, evaluate = (code) => (0, eval)(code)) {
|
|
|
653
682
|
return {};
|
|
654
683
|
case "eval": {
|
|
655
684
|
handles.clear();
|
|
685
|
+
shim.startCell?.();
|
|
656
686
|
try {
|
|
657
687
|
const box = await evaluate(request.code);
|
|
658
688
|
return { result: box === void 0 ? { type: "undefined" } : remote(box[0]) };
|
|
@@ -671,7 +701,13 @@ function createHandler(shim, evaluate = (code) => (0, eval)(code)) {
|
|
|
671
701
|
return { value: shim.describeError(take(request.handle)) };
|
|
672
702
|
case "comm":
|
|
673
703
|
try {
|
|
674
|
-
const handled = await shim.commDispatch(
|
|
704
|
+
const handled = await shim.commDispatch(
|
|
705
|
+
request.msgType,
|
|
706
|
+
request.content,
|
|
707
|
+
request.metadata,
|
|
708
|
+
request.buffers,
|
|
709
|
+
request.ownSignal === true
|
|
710
|
+
);
|
|
675
711
|
return { handled: handled === true };
|
|
676
712
|
} catch (err) {
|
|
677
713
|
return { error: JSON.parse(shim.describeError(err)) };
|
|
@@ -693,6 +729,9 @@ function createHandler(shim, evaluate = (code) => (0, eval)(code)) {
|
|
|
693
729
|
return { text: shim.inspectPath(request.path, request.detail ?? 0) };
|
|
694
730
|
case "barrier":
|
|
695
731
|
return { value: shim.barrier() };
|
|
732
|
+
case "abort":
|
|
733
|
+
shim.abortSignal?.(request.cell !== false);
|
|
734
|
+
return {};
|
|
696
735
|
default:
|
|
697
736
|
throw new Error(`unknown request ${JSON.stringify(request.op)}`);
|
|
698
737
|
}
|
|
@@ -1034,9 +1073,13 @@ function install({ base, cdn: cdnBase }) {
|
|
|
1034
1073
|
broadcastResult: jupyter.broadcastResult,
|
|
1035
1074
|
// execute_request's user_expressions (#340).
|
|
1036
1075
|
formatJson: async (value) => JSON.stringify(await jupyter.namespace.format(value)),
|
|
1037
|
-
|
|
1076
|
+
// jupyter.signal (#418, #421): a new one as each cell starts; the host
|
|
1077
|
+
// aborts the running ones on interrupt.
|
|
1078
|
+
startCell: jupyter.startCell,
|
|
1079
|
+
abortSignal: jupyter.abortSignal,
|
|
1080
|
+
async commDispatch(msgType, content, metadata, buffers64, ownSignal) {
|
|
1038
1081
|
const buffers = buffers64.map((b) => Uint8Array.from(atob(b), (c) => c.charCodeAt(0)));
|
|
1039
|
-
return await jupyter.commDispatch(msgType, content, metadata, buffers);
|
|
1082
|
+
return await jupyter.commDispatch(msgType, content, metadata, buffers, ownSignal);
|
|
1040
1083
|
},
|
|
1041
1084
|
describeError(value) {
|
|
1042
1085
|
if (value instanceof Error) {
|