@fails-components/jupyter-filesystem-extension
Version:
A collection of extensions, that redirect's filesystems access to fails and let fails puppeteer Jupyter lite.
209 lines (208 loc) • 7.3 kB
JavaScript
import { ServerConnection } from '@jupyterlab/services';
import { Signal } from '@lumino/signaling';
// portions used from Jupyterlab:
/* -----------------------------------------------------------------------------
| Copyright (c) Jupyter Development Team.
| Distributed under the terms of the Modified BSD License.
|----------------------------------------------------------------------------*/
// This code contains portions from or is inspired by Jupyter lab and lite
// especially the Drive implementation
const jsonMime = 'application/json';
class FailsDrive {
constructor(options) {
var _a;
this._fileContent = JSON.stringify(FailsDrive.EMPTY_NB);
this._isDisposed = false;
this._fileChanged = new Signal(this);
this._fileName = 'unloaded.ipynb';
this._serverSettings =
(_a = options.serverSettings) !== null && _a !== void 0 ? _a : ServerConnection.makeSettings();
}
dispose() {
if (this.isDisposed) {
return;
}
this._isDisposed = true;
Signal.clearData(this);
}
get isDisposed() {
return this._isDisposed;
}
get name() {
return 'JupyterFailsSingleFileDrive';
}
get serverSettings() {
return this._serverSettings;
}
get fileChanged() {
return this._fileChanged;
}
async getDownloadUrl(path) {
throw new Error('Method not implemented.');
}
async onMessage(event) {
// todo handle events
switch (event.task) {
case 'loadFile':
{
const loadevent = event;
this._fileContent = JSON.stringify(loadevent.fileData || FailsDrive.EMPTY_NB);
this._fileName = loadevent.fileName;
this._fileChanged.emit({
type: 'save',
oldValue: null,
newValue: {
name: this._fileName,
path: this._fileName,
last_modified: new Date(0).toISOString(),
created: new Date(0).toISOString(),
format: 'json',
mimetype: jsonMime,
content: JSON.parse(this._fileContent),
size: 0,
writable: true,
type: 'notebook'
}
});
}
break;
case 'savedFile':
{
const savedevent = event;
if (this._fileName !== savedevent.fileName) {
return { error: 'Filename not found' };
}
return {
fileData: JSON.parse(this._fileContent)
};
}
break;
}
}
async get(path, options) {
// remove leading slash
path = decodeURIComponent(path.replace(/^\//, ''));
const serverFile = {
name: this._fileName,
path: this._fileName,
last_modified: new Date(0).toISOString(),
created: new Date(0).toISOString(),
format: 'json',
mimetype: jsonMime,
content: JSON.parse(this._fileContent),
size: 0,
writable: true,
type: 'notebook'
};
if (path === '') {
// the local directory, return the info about the proxy notebook
return {
name: '',
path,
last_modified: new Date(0).toISOString(),
created: new Date(0).toISOString(),
format: 'json',
mimetype: jsonMime,
content: [serverFile],
size: 0,
writable: true,
type: 'directory'
};
}
if (path === this._fileName) {
return serverFile;
}
throw Error(`Could not find content with path ${path}`);
}
async save(path, options = {}) {
path = decodeURIComponent(path);
if (path !== this._fileName) {
// we only allow the proxy object
throw Error(`File ${path} is not the proxy file`);
}
const chunk = options.chunk;
const chunked = chunk ? chunk > 1 || chunk === -1 : false;
let item = await this.get(path, {
content: chunked
});
if (!item) {
throw Error(`Could not find file with path ${path}`);
}
const modified = new Date().toISOString();
// override with the new values
item = {
...item,
...options,
last_modified: modified
};
if (options.content && options.format === 'base64') {
const lastChunk = chunk ? chunk === -1 : true;
const modified = new Date().toISOString();
// override with the new values
item = {
...item,
...options,
last_modified: modified
};
const originalContent = item.content;
const escaped = decodeURIComponent(escape(atob(options.content)));
const newcontent = chunked ? originalContent + escaped : escaped;
item = {
...item,
content: lastChunk ? JSON.parse(newcontent) : newcontent,
format: 'json',
type: 'notebook',
size: newcontent.length
};
this._fileContent = JSON.stringify(newcontent); // no parsing
this._fileChanged.emit({
type: 'save',
oldValue: null,
newValue: item
});
return item;
}
this._fileContent = JSON.stringify(item.content); // no parsing
this._fileChanged.emit({
type: 'save',
oldValue: null,
newValue: item
});
return item;
}
// For fails creating a new file is not allowed, so no need to implment it
async newUntitled(options) {
throw new Error('NewUntitled not implemented');
}
async rename(oldLocalPath, newLocalPath) {
throw new Error('rename not implemented');
}
async delete(path) {
throw new Error('delete not implemented');
}
async copy(path, toDir) {
throw new Error('copy not implemented');
}
async createCheckpoint(path) {
throw new Error('createCheckpoint not (yet?) implemented');
}
async listCheckpoints(path) {
// throw new Error('listCheckpoints not (yet?) implemented');
return [{ id: 'fakeCheckpoint', last_modified: new Date().toISOString() }];
}
async restoreCheckpoint(path, checkpointID) {
throw new Error('restoreCheckpoint not (yet?) implemented');
}
async deleteCheckpoint(path, checkpointID) {
throw new Error('deleteCheckpoint not (yet?) implemented');
}
}
FailsDrive.EMPTY_NB = {
metadata: {
orig_nbformat: 4
},
nbformat_minor: 5,
nbformat: 4,
cells: []
};
export { FailsDrive };