@difizen/libro-kernel 0.0.2-alpha.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/LICENSE +21 -0
- package/README.md +1 -0
- package/es/basemanager.d.ts +94 -0
- package/es/basemanager.d.ts.map +1 -0
- package/es/basemanager.js +110 -0
- package/es/contents/contents-drive.d.ts +189 -0
- package/es/contents/contents-drive.d.ts.map +1 -0
- package/es/contents/contents-drive.js +792 -0
- package/es/contents/contents-manager.d.ts +229 -0
- package/es/contents/contents-manager.d.ts.map +1 -0
- package/es/contents/contents-manager.js +551 -0
- package/es/contents/contents-module.d.ts +3 -0
- package/es/contents/contents-module.d.ts.map +1 -0
- package/es/contents/contents-module.js +4 -0
- package/es/contents/contents-protocol.d.ts +487 -0
- package/es/contents/contents-protocol.d.ts.map +1 -0
- package/es/contents/contents-protocol.js +1 -0
- package/es/contents/index.d.ts +6 -0
- package/es/contents/index.d.ts.map +1 -0
- package/es/contents/index.js +5 -0
- package/es/contents/validate.d.ts +10 -0
- package/es/contents/validate.d.ts.map +1 -0
- package/es/contents/validate.js +22 -0
- package/es/index.d.ts +10 -0
- package/es/index.d.ts.map +1 -0
- package/es/index.js +9 -0
- package/es/index.less +0 -0
- package/es/kernel/comm.d.ts +92 -0
- package/es/kernel/comm.d.ts.map +1 -0
- package/es/kernel/comm.js +216 -0
- package/es/kernel/future.d.ts +178 -0
- package/es/kernel/future.d.ts.map +1 -0
- package/es/kernel/future.js +587 -0
- package/es/kernel/index.d.ts +8 -0
- package/es/kernel/index.d.ts.map +1 -0
- package/es/kernel/index.js +8 -0
- package/es/kernel/kernel-connection.d.ts +550 -0
- package/es/kernel/kernel-connection.d.ts.map +1 -0
- package/es/kernel/kernel-connection.js +1957 -0
- package/es/kernel/kernel-module.d.ts +3 -0
- package/es/kernel/kernel-module.d.ts.map +1 -0
- package/es/kernel/kernel-module.js +32 -0
- package/es/kernel/libro-kernel-manager.d.ts +69 -0
- package/es/kernel/libro-kernel-manager.d.ts.map +1 -0
- package/es/kernel/libro-kernel-manager.js +349 -0
- package/es/kernel/libro-kernel-protocol.d.ts +675 -0
- package/es/kernel/libro-kernel-protocol.d.ts.map +1 -0
- package/es/kernel/libro-kernel-protocol.js +60 -0
- package/es/kernel/libro-kernel-utils.d.ts +95 -0
- package/es/kernel/libro-kernel-utils.d.ts.map +1 -0
- package/es/kernel/libro-kernel-utils.js +130 -0
- package/es/kernel/libro-kernel.d.ts +14 -0
- package/es/kernel/libro-kernel.d.ts.map +1 -0
- package/es/kernel/libro-kernel.js +54 -0
- package/es/kernel/messages.d.ts +845 -0
- package/es/kernel/messages.d.ts.map +1 -0
- package/es/kernel/messages.js +457 -0
- package/es/kernel/restapi.d.ts +78 -0
- package/es/kernel/restapi.d.ts.map +1 -0
- package/es/kernel/restapi.js +367 -0
- package/es/kernel/serialize.d.ts +10 -0
- package/es/kernel/serialize.d.ts.map +1 -0
- package/es/kernel/serialize.js +214 -0
- package/es/kernel/validate.d.ts +15 -0
- package/es/kernel/validate.d.ts.map +1 -0
- package/es/kernel/validate.js +125 -0
- package/es/kernelspec/index.d.ts +5 -0
- package/es/kernelspec/index.d.ts.map +1 -0
- package/es/kernelspec/index.js +4 -0
- package/es/kernelspec/kernelspec-module.d.ts +3 -0
- package/es/kernelspec/kernelspec-module.d.ts.map +1 -0
- package/es/kernelspec/kernelspec-module.js +4 -0
- package/es/kernelspec/kernelspec.d.ts +33 -0
- package/es/kernelspec/kernelspec.d.ts.map +1 -0
- package/es/kernelspec/kernelspec.js +1 -0
- package/es/kernelspec/manager.d.ts +81 -0
- package/es/kernelspec/manager.d.ts.map +1 -0
- package/es/kernelspec/manager.js +248 -0
- package/es/kernelspec/restapi.d.ts +71 -0
- package/es/kernelspec/restapi.d.ts.map +1 -0
- package/es/kernelspec/restapi.js +107 -0
- package/es/kernelspec/validate.d.ts +10 -0
- package/es/kernelspec/validate.d.ts.map +1 -0
- package/es/kernelspec/validate.js +69 -0
- package/es/libro-kernel-connection-manager.d.ts +19 -0
- package/es/libro-kernel-connection-manager.d.ts.map +1 -0
- package/es/libro-kernel-connection-manager.js +142 -0
- package/es/module.d.ts +3 -0
- package/es/module.d.ts.map +1 -0
- package/es/module.js +9 -0
- package/es/page-config.d.ts +36 -0
- package/es/page-config.d.ts.map +1 -0
- package/es/page-config.js +129 -0
- package/es/protocol.d.ts +13 -0
- package/es/protocol.d.ts.map +1 -0
- package/es/protocol.js +8 -0
- package/es/server/connection-error.d.ts +36 -0
- package/es/server/connection-error.d.ts.map +1 -0
- package/es/server/connection-error.js +109 -0
- package/es/server/index.d.ts +6 -0
- package/es/server/index.d.ts.map +1 -0
- package/es/server/index.js +5 -0
- package/es/server/server-connection-protocol.d.ts +49 -0
- package/es/server/server-connection-protocol.d.ts.map +1 -0
- package/es/server/server-connection-protocol.js +0 -0
- package/es/server/server-connection.d.ts +25 -0
- package/es/server/server-connection.d.ts.map +1 -0
- package/es/server/server-connection.js +159 -0
- package/es/server/server-manager.d.ts +22 -0
- package/es/server/server-manager.d.ts.map +1 -0
- package/es/server/server-manager.js +163 -0
- package/es/server/server-module.d.ts +3 -0
- package/es/server/server-module.d.ts.map +1 -0
- package/es/server/server-module.js +4 -0
- package/es/session/index.d.ts +5 -0
- package/es/session/index.d.ts.map +1 -0
- package/es/session/index.js +4 -0
- package/es/session/libro-session-manager.d.ts +71 -0
- package/es/session/libro-session-manager.d.ts.map +1 -0
- package/es/session/libro-session-manager.js +539 -0
- package/es/session/libro-session-protocol.d.ts +50 -0
- package/es/session/libro-session-protocol.d.ts.map +1 -0
- package/es/session/libro-session-protocol.js +21 -0
- package/es/session/libro-session.d.ts +12 -0
- package/es/session/libro-session.d.ts.map +1 -0
- package/es/session/libro-session.js +19 -0
- package/es/session/restapi.d.ts +28 -0
- package/es/session/restapi.d.ts.map +1 -0
- package/es/session/restapi.js +214 -0
- package/es/session/session-module.d.ts +3 -0
- package/es/session/session-module.d.ts.map +1 -0
- package/es/session/session-module.js +18 -0
- package/es/session/validate.d.ts +14 -0
- package/es/session/validate.d.ts.map +1 -0
- package/es/session/validate.js +37 -0
- package/es/utils.d.ts +4 -0
- package/es/utils.d.ts.map +1 -0
- package/es/utils.js +29 -0
- package/es/validate-property.d.ts +2 -0
- package/es/validate-property.d.ts.map +1 -0
- package/es/validate-property.js +35 -0
- package/package.json +62 -0
- package/src/basemanager.ts +133 -0
- package/src/contents/contents-drive.ts +495 -0
- package/src/contents/contents-manager.ts +465 -0
- package/src/contents/contents-module.ts +6 -0
- package/src/contents/contents-protocol.ts +604 -0
- package/src/contents/index.ts +5 -0
- package/src/contents/validate.ts +29 -0
- package/src/index.tsx +9 -0
- package/src/kernel/comm.ts +220 -0
- package/src/kernel/future.ts +474 -0
- package/src/kernel/index.ts +7 -0
- package/src/kernel/kernel-connection.ts +1770 -0
- package/src/kernel/kernel-module.ts +50 -0
- package/src/kernel/libro-kernel-manager.ts +199 -0
- package/src/kernel/libro-kernel-protocol.ts +858 -0
- package/src/kernel/libro-kernel-utils.ts +152 -0
- package/src/kernel/libro-kernel.ts +39 -0
- package/src/kernel/messages.ts +1104 -0
- package/src/kernel/restapi.ts +183 -0
- package/src/kernel/serialize.ts +262 -0
- package/src/kernel/validate.ts +101 -0
- package/src/kernelspec/index.ts +5 -0
- package/src/kernelspec/kernelspec-module.ts +9 -0
- package/src/kernelspec/kernelspec.ts +37 -0
- package/src/kernelspec/manager.ts +173 -0
- package/src/kernelspec/restapi.ts +104 -0
- package/src/kernelspec/validate.ts +80 -0
- package/src/libro-kernel-connection-manager.ts +73 -0
- package/src/module.ts +19 -0
- package/src/page-config.ts +106 -0
- package/src/protocol.ts +24 -0
- package/src/server/connection-error.ts +60 -0
- package/src/server/index.ts +5 -0
- package/src/server/server-connection-protocol.ts +57 -0
- package/src/server/server-connection.ts +144 -0
- package/src/server/server-manager.ts +76 -0
- package/src/server/server-module.ts +9 -0
- package/src/session/index.ts +4 -0
- package/src/session/libro-session-manager.ts +377 -0
- package/src/session/libro-session-protocol.ts +61 -0
- package/src/session/libro-session.ts +33 -0
- package/src/session/restapi.ts +126 -0
- package/src/session/session-module.ts +26 -0
- package/src/session/validate.ts +39 -0
- package/src/utils.ts +28 -0
- package/src/validate-property.ts +38 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { URL } from '@difizen/libro-common';
|
|
2
|
+
import { inject, singleton } from '@difizen/mana-app';
|
|
3
|
+
|
|
4
|
+
import type { ISettings } from '../server/index.js';
|
|
5
|
+
import { createResponseError, ServerConnection } from '../server/index.js';
|
|
6
|
+
|
|
7
|
+
import type { IKernelModel, KernelMeta } from './libro-kernel-protocol.js';
|
|
8
|
+
import { validateModel, validateModels } from './validate.js';
|
|
9
|
+
|
|
10
|
+
export const KERNEL_SERVICE_URL = 'api/kernels';
|
|
11
|
+
|
|
12
|
+
@singleton()
|
|
13
|
+
export class KernelRestAPI {
|
|
14
|
+
@inject(ServerConnection) serverConnection: ServerConnection;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Get a full kernel model from the server by kernel id string.
|
|
18
|
+
*
|
|
19
|
+
* #### Notes
|
|
20
|
+
* Uses the [Jupyter Notebook API](http://petstore.swagger.io/?url=https://raw.githubusercontent.com/jupyter/notebook/master/notebook/services/api/api.yaml#!/kernels) and validates the response model.
|
|
21
|
+
*
|
|
22
|
+
* The promise is fulfilled on a valid response and rejected otherwise.
|
|
23
|
+
*/
|
|
24
|
+
async getKernelModel(
|
|
25
|
+
id: string,
|
|
26
|
+
serverSettings?: Partial<ISettings>,
|
|
27
|
+
): Promise<IKernelModel | undefined> {
|
|
28
|
+
const settings = { ...this.serverConnection.settings, ...serverSettings };
|
|
29
|
+
const url = URL.join(settings.baseUrl, KERNEL_SERVICE_URL, encodeURIComponent(id));
|
|
30
|
+
|
|
31
|
+
const response = await this.serverConnection.makeRequest(url, {}, settings);
|
|
32
|
+
if (response.status === 404) {
|
|
33
|
+
return undefined;
|
|
34
|
+
} else if (response.status !== 200) {
|
|
35
|
+
const err = await createResponseError(response);
|
|
36
|
+
throw err;
|
|
37
|
+
}
|
|
38
|
+
const data = await response.json();
|
|
39
|
+
validateModel(data);
|
|
40
|
+
return data;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Start a new kernel.
|
|
45
|
+
*
|
|
46
|
+
* @param options - The options used to create the kernel.
|
|
47
|
+
*
|
|
48
|
+
* @returns A promise that resolves with a kernel connection object.
|
|
49
|
+
*
|
|
50
|
+
* #### Notes
|
|
51
|
+
* Uses the [Jupyter Notebook API](http://petstore.swagger.io/?url=https://raw.githubusercontent.com/jupyter/notebook/master/notebook/services/api/api.yaml#!/kernels) and validates the response model.
|
|
52
|
+
*
|
|
53
|
+
* The promise is fulfilled on a valid response and rejected otherwise.
|
|
54
|
+
*/
|
|
55
|
+
async startNew(
|
|
56
|
+
options: IKernelOptions = {},
|
|
57
|
+
serverSettings?: Partial<ISettings>,
|
|
58
|
+
): Promise<IKernelModel> {
|
|
59
|
+
const settings = { ...this.serverConnection.settings, ...serverSettings };
|
|
60
|
+
const url = URL.join(settings.baseUrl, KERNEL_SERVICE_URL);
|
|
61
|
+
const init = {
|
|
62
|
+
method: 'POST',
|
|
63
|
+
body: JSON.stringify(options),
|
|
64
|
+
};
|
|
65
|
+
const response = await this.serverConnection.makeRequest(url, init, settings);
|
|
66
|
+
if (response.status !== 201) {
|
|
67
|
+
const err = await createResponseError(response);
|
|
68
|
+
throw err;
|
|
69
|
+
}
|
|
70
|
+
const data = await response.json();
|
|
71
|
+
validateModel(data);
|
|
72
|
+
return data;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Fetch the running kernels.
|
|
77
|
+
*
|
|
78
|
+
* @param settings - The optional server settings.
|
|
79
|
+
*
|
|
80
|
+
* @returns A promise that resolves with the list of running kernels.
|
|
81
|
+
*
|
|
82
|
+
* #### Notes
|
|
83
|
+
* Uses the [Jupyter Notebook API](http://petstore.swagger.io/?url=https://raw.githubusercontent.com/jupyter/notebook/master/notebook/services/api/api.yaml#!/kernels) and validates the response model.
|
|
84
|
+
*
|
|
85
|
+
* The promise is fulfilled on a valid response and rejected otherwise.
|
|
86
|
+
*/
|
|
87
|
+
async listRunning(serverSettings?: Partial<ISettings>): Promise<KernelMeta[]> {
|
|
88
|
+
const settings = { ...this.serverConnection.settings, ...serverSettings };
|
|
89
|
+
const url = URL.join(settings.baseUrl, KERNEL_SERVICE_URL);
|
|
90
|
+
const response = await this.serverConnection.makeRequest(url, {}, settings);
|
|
91
|
+
if (response.status !== 200) {
|
|
92
|
+
const err = await createResponseError(response);
|
|
93
|
+
throw err;
|
|
94
|
+
}
|
|
95
|
+
const data = await response.json();
|
|
96
|
+
validateModels(data);
|
|
97
|
+
return data;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Shut down a kernel.
|
|
102
|
+
*
|
|
103
|
+
* @param id - The id of the running kernel.
|
|
104
|
+
*
|
|
105
|
+
* @param settings - The server settings for the request.
|
|
106
|
+
*
|
|
107
|
+
* @returns A promise that resolves when the kernel is shut down.
|
|
108
|
+
*
|
|
109
|
+
*
|
|
110
|
+
* #### Notes
|
|
111
|
+
* Uses the [Jupyter Notebook API](http://petstore.swagger.io/?url=https://raw.githubusercontent.com/jupyter/notebook/master/notebook/services/api/api.yaml#!/kernels) and validates the response model.
|
|
112
|
+
*
|
|
113
|
+
* The promise is fulfilled on a valid response and rejected otherwise.
|
|
114
|
+
*/
|
|
115
|
+
async shutdownKernel(id: string, serverSettings?: Partial<ISettings>): Promise<void> {
|
|
116
|
+
const settings = { ...this.serverConnection.settings, ...serverSettings };
|
|
117
|
+
const url = URL.join(settings.baseUrl, KERNEL_SERVICE_URL, encodeURIComponent(id));
|
|
118
|
+
const init = { method: 'DELETE' };
|
|
119
|
+
const response = await this.serverConnection.makeRequest(url, init, settings);
|
|
120
|
+
if (response.status === 404) {
|
|
121
|
+
const msg = `The kernel "${id}" does not exist on the server`;
|
|
122
|
+
console.warn(msg);
|
|
123
|
+
} else if (response.status !== 204) {
|
|
124
|
+
const err = await createResponseError(response);
|
|
125
|
+
throw err;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Restart a kernel.
|
|
131
|
+
*
|
|
132
|
+
* #### Notes
|
|
133
|
+
* Uses the [Jupyter Notebook API](http://petstore.swagger.io/?url=https://raw.githubusercontent.com/jupyter/notebook/master/notebook/services/api/api.yaml#!/kernels) and validates the response model.
|
|
134
|
+
*
|
|
135
|
+
* The promise is fulfilled on a valid response (and thus after a restart) and rejected otherwise.
|
|
136
|
+
*/
|
|
137
|
+
async restartKernel(id: string, serverSettings?: Partial<ISettings>): Promise<void> {
|
|
138
|
+
const settings = { ...this.serverConnection.settings, ...serverSettings };
|
|
139
|
+
const url = URL.join(
|
|
140
|
+
settings.baseUrl,
|
|
141
|
+
KERNEL_SERVICE_URL,
|
|
142
|
+
encodeURIComponent(id),
|
|
143
|
+
'restart',
|
|
144
|
+
);
|
|
145
|
+
const init = { method: 'POST' };
|
|
146
|
+
|
|
147
|
+
const response = await this.serverConnection.makeRequest(url, init, settings);
|
|
148
|
+
if (response.status !== 200) {
|
|
149
|
+
const err = await createResponseError(response);
|
|
150
|
+
throw err;
|
|
151
|
+
}
|
|
152
|
+
const data = await response.json();
|
|
153
|
+
validateModel(data);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Interrupt a kernel.
|
|
158
|
+
*
|
|
159
|
+
* #### Notes
|
|
160
|
+
* Uses the [Jupyter Notebook API](http://petstore.swagger.io/?url=https://raw.githubusercontent.com/jupyter/notebook/master/notebook/services/api/api.yaml#!/kernels) and validates the response model.
|
|
161
|
+
*
|
|
162
|
+
* The promise is fulfilled on a valid response and rejected otherwise.
|
|
163
|
+
*/
|
|
164
|
+
async interruptKernel(
|
|
165
|
+
id: string,
|
|
166
|
+
serverSettings?: Partial<ISettings>,
|
|
167
|
+
): Promise<void> {
|
|
168
|
+
const settings = { ...this.serverConnection.settings, ...serverSettings };
|
|
169
|
+
const url = URL.join(
|
|
170
|
+
settings.baseUrl,
|
|
171
|
+
KERNEL_SERVICE_URL,
|
|
172
|
+
encodeURIComponent(id),
|
|
173
|
+
'interrupt',
|
|
174
|
+
);
|
|
175
|
+
const init = { method: 'POST' };
|
|
176
|
+
const response = await this.serverConnection.makeRequest(url, init, settings);
|
|
177
|
+
if (response.status !== 204) {
|
|
178
|
+
const err = await createResponseError(response);
|
|
179
|
+
throw err;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
export type IKernelOptions = Partial<Pick<IKernelModel, 'name'>>;
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import * as KernelMessage from './messages.js';
|
|
2
|
+
|
|
3
|
+
namespace Private {
|
|
4
|
+
/**
|
|
5
|
+
* Deserialize and return the unpacked message.
|
|
6
|
+
* Protocol `v1.kernel.websocket.jupyter.org`
|
|
7
|
+
*/
|
|
8
|
+
export function deserializeV1KernelWebsocketJupyterOrg(
|
|
9
|
+
binMsg: ArrayBuffer,
|
|
10
|
+
): KernelMessage.IMessage {
|
|
11
|
+
const data = new DataView(binMsg);
|
|
12
|
+
const offsetNumber = Number(data.getBigUint64(0, true /* littleEndian */));
|
|
13
|
+
const offsets: number[] = [];
|
|
14
|
+
for (let i = 0; i < offsetNumber; i++) {
|
|
15
|
+
// WARNING: we cast our 64-bit unsigned int to a number!
|
|
16
|
+
// so offsets cannot index up to 2**64 bytes
|
|
17
|
+
offsets.push(Number(data.getBigUint64(8 * (i + 1), true /* littleEndian */)));
|
|
18
|
+
}
|
|
19
|
+
const decoder = new TextDecoder('utf8');
|
|
20
|
+
const channel = decoder.decode(
|
|
21
|
+
binMsg.slice(offsets[0], offsets[1]),
|
|
22
|
+
) as KernelMessage.Channel;
|
|
23
|
+
const header = JSON.parse(decoder.decode(binMsg.slice(offsets[1], offsets[2])));
|
|
24
|
+
const parent_header = JSON.parse(
|
|
25
|
+
decoder.decode(binMsg.slice(offsets[2], offsets[3])),
|
|
26
|
+
);
|
|
27
|
+
const metadata = JSON.parse(decoder.decode(binMsg.slice(offsets[3], offsets[4])));
|
|
28
|
+
const content = JSON.parse(decoder.decode(binMsg.slice(offsets[4], offsets[5])));
|
|
29
|
+
const buffers = [];
|
|
30
|
+
for (let i = 5; i < offsets.length; i++) {
|
|
31
|
+
if (i === offsets.length - 1) {
|
|
32
|
+
buffers.push(binMsg.slice(offsets[i]));
|
|
33
|
+
} else {
|
|
34
|
+
buffers.push(binMsg.slice(offsets[i], offsets[i + 1]));
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
const msg: KernelMessage.IMessage = {
|
|
38
|
+
channel,
|
|
39
|
+
header,
|
|
40
|
+
parent_header,
|
|
41
|
+
metadata,
|
|
42
|
+
content,
|
|
43
|
+
buffers,
|
|
44
|
+
};
|
|
45
|
+
return msg;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Serialize a kernel message for transport.
|
|
50
|
+
* Protocol `v1.kernel.websocket.jupyter.org`
|
|
51
|
+
*/
|
|
52
|
+
export function serializeV1KernelWebsocketJupyterOrg(
|
|
53
|
+
msg: KernelMessage.IMessage,
|
|
54
|
+
): ArrayBuffer {
|
|
55
|
+
const header = JSON.stringify(msg.header);
|
|
56
|
+
const parentHeader =
|
|
57
|
+
msg.parent_header === null ? '{}' : JSON.stringify(msg.parent_header);
|
|
58
|
+
const metadata = JSON.stringify(msg.metadata);
|
|
59
|
+
const content = JSON.stringify(msg.content);
|
|
60
|
+
const buffers: (ArrayBuffer | ArrayBufferView)[] =
|
|
61
|
+
msg.buffers !== undefined ? msg.buffers : [];
|
|
62
|
+
const offsetNumber: number = 1 + 4 + buffers.length + 1;
|
|
63
|
+
const offsets: number[] = [];
|
|
64
|
+
offsets.push(8 * (1 + offsetNumber));
|
|
65
|
+
offsets.push(msg.channel.length + offsets[offsets.length - 1]);
|
|
66
|
+
const encoder = new TextEncoder();
|
|
67
|
+
const channelEncoded = encoder.encode(msg.channel);
|
|
68
|
+
const headerEncoded = encoder.encode(header);
|
|
69
|
+
const parentHeaderEncoded = encoder.encode(parentHeader);
|
|
70
|
+
const metadataEncoded = encoder.encode(metadata);
|
|
71
|
+
const contentEncoded = encoder.encode(content);
|
|
72
|
+
const binMsgNoBuff = new Uint8Array(
|
|
73
|
+
channelEncoded.length +
|
|
74
|
+
headerEncoded.length +
|
|
75
|
+
parentHeaderEncoded.length +
|
|
76
|
+
metadataEncoded.length +
|
|
77
|
+
contentEncoded.length,
|
|
78
|
+
);
|
|
79
|
+
binMsgNoBuff.set(channelEncoded);
|
|
80
|
+
binMsgNoBuff.set(headerEncoded, channelEncoded.length);
|
|
81
|
+
binMsgNoBuff.set(parentHeaderEncoded, channelEncoded.length + headerEncoded.length);
|
|
82
|
+
binMsgNoBuff.set(
|
|
83
|
+
metadataEncoded,
|
|
84
|
+
channelEncoded.length + headerEncoded.length + parentHeaderEncoded.length,
|
|
85
|
+
);
|
|
86
|
+
binMsgNoBuff.set(
|
|
87
|
+
contentEncoded,
|
|
88
|
+
channelEncoded.length +
|
|
89
|
+
headerEncoded.length +
|
|
90
|
+
parentHeaderEncoded.length +
|
|
91
|
+
metadataEncoded.length,
|
|
92
|
+
);
|
|
93
|
+
for (const length of [
|
|
94
|
+
headerEncoded.length,
|
|
95
|
+
parentHeaderEncoded.length,
|
|
96
|
+
metadataEncoded.length,
|
|
97
|
+
contentEncoded.length,
|
|
98
|
+
]) {
|
|
99
|
+
offsets.push(length + offsets[offsets.length - 1]);
|
|
100
|
+
}
|
|
101
|
+
let buffersByteLength = 0;
|
|
102
|
+
for (const buffer of buffers) {
|
|
103
|
+
const length = buffer.byteLength;
|
|
104
|
+
offsets.push(length + offsets[offsets.length - 1]);
|
|
105
|
+
buffersByteLength += length;
|
|
106
|
+
}
|
|
107
|
+
const binMsg = new Uint8Array(
|
|
108
|
+
8 * (1 + offsetNumber) + binMsgNoBuff.byteLength + buffersByteLength,
|
|
109
|
+
);
|
|
110
|
+
const word = new ArrayBuffer(8);
|
|
111
|
+
const data = new DataView(word);
|
|
112
|
+
data.setBigUint64(0, BigInt(offsetNumber), true /* littleEndian */);
|
|
113
|
+
binMsg.set(new Uint8Array(word), 0);
|
|
114
|
+
for (let i = 0; i < offsets.length; i++) {
|
|
115
|
+
data.setBigUint64(0, BigInt(offsets[i]), true /* littleEndian */);
|
|
116
|
+
binMsg.set(new Uint8Array(word), 8 * (i + 1));
|
|
117
|
+
}
|
|
118
|
+
binMsg.set(binMsgNoBuff, offsets[0]);
|
|
119
|
+
for (let i = 0; i < buffers.length; i++) {
|
|
120
|
+
const buffer = buffers[i];
|
|
121
|
+
binMsg.set(
|
|
122
|
+
new Uint8Array(ArrayBuffer.isView(buffer) ? buffer.buffer : buffer),
|
|
123
|
+
offsets[5 + i],
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
return binMsg.buffer;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Deserialize and return the unpacked message.
|
|
131
|
+
* Default protocol
|
|
132
|
+
*
|
|
133
|
+
* #### Notes
|
|
134
|
+
* Handles JSON blob strings and binary messages.
|
|
135
|
+
*/
|
|
136
|
+
export function deserializeDefault(
|
|
137
|
+
data: ArrayBuffer | string,
|
|
138
|
+
): KernelMessage.IMessage {
|
|
139
|
+
let value: KernelMessage.IMessage;
|
|
140
|
+
if (typeof data === 'string') {
|
|
141
|
+
value = JSON.parse(data);
|
|
142
|
+
} else {
|
|
143
|
+
value = deserializeBinary(data);
|
|
144
|
+
}
|
|
145
|
+
return value;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Serialize a kernel message for transport.
|
|
150
|
+
* Default protocol
|
|
151
|
+
*
|
|
152
|
+
* #### Notes
|
|
153
|
+
* If there is binary content, an `ArrayBuffer` is returned,
|
|
154
|
+
* otherwise the message is converted to a JSON string.
|
|
155
|
+
*/
|
|
156
|
+
export function serializeDefault(msg: KernelMessage.IMessage): string | ArrayBuffer {
|
|
157
|
+
let value: string | ArrayBuffer;
|
|
158
|
+
if (msg.buffers?.length) {
|
|
159
|
+
value = serializeBinary(msg);
|
|
160
|
+
} else {
|
|
161
|
+
value = JSON.stringify(msg);
|
|
162
|
+
}
|
|
163
|
+
return value;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Serialize a kernel message for transport.
|
|
169
|
+
*/
|
|
170
|
+
export function serialize(
|
|
171
|
+
msg: KernelMessage.IMessage,
|
|
172
|
+
protocol = '',
|
|
173
|
+
): string | ArrayBuffer {
|
|
174
|
+
switch (protocol) {
|
|
175
|
+
case KernelMessage.supportedKernelWebSocketProtocols.v1KernelWebsocketJupyterOrg:
|
|
176
|
+
return Private.serializeV1KernelWebsocketJupyterOrg(msg);
|
|
177
|
+
default:
|
|
178
|
+
return Private.serializeDefault(msg);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Deserialize and return the unpacked message.
|
|
184
|
+
*/
|
|
185
|
+
export function deserialize(data: ArrayBuffer, protocol = ''): KernelMessage.IMessage {
|
|
186
|
+
switch (protocol) {
|
|
187
|
+
case KernelMessage.supportedKernelWebSocketProtocols.v1KernelWebsocketJupyterOrg:
|
|
188
|
+
return Private.deserializeV1KernelWebsocketJupyterOrg(data);
|
|
189
|
+
default:
|
|
190
|
+
return Private.deserializeDefault(data);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Deserialize a binary message to a Kernel Message.
|
|
195
|
+
*/
|
|
196
|
+
function deserializeBinary(buf: ArrayBuffer): KernelMessage.IMessage {
|
|
197
|
+
const data = new DataView(buf);
|
|
198
|
+
// read the header: 1 + nbufs 32b integers
|
|
199
|
+
const nbufs = data.getUint32(0);
|
|
200
|
+
const offsets: number[] = [];
|
|
201
|
+
if (nbufs < 2) {
|
|
202
|
+
throw new Error('Invalid incoming Kernel Message');
|
|
203
|
+
}
|
|
204
|
+
for (let i = 1; i <= nbufs; i++) {
|
|
205
|
+
offsets.push(data.getUint32(i * 4));
|
|
206
|
+
}
|
|
207
|
+
const jsonBytes = new Uint8Array(buf.slice(offsets[0], offsets[1]));
|
|
208
|
+
const msg = JSON.parse(new TextDecoder('utf8').decode(jsonBytes));
|
|
209
|
+
// the remaining chunks are stored as DataViews in msg.buffers
|
|
210
|
+
msg.buffers = [];
|
|
211
|
+
for (let i = 1; i < nbufs; i++) {
|
|
212
|
+
const start = offsets[i];
|
|
213
|
+
const stop = offsets[i + 1] || buf.byteLength;
|
|
214
|
+
msg.buffers.push(new DataView(buf.slice(start, stop)));
|
|
215
|
+
}
|
|
216
|
+
return msg;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Implement the binary serialization protocol.
|
|
221
|
+
*
|
|
222
|
+
* Serialize Kernel message to ArrayBuffer.
|
|
223
|
+
*/
|
|
224
|
+
function serializeBinary(msg: KernelMessage.IMessage): ArrayBuffer {
|
|
225
|
+
const offsets: number[] = [];
|
|
226
|
+
const buffers: ArrayBuffer[] = [];
|
|
227
|
+
const encoder = new TextEncoder();
|
|
228
|
+
let origBuffers: (ArrayBuffer | ArrayBufferView)[] = [];
|
|
229
|
+
if (msg.buffers !== undefined) {
|
|
230
|
+
origBuffers = msg.buffers;
|
|
231
|
+
delete msg.buffers;
|
|
232
|
+
}
|
|
233
|
+
const jsonUtf8 = encoder.encode(JSON.stringify(msg));
|
|
234
|
+
buffers.push(jsonUtf8.buffer);
|
|
235
|
+
for (let i = 0; i < origBuffers.length; i++) {
|
|
236
|
+
// msg.buffers elements could be either views or ArrayBuffers
|
|
237
|
+
// buffers elements are ArrayBuffers
|
|
238
|
+
const b: any = origBuffers[i];
|
|
239
|
+
buffers.push(ArrayBuffer.isView(b) ? b.buffer : b);
|
|
240
|
+
}
|
|
241
|
+
const nbufs = buffers.length;
|
|
242
|
+
offsets.push(4 * (nbufs + 1));
|
|
243
|
+
for (let i = 0; i + 1 < buffers.length; i++) {
|
|
244
|
+
offsets.push(offsets[offsets.length - 1] + buffers[i].byteLength);
|
|
245
|
+
}
|
|
246
|
+
const msgBuf = new Uint8Array(
|
|
247
|
+
offsets[offsets.length - 1] + buffers[buffers.length - 1].byteLength,
|
|
248
|
+
);
|
|
249
|
+
// use DataView.setUint32 for network byte-order
|
|
250
|
+
const view = new DataView(msgBuf.buffer);
|
|
251
|
+
// write nbufs to first 4 bytes
|
|
252
|
+
view.setUint32(0, nbufs);
|
|
253
|
+
// write offsets to next 4 * nbufs bytes
|
|
254
|
+
for (let i = 0; i < offsets.length; i++) {
|
|
255
|
+
view.setUint32(4 * (i + 1), offsets[i]);
|
|
256
|
+
}
|
|
257
|
+
// write all the buffers at their respective offsets
|
|
258
|
+
for (let i = 0; i < buffers.length; i++) {
|
|
259
|
+
msgBuf.set(new Uint8Array(buffers[i]), offsets[i]);
|
|
260
|
+
}
|
|
261
|
+
return msgBuf.buffer;
|
|
262
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { validateProperty } from '../validate-property.js';
|
|
2
|
+
|
|
3
|
+
import type { KernelMeta } from './libro-kernel-protocol.js';
|
|
4
|
+
import type * as KernelMessage from './messages.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Required fields for `IKernelHeader`.
|
|
8
|
+
*/
|
|
9
|
+
const HEADER_FIELDS = ['username', 'version', 'session', 'msg_id', 'msg_type'];
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Required fields and types for contents of various types of `kernel.IMessage`
|
|
13
|
+
* messages on the iopub channel.
|
|
14
|
+
*/
|
|
15
|
+
const IOPUB_CONTENT_FIELDS: Record<string, any> = {
|
|
16
|
+
stream: { name: 'string', text: 'string' },
|
|
17
|
+
display_data: { data: 'object', metadata: 'object' },
|
|
18
|
+
execute_input: { code: 'string', execution_count: 'number' },
|
|
19
|
+
execute_result: {
|
|
20
|
+
execution_count: 'number',
|
|
21
|
+
data: 'object',
|
|
22
|
+
metadata: 'object',
|
|
23
|
+
},
|
|
24
|
+
error: { ename: 'string', evalue: 'string', traceback: 'object' },
|
|
25
|
+
status: {
|
|
26
|
+
execution_state: ['string', ['starting', 'idle', 'busy', 'restarting', 'dead']],
|
|
27
|
+
},
|
|
28
|
+
clear_output: { wait: 'boolean' },
|
|
29
|
+
comm_open: { comm_id: 'string', target_name: 'string', data: 'object' },
|
|
30
|
+
comm_msg: { comm_id: 'string', data: 'object' },
|
|
31
|
+
comm_close: { comm_id: 'string' },
|
|
32
|
+
shutdown_reply: { restart: 'boolean' }, // Emitted by the IPython kernel.
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Validate the header of a kernel message.
|
|
37
|
+
*/
|
|
38
|
+
function validateHeader(
|
|
39
|
+
header: KernelMessage.IHeader,
|
|
40
|
+
): asserts header is KernelMessage.IHeader {
|
|
41
|
+
for (let i = 0; i < HEADER_FIELDS.length; i++) {
|
|
42
|
+
validateProperty(header, HEADER_FIELDS[i], 'string');
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Validate a kernel message object.
|
|
48
|
+
*/
|
|
49
|
+
export function validateMessage(
|
|
50
|
+
msg: KernelMessage.IMessage,
|
|
51
|
+
): asserts msg is KernelMessage.IMessage {
|
|
52
|
+
validateProperty(msg, 'metadata', 'object');
|
|
53
|
+
validateProperty(msg, 'content', 'object');
|
|
54
|
+
validateProperty(msg, 'channel', 'string');
|
|
55
|
+
validateHeader(msg.header);
|
|
56
|
+
if (msg.channel === 'iopub') {
|
|
57
|
+
validateIOPubContent(msg as KernelMessage.IIOPubMessage);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Validate content an kernel message on the iopub channel.
|
|
63
|
+
*/
|
|
64
|
+
function validateIOPubContent(
|
|
65
|
+
msg: KernelMessage.IIOPubMessage,
|
|
66
|
+
): asserts msg is KernelMessage.IIOPubMessage {
|
|
67
|
+
if (msg.channel === 'iopub') {
|
|
68
|
+
const fields = IOPUB_CONTENT_FIELDS[msg.header.msg_type];
|
|
69
|
+
// Check for unknown message type.
|
|
70
|
+
if (fields === undefined) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const names = Object.keys(fields);
|
|
74
|
+
const content = msg.content;
|
|
75
|
+
for (let i = 0; i < names.length; i++) {
|
|
76
|
+
let args = fields[names[i]];
|
|
77
|
+
if (!Array.isArray(args)) {
|
|
78
|
+
args = [args];
|
|
79
|
+
}
|
|
80
|
+
validateProperty(content, names[i], ...args);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Validate a `Kernel.KernelMeta` object.
|
|
87
|
+
*/
|
|
88
|
+
export function validateModel(model: KernelMeta): asserts model is KernelMeta {
|
|
89
|
+
validateProperty(model, 'name', 'string');
|
|
90
|
+
validateProperty(model, 'id', 'string');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Validate an array of `KernelMeta` objects.
|
|
95
|
+
*/
|
|
96
|
+
export function validateModels(models: KernelMeta[]): asserts models is KernelMeta[] {
|
|
97
|
+
if (!Array.isArray(models)) {
|
|
98
|
+
throw new Error('Invalid kernel list');
|
|
99
|
+
}
|
|
100
|
+
models.forEach((d) => validateModel(d));
|
|
101
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ManaModule } from '@difizen/mana-app';
|
|
2
|
+
|
|
3
|
+
import { KernelSpecManager } from './manager.js';
|
|
4
|
+
import { KernelSpecRestAPI } from './restapi.js';
|
|
5
|
+
|
|
6
|
+
export const LibroKernelSpecModule = ManaModule.create().register(
|
|
7
|
+
KernelSpecManager,
|
|
8
|
+
KernelSpecRestAPI,
|
|
9
|
+
);
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Event as ManaEvent } from '@difizen/mana-app';
|
|
2
|
+
|
|
3
|
+
import type { IManager as IBaseManager } from '../basemanager.js';
|
|
4
|
+
|
|
5
|
+
import type { ISpecModels } from './restapi.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Object which manages kernel instances for a given base url.
|
|
9
|
+
*
|
|
10
|
+
* #### Notes
|
|
11
|
+
* The manager is responsible for maintaining the state of kernel specs.
|
|
12
|
+
*/
|
|
13
|
+
export interface IManager extends IBaseManager {
|
|
14
|
+
/**
|
|
15
|
+
* A signal emitted when the kernel specs change.
|
|
16
|
+
*/
|
|
17
|
+
specsChanged: ManaEvent<ISpecModels>;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The kernel spec models.
|
|
21
|
+
*
|
|
22
|
+
* #### Notes
|
|
23
|
+
* The value will be null until the manager is ready.
|
|
24
|
+
*/
|
|
25
|
+
readonly specs: ISpecModels | null;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Force a refresh of the specs from the server.
|
|
29
|
+
*
|
|
30
|
+
* @returns A promise that resolves when the specs are fetched.
|
|
31
|
+
*
|
|
32
|
+
* #### Notes
|
|
33
|
+
* This is intended to be called only in response to a user action,
|
|
34
|
+
* since the manager maintains its internal state.
|
|
35
|
+
*/
|
|
36
|
+
refreshSpecs: () => Promise<void>;
|
|
37
|
+
}
|