@nteract/commutable
Advanced tools
Comparing version 6.0.0-alpha.0 to 6.0.2-alpha.0
@@ -1,11 +0,8 @@ | ||
import { Map as ImmutableMap } from "immutable"; | ||
import * as v4 from "./v4"; | ||
import * as v3 from "./v3"; | ||
export * from "./types"; | ||
export { emptyCodeCell, emptyMarkdownCell, emptyNotebook, monocellNotebook, createCodeCell, insertCellAt, insertCellAfter, deleteCell, appendCellToNotebook } from "./structures"; | ||
export { StreamOutput, Output, createImmutableMimeBundle, createImmutableOutput } from "./v4"; | ||
export declare type Notebook = v4.Notebook | v3.Notebook; | ||
export declare const parseNotebook: (notebookString: string) => Notebook; | ||
export declare const fromJS: (notebook: ImmutableMap<string, any> | v4.Notebook | v3.Notebook) => ImmutableMap<string, any>; | ||
export declare const toJS: (immnb: ImmutableMap<string, any>) => v4.Notebook; | ||
export declare const stringifyNotebook: (notebook: v4.Notebook) => string; | ||
/** | ||
* @module commutable | ||
*/ | ||
export * from "./primitives"; | ||
export * from "./structures"; | ||
export * from "./outputs"; | ||
export * from "./cells"; | ||
export * from "./notebook"; |
126
lib/index.js
"use strict"; | ||
var __importStar = (this && this.__importStar) || function (mod) { | ||
if (mod && mod.__esModule) return mod; | ||
var result = {}; | ||
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; | ||
result["default"] = mod; | ||
return result; | ||
}; | ||
exports.__esModule = true; | ||
var immutable_1 = require("immutable"); | ||
var v4 = __importStar(require("./v4")); | ||
var v3 = __importStar(require("./v3")); | ||
/** | ||
* @module commutable | ||
*/ | ||
// ..................................... | ||
// API Exports | ||
// | ||
function __export(m) { | ||
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; | ||
} | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
__export(require("./primitives")); | ||
__export(require("./structures")); | ||
__export(require("./outputs")); | ||
__export(require("./cells")); | ||
__export(require("./notebook")); | ||
/* | ||
// from structures | ||
var structures_1 = require("./structures"); | ||
exports.emptyCodeCell = structures_1.emptyCodeCell; | ||
exports.emptyMarkdownCell = structures_1.emptyMarkdownCell; | ||
exports.emptyNotebook = structures_1.emptyNotebook; | ||
exports.monocellNotebook = structures_1.monocellNotebook; | ||
exports.createCodeCell = structures_1.createCodeCell; | ||
exports.insertCellAt = structures_1.insertCellAt; | ||
exports.insertCellAfter = structures_1.insertCellAfter; | ||
exports.deleteCell = structures_1.deleteCell; | ||
exports.appendCellToNotebook = structures_1.appendCellToNotebook; | ||
export { | ||
emptyCodeCell, | ||
emptyMarkdownCell, | ||
emptyNotebook, | ||
monocellNotebook, | ||
createCodeCell, | ||
insertCellAt, | ||
insertCellAfter, | ||
deleteCell, | ||
appendCellToNotebook | ||
} from "./structures"; | ||
// v4 | ||
var v4_1 = require("./v4"); | ||
exports.createImmutableMimeBundle = v4_1.createImmutableMimeBundle; | ||
exports.createImmutableOutput = v4_1.createImmutableOutput; | ||
var freezeReviver = function (k, v) { | ||
return Object.freeze(v); | ||
}; | ||
// Expected usage of below is fromJS(parseNotebook(string|buffer)) | ||
exports.parseNotebook = function (notebookString) { | ||
return JSON.parse(notebookString, freezeReviver); | ||
}; | ||
exports.fromJS = function (notebook) { | ||
if (immutable_1.Map.isMap(notebook)) { | ||
if (notebook.has("cellOrder") && notebook.has("cellMap")) { | ||
return notebook; | ||
} | ||
throw new TypeError("commutable was passed an Immutable.Map structure that is not a notebook"); | ||
} | ||
if (notebook.nbformat === 4 && notebook.nbformat_minor >= 0) { | ||
if (Array.isArray(notebook.cells) && | ||
typeof notebook.metadata === "object") { | ||
return v4.fromJS(notebook); | ||
} | ||
} | ||
else if (notebook.nbformat === 3 && notebook.nbformat_minor >= 0) { | ||
return v3.fromJS(notebook); | ||
} | ||
if (notebook.nbformat) { | ||
throw new TypeError("nbformat v" + notebook.nbformat + "." + notebook.nbformat_minor + " not recognized"); | ||
} | ||
throw new TypeError("This notebook format is not supported"); | ||
}; | ||
exports.toJS = function (immnb) { | ||
var minorVersion = immnb.get("nbformat_minor", null); | ||
if (immnb.get("nbformat") === 4 && | ||
typeof minorVersion === "number" && | ||
minorVersion >= 0) { | ||
return v4.toJS(immnb); | ||
} | ||
throw new TypeError("Only notebook formats 3 and 4 are supported!"); | ||
}; | ||
// Expected usage is stringifyNotebook(toJS(immutableNotebook)) | ||
exports.stringifyNotebook = function (notebook) { | ||
return JSON.stringify(notebook, null, 2); | ||
}; | ||
export { StreamOutput, Output, createImmutableOutput } from "./v4"; | ||
export { | ||
createImmutableMimeBundle, | ||
makeDisplayData, | ||
makeErrorOutput, | ||
makeStreamOutput, | ||
makeExecuteResult, | ||
MimeBundle | ||
} from "./outputs"; | ||
export { | ||
makeRawCell, | ||
makeCodeCell, | ||
makeMarkdownCell, | ||
ImmutableCodeCell, | ||
ImmutableMarkdownCell, | ||
ImmutableRawCell, | ||
ImmutableCell, | ||
CellType | ||
} from "./cells"; | ||
export { | ||
toJS, | ||
stringifyNotebook, | ||
fromJS, | ||
parseNotebook, | ||
makeNotebookRecord, | ||
Notebook, | ||
ImmutableNotebook | ||
} from "./notebook"; | ||
*/ |
@@ -0,43 +1,101 @@ | ||
import { CellId } from "./primitives"; | ||
import { ImmutableNotebook } from "./notebook"; | ||
import { ImmutableCell } from "./cells"; | ||
import { Map as ImmutableMap, List as ImmutableList } from "immutable"; | ||
import { ImmutableOutput, ImmutableCell, ImmutableCellOrder, ImmutableCellMap, ImmutableJSONType, ExecutionCount } from "./types"; | ||
interface Notebook { | ||
nbformat: 4; | ||
nbformat_minor: 4; | ||
metadata: ImmutableMap<string, ImmutableJSONType>; | ||
cellOrder: ImmutableList<string>; | ||
cellMap: ImmutableMap<string, ImmutableCell>; | ||
} | ||
interface CodeCell { | ||
export declare const createCodeCell: import("immutable").Record.Factory<{ | ||
cell_type: "code"; | ||
metadata: ImmutableMap<string, any>; | ||
execution_count: ExecutionCount; | ||
execution_count: number | null; | ||
source: string; | ||
outputs: ImmutableList<ImmutableOutput>; | ||
} | ||
interface MarkdownCell { | ||
outputs: ImmutableList<import("./outputs").ImmutableOutput>; | ||
}>; | ||
export declare const createMarkdownCell: import("immutable").Record.Factory<{ | ||
cell_type: "markdown"; | ||
source: string; | ||
metadata: ImmutableMap<string, any>; | ||
} | ||
export declare const createCodeCell: (cell?: CodeCell) => ImmutableMap<string, any>; | ||
export declare const createMarkdownCell: (cell?: MarkdownCell) => ImmutableMap<string, any>; | ||
export declare const emptyCodeCell: ImmutableMap<string, any>; | ||
export declare const emptyMarkdownCell: ImmutableMap<string, any>; | ||
export declare const defaultNotebook: Notebook; | ||
export declare const createNotebook: (notebook?: Notebook) => ImmutableMap<string, any>; | ||
export declare const emptyNotebook: ImmutableMap<string, any>; | ||
}>; | ||
export declare const emptyCodeCell: import("immutable").RecordOf<{ | ||
cell_type: "code"; | ||
metadata: ImmutableMap<string, any>; | ||
execution_count: number | null; | ||
source: string; | ||
outputs: ImmutableList<import("./outputs").ImmutableOutput>; | ||
}>; | ||
export declare const emptyMarkdownCell: import("immutable").RecordOf<{ | ||
cell_type: "markdown"; | ||
source: string; | ||
metadata: ImmutableMap<string, any>; | ||
}>; | ||
export declare const defaultNotebook: ImmutableNotebook; | ||
export declare const createNotebook: import("immutable").Record.Factory<import("./notebook").NotebookRecordParams>; | ||
export declare const emptyNotebook: ImmutableNotebook; | ||
export declare type CellStructure = { | ||
cellOrder: ImmutableCellOrder; | ||
cellMap: ImmutableCellMap; | ||
cellOrder: ImmutableList<CellId>; | ||
cellMap: ImmutableMap<CellId, ImmutableCell>; | ||
}; | ||
export declare const appendCell: (cellStructure: CellStructure, immutableCell: ImmutableMap<string, any>, id?: string) => CellStructure; | ||
export declare const appendCellToNotebook: (immnb: ImmutableMap<string, any>, immCell: ImmutableMap<string, any>) => ImmutableMap<string, any>; | ||
export declare const insertCellAt: (notebook: ImmutableMap<string, any>, cell: ImmutableMap<string, any>, cellID: string, index: number) => ImmutableMap<string, any>; | ||
export declare const insertCellAfter: (notebook: ImmutableMap<string, any>, cell: ImmutableMap<string, any>, cellID: string, priorCellID: string) => ImmutableMap<string, any>; | ||
/** | ||
* A function that appends a new cell to a CellStructure object. | ||
* | ||
* @param cellStructure The cellOrder and cellMap of the current notebook | ||
* @param immutableCell The cell that will be inserted into the cellStructure | ||
* @param id The id of the new cell, defaults to a new UUID | ||
* | ||
* @returns Cell structure with the new cell appended at the end | ||
*/ | ||
export declare const appendCell: (cellStructure: CellStructure, immutableCell: ImmutableCell, id?: string) => CellStructure; | ||
/** | ||
* A function that appends a cell to an immutable notebook. | ||
* | ||
* @param immnb An immutable data structure representing the notebook that will be modified | ||
* @param immCell The new cell that will be inserted into the notebook | ||
* | ||
* @returns The modified notebook | ||
*/ | ||
export declare const appendCellToNotebook: (immnb: ImmutableNotebook, immCell: ImmutableCell) => ImmutableNotebook; | ||
/** | ||
* Inserts a cell with cellID at a given index within the notebook. | ||
* | ||
* @param notebook The notebook the cell will be inserted into. | ||
* @param cell The cell that will be inserted | ||
* @param cellID The ID of the cell. | ||
* @param index The position we would like to insert the cell at | ||
* | ||
* @returns The modified notebook. | ||
*/ | ||
export declare const insertCellAt: (notebook: ImmutableNotebook, cell: ImmutableCell, cellId: string, index: number) => ImmutableNotebook; | ||
/** | ||
* Inserts a new cell with cellID before an existing cell with priorCellID | ||
* in the notebook. | ||
* | ||
* @param notebook The notebook the cell will be inserted into. | ||
* @param cell The cell that will be inserted | ||
* @param cellID The ID of the cell. | ||
* @param priorCellID The ID of the existing cell. | ||
*/ | ||
export declare const insertCellAfter: (notebook: ImmutableNotebook, cell: ImmutableCell, cellId: string, priorCellId: string) => ImmutableNotebook; | ||
/** | ||
* Delete a cell with CellID at a given location. Note that this function | ||
* is deprecated in favor of `deleteCell`. | ||
* | ||
* @param notebook The notebook containing the cell. | ||
* @param cellID The ID of the cell that will be deleted. | ||
* | ||
* @returns The modified notebook | ||
* | ||
* @deprecated use `deleteCell()` instead | ||
*/ | ||
export declare const removeCell: (notebook: ImmutableMap<string, any>, cellID: string) => ImmutableMap<string, any>; | ||
export declare const deleteCell: (notebook: ImmutableMap<string, any>, cellID: string) => ImmutableMap<string, any>; | ||
export declare const monocellNotebook: ImmutableMap<string, any>; | ||
export {}; | ||
export declare const removeCell: (notebook: ImmutableNotebook, cellId: string) => ImmutableNotebook; | ||
/** | ||
* Delete a cell with CellID at a given location. | ||
* | ||
* @param notebook The notebook containing the cell. | ||
* @param cellID The ID of the cell that will be deleted. | ||
* | ||
* @returns The modified notebook | ||
*/ | ||
export declare const deleteCell: (notebook: ImmutableNotebook, cellId: string) => ImmutableNotebook; | ||
/** | ||
* A new notebook with a single empty code cell. This function is useful | ||
* if you are looking to initialize a fresh, new notebook. | ||
*/ | ||
export declare const monocellNotebook: ImmutableNotebook; |
"use strict"; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
exports.__esModule = true; | ||
var v4_1 = __importDefault(require("uuid/v4")); | ||
var immutable_1 = require("immutable"); | ||
var defaultCodeCell = Object.freeze({ | ||
cell_type: "code", | ||
execution_count: null, | ||
metadata: immutable_1.Map({ | ||
collapsed: false, | ||
outputHidden: false, | ||
inputHidden: false | ||
}), | ||
source: "", | ||
outputs: immutable_1.List() | ||
}); | ||
var defaultMarkdownCell = Object.freeze({ | ||
cell_type: "markdown", | ||
metadata: immutable_1.Map(), | ||
source: "" | ||
}); | ||
exports.createCodeCell = function (cell) { | ||
if (cell === void 0) { cell = defaultCodeCell; } | ||
return immutable_1.Map(cell); | ||
}; | ||
exports.createMarkdownCell = function (cell) { | ||
if (cell === void 0) { cell = defaultMarkdownCell; } | ||
return immutable_1.Map(cell); | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const primitives_1 = require("./primitives"); | ||
const notebook_1 = require("./notebook"); | ||
const cells_1 = require("./cells"); | ||
// The cell creators here are a bit duplicative | ||
exports.createCodeCell = cells_1.makeCodeCell; | ||
exports.createMarkdownCell = cells_1.makeMarkdownCell; | ||
exports.emptyCodeCell = exports.createCodeCell(); | ||
exports.emptyMarkdownCell = exports.createMarkdownCell(); | ||
exports.defaultNotebook = Object.freeze({ | ||
nbformat: 4, | ||
nbformat_minor: 4, | ||
metadata: immutable_1.Map(), | ||
cellOrder: immutable_1.List(), | ||
cellMap: immutable_1.Map() | ||
// These are all kind of duplicative now that we're on records. | ||
// Since we export these though, they're left for | ||
// backwards compatiblity | ||
exports.defaultNotebook = notebook_1.makeNotebookRecord(); | ||
exports.createNotebook = notebook_1.makeNotebookRecord; | ||
exports.emptyNotebook = notebook_1.makeNotebookRecord(); | ||
/** | ||
* A function that appends a new cell to a CellStructure object. | ||
* | ||
* @param cellStructure The cellOrder and cellMap of the current notebook | ||
* @param immutableCell The cell that will be inserted into the cellStructure | ||
* @param id The id of the new cell, defaults to a new UUID | ||
* | ||
* @returns Cell structure with the new cell appended at the end | ||
*/ | ||
exports.appendCell = (cellStructure, immutableCell, id = primitives_1.createCellId()) => ({ | ||
cellOrder: cellStructure.cellOrder.push(id), | ||
cellMap: cellStructure.cellMap.set(id, immutableCell) | ||
}); | ||
exports.createNotebook = function (notebook) { | ||
if (notebook === void 0) { notebook = exports.defaultNotebook; } | ||
return immutable_1.Map(notebook); | ||
}; | ||
exports.emptyNotebook = exports.createNotebook(); | ||
// Intended to make it easy to use this with (temporary mutable cellOrder + | ||
// cellMap) | ||
exports.appendCell = function (cellStructure, immutableCell, id) { | ||
if (id === void 0) { id = v4_1["default"](); } | ||
return ({ | ||
cellOrder: cellStructure.cellOrder.push(id), | ||
cellMap: cellStructure.cellMap.set(id, immutableCell) | ||
}); | ||
}; | ||
exports.appendCellToNotebook = function (immnb, immCell) { | ||
return immnb.withMutations(function (nb) { | ||
// $FlowFixMe: Fixed by making ImmutableNotebook a typed Record. | ||
var cellStructure = { | ||
cellOrder: nb.get("cellOrder"), | ||
// $FlowFixMe: Fixed by making ImmutableNotebook a typed Record. | ||
cellMap: nb.get("cellMap") | ||
}; | ||
var _a = exports.appendCell(cellStructure, immCell), cellOrder = _a.cellOrder, cellMap = _a.cellMap; | ||
return nb.set("cellOrder", cellOrder).set("cellMap", cellMap); | ||
}); | ||
}; | ||
exports.insertCellAt = function (notebook, cell, cellID, index) { | ||
return notebook.withMutations(function (nb) { | ||
return nb | ||
.setIn(["cellMap", cellID], cell) | ||
// $FlowFixMe: Fixed by making ImmutableNotebook a typed record. | ||
.set("cellOrder", nb.get("cellOrder").insert(index, cellID)); | ||
}); | ||
}; | ||
exports.insertCellAfter = function (notebook, cell, cellID, priorCellID) { | ||
return exports.insertCellAt(notebook, cell, cellID, | ||
// $FlowFixMe: Fixed by making ImmutableNotebook a typed record. | ||
notebook.get("cellOrder").indexOf(priorCellID) + 1); | ||
}; | ||
/** | ||
* A function that appends a cell to an immutable notebook. | ||
* | ||
* @param immnb An immutable data structure representing the notebook that will be modified | ||
* @param immCell The new cell that will be inserted into the notebook | ||
* | ||
* @returns The modified notebook | ||
*/ | ||
exports.appendCellToNotebook = (immnb, immCell) => immnb.withMutations(nb => { | ||
const cellStructure = { | ||
cellOrder: nb.get("cellOrder"), | ||
cellMap: nb.get("cellMap") | ||
}; | ||
const { cellOrder, cellMap } = exports.appendCell(cellStructure, immCell); | ||
return nb.set("cellOrder", cellOrder).set("cellMap", cellMap); | ||
}); | ||
/** | ||
* Inserts a cell with cellID at a given index within the notebook. | ||
* | ||
* @param notebook The notebook the cell will be inserted into. | ||
* @param cell The cell that will be inserted | ||
* @param cellID The ID of the cell. | ||
* @param index The position we would like to insert the cell at | ||
* | ||
* @returns The modified notebook. | ||
*/ | ||
exports.insertCellAt = (notebook, cell, cellId, index) => notebook.withMutations(nb => nb | ||
.setIn(["cellMap", cellId], cell) | ||
.set("cellOrder", nb.get("cellOrder").insert(index, cellId))); | ||
/** | ||
* Inserts a new cell with cellID before an existing cell with priorCellID | ||
* in the notebook. | ||
* | ||
* @param notebook The notebook the cell will be inserted into. | ||
* @param cell The cell that will be inserted | ||
* @param cellID The ID of the cell. | ||
* @param priorCellID The ID of the existing cell. | ||
*/ | ||
exports.insertCellAfter = (notebook, cell, cellId, priorCellId) => exports.insertCellAt(notebook, cell, cellId, notebook.get("cellOrder").indexOf(priorCellId) + 1); | ||
/** | ||
* Delete a cell with CellID at a given location. Note that this function | ||
* is deprecated in favor of `deleteCell`. | ||
* | ||
* @param notebook The notebook containing the cell. | ||
* @param cellID The ID of the cell that will be deleted. | ||
* | ||
* @returns The modified notebook | ||
* | ||
* @deprecated use `deleteCell()` instead | ||
*/ | ||
exports.removeCell = function (notebook, cellID) { | ||
exports.removeCell = (notebook, cellId) => { | ||
console.log("Deprecation Warning: removeCell() is being deprecated. Please use deleteCell() instead"); | ||
return notebook | ||
.removeIn(["cellMap", cellID]) | ||
.update("cellOrder", function (cellOrder) { | ||
return cellOrder.filterNot(function (id) { return id === cellID; }); | ||
}); | ||
return exports.deleteCell(notebook, cellId); | ||
}; | ||
exports.deleteCell = function (notebook, cellID) { | ||
return notebook | ||
.removeIn(["cellMap", cellID]) | ||
.update("cellOrder", function (cellOrder) { | ||
return cellOrder.filterNot(function (id) { return id === cellID; }); | ||
}); | ||
}; | ||
/** | ||
* Delete a cell with CellID at a given location. | ||
* | ||
* @param notebook The notebook containing the cell. | ||
* @param cellID The ID of the cell that will be deleted. | ||
* | ||
* @returns The modified notebook | ||
*/ | ||
exports.deleteCell = (notebook, cellId) => notebook | ||
.removeIn(["cellMap", cellId]) | ||
.update("cellOrder", cellOrder => cellOrder.filterNot(id => id === cellId)); | ||
/** | ||
* A new notebook with a single empty code cell. This function is useful | ||
* if you are looking to initialize a fresh, new notebook. | ||
*/ | ||
exports.monocellNotebook = exports.appendCellToNotebook(exports.emptyNotebook, exports.emptyCodeCell); |
@@ -1,4 +0,4 @@ | ||
import { Map as ImmutableMap } from "immutable"; | ||
import { MultiLineString, JSONObject } from "./types"; | ||
import { ErrorOutput, RawCell, MarkdownCell } from "./v4"; | ||
import { MultiLineString, JSONObject } from "./primitives"; | ||
import { ErrorOutput } from "./outputs"; | ||
import { RawCell, MarkdownCell } from "./v4"; | ||
declare const VALID_MIMETYPES: { | ||
@@ -54,3 +54,3 @@ text: string; | ||
} | ||
export interface Notebook { | ||
export declare type Notebook = { | ||
worksheets: Worksheet[]; | ||
@@ -60,9 +60,4 @@ metadata: object; | ||
nbformat_minor: number; | ||
} | ||
export declare const sanitize: (o: ExecuteResult | DisplayData) => { | ||
metadata: any; | ||
} | { | ||
metadata?: undefined; | ||
}; | ||
export declare const fromJS: (notebook: Notebook) => ImmutableMap<string, any>; | ||
export declare const fromJS: (notebook: Notebook) => import("./notebook").ImmutableNotebook; | ||
export {}; |
150
lib/v3.js
"use strict"; | ||
var __assign = (this && this.__assign) || function () { | ||
__assign = Object.assign || function(t) { | ||
for (var s, i = 1, n = arguments.length; i < n; i++) { | ||
s = arguments[i]; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) | ||
t[p] = s[p]; | ||
} | ||
return t; | ||
}; | ||
return __assign.apply(this, arguments); | ||
}; | ||
exports.__esModule = true; | ||
var immutable_1 = require("immutable"); | ||
var structures_1 = require("./structures"); | ||
var v4_1 = require("./v4"); | ||
var VALID_MIMETYPES = { | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
/** | ||
* @module commutable | ||
*/ | ||
const immutable_1 = require("immutable"); | ||
const notebook_1 = require("./notebook"); | ||
const cells_1 = require("./cells"); | ||
const outputs_1 = require("./outputs"); | ||
const structures_1 = require("./structures"); | ||
const VALID_MIMETYPES = { | ||
text: "text/plain", | ||
@@ -28,13 +22,11 @@ latex: "text/latex", | ||
}; | ||
var createImmutableMarkdownCell = function (cell) { | ||
return immutable_1.Map({ | ||
cell_type: cell.cell_type, | ||
source: v4_1.demultiline(cell.source), | ||
metadata: immutable_1.fromJS(cell.metadata) | ||
}); | ||
}; | ||
var createImmutableMimeBundle = function (output) { | ||
var mimeBundle = {}; | ||
for (var _i = 0, _a = Object.keys(output); _i < _a.length; _i++) { | ||
var key = _a[_i]; | ||
const createImmutableMarkdownCell = (cell) => cells_1.makeMarkdownCell({ | ||
cell_type: cell.cell_type, | ||
source: outputs_1.demultiline(cell.source), | ||
metadata: immutable_1.fromJS(cell.metadata) | ||
}); | ||
const createImmutableMimeBundle = (output) => { | ||
const mimeBundle = {}; | ||
for (const key of Object.keys(output)) { | ||
// v3 had non-media types for rich media | ||
if (key in VALID_MIMETYPES) { | ||
@@ -45,22 +37,27 @@ mimeBundle[VALID_MIMETYPES[key]] = | ||
} | ||
return Object.keys(mimeBundle).reduce(v4_1.cleanMimeAtKey.bind(null, mimeBundle), immutable_1.Map()); | ||
return Object.keys(mimeBundle).reduce(outputs_1.cleanMimeAtKey.bind(null, mimeBundle), immutable_1.Map()); | ||
}; | ||
exports.sanitize = function (o) { | ||
return o.metadata ? { metadata: immutable_1.fromJS(o.metadata) } : {}; | ||
}; | ||
var createImmutableOutput = function (output) { | ||
const createImmutableOutput = (output) => { | ||
switch (output.output_type) { | ||
case "pyout": | ||
return immutable_1.Map(__assign({ output_type: output.output_type, execution_count: output.prompt_number, data: createImmutableMimeBundle(output) }, exports.sanitize(output))); | ||
return outputs_1.makeExecuteResult({ | ||
execution_count: output.prompt_number, | ||
// Note strangeness with v4 API | ||
data: createImmutableMimeBundle(output), | ||
metadata: immutable_1.fromJS(output.metadata) | ||
}); | ||
case "display_data": | ||
return immutable_1.Map(__assign({ output_type: output.output_type, data: createImmutableMimeBundle(output) }, exports.sanitize(output))); | ||
return outputs_1.makeDisplayData({ | ||
data: createImmutableMimeBundle(output), | ||
metadata: immutable_1.fromJS(output.metadata) | ||
}); | ||
case "stream": | ||
return immutable_1.Map({ | ||
output_type: output.output_type, | ||
name: output.stream, | ||
text: v4_1.demultiline(output.text) | ||
// Default to stdout in all cases unless it's stderr | ||
const name = output.stream === "stderr" ? "stderr" : "stdout"; | ||
return outputs_1.makeStreamOutput({ | ||
name, | ||
text: outputs_1.demultiline(output.text) | ||
}); | ||
case "pyerr": | ||
return immutable_1.Map({ | ||
output_type: "error", | ||
return outputs_1.makeErrorOutput({ | ||
ename: output.ename, | ||
@@ -71,36 +68,30 @@ evalue: output.evalue, | ||
default: | ||
throw new TypeError("Output type " + output.output_type + " not recognized"); | ||
throw new TypeError(`Output type ${output.output_type} not recognized`); | ||
} | ||
}; | ||
var createImmutableCodeCell = function (cell) { | ||
return immutable_1.Map({ | ||
cell_type: cell.cell_type, | ||
source: v4_1.demultiline(cell.input), | ||
outputs: immutable_1.List(cell.outputs.map(createImmutableOutput)), | ||
execution_count: cell.prompt_number, | ||
metadata: immutable_1.fromJS(cell.metadata) | ||
}); | ||
}; | ||
var createImmutableRawCell = function (cell) { | ||
return immutable_1.Map({ | ||
cell_type: cell.cell_type, | ||
source: v4_1.demultiline(cell.source), | ||
metadata: immutable_1.fromJS(cell.metadata) | ||
}); | ||
}; | ||
var createImmutableHeadingCell = function (cell) { | ||
return immutable_1.Map({ | ||
cell_type: "markdown", | ||
source: Array.isArray(cell.source) | ||
? v4_1.demultiline(cell.source.map(function (line) { | ||
return Array(cell.level) | ||
.join("#") | ||
.concat(" ") | ||
.concat(line); | ||
})) | ||
: cell.source, | ||
metadata: immutable_1.fromJS(cell.metadata) | ||
}); | ||
}; | ||
var createImmutableCell = function (cell) { | ||
const createImmutableCodeCell = (cell) => cells_1.makeCodeCell({ | ||
cell_type: cell.cell_type, | ||
source: outputs_1.demultiline(cell.input), | ||
outputs: immutable_1.List(cell.outputs.map(createImmutableOutput)), | ||
execution_count: cell.prompt_number, | ||
metadata: immutable_1.fromJS(cell.metadata) | ||
}); | ||
const createImmutableRawCell = (cell) => cells_1.makeRawCell({ | ||
cell_type: cell.cell_type, | ||
source: outputs_1.demultiline(cell.source), | ||
metadata: immutable_1.fromJS(cell.metadata) | ||
}); | ||
const createImmutableHeadingCell = (cell) => | ||
// v3 heading cells are just markdown cells in v4+ | ||
cells_1.makeMarkdownCell({ | ||
cell_type: "markdown", | ||
source: Array.isArray(cell.source) | ||
? outputs_1.demultiline(cell.source.map(line => Array(cell.level) | ||
.join("#") | ||
.concat(" ") | ||
.concat(line))) | ||
: cell.source, | ||
metadata: immutable_1.fromJS(cell.metadata) | ||
}); | ||
const createImmutableCell = (cell) => { | ||
switch (cell.cell_type) { | ||
@@ -116,17 +107,16 @@ case "markdown": | ||
default: | ||
throw new TypeError("Cell type " + cell.cell_type + " unknown"); | ||
throw new TypeError(`Cell type ${cell.cell_type} unknown`); | ||
} | ||
}; | ||
exports.fromJS = function (notebook) { | ||
exports.fromJS = (notebook) => { | ||
if (notebook.nbformat !== 3 || notebook.nbformat_minor < 0) { | ||
throw new TypeError("Notebook is not a valid v3 notebook. v3 notebooks must be of form 3.x\n It lists nbformat v" + notebook.nbformat + "." + notebook.nbformat_minor); | ||
throw new TypeError(`Notebook is not a valid v3 notebook. v3 notebooks must be of form 3.x | ||
It lists nbformat v${notebook.nbformat}.${notebook.nbformat_minor}`); | ||
} | ||
var starterCellStructure = { | ||
const starterCellStructure = { | ||
cellOrder: immutable_1.List().asMutable(), | ||
cellMap: immutable_1.Map().asMutable() | ||
}; | ||
var cellStructure = [].concat.apply([], notebook.worksheets.map(function (worksheet) { | ||
return worksheet.cells.reduce(function (cellStruct, cell) { return structures_1.appendCell(cellStruct, createImmutableCell(cell)); }, starterCellStructure); | ||
}))[0]; | ||
return immutable_1.Map({ | ||
const cellStructure = [].concat.apply([], notebook.worksheets.map(worksheet => worksheet.cells.reduce((cellStruct, cell) => structures_1.appendCell(cellStruct, createImmutableCell(cell)), starterCellStructure)))[0]; | ||
return notebook_1.makeNotebookRecord({ | ||
cellOrder: cellStructure.cellOrder.asImmutable(), | ||
@@ -133,0 +123,0 @@ cellMap: cellStructure.cellMap.asImmutable(), |
@@ -1,33 +0,8 @@ | ||
import { Map as ImmutableMap } from "immutable"; | ||
import { ExecutionCount, JSONObject, MultiLineString } from "./types"; | ||
export declare type MimeBundle = { | ||
[key: string]: string | string[] | Object; | ||
}; | ||
/** | ||
* @module commutable | ||
*/ | ||
import { ImmutableNotebook } from "./notebook"; | ||
import { JSONObject, MultiLineString, ExecutionCount } from "./primitives"; | ||
import { Output } from "./outputs"; | ||
/** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | ||
* Output Types | ||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | ||
export interface ExecuteResult { | ||
output_type: "execute_result"; | ||
execution_count: ExecutionCount; | ||
data: MimeBundle; | ||
metadata: JSONObject; | ||
} | ||
export interface DisplayData { | ||
output_type: "display_data"; | ||
data: MimeBundle; | ||
metadata: JSONObject; | ||
} | ||
export interface StreamOutput { | ||
output_type: "stream"; | ||
name: "stdout" | "stderr"; | ||
text: MultiLineString; | ||
} | ||
export interface ErrorOutput { | ||
output_type: "error" | "pyerr"; | ||
ename: string; | ||
evalue: string; | ||
traceback: string[]; | ||
} | ||
export declare type Output = ExecuteResult | DisplayData | StreamOutput | ErrorOutput; | ||
/** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | ||
* Cell Types | ||
@@ -53,3 +28,3 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | ||
export declare type Cell = CodeCell | MarkdownCell | RawCell; | ||
export interface Notebook { | ||
export declare type Notebook = { | ||
cells: Array<Cell>; | ||
@@ -59,19 +34,11 @@ metadata: Object; | ||
nbformat_minor: number; | ||
} | ||
export declare const demultiline: (s: string | string[]) => string; | ||
}; | ||
export declare const fromJS: (notebook: Notebook) => ImmutableNotebook; | ||
/** | ||
* Split string into a list of strings delimited by newlines | ||
* Converts an immutable representation of a notebook to a JSON representation. | ||
* | ||
* @param immnb The immutable representation of a notebook. | ||
* | ||
* @returns The JSON representation of a notebook. | ||
*/ | ||
export declare const remultiline: (s: string | string[]) => string[]; | ||
export declare const isJSONKey: (key: string) => boolean; | ||
export declare const cleanMimeData: (key: string, data: string | object | string[]) => string | object; | ||
export declare const cleanMimeAtKey: (mimeBundle: MimeBundle, previous: ImmutableMap<string, any>, key: string) => ImmutableMap<string, any>; | ||
export declare const createImmutableMimeBundle: (mimeBundle: MimeBundle) => ImmutableMap<string, any>; | ||
export declare const sanitize: (o: ExecuteResult | DisplayData) => { | ||
metadata: any; | ||
} | { | ||
metadata?: undefined; | ||
}; | ||
export declare const createImmutableOutput: (output: Output) => ImmutableMap<string, any>; | ||
export declare const fromJS: (notebook: Notebook) => ImmutableMap<string, any>; | ||
export declare const toJS: (immnb: ImmutableMap<string, any>) => Notebook; | ||
export declare const toJS: (immnb: ImmutableNotebook) => Notebook; |
297
lib/v4.js
"use strict"; | ||
/** | ||
* @module commutable | ||
*/ | ||
/* | ||
@@ -13,129 +16,52 @@ * Functions in this module are provided for converting from Jupyter Notebook | ||
* | ||
* To assist in the developer experience, types are included through the use of | ||
* flow. | ||
* | ||
*/ | ||
var __assign = (this && this.__assign) || function () { | ||
__assign = Object.assign || function(t) { | ||
for (var s, i = 1, n = arguments.length; i < n; i++) { | ||
s = arguments[i]; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) | ||
t[p] = s[p]; | ||
} | ||
return t; | ||
}; | ||
return __assign.apply(this, arguments); | ||
}; | ||
exports.__esModule = true; | ||
var immutable_1 = require("immutable"); | ||
var structures_1 = require("./structures"); | ||
exports.demultiline = function (s) { | ||
return Array.isArray(s) ? s.join("") : s; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const immutable_1 = require("immutable"); | ||
const notebook_1 = require("./notebook"); | ||
const cells_1 = require("./cells"); | ||
const outputs_1 = require("./outputs"); | ||
const structures_1 = require("./structures"); | ||
/** | ||
* Split string into a list of strings delimited by newlines | ||
* Converts a mutable representation of metadata to an immutable representation. | ||
* | ||
* @param metadata A JSON representation of notebook metadata. | ||
* | ||
* @returns ImmutableMetadata An immutable representation of the metadata. | ||
*/ | ||
exports.remultiline = function (s) { | ||
return Array.isArray(s) ? s : s.split(/(.+?(?:\r\n|\n))/g).filter(function (x) { return x !== ""; }); | ||
}; | ||
exports.isJSONKey = function (key) { | ||
return /^application\/(.*\+)?json$/.test(key); | ||
}; | ||
exports.cleanMimeData = function (key, data) { | ||
// See https://github.com/jupyter/nbformat/blob/62d6eb8803616d198eaa2024604d1fe923f2a7b3/nbformat/v4/nbformat.v4.schema.json#L368 | ||
if (exports.isJSONKey(key)) { | ||
// Data stays as is for JSON types | ||
return data; | ||
const createImmutableMetadata = (metadata) => immutable_1.Map(metadata).map((v, k) => { | ||
if (k !== "tags") { | ||
return v; | ||
} | ||
if (typeof data === "string" || Array.isArray(data)) { | ||
return exports.demultiline(data); | ||
if (Array.isArray(v)) { | ||
return immutable_1.Set(v); | ||
} | ||
throw new TypeError("Data for " + key + " is expected to be a string or an Array of strings"); | ||
}; | ||
exports.cleanMimeAtKey = function (mimeBundle, previous, key) { | ||
return previous.set(key, exports.cleanMimeData(key, mimeBundle[key])); | ||
}; | ||
// Map over all the mimetypes, turning them into our in-memory format | ||
// | ||
// { | ||
// "application/json": {"a": 3, "b": 2}, | ||
// "text/html": ["<p>\n", "Hey\n", "</p>"], | ||
// "text/plain": "Hey" | ||
// } | ||
// | ||
// to | ||
// | ||
// { | ||
// "application/json": {"a": 3, "b": 2}, | ||
// "text/html": "<p>\nHey\n</p>", | ||
// "text/plain": "Hey" | ||
// } | ||
// | ||
exports.createImmutableMimeBundle = function (mimeBundle) { | ||
return Object.keys(mimeBundle).reduce(exports.cleanMimeAtKey.bind(null, mimeBundle), immutable_1.Map()); | ||
}; | ||
exports.sanitize = function (o) { | ||
return o.metadata ? { metadata: immutable_1.fromJS(o.metadata) } : {}; | ||
}; | ||
exports.createImmutableOutput = function (output) { | ||
switch (output.output_type) { | ||
case "execute_result": | ||
return immutable_1.Map(__assign({ output_type: output.output_type, execution_count: output.execution_count, data: exports.createImmutableMimeBundle(output.data) }, exports.sanitize(output))); | ||
case "display_data": | ||
return immutable_1.Map(__assign({ output_type: output.output_type, data: exports.createImmutableMimeBundle(output.data) }, exports.sanitize(output))); | ||
case "stream": | ||
return immutable_1.Map({ | ||
output_type: output.output_type, | ||
name: output.name, | ||
text: exports.demultiline(output.text) | ||
}); | ||
case "error": | ||
return immutable_1.Map({ | ||
output_type: "error", | ||
ename: output.ename, | ||
evalue: output.evalue, | ||
// Note: this is one of the cases where the Array of strings (for | ||
// traceback) is part of the format, not a multiline string | ||
traceback: immutable_1.List(output.traceback) | ||
}); | ||
default: | ||
throw new TypeError("Output type " + output.output_type + " not recognized"); | ||
} | ||
}; | ||
var createImmutableMetadata = function (metadata) { | ||
return immutable_1.Map(metadata).map(function (v, k) { | ||
if (k !== "tags") { | ||
return v; | ||
} | ||
if (Array.isArray(v)) { | ||
return immutable_1.Set(v); | ||
} | ||
// The notebook spec requires that this field is an Array of strings | ||
return immutable_1.Set(); | ||
}); | ||
}; | ||
var createImmutableRawCell = function (cell) { | ||
return immutable_1.Map({ | ||
cell_type: cell.cell_type, | ||
source: exports.demultiline(cell.source), | ||
metadata: createImmutableMetadata(cell.metadata) | ||
}); | ||
}; | ||
var createImmutableMarkdownCell = function (cell) { | ||
return immutable_1.Map({ | ||
cell_type: cell.cell_type, | ||
source: exports.demultiline(cell.source), | ||
metadata: createImmutableMetadata(cell.metadata) | ||
}); | ||
}; | ||
var createImmutableCodeCell = function (cell) { | ||
return immutable_1.Map({ | ||
cell_type: cell.cell_type, | ||
source: exports.demultiline(cell.source), | ||
outputs: immutable_1.List(cell.outputs.map(exports.createImmutableOutput)), | ||
execution_count: cell.execution_count, | ||
metadata: createImmutableMetadata(cell.metadata) | ||
}); | ||
}; | ||
var createImmutableCell = function (cell) { | ||
// The notebook spec requires that this field is an Array of strings | ||
return immutable_1.Set(); | ||
}); | ||
const createImmutableRawCell = (cell) => cells_1.makeRawCell({ | ||
cell_type: cell.cell_type, | ||
source: outputs_1.demultiline(cell.source), | ||
metadata: createImmutableMetadata(cell.metadata) | ||
}); | ||
const createImmutableMarkdownCell = (cell) => cells_1.makeMarkdownCell({ | ||
cell_type: cell.cell_type, | ||
source: outputs_1.demultiline(cell.source), | ||
metadata: createImmutableMetadata(cell.metadata) | ||
}); | ||
const createImmutableCodeCell = (cell) => cells_1.makeCodeCell({ | ||
cell_type: cell.cell_type, | ||
source: outputs_1.demultiline(cell.source), | ||
outputs: immutable_1.List(cell.outputs.map(outputs_1.createImmutableOutput)), | ||
execution_count: cell.execution_count, | ||
metadata: createImmutableMetadata(cell.metadata) | ||
}); | ||
/** | ||
* Converts a JSON representation of a cell of any type to the correct | ||
* immutable representation, per the v4 nbformat specification. | ||
* | ||
* @param cell A JSON representation of a cell. | ||
* | ||
* @returns An immutable representation of the same cell. | ||
*/ | ||
const createImmutableCell = (cell) => { | ||
switch (cell.cell_type) { | ||
@@ -149,17 +75,18 @@ case "markdown": | ||
default: | ||
throw new TypeError("Cell type " + cell.cell_type + " unknown"); | ||
throw new TypeError(`Cell type ${cell.cell_type} unknown`); | ||
} | ||
}; | ||
exports.fromJS = function (notebook) { | ||
exports.fromJS = (notebook) => { | ||
if (notebook.nbformat !== 4 || notebook.nbformat_minor < 0) { | ||
throw new TypeError("Notebook is not a valid v4 notebook. v4 notebooks must be of form 4.x\n It lists nbformat v" + notebook.nbformat + "." + notebook.nbformat_minor); | ||
throw new TypeError(`Notebook is not a valid v4 notebook. v4 notebooks must be of form 4.x | ||
It lists nbformat v${notebook.nbformat}.${notebook.nbformat_minor}`); | ||
} | ||
// Since we're doing N cell operations all at once, switch to mutable then | ||
// switch back after. | ||
var starterCellStructure = { | ||
const starterCellStructure = { | ||
cellOrder: immutable_1.List().asMutable(), | ||
cellMap: immutable_1.Map().asMutable() | ||
}; | ||
var cellStructure = notebook.cells.reduce(function (cellStruct, cell) { return structures_1.appendCell(cellStruct, createImmutableCell(cell)); }, starterCellStructure); | ||
return immutable_1.Map({ | ||
const cellStructure = notebook.cells.reduce((cellStruct, cell) => structures_1.appendCell(cellStruct, createImmutableCell(cell)), starterCellStructure); | ||
return notebook_1.makeNotebookRecord({ | ||
cellOrder: cellStructure.cellOrder.asImmutable(), | ||
@@ -172,14 +99,7 @@ cellMap: cellStructure.cellMap.asImmutable(), | ||
}; | ||
var metadataToJS = function (immMetadata) { | ||
return immMetadata.toJS(); | ||
}; | ||
var markdownCellToJS = function (immCell) { return ({ | ||
cell_type: "markdown", | ||
source: exports.remultiline(immCell.get("source", "")), | ||
metadata: metadataToJS(immCell.get("metadata", immutable_1.Map())) | ||
}); }; | ||
var mimeBundleToJS = function (immMimeBundle) { | ||
var bundle = immMimeBundle.toObject(); | ||
Object.keys(bundle).map(function (key) { | ||
if (exports.isJSONKey(key)) { | ||
const metadataToJS = (immMetadata) => immMetadata.toJS(); | ||
const mimeBundleToJS = (immMimeBundle) => { | ||
const bundle = immMimeBundle.toObject(); | ||
Object.keys(bundle).map(key => { | ||
if (outputs_1.isJSONKey(key)) { | ||
if (immutable_1.Map.isMap(bundle[key])) { | ||
@@ -190,14 +110,12 @@ bundle[key] = bundle[key].toJS(); | ||
} | ||
var data = bundle[key]; | ||
const data = bundle[key]; | ||
if (typeof data === "string" || Array.isArray(data)) { | ||
bundle[key] = exports.remultiline(data); | ||
bundle[key] = outputs_1.remultiline(data); | ||
return bundle; | ||
} | ||
throw new TypeError("Data for " + key + " is expected to be a string or an Array of strings"); | ||
throw new TypeError(`Data for ${key} is expected to be a string or an Array of strings`); | ||
}); | ||
return bundle; | ||
}; | ||
var outputToJS = function (immOutput) { | ||
// Technically this is an intermediate output with Immutables inside | ||
var output = immOutput.toObject(); | ||
const outputToJS = (output) => { | ||
switch (output.output_type) { | ||
@@ -221,33 +139,59 @@ case "execute_result": | ||
name: output.name, | ||
text: exports.remultiline(output.text) | ||
text: outputs_1.remultiline(output.text) | ||
}; | ||
case "error": | ||
// Note: this is one of the cases where the Array of strings (for | ||
// traceback) is part of the format, not a multiline string | ||
return immOutput.toJS(); | ||
default: | ||
throw new TypeError("Output type " + output.output_type + " not recognized"); | ||
return { | ||
output_type: output.output_type, | ||
ename: output.ename, | ||
evalue: output.evalue, | ||
// Note: this is one of the cases where the Array of strings (for | ||
// traceback) is part of the format, not a multiline string | ||
traceback: output.traceback.toJS() | ||
}; | ||
} | ||
}; | ||
var codeCellToJS = function (immCell) { | ||
var cell = immCell.toObject(); | ||
const markdownCellToJS = (immCell) => ({ | ||
cell_type: "markdown", | ||
source: outputs_1.remultiline(immCell.source), | ||
metadata: metadataToJS(immCell.metadata) | ||
}); | ||
/** | ||
* Converts an immutable representation of a code cell to a JSON representation. | ||
* | ||
* @param immCell An immutable representation of a code cell. | ||
* | ||
* @returns A JSON representation of the same code cell. | ||
*/ | ||
const codeCellToJS = (immCell) => { | ||
return { | ||
cell_type: "code", | ||
source: exports.remultiline(cell.source), | ||
outputs: cell.outputs.map(outputToJS).toArray(), | ||
execution_count: cell.execution_count, | ||
metadata: metadataToJS(immCell.get("metadata", immutable_1.Map())) | ||
source: outputs_1.remultiline(immCell.source), | ||
outputs: immCell.outputs.map(outputToJS).toArray(), | ||
execution_count: immCell.execution_count, | ||
metadata: metadataToJS(immCell.metadata) | ||
}; | ||
}; | ||
var rawCellToJS = function (immCell) { | ||
var cell = immCell.toObject(); | ||
/** | ||
* Converts an immutable representation of a raw cell to a JSON representation. | ||
* | ||
* @param immCell An immutable representation of a raw cell. | ||
* | ||
* @returns A JSON representation of the same raw cell. | ||
*/ | ||
const rawCellToJS = (immCell) => { | ||
return { | ||
cell_type: "raw", | ||
source: exports.remultiline(cell.source), | ||
source: outputs_1.remultiline(immCell.source), | ||
metadata: metadataToJS(immCell.get("metadata", immutable_1.Map())) | ||
}; | ||
}; | ||
var cellToJS = function (immCell) { | ||
var cellType = immCell.get("cell_type"); | ||
switch (cellType) { | ||
/** | ||
* Converts an immutable cell to a JSON cell. | ||
* | ||
* @param immCell An immutable representation of a cell. | ||
* | ||
* @returns A JSON representation of the same cell. | ||
*/ | ||
const cellToJS = (immCell) => { | ||
switch (immCell.cell_type) { | ||
case "markdown": | ||
@@ -260,18 +204,23 @@ return markdownCellToJS(immCell); | ||
default: | ||
throw new TypeError("Cell type " + cellType + " unknown"); | ||
throw new TypeError(`Cell type unknown at runtime`); | ||
} | ||
}; | ||
exports.toJS = function (immnb) { | ||
var plainNotebook = immnb.toObject(); | ||
var plainCellOrder = plainNotebook.cellOrder.toArray(); | ||
var plainCellMap = plainNotebook.cellMap.toObject(); | ||
var cells = plainCellOrder.map(function (cellID) { | ||
return cellToJS(plainCellMap[cellID]); | ||
}); | ||
/** | ||
* Converts an immutable representation of a notebook to a JSON representation. | ||
* | ||
* @param immnb The immutable representation of a notebook. | ||
* | ||
* @returns The JSON representation of a notebook. | ||
*/ | ||
exports.toJS = (immnb) => { | ||
const plainNotebook = immnb.toObject(); | ||
const plainCellOrder = plainNotebook.cellOrder.toArray(); | ||
const plainCellMap = plainNotebook.cellMap.toObject(); | ||
const cells = plainCellOrder.map((cellId) => cellToJS(plainCellMap[cellId])); | ||
return { | ||
cells: cells, | ||
cells, | ||
metadata: plainNotebook.metadata.toJS(), | ||
nbformat: plainNotebook.nbformat, | ||
nbformat: 4, | ||
nbformat_minor: plainNotebook.nbformat_minor | ||
}; | ||
}; |
{ | ||
"name": "@nteract/commutable", | ||
"version": "6.0.0-alpha.0", | ||
"version": "6.0.2-alpha.0", | ||
"description": "library for immutable notebook operations", | ||
@@ -8,11 +8,3 @@ "main": "lib/index.js", | ||
"nteractDesktop": "src/index.ts", | ||
"scripts": { | ||
"prepare": "npm run build", | ||
"prepublishOnly": "npm run build", | ||
"build": "npm run build:clean && npm run build:lib", | ||
"build:clean": "tsc -b --clean", | ||
"build:lib": "tsc -b", | ||
"build:lib:watch": "tsc -b --watch", | ||
"build:watch": "npm run build:clean && npm run build:lib:watch" | ||
}, | ||
"scripts": {}, | ||
"repository": "https://github.com/nteract/nteract/tree/master/packages/commutable", | ||
@@ -30,3 +22,3 @@ "keywords": [ | ||
"dependencies": { | ||
"immutable": "^4.0.0-rc.9", | ||
"immutable": "^4.0.0-rc.12", | ||
"uuid": "^3.1.0" | ||
@@ -37,3 +29,3 @@ }, | ||
}, | ||
"gitHead": "fe5b95c8bc364388f37a7aacfcb22cee01864bf9" | ||
"gitHead": "9ef8b723c581862d5c56b7ffd62a21393e7d83b9" | ||
} |
@@ -1,32 +0,48 @@ | ||
# com·mut·a·ble | ||
# @nteract/commutable | ||
> /kəˈmyo͞otəbəl/ | ||
> | ||
> 1. (of a place or journey) allowing regular commuting to and from work. | ||
> 2. **capable of being exchanged or converted.** | ||
This is a package for Jupyter Notebook operations, helping to enable history stored as a series of immutable notebooks. | ||
`commutable` is a library for Jupyter Notebook operations, helping to enable | ||
history stored as a series of immutable notebooks. | ||
This package follows the principles below, based on [Tom MacWright](http://www.macwright.org/2015/05/18/practical-undo.html)'s outline for practical undo. | ||
## Principles | ||
- **A notebook document is immutable**. It is never mutated in-place. | ||
- Changes to a notebook document are encapsulated into **operations** that take a previous version and return a new one. | ||
- History is represented as a **list of states**, with past on one end, the present on the other, and an index that can back up into 'undo states'. | ||
- Modifying a notebook document causes any **future states to be thrown away**. | ||
* **A notebook document is immutable**. It is never mutated in-place. | ||
* Changes to a notebook document are encapsulated into **operations** that take a previous version and return a new one. | ||
* History is represented as a **list of states**, with past on one end, the present on the other, and an index that can back up into 'undo states'. | ||
* Modifying a notebook document causes any **future states to be thrown away**. | ||
## Installation | ||
Credits to [Tom MacWright](http://www.macwright.org/2015/05/18/practical-undo.html) for the outline. | ||
``` | ||
$ yarn add @nteract/commutable | ||
``` | ||
## Installation | ||
``` | ||
$ npm install --save @nteract/commutable | ||
``` | ||
You may use whichever package manager (`npm` or `yarn`) best suits your workflow. The `nteract` team internally uses `yarn`. | ||
## Usage | ||
```bash | ||
npm install --save @nteract/commutable | ||
# OR | ||
yarn add @nteract/commutable | ||
The example below shows how we can use the `emptyMarkdownCell` immutable object exported from this package to quickly create an empty Markdown cell in our nteract application. | ||
```javascript | ||
import { emptyMarkdownCell } from "@nteract/commutable"; | ||
export default () => ( | ||
<MarkdownPreview | ||
id="a-random-cell-id" | ||
cell={emptyMarkdownCell} | ||
editorFocused={false} | ||
/> | ||
); | ||
``` | ||
## Docs | ||
## Documentation | ||
Check out our [docs](https://nteract.github.io/docs/commutable/) for getting started. | ||
You can view the reference documentation for `@nteract/commutable` in the [package docs](https://packages.nteract.io/modules/commutable). | ||
## Support | ||
If you experience an issue while using this package or have a feature request, please file an issue on the [issue board](https://github.com/nteract/nteract/issues/new/choose) and add the `pkg:commutable` label. | ||
## License | ||
[BSD-3-Clause](https://choosealicense.com/licenses/bsd-3-clause/) |
106
src/index.ts
@@ -1,14 +0,15 @@ | ||
import { Map as ImmutableMap } from "immutable"; | ||
import * as v4 from "./v4"; | ||
import * as v3 from "./v3"; | ||
import { ImmutableNotebook, JSONType } from "./types"; | ||
/** | ||
* @module commutable | ||
*/ | ||
// ..................................... | ||
// API Exports | ||
// Make sure the index.js.flow types stay in sync with this section | ||
// | ||
// from types | ||
export * from "./types"; | ||
export * from "./primitives"; | ||
export * from "./structures"; | ||
export * from "./outputs"; | ||
export * from "./cells"; | ||
export * from "./notebook"; | ||
/* | ||
// from structures | ||
@@ -28,67 +29,34 @@ export { | ||
// v4 | ||
export { StreamOutput, Output, createImmutableOutput } from "./v4"; | ||
export { | ||
StreamOutput, | ||
Output, | ||
createImmutableMimeBundle, | ||
createImmutableOutput | ||
} from "./v4"; | ||
makeDisplayData, | ||
makeErrorOutput, | ||
makeStreamOutput, | ||
makeExecuteResult, | ||
MimeBundle | ||
} from "./outputs"; | ||
// general | ||
export { | ||
makeRawCell, | ||
makeCodeCell, | ||
makeMarkdownCell, | ||
ImmutableCodeCell, | ||
ImmutableMarkdownCell, | ||
ImmutableRawCell, | ||
ImmutableCell, | ||
CellType | ||
} from "./cells"; | ||
export type Notebook = v4.Notebook | v3.Notebook; | ||
export { | ||
toJS, | ||
stringifyNotebook, | ||
fromJS, | ||
parseNotebook, | ||
makeNotebookRecord, | ||
Notebook, | ||
ImmutableNotebook | ||
} from "./notebook"; | ||
const freezeReviver = <T extends JSONType>(k: string, v: T) => | ||
Object.freeze(v) as T; | ||
// Expected usage of below is fromJS(parseNotebook(string|buffer)) | ||
export const parseNotebook = (notebookString: string): Notebook => | ||
JSON.parse(notebookString, freezeReviver); | ||
export const fromJS = ( | ||
notebook: Notebook | ImmutableNotebook | ||
): ImmutableNotebook => { | ||
if (ImmutableMap.isMap(notebook)) { | ||
if (notebook.has("cellOrder") && notebook.has("cellMap")) { | ||
return notebook; | ||
} | ||
throw new TypeError( | ||
`commutable was passed an Immutable.Map structure that is not a notebook` | ||
); | ||
} | ||
if (notebook.nbformat === 4 && notebook.nbformat_minor >= 0) { | ||
if ( | ||
Array.isArray(notebook.cells) && | ||
typeof notebook.metadata === "object" | ||
) { | ||
return v4.fromJS(notebook); | ||
} | ||
} else if (notebook.nbformat === 3 && notebook.nbformat_minor >= 0) { | ||
return v3.fromJS(notebook); | ||
} | ||
if (notebook.nbformat) { | ||
throw new TypeError( | ||
`nbformat v${notebook.nbformat}.${notebook.nbformat_minor} not recognized` | ||
); | ||
} | ||
throw new TypeError("This notebook format is not supported"); | ||
}; | ||
export const toJS = (immnb: ImmutableNotebook): v4.Notebook => { | ||
const minorVersion: null | number = immnb.get("nbformat_minor", null); | ||
if ( | ||
immnb.get("nbformat") === 4 && | ||
typeof minorVersion === "number" && | ||
minorVersion >= 0 | ||
) { | ||
return v4.toJS(immnb); | ||
} | ||
throw new TypeError("Only notebook formats 3 and 4 are supported!"); | ||
}; | ||
// Expected usage is stringifyNotebook(toJS(immutableNotebook)) | ||
export const stringifyNotebook = (notebook: v4.Notebook) => | ||
JSON.stringify(notebook, null, 2); | ||
*/ |
@@ -0,91 +1,46 @@ | ||
/** | ||
* @module commutable | ||
*/ | ||
import uuid from "uuid/v4"; | ||
import { Map as ImmutableMap, List as ImmutableList } from "immutable"; | ||
import { | ||
ImmutableOutput, | ||
ImmutableCell, | ||
ImmutableCodeCell, | ||
ImmutableMarkdownCell, | ||
ImmutableNotebook, | ||
ImmutableCellOrder, | ||
ImmutableCellMap, | ||
ImmutableJSONType, | ||
ExecutionCount | ||
} from "./types"; | ||
import { CellId, createCellId } from "./primitives"; | ||
// We're hardset to nbformat v4.4 for what we use in-memory | ||
interface Notebook { | ||
nbformat: 4; | ||
nbformat_minor: 4; | ||
metadata: ImmutableMap<string, ImmutableJSONType>; | ||
cellOrder: ImmutableList<string>; | ||
cellMap: ImmutableMap<string, ImmutableCell>; | ||
} | ||
import { makeNotebookRecord, ImmutableNotebook } from "./notebook"; | ||
interface CodeCell { | ||
cell_type: "code"; | ||
metadata: ImmutableMap<string, any>; | ||
execution_count: ExecutionCount; | ||
source: string; | ||
outputs: ImmutableList<ImmutableOutput>; | ||
} | ||
import { makeCodeCell, makeMarkdownCell, ImmutableCell } from "./cells"; | ||
interface MarkdownCell { | ||
cell_type: "markdown"; | ||
source: string; | ||
metadata: ImmutableMap<string, any>; | ||
} | ||
import { Map as ImmutableMap, List as ImmutableList } from "immutable"; | ||
const defaultCodeCell = Object.freeze({ | ||
cell_type: "code", | ||
execution_count: null, | ||
metadata: ImmutableMap({ | ||
collapsed: false, | ||
outputHidden: false, | ||
inputHidden: false | ||
}), | ||
source: "", | ||
outputs: ImmutableList() | ||
}) as CodeCell; | ||
// The cell creators here are a bit duplicative | ||
export const createCodeCell = makeCodeCell; | ||
export const createMarkdownCell = makeMarkdownCell; | ||
const defaultMarkdownCell = Object.freeze({ | ||
cell_type: "markdown", | ||
metadata: ImmutableMap(), | ||
source: "" | ||
}) as MarkdownCell; | ||
export const createCodeCell = (cell = defaultCodeCell): ImmutableCodeCell => | ||
ImmutableMap(cell); | ||
export const createMarkdownCell = ( | ||
cell = defaultMarkdownCell | ||
): ImmutableMarkdownCell => ImmutableMap(cell); | ||
export const emptyCodeCell = createCodeCell(); | ||
export const emptyMarkdownCell = createMarkdownCell(); | ||
export const defaultNotebook = Object.freeze({ | ||
nbformat: 4, | ||
nbformat_minor: 4, | ||
metadata: ImmutableMap(), | ||
cellOrder: ImmutableList(), | ||
cellMap: ImmutableMap() | ||
}) as Notebook; | ||
// These are all kind of duplicative now that we're on records. | ||
// Since we export these though, they're left for | ||
// backwards compatiblity | ||
export const defaultNotebook = makeNotebookRecord(); | ||
export const createNotebook = makeNotebookRecord; | ||
export const emptyNotebook = makeNotebookRecord(); | ||
export const createNotebook = (notebook = defaultNotebook): ImmutableNotebook => | ||
ImmutableMap(notebook); | ||
export const emptyNotebook = createNotebook(); | ||
export type CellStructure = { | ||
cellOrder: ImmutableCellOrder; | ||
cellMap: ImmutableCellMap; | ||
cellOrder: ImmutableList<CellId>; | ||
cellMap: ImmutableMap<CellId, ImmutableCell>; | ||
}; | ||
// Intended to make it easy to use this with (temporary mutable cellOrder + | ||
// cellMap) | ||
/** | ||
* A function that appends a new cell to a CellStructure object. | ||
* | ||
* @param cellStructure The cellOrder and cellMap of the current notebook | ||
* @param immutableCell The cell that will be inserted into the cellStructure | ||
* @param id The id of the new cell, defaults to a new UUID | ||
* | ||
* @returns Cell structure with the new cell appended at the end | ||
*/ | ||
export const appendCell = ( | ||
cellStructure: CellStructure, | ||
immutableCell: ImmutableCell, | ||
id: string = uuid() | ||
id: CellId = createCellId() | ||
): CellStructure => ({ | ||
@@ -96,2 +51,10 @@ cellOrder: cellStructure.cellOrder.push(id), | ||
/** | ||
* A function that appends a cell to an immutable notebook. | ||
* | ||
* @param immnb An immutable data structure representing the notebook that will be modified | ||
* @param immCell The new cell that will be inserted into the notebook | ||
* | ||
* @returns The modified notebook | ||
*/ | ||
export const appendCellToNotebook = ( | ||
@@ -102,6 +65,4 @@ immnb: ImmutableNotebook, | ||
immnb.withMutations(nb => { | ||
// $FlowFixMe: Fixed by making ImmutableNotebook a typed Record. | ||
const cellStructure: CellStructure = { | ||
cellOrder: nb.get("cellOrder"), | ||
// $FlowFixMe: Fixed by making ImmutableNotebook a typed Record. | ||
cellMap: nb.get("cellMap") | ||
@@ -113,6 +74,16 @@ }; | ||
/** | ||
* Inserts a cell with cellID at a given index within the notebook. | ||
* | ||
* @param notebook The notebook the cell will be inserted into. | ||
* @param cell The cell that will be inserted | ||
* @param cellID The ID of the cell. | ||
* @param index The position we would like to insert the cell at | ||
* | ||
* @returns The modified notebook. | ||
*/ | ||
export const insertCellAt = ( | ||
notebook: ImmutableNotebook, | ||
cell: ImmutableCell, | ||
cellID: string, | ||
cellId: string, | ||
index: number | ||
@@ -122,12 +93,20 @@ ): ImmutableNotebook => | ||
nb | ||
.setIn(["cellMap", cellID], cell) | ||
// $FlowFixMe: Fixed by making ImmutableNotebook a typed record. | ||
.set("cellOrder", nb.get("cellOrder").insert(index, cellID)) | ||
.setIn(["cellMap", cellId], cell) | ||
.set("cellOrder", nb.get("cellOrder").insert(index, cellId)) | ||
); | ||
/** | ||
* Inserts a new cell with cellID before an existing cell with priorCellID | ||
* in the notebook. | ||
* | ||
* @param notebook The notebook the cell will be inserted into. | ||
* @param cell The cell that will be inserted | ||
* @param cellID The ID of the cell. | ||
* @param priorCellID The ID of the existing cell. | ||
*/ | ||
export const insertCellAfter = ( | ||
notebook: ImmutableNotebook, | ||
cell: ImmutableCell, | ||
cellID: string, | ||
priorCellID: string | ||
cellId: string, | ||
priorCellId: string | ||
): ImmutableNotebook => | ||
@@ -137,8 +116,15 @@ insertCellAt( | ||
cell, | ||
cellID, | ||
// $FlowFixMe: Fixed by making ImmutableNotebook a typed record. | ||
notebook.get("cellOrder").indexOf(priorCellID) + 1 | ||
cellId, | ||
notebook.get("cellOrder").indexOf(priorCellId) + 1 | ||
); | ||
/** | ||
* Delete a cell with CellID at a given location. Note that this function | ||
* is deprecated in favor of `deleteCell`. | ||
* | ||
* @param notebook The notebook containing the cell. | ||
* @param cellID The ID of the cell that will be deleted. | ||
* | ||
* @returns The modified notebook | ||
* | ||
* @deprecated use `deleteCell()` instead | ||
@@ -148,3 +134,3 @@ */ | ||
notebook: ImmutableNotebook, | ||
cellID: string | ||
cellId: string | ||
): ImmutableNotebook => { | ||
@@ -154,19 +140,26 @@ console.log( | ||
); | ||
return notebook | ||
.removeIn(["cellMap", cellID]) | ||
.update("cellOrder", (cellOrder: ImmutableCellOrder) => | ||
cellOrder.filterNot(id => id === cellID) | ||
); | ||
return deleteCell(notebook, cellId); | ||
}; | ||
/** | ||
* Delete a cell with CellID at a given location. | ||
* | ||
* @param notebook The notebook containing the cell. | ||
* @param cellID The ID of the cell that will be deleted. | ||
* | ||
* @returns The modified notebook | ||
*/ | ||
export const deleteCell = ( | ||
notebook: ImmutableNotebook, | ||
cellID: string | ||
cellId: string | ||
): ImmutableNotebook => | ||
notebook | ||
.removeIn(["cellMap", cellID]) | ||
.update("cellOrder", (cellOrder: ImmutableCellOrder) => | ||
cellOrder.filterNot(id => id === cellID) | ||
); | ||
.removeIn(["cellMap", cellId]) | ||
.update("cellOrder", cellOrder => cellOrder.filterNot(id => id === cellId)); | ||
/** | ||
* A new notebook with a single empty code cell. This function is useful | ||
* if you are looking to initialize a fresh, new notebook. | ||
*/ | ||
export const monocellNotebook = appendCellToNotebook( | ||
@@ -173,0 +166,0 @@ emptyNotebook, |
@@ -0,1 +1,4 @@ | ||
/** | ||
* @module commutable | ||
*/ | ||
import { | ||
@@ -7,21 +10,30 @@ Map as ImmutableMap, | ||
import { MultiLineString, JSONObject } from "./primitives"; | ||
import { makeNotebookRecord } from "./notebook"; | ||
import { | ||
ImmutableNotebook, | ||
ImmutableCodeCell, | ||
ImmutableMarkdownCell, | ||
ImmutableRawCell, | ||
makeCodeCell, | ||
makeRawCell, | ||
makeMarkdownCell | ||
} from "./cells"; | ||
import { | ||
ImmutableOutput, | ||
ImmutableMimeBundle, | ||
MultiLineString, | ||
JSONObject | ||
} from "./types"; | ||
import { CellStructure, appendCell } from "./structures"; | ||
import { | ||
makeExecuteResult, | ||
makeDisplayData, | ||
makeStreamOutput, | ||
makeErrorOutput, | ||
demultiline, | ||
cleanMimeAtKey, | ||
ErrorOutput, | ||
RawCell, | ||
MarkdownCell | ||
} from "./v4"; | ||
ErrorOutput | ||
} from "./outputs"; | ||
import { CellStructure, appendCell } from "./structures"; | ||
import { RawCell, MarkdownCell } from "./v4"; | ||
const VALID_MIMETYPES = { | ||
@@ -82,3 +94,3 @@ text: "text/plain", | ||
export interface Notebook { | ||
export type Notebook = { | ||
worksheets: Worksheet[]; | ||
@@ -88,3 +100,3 @@ metadata: object; | ||
nbformat_minor: number; | ||
} | ||
}; | ||
@@ -94,3 +106,3 @@ const createImmutableMarkdownCell = ( | ||
): ImmutableMarkdownCell => | ||
ImmutableMap({ | ||
makeMarkdownCell({ | ||
cell_type: cell.cell_type, | ||
@@ -104,2 +116,3 @@ source: demultiline(cell.source), | ||
for (const key of Object.keys(output)) { | ||
// v3 had non-media types for rich media | ||
if (key in VALID_MIMETYPES) { | ||
@@ -116,29 +129,26 @@ mimeBundle[VALID_MIMETYPES[key as MimeTypeKey]] = | ||
export const sanitize = (o: ExecuteResult | DisplayData) => | ||
o.metadata ? { metadata: immutableFromJS(o.metadata) } : {}; | ||
const createImmutableOutput = (output: Output): ImmutableOutput => { | ||
switch (output.output_type) { | ||
case "pyout": | ||
return ImmutableMap({ | ||
output_type: output.output_type, | ||
return makeExecuteResult({ | ||
execution_count: output.prompt_number, | ||
// Note strangeness with v4 API | ||
data: createImmutableMimeBundle(output), | ||
...sanitize(output) | ||
metadata: immutableFromJS(output.metadata) | ||
}); | ||
case "display_data": | ||
return ImmutableMap({ | ||
output_type: output.output_type, | ||
return makeDisplayData({ | ||
data: createImmutableMimeBundle(output), | ||
...sanitize(output) | ||
metadata: immutableFromJS(output.metadata) | ||
}); | ||
case "stream": | ||
return ImmutableMap({ | ||
output_type: output.output_type, | ||
name: output.stream, | ||
// Default to stdout in all cases unless it's stderr | ||
const name = output.stream === "stderr" ? "stderr" : "stdout"; | ||
return makeStreamOutput({ | ||
name, | ||
text: demultiline(output.text) | ||
}); | ||
case "pyerr": | ||
return ImmutableMap({ | ||
output_type: "error", | ||
return makeErrorOutput({ | ||
ename: output.ename, | ||
@@ -154,3 +164,3 @@ evalue: output.evalue, | ||
const createImmutableCodeCell = (cell: CodeCell): ImmutableCodeCell => | ||
ImmutableMap({ | ||
makeCodeCell({ | ||
cell_type: cell.cell_type, | ||
@@ -164,3 +174,3 @@ source: demultiline(cell.input), | ||
const createImmutableRawCell = (cell: RawCell): ImmutableRawCell => | ||
ImmutableMap({ | ||
makeRawCell({ | ||
cell_type: cell.cell_type, | ||
@@ -172,3 +182,4 @@ source: demultiline(cell.source), | ||
const createImmutableHeadingCell = (cell: HeadingCell): ImmutableMarkdownCell => | ||
ImmutableMap({ | ||
// v3 heading cells are just markdown cells in v4+ | ||
makeMarkdownCell({ | ||
cell_type: "markdown", | ||
@@ -203,3 +214,3 @@ source: Array.isArray(cell.source) | ||
export const fromJS = (notebook: Notebook): ImmutableNotebook => { | ||
export const fromJS = (notebook: Notebook) => { | ||
if (notebook.nbformat !== 3 || notebook.nbformat_minor < 0) { | ||
@@ -217,3 +228,3 @@ throw new TypeError( | ||
const cellStructure = [].concat.apply( | ||
const cellStructure = ([] as CellStructure[]).concat.apply( | ||
[], | ||
@@ -228,3 +239,3 @@ notebook.worksheets.map(worksheet => | ||
return ImmutableMap({ | ||
return makeNotebookRecord({ | ||
cellOrder: cellStructure.cellOrder.asImmutable(), | ||
@@ -231,0 +242,0 @@ cellMap: cellStructure.cellMap.asImmutable(), |
307
src/v4.ts
@@ -0,1 +1,4 @@ | ||
/** | ||
* @module commutable | ||
*/ | ||
/* | ||
@@ -12,5 +15,2 @@ * Functions in this module are provided for converting from Jupyter Notebook | ||
* | ||
* To assist in the developer experience, types are included through the use of | ||
* flow. | ||
* | ||
*/ | ||
@@ -26,3 +26,10 @@ | ||
import { | ||
makeNotebookRecord, | ||
ImmutableNotebook, | ||
NotebookRecordParams | ||
} from "./notebook"; | ||
import { JSONObject, MultiLineString, ExecutionCount } from "./primitives"; | ||
import { | ||
ImmutableCodeCell, | ||
@@ -32,56 +39,27 @@ ImmutableMarkdownCell, | ||
ImmutableCell, | ||
makeCodeCell, | ||
makeRawCell, | ||
makeMarkdownCell | ||
} from "./cells"; | ||
import { | ||
createImmutableMimeBundle, | ||
ImmutableOutput, | ||
makeExecuteResult, | ||
makeDisplayData, | ||
makeStreamOutput, | ||
makeErrorOutput, | ||
demultiline, | ||
remultiline, | ||
isJSONKey, | ||
MimeBundle, | ||
ImmutableMimeBundle, | ||
ExecutionCount, | ||
JSONObject, | ||
JSONType, | ||
MultiLineString | ||
} from "./types"; | ||
Output, | ||
StreamOutput, | ||
createImmutableOutput | ||
} from "./outputs"; | ||
import { appendCell, CellStructure } from "./structures"; | ||
// | ||
// MimeBundle example (disk format) | ||
// | ||
// { | ||
// "application/json": {"a": 3, "b": 2}, | ||
// "text/html": ["<p>\n", "Hey\n", "</p>"], | ||
// "text/plain": "Hey" | ||
// } | ||
// | ||
export type MimeBundle = { [key: string]: string | string[] | Object }; | ||
/** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | ||
* Output Types | ||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | ||
export interface ExecuteResult { | ||
output_type: "execute_result"; | ||
execution_count: ExecutionCount; | ||
data: MimeBundle; | ||
metadata: JSONObject; | ||
} | ||
export interface DisplayData { | ||
output_type: "display_data"; | ||
data: MimeBundle; | ||
metadata: JSONObject; | ||
} | ||
export interface StreamOutput { | ||
output_type: "stream"; | ||
name: "stdout" | "stderr"; | ||
text: MultiLineString; | ||
} | ||
export interface ErrorOutput { | ||
output_type: "error" | "pyerr"; | ||
ename: string; | ||
evalue: string; | ||
traceback: string[]; | ||
} | ||
export type Output = ExecuteResult | DisplayData | StreamOutput | ErrorOutput; | ||
/** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | ||
* Cell Types | ||
@@ -112,3 +90,3 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | ||
export interface Notebook { | ||
export type Notebook = { | ||
cells: Array<Cell>; | ||
@@ -118,104 +96,11 @@ metadata: Object; | ||
nbformat_minor: number; | ||
} | ||
}; | ||
export const demultiline = (s: string | string[]) => | ||
Array.isArray(s) ? s.join("") : s; | ||
/** | ||
* Split string into a list of strings delimited by newlines | ||
* Converts a mutable representation of metadata to an immutable representation. | ||
* | ||
* @param metadata A JSON representation of notebook metadata. | ||
* | ||
* @returns ImmutableMetadata An immutable representation of the metadata. | ||
*/ | ||
export const remultiline = (s: string | string[]): string[] => | ||
Array.isArray(s) ? s : s.split(/(.+?(?:\r\n|\n))/g).filter(x => x !== ""); | ||
export const isJSONKey = (key: string) => | ||
/^application\/(.*\+)?json$/.test(key); | ||
export const cleanMimeData = ( | ||
key: string, | ||
data: string | string[] | object | ||
) => { | ||
// See https://github.com/jupyter/nbformat/blob/62d6eb8803616d198eaa2024604d1fe923f2a7b3/nbformat/v4/nbformat.v4.schema.json#L368 | ||
if (isJSONKey(key)) { | ||
// Data stays as is for JSON types | ||
return data; | ||
} | ||
if (typeof data === "string" || Array.isArray(data)) { | ||
return demultiline(data); | ||
} | ||
throw new TypeError( | ||
`Data for ${key} is expected to be a string or an Array of strings` | ||
); | ||
}; | ||
export const cleanMimeAtKey = ( | ||
mimeBundle: MimeBundle, | ||
previous: ImmutableMimeBundle, | ||
key: string | ||
): ImmutableMimeBundle => | ||
previous.set(key, cleanMimeData(key, mimeBundle[key])); | ||
// Map over all the mimetypes, turning them into our in-memory format | ||
// | ||
// { | ||
// "application/json": {"a": 3, "b": 2}, | ||
// "text/html": ["<p>\n", "Hey\n", "</p>"], | ||
// "text/plain": "Hey" | ||
// } | ||
// | ||
// to | ||
// | ||
// { | ||
// "application/json": {"a": 3, "b": 2}, | ||
// "text/html": "<p>\nHey\n</p>", | ||
// "text/plain": "Hey" | ||
// } | ||
// | ||
export const createImmutableMimeBundle = ( | ||
mimeBundle: MimeBundle | ||
): ImmutableMimeBundle => | ||
Object.keys(mimeBundle).reduce( | ||
cleanMimeAtKey.bind(null, mimeBundle), | ||
ImmutableMap() | ||
); | ||
export const sanitize = (o: ExecuteResult | DisplayData) => | ||
o.metadata ? { metadata: immutableFromJS(o.metadata) } : {}; | ||
export const createImmutableOutput = (output: Output): ImmutableOutput => { | ||
switch (output.output_type) { | ||
case "execute_result": | ||
return ImmutableMap({ | ||
output_type: output.output_type, | ||
execution_count: output.execution_count, | ||
data: createImmutableMimeBundle(output.data), | ||
...sanitize(output) | ||
}); | ||
case "display_data": | ||
return ImmutableMap({ | ||
output_type: output.output_type, | ||
data: createImmutableMimeBundle(output.data), | ||
...sanitize(output) | ||
}); | ||
case "stream": | ||
return ImmutableMap({ | ||
output_type: output.output_type, | ||
name: output.name, | ||
text: demultiline(output.text) | ||
}); | ||
case "error": | ||
return ImmutableMap({ | ||
output_type: "error", | ||
ename: output.ename, | ||
evalue: output.evalue, | ||
// Note: this is one of the cases where the Array of strings (for | ||
// traceback) is part of the format, not a multiline string | ||
traceback: ImmutableList(output.traceback) | ||
}); | ||
default: | ||
throw new TypeError(`Output type ${output.output_type} not recognized`); | ||
} | ||
}; | ||
const createImmutableMetadata = (metadata: JSONObject) => | ||
@@ -236,3 +121,3 @@ ImmutableMap(metadata).map((v, k: string) => { | ||
const createImmutableRawCell = (cell: RawCell): ImmutableRawCell => | ||
ImmutableMap({ | ||
makeRawCell({ | ||
cell_type: cell.cell_type, | ||
@@ -246,3 +131,3 @@ source: demultiline(cell.source), | ||
): ImmutableMarkdownCell => | ||
ImmutableMap({ | ||
makeMarkdownCell({ | ||
cell_type: cell.cell_type, | ||
@@ -254,3 +139,3 @@ source: demultiline(cell.source), | ||
const createImmutableCodeCell = (cell: CodeCell): ImmutableCodeCell => | ||
ImmutableMap({ | ||
makeCodeCell({ | ||
cell_type: cell.cell_type, | ||
@@ -263,2 +148,10 @@ source: demultiline(cell.source), | ||
/** | ||
* Converts a JSON representation of a cell of any type to the correct | ||
* immutable representation, per the v4 nbformat specification. | ||
* | ||
* @param cell A JSON representation of a cell. | ||
* | ||
* @returns An immutable representation of the same cell. | ||
*/ | ||
const createImmutableCell = (cell: Cell): ImmutableCell => { | ||
@@ -277,3 +170,3 @@ switch (cell.cell_type) { | ||
export const fromJS = (notebook: Notebook): ImmutableNotebook => { | ||
export const fromJS = (notebook: Notebook) => { | ||
if (notebook.nbformat !== 4 || notebook.nbformat_minor < 0) { | ||
@@ -298,3 +191,3 @@ throw new TypeError( | ||
return ImmutableMap({ | ||
return makeNotebookRecord({ | ||
cellOrder: cellStructure.cellOrder.asImmutable(), | ||
@@ -308,19 +201,5 @@ cellMap: cellStructure.cellMap.asImmutable(), | ||
interface PlainNotebook { | ||
cellOrder: ImmutableList<string>; | ||
cellMap: ImmutableMap<string, ImmutableCell>; | ||
metadata: ImmutableMap<string, any>; | ||
nbformat: 4; | ||
nbformat_minor: number; | ||
} | ||
const metadataToJS = (immMetadata: ImmutableMap<string, any>) => | ||
immMetadata.toJS() as JSONObject; | ||
const markdownCellToJS = (immCell: ImmutableCell): MarkdownCell => ({ | ||
cell_type: "markdown", | ||
source: remultiline(immCell.get("source", "")), | ||
metadata: metadataToJS(immCell.get("metadata", ImmutableMap())) | ||
}); | ||
const mimeBundleToJS = (immMimeBundle: ImmutableMimeBundle): MimeBundle => { | ||
@@ -351,6 +230,3 @@ const bundle = immMimeBundle.toObject(); | ||
const outputToJS = (immOutput: ImmutableOutput): Output => { | ||
// Technically this is an intermediate output with Immutables inside | ||
const output = immOutput.toObject(); | ||
const outputToJS = (output: ImmutableOutput): Output => { | ||
switch (output.output_type) { | ||
@@ -377,35 +253,47 @@ case "execute_result": | ||
case "error": | ||
// Note: this is one of the cases where the Array of strings (for | ||
// traceback) is part of the format, not a multiline string | ||
return immOutput.toJS() as any; | ||
default: | ||
throw new TypeError(`Output type ${output.output_type} not recognized`); | ||
return { | ||
output_type: output.output_type, | ||
ename: output.ename, | ||
evalue: output.evalue, | ||
// Note: this is one of the cases where the Array of strings (for | ||
// traceback) is part of the format, not a multiline string | ||
traceback: output.traceback.toJS() | ||
}; | ||
} | ||
}; | ||
interface IntermediateCodeCell { | ||
cell_type: "code"; | ||
metadata: ImmutableMap<string, JSONType>; | ||
execution_count: ExecutionCount; | ||
source: string; | ||
outputs: ImmutableList<ImmutableOutput>; | ||
} | ||
const markdownCellToJS = (immCell: ImmutableMarkdownCell): MarkdownCell => ({ | ||
cell_type: "markdown", | ||
source: remultiline(immCell.source), | ||
metadata: metadataToJS(immCell.metadata) | ||
}); | ||
const codeCellToJS = (immCell: ImmutableCell): CodeCell => { | ||
const cell = immCell.toObject() as IntermediateCodeCell; | ||
/** | ||
* Converts an immutable representation of a code cell to a JSON representation. | ||
* | ||
* @param immCell An immutable representation of a code cell. | ||
* | ||
* @returns A JSON representation of the same code cell. | ||
*/ | ||
const codeCellToJS = (immCell: ImmutableCodeCell): CodeCell => { | ||
return { | ||
cell_type: "code", | ||
source: remultiline(cell.source), | ||
outputs: cell.outputs.map(outputToJS).toArray(), | ||
execution_count: cell.execution_count, | ||
metadata: metadataToJS(immCell.get("metadata", ImmutableMap())) | ||
source: remultiline(immCell.source), | ||
outputs: immCell.outputs.map(outputToJS).toArray(), | ||
execution_count: immCell.execution_count, | ||
metadata: metadataToJS(immCell.metadata) | ||
}; | ||
}; | ||
const rawCellToJS = (immCell: ImmutableCell): RawCell => { | ||
const cell = immCell.toObject() as Cell; | ||
/** | ||
* Converts an immutable representation of a raw cell to a JSON representation. | ||
* | ||
* @param immCell An immutable representation of a raw cell. | ||
* | ||
* @returns A JSON representation of the same raw cell. | ||
*/ | ||
const rawCellToJS = (immCell: ImmutableRawCell): RawCell => { | ||
return { | ||
cell_type: "raw", | ||
source: remultiline(cell.source), | ||
source: remultiline(immCell.source), | ||
metadata: metadataToJS(immCell.get("metadata", ImmutableMap())) | ||
@@ -415,5 +303,11 @@ }; | ||
/** | ||
* Converts an immutable cell to a JSON cell. | ||
* | ||
* @param immCell An immutable representation of a cell. | ||
* | ||
* @returns A JSON representation of the same cell. | ||
*/ | ||
const cellToJS = (immCell: ImmutableCell): Cell => { | ||
const cellType: "markdown" | "raw" | "code" = immCell.get("cell_type"); | ||
switch (cellType) { | ||
switch (immCell.cell_type) { | ||
case "markdown": | ||
@@ -426,8 +320,15 @@ return markdownCellToJS(immCell); | ||
default: | ||
throw new TypeError(`Cell type ${cellType} unknown`); | ||
throw new TypeError(`Cell type unknown at runtime`); | ||
} | ||
}; | ||
/** | ||
* Converts an immutable representation of a notebook to a JSON representation. | ||
* | ||
* @param immnb The immutable representation of a notebook. | ||
* | ||
* @returns The JSON representation of a notebook. | ||
*/ | ||
export const toJS = (immnb: ImmutableNotebook): Notebook => { | ||
const plainNotebook = immnb.toObject() as PlainNotebook; | ||
const plainNotebook = immnb.toObject() as NotebookRecordParams; | ||
const plainCellOrder: string[] = plainNotebook.cellOrder.toArray(); | ||
@@ -438,4 +339,4 @@ const plainCellMap: { | ||
const cells = plainCellOrder.map((cellID: string) => | ||
cellToJS(plainCellMap[cellID]) | ||
const cells = plainCellOrder.map((cellId: string) => | ||
cellToJS(plainCellMap[cellId]) | ||
); | ||
@@ -446,5 +347,5 @@ | ||
metadata: plainNotebook.metadata.toJS(), | ||
nbformat: plainNotebook.nbformat, | ||
nbformat: 4, | ||
nbformat_minor: plainNotebook.nbformat_minor | ||
}; | ||
}; |
{ | ||
"extends": "../../tsconfig.base.json", | ||
"compilerOptions": { | ||
"outDir": "lib" | ||
"outDir": "lib", | ||
"rootDir": "src" | ||
}, | ||
"include": ["src"] | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
78601
28
2288
49
Updatedimmutable@^4.0.0-rc.12