thebe-core
Version:
Typescript based core functionality for Thebe
131 lines (110 loc) • 3.16 kB
text/typescript
import type ThebeCodeCell from './cell';
import type ThebeNotebook from './notebook';
import type ThebeServer from './server';
import type ThebeSession from './session';
import type { IError } from '@jupyterlab/nbformat';
// value of some of these enums are not arbitrary
// but relate to events sent from the server via the
// event stream
export enum ServerStatusEvent {
'launching' = 'launching',
'ready' = 'server-ready',
'closed' = 'closed',
'unknown' = 'unknown',
}
export enum SessionStatusEvent {
'starting' = 'starting',
'ready' = 'ready',
'shutdown' = 'shutdown',
}
export enum KernelStatusEvent {
'starting' = 'starting',
'ready' = 'ready',
'shutdown' = 'shutdown',
}
export enum NotebookStatusEvent {
'attached' = 'attached',
'detached' = 'detached',
'executing' = 'executing',
'idle' = 'idle',
}
export enum CellStatusEvent {
'attached' = 'attached',
'detached' = 'detached',
'executing' = 'executing',
'idle' = 'idle',
}
export enum EventSubject {
'server' = 'server',
'session' = 'session',
'kernel' = 'kernel',
'notebook' = 'notebook',
'cell' = 'cell',
}
// TODO improve typing around status's
export enum ErrorStatusEvent {
'warning' = 'warning',
'executeError' = 'execute-error',
'error' = 'error',
'server' = 'server-error',
'session' = 'session-error',
}
export function errorToMessage(json: IError): string {
if (!json.traceback) {
return json.evalue;
} else if (Array.isArray(json.traceback)) {
return `${json.evalue}\n${(json.traceback ?? []).join('')}`;
} else {
return `${json.evalue}\n${JSON.stringify(json.traceback)}`;
}
}
export enum ThebeEventType {
'status' = 'status',
'error' = 'error',
}
export type EventObject = ThebeServer | ThebeSession | ThebeNotebook | ThebeCodeCell;
export type StatusEvent =
| ServerStatusEvent
| SessionStatusEvent
| NotebookStatusEvent
| CellStatusEvent
| KernelStatusEvent
| ErrorStatusEvent;
export interface ThebeEventData {
subject?: EventSubject;
id?: string;
status?: StatusEvent;
message: string;
object?: EventObject;
}
export type ThebeEventCb = (event: string, data: ThebeEventData) => void;
export class ThebeEvents {
listeners: Record<string, Map<ThebeEventCb, { unbind: boolean }>>;
constructor() {
this.listeners = {};
}
_ensureMap(event: string) {
if (!(event in this.listeners)) this.listeners[event] = new Map();
}
trigger(event: ThebeEventType, evt: ThebeEventData) {
if (!(event in this.listeners)) return;
this.listeners[event].forEach(({ unbind }, cb) => {
cb(event, evt);
if (unbind) this.listeners[event].delete(cb);
});
}
on(event: ThebeEventType, cb: ThebeEventCb) {
this._ensureMap(event);
this.listeners[event].set(cb, { unbind: false });
return () => this.off(event, cb);
}
one(event: ThebeEventType, cb: ThebeEventCb) {
this._ensureMap(event);
this.listeners[event].set(cb, { unbind: true });
return () => this.off(event, cb);
}
off(event: ThebeEventType, cb: ThebeEventCb) {
if (!(event in this.listeners)) return;
this.listeners[event].delete(cb);
}
}