|
function isSerializable(object) { |
|
return typeof object === "object" && object && object.toJSON; |
|
} |
|
|
|
function isObject(value) { |
|
return value && typeof value === "object" && value.constructor === Object; |
|
} |
|
|
|
function put_buffers(state, buffer_paths, buffers) { |
|
buffers = buffers.map(b => b instanceof DataView ? b.buffer : b); |
|
for (let i = 0; i < buffer_paths.length; i++) { |
|
const buffer_path = buffer_paths[i]; |
|
let obj = state; |
|
for (let j = 0; j < buffer_path.length - 1; j++) { |
|
obj = obj[buffer_path[j]]; |
|
} |
|
obj[buffer_path[buffer_path.length - 1]] = buffers[i]; |
|
} |
|
} |
|
|
|
function remove_buffers(state) { |
|
const buffers = []; |
|
const buffer_paths = []; |
|
function remove(obj, path) { |
|
if (isSerializable(obj)) { |
|
obj = obj.toJSON(); |
|
} |
|
if (Array.isArray(obj)) { |
|
let is_cloned = false; |
|
for (let i = 0; i < obj.length; i++) { |
|
const value = obj[i]; |
|
if (value) { |
|
if (value instanceof ArrayBuffer || ArrayBuffer.isView(value)) { |
|
if (!is_cloned) { |
|
obj = obj.slice(); |
|
is_cloned = true; |
|
} |
|
buffers.push(ArrayBuffer.isView(value) ? value.buffer : value); |
|
buffer_paths.push(path.concat([i])); |
|
obj[i] = null; |
|
} else { |
|
const new_value = remove(value, path.concat([i])); |
|
if (new_value !== value) { |
|
if (!is_cloned) { |
|
obj = obj.slice(); |
|
is_cloned = true; |
|
} |
|
obj[i] = new_value; |
|
} |
|
} |
|
} |
|
} |
|
} else if (isObject(obj)) { |
|
for (const key in obj) { |
|
let is_cloned = false; |
|
if (Object.prototype.hasOwnProperty.call(obj, key)) { |
|
const value = obj[key]; |
|
if (value) { |
|
if (value instanceof ArrayBuffer || ArrayBuffer.isView(value)) { |
|
if (!is_cloned) { |
|
obj = { ...obj }; |
|
is_cloned = true; |
|
} |
|
buffers.push(ArrayBuffer.isView(value) ? value.buffer : value); |
|
buffer_paths.push(path.concat([key])); |
|
delete obj[key]; |
|
} else { |
|
const new_value = remove(value, path.concat([key])); |
|
if (new_value !== value) { |
|
if (!is_cloned) { |
|
obj = { ...obj }; |
|
is_cloned = true; |
|
} |
|
obj[key] = new_value; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
return obj; |
|
} |
|
const new_state = remove(state, []); |
|
return { state: new_state, buffers, buffer_paths }; |
|
} |
|
|
|
class LocalWebSocket { |
|
constructor(url, client_id, workspace) { |
|
this.url = url; |
|
this.onopen = () => {}; |
|
this.onmessage = () => {}; |
|
this.onclose = () => {}; |
|
this.onerror = () => {}; |
|
this.client_id = client_id; |
|
this.workspace = workspace; |
|
|
|
console.log('Initializing LocalWebSocket with URL:', url); |
|
console.log('Client ID:', client_id); |
|
console.log('Workspace:', workspace); |
|
|
|
this.postMessage = (message) => { |
|
console.log('Posting message to kernel:', message); |
|
if (this.comm) { |
|
this.comm.send(message); |
|
} else { |
|
console.error('Comm not initialized.'); |
|
} |
|
}; |
|
|
|
this.readyState = WebSocket.CONNECTING; |
|
console.log('Initial readyState:', this.readyState); |
|
|
|
google.colab.kernel.comms.open(this.workspace, {}).then((comm) => { |
|
setTimeout(async () => { |
|
this.readyState = WebSocket.OPEN; |
|
console.log('===> WebSocket connection opened'); |
|
this.onopen(); |
|
for await (const msg of comm.messages) { |
|
const data = msg.data; |
|
console.log('Received message from kernel:', data); |
|
const buffer_paths = data.__buffer_paths__ || []; |
|
delete data.__buffer_paths__; |
|
put_buffers(data, buffer_paths, msg.buffers || []); |
|
if (data.type === "log" || data.type === "info") { |
|
console.log(data.message); |
|
} else if (data.type === "error") { |
|
console.error(data.message); |
|
} else { |
|
this.onmessage(data); |
|
} |
|
} |
|
|
|
}, 0) |
|
|
|
this.comm = comm; |
|
}).catch((e) => { |
|
console.error("Failed to connect to kernel comm:", e); |
|
}); |
|
|
|
this._initUI(); |
|
} |
|
|
|
_initUI() { |
|
console.log('Initializing UI'); |
|
const container = document.createElement('div'); |
|
container.style.border = '1px solid black'; |
|
container.style.padding = '10px'; |
|
container.style.marginTop = '10px'; |
|
|
|
const title = document.createElement('h3'); |
|
title.innerText = 'WebSocket Debug Console'; |
|
container.appendChild(title); |
|
|
|
const input = document.createElement('input'); |
|
input.type = 'text'; |
|
input.placeholder = 'Enter message...'; |
|
container.appendChild(input); |
|
|
|
const button = document.createElement('button'); |
|
button.innerText = 'Send'; |
|
button.onclick = () => { |
|
const message = input.value; |
|
console.log('Sending message:', message); |
|
if (message && this.readyState === WebSocket.OPEN) { |
|
this.send(message); |
|
const log = document.createElement('p'); |
|
log.innerText = `Sent: ${message}`; |
|
container.appendChild(log); |
|
} else { |
|
console.log('Cannot send message, WebSocket not open:', this.readyState); |
|
} |
|
}; |
|
container.appendChild(button); |
|
|
|
const logContainer = document.createElement('div'); |
|
container.appendChild(logContainer); |
|
|
|
this.onmessage = (event) => { |
|
const log = document.createElement('p'); |
|
log.innerText = `Received: ${JSON.stringify(event.data)}`; |
|
logContainer.appendChild(log); |
|
}; |
|
|
|
document.body.appendChild(container); |
|
} |
|
|
|
send(data) { |
|
if (this.readyState === WebSocket.OPEN) { |
|
console.log('Sending data:', data); |
|
this.postMessage({ |
|
type: "message", |
|
data, |
|
}); |
|
} else { |
|
console.log('Cannot send data, WebSocket not open:', this.readyState); |
|
} |
|
} |
|
|
|
close() { |
|
this.readyState = WebSocket.CLOSING; |
|
console.log('Closing connection'); |
|
this.postMessage({ |
|
type: "close" |
|
}); |
|
this.onclose(); |
|
} |
|
|
|
addEventListener(type, listener) { |
|
if (type === "message") this.onmessage = listener; |
|
if (type === "open") this.onopen = listener; |
|
if (type === "close") this.onclose = listener; |
|
if (type === "error") this.onerror = listener; |
|
} |
|
} |
|
|
|
(function() { |
|
const client_id = "<client_id>"; |
|
const ws_url = "<ws_url>"; |
|
const comm_target = "colab_ws_proxy_" + client_id; |
|
const ws = new LocalWebSocket(ws_url, client_id, comm_target); |
|
|
|
ws.onopen = () => { |
|
console.log('WebSocket connection opened'); |
|
}; |
|
|
|
ws.onclose = () => { |
|
console.log('WebSocket connection closed'); |
|
}; |
|
})(); |
|
|