Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@nteract/commutable

Package Overview
Dependencies
Maintainers
14
Versions
85
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@nteract/commutable - npm Package Compare versions

Comparing version 6.0.4-alpha.0 to 7.0.0

__tests__/primitive.spec.ts

15

lib/cells.d.ts

@@ -6,4 +6,4 @@ /**

import { ExecutionCount } from "./primitives";
import { Map as ImmutableMap, List as ImmutableList, Record, RecordOf } from "immutable";
declare type CodeCellParams = {
import { List as ImmutableList, Map as ImmutableMap, Record, RecordOf } from "immutable";
export interface CodeCellParams {
cell_type: "code";

@@ -14,17 +14,17 @@ metadata: ImmutableMap<string, any>;

outputs: ImmutableList<ImmutableOutput>;
};
}
export declare const makeCodeCell: Record.Factory<CodeCellParams>;
export declare type ImmutableCodeCell = RecordOf<CodeCellParams>;
declare type MarkdownCellParams = {
export interface MarkdownCellParams {
cell_type: "markdown";
source: string;
metadata: ImmutableMap<string, any>;
};
}
export declare const makeMarkdownCell: Record.Factory<MarkdownCellParams>;
export declare type ImmutableMarkdownCell = RecordOf<MarkdownCellParams>;
declare type RawCellParams = {
export interface RawCellParams {
cell_type: "raw";
source: string;
metadata: ImmutableMap<string, any>;
};
}
export declare const makeRawCell: Record.Factory<RawCellParams>;

@@ -34,2 +34,1 @@ export declare type ImmutableRawCell = RecordOf<RawCellParams>;

export declare type CellType = "raw" | "markdown" | "code";
export {};

@@ -5,5 +5,3 @@ "use strict";

*/
// .....................................
// API Exports
//
function __export(m) {

@@ -18,49 +16,1 @@ for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];

__export(require("./notebook"));
/*
// from structures
export {
emptyCodeCell,
emptyMarkdownCell,
emptyNotebook,
monocellNotebook,
createCodeCell,
insertCellAt,
insertCellAfter,
deleteCell,
appendCellToNotebook
} from "./structures";
// v4
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";
*/

@@ -10,8 +10,8 @@ /**

*/
import * as v3 from "./v3";
import * as v4 from "./v4";
import * as v3 from "./v3";
import { Map as ImmutableMap, List as ImmutableList, Record } from "immutable";
import { List as ImmutableList, Map as ImmutableMap, Record } from "immutable";
import { ImmutableCell } from "./cells";
import { CellId } from "./primitives";
export declare type NotebookRecordParams = {
export interface NotebookRecordParams {
cellOrder: ImmutableList<CellId>;

@@ -22,6 +22,6 @@ cellMap: ImmutableMap<CellId, ImmutableCell>;

metadata: ImmutableMap<string, any>;
};
}
export declare const makeNotebookRecord: Record.Factory<NotebookRecordParams>;
export declare type ImmutableNotebook = Record<NotebookRecordParams> & Readonly<NotebookRecordParams>;
export declare type Notebook = v4.Notebook | v3.Notebook;
export declare type Notebook = v4.NotebookV4 | v3.NotebookV3;
/**

@@ -34,4 +34,4 @@ * Converts a string representation of a notebook into a JSON representation.

*/
export declare const parseNotebook: (notebookString: string) => Notebook;
export declare const fromJS: (notebook: v4.Notebook | ImmutableNotebook | v3.Notebook) => ImmutableNotebook;
export declare function parseNotebook(notebookString: string): Notebook;
export declare function fromJS(notebook: Notebook | ImmutableNotebook): Record<NotebookRecordParams> & Readonly<NotebookRecordParams>;
/**

@@ -45,3 +45,3 @@ * Converts an immutable representation of a notebook to a JSON representation of the

*/
export declare const toJS: (immnb: ImmutableNotebook) => v4.Notebook;
export declare function toJS(immnb: ImmutableNotebook): v4.NotebookV4;
/**

@@ -54,2 +54,2 @@ * Converts a JSON representation of a notebook into a string representation.

*/
export declare const stringifyNotebook: (notebook: v4.Notebook) => string;
export declare function stringifyNotebook(notebook: v4.NotebookV4): string;

@@ -19,4 +19,4 @@ "use strict";

*/
const v3 = __importStar(require("./v3"));
const v4 = __importStar(require("./v4"));
const v3 = __importStar(require("./v3"));
const immutable_1 = require("immutable");

@@ -30,3 +30,5 @@ exports.makeNotebookRecord = immutable_1.Record({

});
const freezeReviver = (_k, v) => Object.freeze(v);
function freezeReviver(_k, v) {
return Object.freeze(v);
}
/**

@@ -39,4 +41,7 @@ * Converts a string representation of a notebook into a JSON representation.

*/
exports.parseNotebook = (notebookString) => JSON.parse(notebookString, freezeReviver);
exports.fromJS = (notebook) => {
function parseNotebook(notebookString) {
return JSON.parse(notebookString, freezeReviver);
}
exports.parseNotebook = parseNotebook;
function fromJS(notebook) {
if (immutable_1.Record.isRecord(notebook)) {

@@ -46,12 +51,11 @@ if (notebook.has("cellOrder") && notebook.has("cellMap")) {

}
throw new TypeError(`commutable was passed an Immutable.Record structure that is not a notebook`);
throw new TypeError("commutable was passed an Immutable.Record structure that is not a notebook");
}
if (notebook.nbformat === 4 && notebook.nbformat_minor >= 0) {
var v4Notebook = notebook;
if (Array.isArray(v4Notebook.cells) &&
if (v4.isNotebookV4(notebook)) {
if (Array.isArray(notebook.cells) &&
typeof notebook.metadata === "object") {
return v4.fromJS(v4Notebook);
return v4.fromJS(notebook);
}
}
else if (notebook.nbformat === 3 && notebook.nbformat_minor >= 0) {
else if (v3.isNotebookV3(notebook)) {
return v3.fromJS(notebook);

@@ -63,3 +67,4 @@ }

throw new TypeError("This notebook format is not supported");
};
}
exports.fromJS = fromJS;
/**

@@ -73,3 +78,3 @@ * Converts an immutable representation of a notebook to a JSON representation of the

*/
exports.toJS = (immnb) => {
function toJS(immnb) {
const minorVersion = immnb.get("nbformat_minor", null);

@@ -82,3 +87,4 @@ if (immnb.get("nbformat") === 4 &&

throw new TypeError("Only notebook formats 3 and 4 are supported!");
};
}
exports.toJS = toJS;
/**

@@ -91,2 +97,5 @@ * Converts a JSON representation of a notebook into a string representation.

*/
exports.stringifyNotebook = (notebook) => JSON.stringify(notebook, null, 2);
function stringifyNotebook(notebook) {
return JSON.stringify(notebook, null, 2);
}
exports.stringifyNotebook = stringifyNotebook;
/**
* @module commutable
*/
import { Map as ImmutableMap, List as ImmutableList, Record, RecordOf } from "immutable";
import { ExecutionCount, JSONObject, MultiLineString } from "./primitives";
export declare type ImmutableMimeBundle = ImmutableMap<string, any>;
export declare type MimeBundle = {
[key: string]: string | string[] | undefined;
};
/**
* 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"
* }
* ```
* @param mimeBundle The mime
* @param previous
* @param key
*/
export declare const cleanMimeAtKey: (mimeBundle: MimeBundle, previous: ImmutableMap<string, any>, key: string) => ImmutableMap<string, any>;
/**
* Cleans mimedata, primarily converts an array of strings into a single string
* joined by newlines.
*
* @param key The key, usually a mime type, that is associated with the mime data.
* @param data The mime data to clean.
*
* @returns The cleaned mime data.
*/
export declare const cleanMimeData: (key: string, data: string | string[] | undefined) => string | string[] | undefined;
export declare const createImmutableMimeBundle: (mimeBundle: MimeBundle) => ImmutableMap<string, any>;
export declare const demultiline: (s: string | string[]) => string;
/**
* Split string into a list of strings delimited by newlines
*
* @param s The newline-delimited string that will be converted into an array of strings.
*
* @returns An array of strings.
*/
export declare const remultiline: (s: string | string[]) => string[];
export declare const isJSONKey: (key: string) => boolean;
import { List as ImmutableList, Record, RecordOf } from "immutable";
import { ExecutionCount, JSONObject, MediaBundle, MultiLineString, OnDiskMediaBundle } from "./primitives";
/** ExecuteResult Record Boilerplate */
declare type ExecuteResultParams = {
export interface ExecuteResultParams {
output_type: "execute_result";
execution_count: ExecutionCount;
data: ImmutableMimeBundle;
data: Readonly<MediaBundle>;
metadata?: any;
};
}
export declare const makeExecuteResult: Record.Factory<ExecuteResultParams>;
declare type ImmutableExecuteResult = RecordOf<ExecuteResultParams>;
export declare type ImmutableExecuteResult = RecordOf<ExecuteResultParams>;
/** DisplayData Record Boilerplate */
declare type DisplayDataParams = {
export interface DisplayDataParams {
data: Readonly<MediaBundle>;
output_type: "display_data";
data: ImmutableMimeBundle;
metadata?: any;
};
}
export declare const makeDisplayData: Record.Factory<DisplayDataParams>;
declare type ImmutableDisplayData = RecordOf<DisplayDataParams>;
export declare type ImmutableDisplayData = RecordOf<DisplayDataParams>;
/** StreamOutput Record Boilerplate */
declare type StreamOutputParams = {
export interface StreamOutputParams {
output_type: "stream";
name: "stdout" | "stderr";
text: string;
};
}
export declare const makeStreamOutput: Record.Factory<StreamOutputParams>;
declare type ImmutableStreamOutput = RecordOf<StreamOutputParams>;
export declare type ImmutableStreamOutput = RecordOf<StreamOutputParams>;
/** ErrorOutput Record Boilerplate */
declare type ErrorOutputParams = {
export interface ErrorOutputParams {
output_type: "error";

@@ -85,22 +37,19 @@ ename: string;

traceback: ImmutableList<string>;
};
}
export declare const makeErrorOutput: Record.Factory<ErrorOutputParams>;
declare type ImmutableErrorOutput = RecordOf<ErrorOutputParams>;
export declare type ImmutableErrorOutput = RecordOf<ErrorOutputParams>;
export declare type ImmutableOutput = ImmutableExecuteResult | ImmutableDisplayData | ImmutableStreamOutput | ImmutableErrorOutput;
/** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Output Types
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
export interface ExecuteResult {
export interface OnDiskExecuteResult {
output_type: "execute_result";
execution_count: ExecutionCount;
data: MimeBundle;
data: OnDiskMediaBundle;
metadata: JSONObject;
}
export interface DisplayData {
export interface OnDiskDisplayData {
output_type: "display_data";
data: MimeBundle;
data: OnDiskMediaBundle;
metadata: JSONObject;
transient?: JSONObject;
}
export interface StreamOutput {
export interface OnDiskStreamOutput {
output_type: "stream";

@@ -110,4 +59,4 @@ name: "stdout" | "stderr";

}
export interface ErrorOutput {
output_type: "error" | "pyerr";
export interface OnDiskErrorOutput {
output_type: "error";
ename: string;

@@ -117,3 +66,3 @@ evalue: string;

}
export declare type Output = ExecuteResult | DisplayData | StreamOutput | ErrorOutput;
export declare type OnDiskOutput = OnDiskExecuteResult | OnDiskDisplayData | OnDiskStreamOutput | OnDiskErrorOutput;
/**

@@ -126,3 +75,2 @@ * Converts a mutable representation of an output to an immutable representation.

*/
export declare const createImmutableOutput: (output: Output) => ImmutableOutput;
export {};
export declare function createImmutableOutput(output: OnDiskOutput): ImmutableOutput;

@@ -7,76 +7,25 @@ "use strict";

const immutable_1 = require("immutable");
/**
* 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"
* }
* ```
* @param mimeBundle The mime
* @param previous
* @param key
*/
exports.cleanMimeAtKey = (mimeBundle, previous, key) => previous.set(key, exports.cleanMimeData(key, mimeBundle[key]));
/**
* Cleans mimedata, primarily converts an array of strings into a single string
* joined by newlines.
*
* @param key The key, usually a mime type, that is associated with the mime data.
* @param data The mime data to clean.
*
* @returns The cleaned mime data.
*/
exports.cleanMimeData = (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;
}
if (typeof data === "string" || Array.isArray(data)) {
return exports.demultiline(data);
}
throw new TypeError(`Data for ${key} is expected to be a string or an Array of strings`);
};
exports.createImmutableMimeBundle = (mimeBundle) => Object.keys(mimeBundle).reduce(exports.cleanMimeAtKey.bind(null, mimeBundle), immutable_1.Map());
exports.demultiline = (s) => Array.isArray(s) ? s.join("") : s;
/**
* Split string into a list of strings delimited by newlines
*
* @param s The newline-delimited string that will be converted into an array of strings.
*
* @returns An array of strings.
*/
exports.remultiline = (s) => Array.isArray(s) ? s : s.split(/(.+?(?:\r\n|\n))/g).filter(x => x !== "");
exports.isJSONKey = (key) => /^application\/(.*\+)?json$/.test(key);
const primitives_1 = require("./primitives");
// Used for initializing all output records
const emptyMediaBundle = Object.freeze({});
exports.makeExecuteResult = immutable_1.Record({
output_type: "execute_result",
data: emptyMediaBundle,
execution_count: null,
data: immutable_1.Map(),
metadata: immutable_1.Map()
metadata: immutable_1.Map(),
output_type: "execute_result"
});
exports.makeDisplayData = immutable_1.Record({
output_type: "display_data",
data: immutable_1.Map(),
metadata: immutable_1.Map()
data: emptyMediaBundle,
metadata: immutable_1.Map(),
output_type: "display_data"
});
exports.makeStreamOutput = immutable_1.Record({
name: "stdout",
output_type: "stream",
name: "stdout",
text: ""
});
exports.makeErrorOutput = immutable_1.Record({
output_type: "error",
ename: "",
evalue: "",
output_type: "error",
traceback: immutable_1.List()

@@ -91,8 +40,8 @@ });

*/
exports.createImmutableOutput = (output) => {
function createImmutableOutput(output) {
switch (output.output_type) {
case "execute_result":
return exports.makeExecuteResult({
data: primitives_1.createFrozenMediaBundle(output.data),
execution_count: output.execution_count,
data: exports.createImmutableMimeBundle(output.data),
metadata: immutable_1.fromJS(output.metadata)

@@ -102,3 +51,3 @@ });

return exports.makeDisplayData({
data: exports.createImmutableMimeBundle(output.data),
data: primitives_1.createFrozenMediaBundle(output.data),
metadata: immutable_1.fromJS(output.metadata)

@@ -109,9 +58,9 @@ });

name: output.name,
text: exports.demultiline(output.text)
text: primitives_1.demultiline(output.text)
});
case "error":
return exports.makeErrorOutput({
output_type: "error",
ename: output.ename,
evalue: output.evalue,
output_type: "error",
// Note: this is one of the cases where the Array of strings (for

@@ -122,4 +71,11 @@ // traceback) is part of the format, not a multiline string

default:
throw new TypeError(`Output type ${output.output_type} not recognized`);
// Since we're well typed, output is never. However we can still get new output types we don't handle
// and need to fail hard instead of making indeterminate behavior
const unknownOutput = output;
if (unknownOutput.output_type) {
throw new TypeError(`Output type ${output.output_type} not recognized`);
}
throw new TypeError(`Output structure not known: ${JSON.stringify(output)}`);
}
};
}
exports.createImmutableOutput = createImmutableOutput;

@@ -0,1 +1,4 @@

/**
* @module commutable
*/
import * as Immutable from "immutable";

@@ -11,3 +14,3 @@ export declare type ExecutionCount = number | null;

export declare type CellId = string;
export declare const createCellId: () => string;
export declare function createCellId(): CellId;
export declare type MultiLineString = string | string[];

@@ -17,1 +20,90 @@ export declare type ImmutableJSONType = PrimitiveImmutable | ImmutableJSONMap | ImmutableJSONList;

export declare type ImmutableJSONList = Immutable.List<any>;
/**
* Media Bundles as they exist on disk from the notebook format
* See https://nbformat.readthedocs.io/en/latest/format_description.html#display-data for docs
* and https://github.com/jupyter/nbformat/blob/master/nbformat/v4/nbformat.v4.schema.json for the schema
*/
export interface OnDiskMediaBundle {
"text/plain"?: MultiLineString;
"text/html"?: MultiLineString;
"text/latex"?: MultiLineString;
"text/markdown"?: MultiLineString;
"application/javascript"?: MultiLineString;
"image/png"?: MultiLineString;
"image/jpeg"?: MultiLineString;
"image/gif"?: MultiLineString;
"image/svg+xml"?: MultiLineString;
"application/json"?: string | string[] | {};
"application/vdom.v1+json"?: {};
"application/vnd.dataresource+json"?: {};
"text/vnd.plotly.v1+html"?: MultiLineString | {};
"application/vnd.plotly.v1+json"?: {};
"application/geo+json"?: {};
"application/x-nteract-model-debug+json"?: {};
"application/vnd.vega.v2+json"?: {};
"application/vnd.vega.v3+json"?: {};
"application/vnd.vegalite.v1+json"?: {};
"application/vnd.vegalite.v2+json"?: {};
[key: string]: string | string[] | {} | undefined;
}
export interface MediaBundle {
"text/plain"?: string;
"text/html"?: string;
"text/latex"?: string;
"text/markdown"?: string;
"application/javascript"?: string;
"image/png"?: string;
"image/jpeg"?: string;
"image/gif"?: string;
"image/svg+xml"?: string;
"application/json"?: {
[key: string]: any;
};
"application/vdom.v1+json"?: {
[key: string]: any;
};
"application/vnd.dataresource+json"?: {
[key: string]: any;
};
"text/vnd.plotly.v1+html"?: string | {
[key: string]: any;
};
"application/vnd.plotly.v1+json"?: {
[key: string]: any;
};
"application/geo+json"?: {
[key: string]: any;
};
"application/x-nteract-model-debug+json"?: {
[key: string]: any;
};
"application/vnd.vega.v2+json"?: {
[key: string]: any;
};
"application/vnd.vega.v3+json"?: {
[key: string]: any;
};
"application/vnd.vegalite.v1+json"?: {
[key: string]: any;
};
"application/vnd.vegalite.v2+json"?: {
[key: string]: any;
};
[key: string]: string | string[] | {} | undefined;
}
/**
* Turn nbformat multiline strings (arrays of strings for simplifying diffs) into strings
*/
export declare function demultiline(s: string | string[]): string;
/**
* Split string into a list of strings delimited by newlines; useful for on-disk git comparisons;
* and is the expectation for jupyter notebooks on disk
*/
export declare function remultiline(s: string | string[]): string[];
declare type DeepReadonly<T> = {
readonly [P in keyof T]: DeepReadonly<T[P]>;
};
export declare function deepFreeze<T>(object: T): DeepReadonly<T>;
export declare function createFrozenMediaBundle(mediaBundle: OnDiskMediaBundle): Readonly<MediaBundle>;
export declare function createOnDiskMediaBundle(mediaBundle: Readonly<MediaBundle>): OnDiskMediaBundle;
export {};

@@ -6,6 +6,97 @@ "use strict";

Object.defineProperty(exports, "__esModule", { value: true });
const v4_1 = __importDefault(require("uuid/v4"));
function createCellId() {
return v4_1.default();
}
exports.createCellId = createCellId;
/**
* @module commutable
* Turn nbformat multiline strings (arrays of strings for simplifying diffs) into strings
*/
const v4_1 = __importDefault(require("uuid/v4"));
exports.createCellId = () => v4_1.default();
function demultiline(s) {
if (Array.isArray(s)) {
return s.join("");
}
return s;
}
exports.demultiline = demultiline;
/**
* Split string into a list of strings delimited by newlines; useful for on-disk git comparisons;
* and is the expectation for jupyter notebooks on disk
*/
function remultiline(s) {
if (Array.isArray(s)) {
// Assume
return s;
}
// Use positive lookahead regex to split on newline and retain newline char
return s.split(/(.+?(?:\r\n|\n))/g).filter(x => x !== "");
}
exports.remultiline = remultiline;
function isJSONKey(key) {
return /^application\/(.*\+)json$/.test(key);
}
// Taken from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze
function deepFreeze(object) {
// Retrieve the property names defined on object
const propNames = Object.getOwnPropertyNames(object);
// Freeze properties before freezing self
for (const name of propNames) {
// getOwnPropertyNames assures us we can index on name
const value = object[name];
object[name] =
value && typeof value === "object" ? deepFreeze(value) : value;
}
return Object.freeze(object);
}
exports.deepFreeze = deepFreeze;
function createFrozenMediaBundle(mediaBundle) {
// 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"
// }
// Since we have to convert from one type to another that has conflicting types; we need to hand convert it in a way that
// flow is able to verify correctly. The way we do that is create a new object that we declare with the type we want;
// set the keys and values we need; then seal the object with Object.freeze
const bundle = {};
for (const key in mediaBundle) {
if (!isJSONKey(key) &&
(typeof mediaBundle[key] === "string" || Array.isArray(mediaBundle[key]))) {
// Because it's a string; we can't mutate it anyways (and don't have to Object.freeze it)
bundle[key] = demultiline(mediaBundle[key]);
}
else {
// we now know it's an Object of some kind
bundle[key] = deepFreeze(mediaBundle[key]);
}
}
return Object.freeze(bundle);
}
exports.createFrozenMediaBundle = createFrozenMediaBundle;
function createOnDiskMediaBundle(mediaBundle) {
// Technically we could return just the mediaBundle as is
// return mediaBundle;
// However for the sake of on-disk readability we write out remultilined versions of the array and string ones
const freshBundle = {};
for (const key in mediaBundle) {
if (!isJSONKey(key) &&
(typeof mediaBundle[key] === "string" || Array.isArray(mediaBundle[key]))) {
freshBundle[key] = remultiline(mediaBundle[key]);
}
else {
freshBundle[key] = mediaBundle[key];
}
}
return freshBundle;
}
exports.createOnDiskMediaBundle = createOnDiskMediaBundle;
import { CellId } from "./primitives";
import { ImmutableNotebook } from "./notebook";
import { ImmutableCell } from "./cells";
import { Map as ImmutableMap, List as ImmutableList } from "immutable";
export declare const createCodeCell: import("immutable").Record.Factory<{
cell_type: "code";
metadata: ImmutableMap<string, any>;
execution_count: number | null;
source: string;
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 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;
import { List as ImmutableList, Map as ImmutableMap } from "immutable";
export declare const createCodeCell: import("immutable").Record.Factory<import("./cells").CodeCellParams>;
export declare const createMarkdownCell: import("immutable").Record.Factory<import("./cells").MarkdownCellParams>;
export declare const emptyCodeCell: import("immutable").RecordOf<import("./cells").CodeCellParams>;
export declare const emptyMarkdownCell: import("immutable").RecordOf<import("./cells").MarkdownCellParams>;
export declare const defaultNotebook: import("immutable").Record<import("./notebook").NotebookRecordParams> & Readonly<import("./notebook").NotebookRecordParams>;
export declare const createNotebook: import("immutable").Record.Factory<import("./notebook").NotebookRecordParams>;
export declare const emptyNotebook: ImmutableNotebook;
export declare type CellStructure = {
export declare const emptyNotebook: import("immutable").Record<import("./notebook").NotebookRecordParams> & Readonly<import("./notebook").NotebookRecordParams>;
export interface CellStructure {
cellOrder: ImmutableList<CellId>;
cellMap: ImmutableMap<CellId, ImmutableCell>;
};
}
/**

@@ -45,3 +25,3 @@ * A function that appends a new cell to a CellStructure object.

*/
export declare const appendCell: (cellStructure: CellStructure, immutableCell: ImmutableCell, id?: string) => CellStructure;
export declare function appendCell(cellStructure: CellStructure, immutableCell: ImmutableCell, id?: CellId): CellStructure;
/**

@@ -55,3 +35,3 @@ * A function that appends a cell to an immutable notebook.

*/
export declare const appendCellToNotebook: (immnb: ImmutableNotebook, immCell: ImmutableCell) => ImmutableNotebook;
export declare function appendCellToNotebook(immnb: ImmutableNotebook, immCell: ImmutableCell): ImmutableNotebook;
/**

@@ -67,3 +47,3 @@ * Inserts a cell with cellID at a given index within the notebook.

*/
export declare const insertCellAt: (notebook: ImmutableNotebook, cell: ImmutableCell, cellId: string, index: number) => ImmutableNotebook;
export declare function insertCellAt(notebook: ImmutableNotebook, cell: ImmutableCell, cellId: string, index: number): ImmutableNotebook;
/**

@@ -78,7 +58,8 @@ * Inserts a new cell with cellID before an existing cell with priorCellID

*/
export declare const insertCellAfter: (notebook: ImmutableNotebook, cell: ImmutableCell, cellId: string, priorCellId: string) => ImmutableNotebook;
export declare function 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`.
* Deprecated: 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.

@@ -91,3 +72,3 @@ * @param cellID The ID of the cell that will be deleted.

*/
export declare const removeCell: (notebook: ImmutableNotebook, cellId: string) => ImmutableNotebook;
export declare function removeCell(notebook: ImmutableNotebook, cellId: string): ImmutableNotebook;
/**

@@ -101,7 +82,7 @@ * Delete a cell with CellID at a given location.

*/
export declare const deleteCell: (notebook: ImmutableNotebook, cellId: string) => ImmutableNotebook;
export declare function deleteCell(notebook: ImmutableNotebook, cellId: string): ImmutableNotebook;
/**
* A new notebook with a single empty code cell. This function is useful
* A new 'monocell' 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;
export declare const monocellNotebook: import("immutable").Record<import("./notebook").NotebookRecordParams> & Readonly<import("./notebook").NotebookRecordParams>;

@@ -26,6 +26,9 @@ "use strict";

*/
exports.appendCell = (cellStructure, immutableCell, id = primitives_1.createCellId()) => ({
cellOrder: cellStructure.cellOrder.push(id),
cellMap: cellStructure.cellMap.set(id, immutableCell)
});
function appendCell(cellStructure, immutableCell, id = primitives_1.createCellId()) {
return {
cellOrder: cellStructure.cellOrder.push(id),
cellMap: cellStructure.cellMap.set(id, immutableCell)
};
}
exports.appendCell = appendCell;
/**

@@ -39,10 +42,13 @@ * A function that appends a cell to an immutable 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);
});
function appendCellToNotebook(immnb, immCell) {
return immnb.withMutations(nb => {
const cellStructure = {
cellOrder: nb.get("cellOrder"),
cellMap: nb.get("cellMap")
};
const { cellOrder, cellMap } = appendCell(cellStructure, immCell);
return nb.set("cellOrder", cellOrder).set("cellMap", cellMap);
});
}
exports.appendCellToNotebook = appendCellToNotebook;
/**

@@ -58,5 +64,8 @@ * Inserts a cell with cellID at a given index within the notebook.

*/
exports.insertCellAt = (notebook, cell, cellId, index) => notebook.withMutations(nb => nb
.setIn(["cellMap", cellId], cell)
.set("cellOrder", nb.get("cellOrder").insert(index, cellId)));
function insertCellAt(notebook, cell, cellId, index) {
return notebook.withMutations(nb => nb
.setIn(["cellMap", cellId], cell)
.set("cellOrder", nb.get("cellOrder").insert(index, cellId)));
}
exports.insertCellAt = insertCellAt;
/**

@@ -71,7 +80,11 @@ * Inserts a new cell with cellID before an existing cell with priorCellID

*/
exports.insertCellAfter = (notebook, cell, cellId, priorCellId) => exports.insertCellAt(notebook, cell, cellId, notebook.get("cellOrder").indexOf(priorCellId) + 1);
function insertCellAfter(notebook, cell, cellId, priorCellId) {
return insertCellAt(notebook, cell, cellId, notebook.get("cellOrder").indexOf(priorCellId) + 1);
}
exports.insertCellAfter = insertCellAfter;
/**
* Delete a cell with CellID at a given location. Note that this function
* is deprecated in favor of `deleteCell`.
* Deprecated: 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.

@@ -84,6 +97,7 @@ * @param cellID The ID of the cell that will be deleted.

*/
exports.removeCell = (notebook, cellId) => {
function removeCell(notebook, cellId) {
console.log("Deprecation Warning: removeCell() is being deprecated. Please use deleteCell() instead");
return exports.deleteCell(notebook, cellId);
};
return deleteCell(notebook, cellId);
}
exports.removeCell = removeCell;
/**

@@ -97,9 +111,12 @@ * Delete a cell with CellID at a given location.

*/
exports.deleteCell = (notebook, cellId) => notebook
.removeIn(["cellMap", cellId])
.update("cellOrder", cellOrder => cellOrder.filterNot(id => id === cellId));
function deleteCell(notebook, cellId) {
return notebook
.removeIn(["cellMap", cellId])
.update("cellOrder", cellOrder => cellOrder.filterNot(id => id === cellId));
}
exports.deleteCell = deleteCell;
/**
* A new notebook with a single empty code cell. This function is useful
* A new 'monocell' 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);
exports.monocellNotebook = appendCellToNotebook(exports.emptyNotebook, exports.emptyCodeCell);

@@ -1,4 +0,3 @@

import { MultiLineString, JSONObject } from "./primitives";
import { ErrorOutput } from "./outputs";
import { RawCell, MarkdownCell } from "./v4";
import { JSONObject, MultiLineString } from "./primitives";
import { MarkdownCell, RawCell } from "./v4";
declare const VALID_MIMETYPES: {

@@ -28,2 +27,8 @@ text: string;

}
export interface ErrorOutput {
output_type: "error" | "pyerr";
ename: string;
evalue: string;
traceback: string[];
}
export interface StreamOutput {

@@ -48,3 +53,3 @@ output_type: "stream";

prompt_number: number;
outputs: Array<Output>;
outputs: Output[];
}

@@ -56,3 +61,3 @@ export declare type Cell = RawCell | MarkdownCell | HeadingCell | CodeCell;

}
export declare type Notebook = {
export interface NotebookV3 {
worksheets: Worksheet[];

@@ -62,4 +67,5 @@ metadata: object;

nbformat_minor: number;
};
export declare const fromJS: (notebook: Notebook) => import("./notebook").ImmutableNotebook;
}
export declare function fromJS(notebook: NotebookV3): import("immutable").Record<import("./notebook").NotebookRecordParams> & Readonly<import("./notebook").NotebookRecordParams>;
export declare function isNotebookV3(value: any): value is NotebookV3;
export {};

@@ -7,2 +7,3 @@ "use strict";

const immutable_1 = require("immutable");
const primitives_1 = require("./primitives");
const notebook_1 = require("./notebook");

@@ -23,19 +24,24 @@ const cells_1 = require("./cells");

};
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 = {};
function createImmutableMarkdownCell(cell) {
return cells_1.makeMarkdownCell({
cell_type: cell.cell_type,
source: primitives_1.demultiline(cell.source),
metadata: immutable_1.fromJS(cell.metadata)
});
}
/**
* Handle the old v3 version of the media
*/
function createImmutableMediaBundle(output) {
const mediaBundle = {};
for (const key of Object.keys(output)) {
// v3 had non-media types for rich media
if (key in VALID_MIMETYPES) {
mimeBundle[VALID_MIMETYPES[key]] =
mediaBundle[VALID_MIMETYPES[key]] =
output[key];
}
}
return Object.keys(mimeBundle).reduce(outputs_1.cleanMimeAtKey.bind(null, mimeBundle), immutable_1.Map());
};
const createImmutableOutput = (output) => {
return primitives_1.createFrozenMediaBundle(mediaBundle);
}
function createImmutableOutput(output) {
switch (output.output_type) {

@@ -46,3 +52,3 @@ case "pyout":

// Note strangeness with v4 API
data: createImmutableMimeBundle(output),
data: createImmutableMediaBundle(output),
metadata: immutable_1.fromJS(output.metadata)

@@ -52,3 +58,3 @@ });

return outputs_1.makeDisplayData({
data: createImmutableMimeBundle(output),
data: createImmutableMediaBundle(output),
metadata: immutable_1.fromJS(output.metadata)

@@ -61,3 +67,3 @@ });

name,
text: outputs_1.demultiline(output.text)
text: primitives_1.demultiline(output.text)
});

@@ -73,28 +79,33 @@ case "pyerr":

}
};
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) => {
}
function createImmutableCodeCell(cell) {
return cells_1.makeCodeCell({
cell_type: cell.cell_type,
source: primitives_1.demultiline(cell.input),
outputs: immutable_1.List(cell.outputs.map(createImmutableOutput)),
execution_count: cell.prompt_number,
metadata: immutable_1.fromJS(cell.metadata)
});
}
function createImmutableRawCell(cell) {
return cells_1.makeRawCell({
cell_type: cell.cell_type,
source: primitives_1.demultiline(cell.source),
metadata: immutable_1.fromJS(cell.metadata)
});
}
function createImmutableHeadingCell(cell) {
// v3 heading cells are just markdown cells in v4+
return cells_1.makeMarkdownCell({
cell_type: "markdown",
source: Array.isArray(cell.source)
? primitives_1.demultiline(cell.source.map(line => Array(cell.level)
.join("#")
.concat(" ")
.concat(line)))
: cell.source,
metadata: immutable_1.fromJS(cell.metadata)
});
}
function createImmutableCell(cell) {
switch (cell.cell_type) {

@@ -112,5 +123,6 @@ case "markdown":

}
};
exports.fromJS = (notebook) => {
if (notebook.nbformat !== 3 || notebook.nbformat_minor < 0) {
}
function fromJS(notebook) {
if (!isNotebookV3(notebook)) {
notebook = notebook;
throw new TypeError(`Notebook is not a valid v3 notebook. v3 notebooks must be of form 3.x

@@ -131,2 +143,10 @@ It lists nbformat v${notebook.nbformat}.${notebook.nbformat_minor}`);

});
};
}
exports.fromJS = fromJS;
function isNotebookV3(value) {
return (value &&
typeof value === "object" &&
value.nbformat === 3 &&
value.nbformat_minor >= 0);
}
exports.isNotebookV3 = isNotebookV3;

@@ -5,7 +5,4 @@ /**

import { ImmutableNotebook } from "./notebook";
import { JSONObject, MultiLineString, ExecutionCount } from "./primitives";
import { Output } from "./outputs";
/** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Cell Types
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
import { ExecutionCount, JSONObject, MultiLineString } from "./primitives";
import { OnDiskOutput } from "./outputs";
export interface CodeCell {

@@ -16,3 +13,3 @@ cell_type: "code";

source: MultiLineString;
outputs: Output[];
outputs: OnDiskOutput[];
}

@@ -30,9 +27,9 @@ export interface MarkdownCell {

export declare type Cell = CodeCell | MarkdownCell | RawCell;
export declare type Notebook = {
cells: Array<Cell>;
metadata: Object;
export interface NotebookV4 {
cells: Cell[];
metadata: JSONObject;
nbformat: 4;
nbformat_minor: number;
};
export declare const fromJS: (notebook: Notebook) => ImmutableNotebook;
}
export declare function fromJS(notebook: NotebookV4): import("immutable").Record<import("./notebook").NotebookRecordParams> & Readonly<import("./notebook").NotebookRecordParams>;
/**

@@ -45,2 +42,3 @@ * Converts an immutable representation of a notebook to a JSON representation.

*/
export declare const toJS: (immnb: ImmutableNotebook) => Notebook;
export declare function toJS(immnb: ImmutableNotebook): NotebookV4;
export declare function isNotebookV4(value: any): value is NotebookV4;
"use strict";
// Due to the on-disk format needing to be written out in an explicit order, we disable ordering for this file
// tslint:disable:object-literal-sort-keys
/**

@@ -20,2 +22,3 @@ * @module commutable

const notebook_1 = require("./notebook");
const primitives_1 = require("./primitives");
const cells_1 = require("./cells");

@@ -31,29 +34,37 @@ const outputs_1 = require("./outputs");

*/
const createImmutableMetadata = (metadata) => immutable_1.Map(metadata).map((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();
});
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)
});
function createImmutableMetadata(metadata) {
return immutable_1.Map(metadata).map((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();
});
}
function createImmutableRawCell(cell) {
return cells_1.makeRawCell({
cell_type: cell.cell_type,
source: primitives_1.demultiline(cell.source),
metadata: createImmutableMetadata(cell.metadata)
});
}
function createImmutableMarkdownCell(cell) {
return cells_1.makeMarkdownCell({
cell_type: cell.cell_type,
source: primitives_1.demultiline(cell.source),
metadata: createImmutableMetadata(cell.metadata)
});
}
function createImmutableCodeCell(cell) {
return cells_1.makeCodeCell({
cell_type: cell.cell_type,
source: primitives_1.demultiline(cell.source),
outputs: immutable_1.List(cell.outputs.map(outputs_1.createImmutableOutput)),
execution_count: cell.execution_count,
metadata: createImmutableMetadata(cell.metadata)
});
}
/**

@@ -67,3 +78,3 @@ * Converts a JSON representation of a cell of any type to the correct

*/
const createImmutableCell = (cell) => {
function createImmutableCell(cell) {
switch (cell.cell_type) {

@@ -79,5 +90,6 @@ case "markdown":

}
};
exports.fromJS = (notebook) => {
if (notebook.nbformat !== 4 || notebook.nbformat_minor < 0) {
}
function fromJS(notebook) {
if (!isNotebookV4(notebook)) {
notebook = notebook;
throw new TypeError(`Notebook is not a valid v4 notebook. v4 notebooks must be of form 4.x

@@ -100,23 +112,8 @@ It lists nbformat v${notebook.nbformat}.${notebook.nbformat_minor}`);

});
};
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])) {
bundle[key] = bundle[key].toJS();
}
return bundle;
}
const data = bundle[key];
if (typeof data === "string" || Array.isArray(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`);
});
return bundle;
};
const outputToJS = (output) => {
}
exports.fromJS = fromJS;
function metadataToJS(immMetadata) {
return immMetadata.toJS();
}
function outputToJS(output) {
switch (output.output_type) {

@@ -127,3 +124,3 @@ case "execute_result":

execution_count: output.execution_count,
data: mimeBundleToJS(output.data),
data: primitives_1.createOnDiskMediaBundle(output.data),
metadata: output.metadata.toJS()

@@ -134,3 +131,3 @@ };

output_type: output.output_type,
data: mimeBundleToJS(output.data),
data: primitives_1.createOnDiskMediaBundle(output.data),
metadata: output.metadata.toJS()

@@ -142,3 +139,3 @@ };

name: output.name,
text: outputs_1.remultiline(output.text)
text: primitives_1.remultiline(output.text)
};

@@ -155,8 +152,10 @@ case "error":

}
};
const markdownCellToJS = (immCell) => ({
cell_type: "markdown",
source: outputs_1.remultiline(immCell.source),
metadata: metadataToJS(immCell.metadata)
});
}
function markdownCellToJS(immCell) {
return {
cell_type: "markdown",
source: primitives_1.remultiline(immCell.source),
metadata: metadataToJS(immCell.metadata)
};
}
/**

@@ -169,6 +168,6 @@ * Converts an immutable representation of a code cell to a JSON representation.

*/
const codeCellToJS = (immCell) => {
function codeCellToJS(immCell) {
return {
cell_type: "code",
source: outputs_1.remultiline(immCell.source),
source: primitives_1.remultiline(immCell.source),
outputs: immCell.outputs.map(outputToJS).toArray(),

@@ -178,3 +177,3 @@ execution_count: immCell.execution_count,

};
};
}
/**

@@ -187,9 +186,9 @@ * Converts an immutable representation of a raw cell to a JSON representation.

*/
const rawCellToJS = (immCell) => {
function rawCellToJS(immCell) {
return {
cell_type: "raw",
source: outputs_1.remultiline(immCell.source),
source: primitives_1.remultiline(immCell.source),
metadata: metadataToJS(immCell.get("metadata", immutable_1.Map()))
};
};
}
/**

@@ -202,3 +201,3 @@ * Converts an immutable cell to a JSON cell.

*/
const cellToJS = (immCell) => {
function cellToJS(immCell) {
switch (immCell.cell_type) {

@@ -212,5 +211,5 @@ case "markdown":

default:
throw new TypeError(`Cell type unknown at runtime`);
throw new TypeError("Cell type unknown at runtime");
}
};
}
/**

@@ -223,3 +222,3 @@ * Converts an immutable representation of a notebook to a JSON representation.

*/
exports.toJS = (immnb) => {
function toJS(immnb) {
const plainNotebook = immnb.toObject();

@@ -235,2 +234,10 @@ const plainCellOrder = plainNotebook.cellOrder.toArray();

};
};
}
exports.toJS = toJS;
function isNotebookV4(value) {
return (value &&
typeof value === "object" &&
value.nbformat === 4 &&
value.nbformat_minor >= 0);
}
exports.isNotebookV4 = isNotebookV4;
{
"name": "@nteract/commutable",
"version": "6.0.4-alpha.0",
"version": "7.0.0",
"description": "library for immutable notebook operations",

@@ -27,3 +27,3 @@ "main": "lib/index.js",

},
"gitHead": "683bdfa1a29472a8947f60457e3ddbdfdfbb328b"
"gitHead": "8125bd8f3d7db2ca479d1b078379b5061625cd8f"
}
# @nteract/commutable
This is a package for Jupyter Notebook operations, helping to enable history stored as a series of immutable notebooks.
Commutable is a package to represent a Jupyter notebook document, as well as
operations on the notebook, as a series of immutable notebooks, each one with
its own state at a point in time.
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.
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.
- **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'.
- **A notebook document is immutable**. The notebook document's representation
is never mutated in-place.
- Changes to a notebook document are encapsulated into **operations** that
take a previous version and return a new version of the notebook without
modifying the old version.
- History is represented as a **list of states**, with _the 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**.

@@ -24,3 +32,6 @@

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.
The example below shows how we can create an empty Markdown cell in our
nteract notebook application. We use the `emptyMarkdownCell` immutable object
exported from this package to represent a new empty Markdown cell in a
notebook document.

@@ -41,7 +52,10 @@ ```javascript

You can view the reference documentation for `@nteract/commutable` in the [package docs](https://packages.nteract.io/modules/commutable).
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.
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, if possible, add the `pkg:commutable` label.

@@ -48,0 +62,0 @@ ## License

@@ -9,4 +9,4 @@ /**

import {
List as ImmutableList,
Map as ImmutableMap,
List as ImmutableList,
Record,

@@ -18,3 +18,3 @@ RecordOf

type CodeCellParams = {
export interface CodeCellParams {
cell_type: "code";

@@ -26,3 +26,4 @@ // Sadly untyped and widely unspecced

outputs: ImmutableList<ImmutableOutput>;
};
}
export const makeCodeCell = Record<CodeCellParams>({

@@ -44,7 +45,7 @@ cell_type: "code",

type MarkdownCellParams = {
export interface MarkdownCellParams {
cell_type: "markdown";
source: string;
metadata: ImmutableMap<string, any>;
};
}

@@ -61,7 +62,7 @@ export const makeMarkdownCell = Record<MarkdownCellParams>({

type RawCellParams = {
export interface RawCellParams {
cell_type: "raw";
source: string;
metadata: ImmutableMap<string, any>;
};
}

@@ -68,0 +69,0 @@ export const makeRawCell = Record<RawCellParams>({

/**
* @module commutable
*/
// .....................................
// API Exports
//

@@ -13,50 +11,1 @@ export * from "./primitives";

export * from "./notebook";
/*
// from structures
export {
emptyCodeCell,
emptyMarkdownCell,
emptyNotebook,
monocellNotebook,
createCodeCell,
insertCellAt,
insertCellAfter,
deleteCell,
appendCellToNotebook
} from "./structures";
// v4
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";
*/

@@ -10,11 +10,11 @@ /**

*/
import * as v3 from "./v3";
import * as v4 from "./v4";
import * as v3 from "./v3";
import { Map as ImmutableMap, List as ImmutableList, Record } from "immutable";
import { List as ImmutableList, Map as ImmutableMap, Record } from "immutable";
import { ImmutableCell } from "./cells";
import { JSONType, CellId } from "./primitives";
import { CellId, JSONType } from "./primitives";
export type NotebookRecordParams = {
export interface NotebookRecordParams {
cellOrder: ImmutableList<CellId>;

@@ -25,3 +25,3 @@ cellMap: ImmutableMap<CellId, ImmutableCell>;

metadata: ImmutableMap<string, any>;
};
}

@@ -39,20 +39,20 @@ export const makeNotebookRecord = Record<NotebookRecordParams>({

const freezeReviver = <T extends JSONType>(_k: string, v: T) =>
Object.freeze(v) as T;
function freezeReviver<T extends JSONType>(_k: string, v: T) {
return Object.freeze(v);
}
export type Notebook = v4.Notebook | v3.Notebook;
export type Notebook = v4.NotebookV4 | v3.NotebookV3;
/**
* Converts a string representation of a notebook into a JSON representation.
*
*
* @param notebookString A string representation of a notebook.
*
*
* @returns A JSON representation of the same notebook.
*/
export const parseNotebook = (notebookString: string): Notebook =>
JSON.parse(notebookString, freezeReviver);
export function parseNotebook(notebookString: string): Notebook {
return JSON.parse(notebookString, freezeReviver);
}
export const fromJS = (
notebook: v4.Notebook | v3.Notebook | ImmutableNotebook
) => {
export function fromJS(notebook: Notebook | ImmutableNotebook) {
if (Record.isRecord(notebook)) {

@@ -63,17 +63,15 @@ if (notebook.has("cellOrder") && notebook.has("cellMap")) {

throw new TypeError(
`commutable was passed an Immutable.Record structure that is not a notebook`
"commutable was passed an Immutable.Record structure that is not a notebook"
);
}
if (notebook.nbformat === 4 && notebook.nbformat_minor >= 0) {
var v4Notebook = notebook as v4.Notebook;
if (v4.isNotebookV4(notebook)) {
if (
Array.isArray(v4Notebook.cells) &&
Array.isArray(notebook.cells) &&
typeof notebook.metadata === "object"
) {
return v4.fromJS(v4Notebook);
return v4.fromJS(notebook);
}
} else if (notebook.nbformat === 3 && notebook.nbformat_minor >= 0) {
return v3.fromJS(notebook as v3.Notebook);
} else if (v3.isNotebookV3(notebook)) {
return v3.fromJS(notebook);
}

@@ -88,13 +86,14 @@

throw new TypeError("This notebook format is not supported");
};
}
/**
* Converts an immutable representation of a notebook to a JSON representation of the
* notebook using the v4 of the nbformat specification.
*
*
* @param immnb The immutable representation of a notebook.
*
*
* @returns The JSON representation of a notebook.
*/
export const toJS = (immnb: ImmutableNotebook): v4.Notebook => {
const minorVersion: null | number = immnb.get("nbformat_minor", null);
export function toJS(immnb: ImmutableNotebook): v4.NotebookV4 {
const minorVersion: number | null = immnb.get("nbformat_minor", null);

@@ -109,12 +108,13 @@ if (

throw new TypeError("Only notebook formats 3 and 4 are supported!");
};
}
/**
* Converts a JSON representation of a notebook into a string representation.
*
*
* @param notebook The JSON representation of a notebook.
*
*
* @returns A string containing the notebook data.
*/
export const stringifyNotebook = (notebook: v4.Notebook) =>
JSON.stringify(notebook, null, 2);
export function stringifyNotebook(notebook: v4.NotebookV4) {
return JSON.stringify(notebook, null, 2);
}

@@ -5,157 +5,75 @@ /**

import {
fromJS as immutableFromJS,
List as ImmutableList,
Map as ImmutableMap,
List as ImmutableList,
Record,
RecordOf,
fromJS as immutableFromJS
RecordOf
} from "immutable";
import { ExecutionCount, JSONObject, MultiLineString } from "./primitives";
import {
createFrozenMediaBundle,
demultiline,
ExecutionCount,
JSONObject,
MediaBundle,
MultiLineString,
OnDiskMediaBundle,
remultiline
} from "./primitives";
export type ImmutableMimeBundle = ImmutableMap<string, any>;
//
// 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[] | undefined };
/**
* 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"
* }
* ```
* @param mimeBundle The mime
* @param previous
* @param key
*/
export const cleanMimeAtKey = (
mimeBundle: MimeBundle,
previous: ImmutableMimeBundle,
key: string
): ImmutableMimeBundle =>
previous.set(key, cleanMimeData(key, mimeBundle[key]));
/**
* Cleans mimedata, primarily converts an array of strings into a single string
* joined by newlines.
*
* @param key The key, usually a mime type, that is associated with the mime data.
* @param data The mime data to clean.
*
* @returns The cleaned mime data.
*/
export const cleanMimeData = (
key: string,
data: string | string[] | undefined
) => {
// 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 createImmutableMimeBundle = (
mimeBundle: MimeBundle
): ImmutableMimeBundle =>
Object.keys(mimeBundle).reduce(
cleanMimeAtKey.bind(null, mimeBundle),
ImmutableMap()
);
export const demultiline = (s: string | string[]): string =>
Array.isArray(s) ? s.join("") : s;
/**
* Split string into a list of strings delimited by newlines
*
* @param s The newline-delimited string that will be converted into an array of strings.
*
* @returns An array of strings.
*/
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);
/** ExecuteResult Record Boilerplate */
type ExecuteResultParams = {
export interface ExecuteResultParams {
output_type: "execute_result";
execution_count: ExecutionCount;
data: ImmutableMimeBundle;
data: Readonly<MediaBundle>;
metadata?: any;
};
}
// Used for initializing all output records
const emptyMediaBundle = Object.freeze({});
export const makeExecuteResult = Record<ExecuteResultParams>({
output_type: "execute_result",
data: emptyMediaBundle,
execution_count: null,
data: ImmutableMap(),
metadata: ImmutableMap()
metadata: ImmutableMap(),
output_type: "execute_result"
});
type ImmutableExecuteResult = RecordOf<ExecuteResultParams>;
export type ImmutableExecuteResult = RecordOf<ExecuteResultParams>;
/** DisplayData Record Boilerplate */
type DisplayDataParams = {
export interface DisplayDataParams {
data: Readonly<MediaBundle>;
output_type: "display_data";
data: ImmutableMimeBundle;
metadata?: any;
};
}
export const makeDisplayData = Record<DisplayDataParams>({
output_type: "display_data",
data: ImmutableMap(),
metadata: ImmutableMap()
data: emptyMediaBundle,
metadata: ImmutableMap(),
output_type: "display_data"
});
type ImmutableDisplayData = RecordOf<DisplayDataParams>;
export type ImmutableDisplayData = RecordOf<DisplayDataParams>;
/** StreamOutput Record Boilerplate */
type StreamOutputParams = {
export interface StreamOutputParams {
output_type: "stream";
name: "stdout" | "stderr";
text: string;
};
}
export const makeStreamOutput = Record<StreamOutputParams>({
name: "stdout",
output_type: "stream",
name: "stdout",
text: ""
});
type ImmutableStreamOutput = RecordOf<StreamOutputParams>;
export type ImmutableStreamOutput = RecordOf<StreamOutputParams>;
/** ErrorOutput Record Boilerplate */
type ErrorOutputParams = {
export interface ErrorOutputParams {
output_type: "error";

@@ -165,12 +83,12 @@ ename: string;

traceback: ImmutableList<string>;
};
}
export const makeErrorOutput = Record<ErrorOutputParams>({
output_type: "error",
ename: "",
evalue: "",
output_type: "error",
traceback: ImmutableList()
});
type ImmutableErrorOutput = RecordOf<ErrorOutputParams>;
export type ImmutableErrorOutput = RecordOf<ErrorOutputParams>;

@@ -187,16 +105,16 @@ //////////////

/** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Output Types
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
export interface ExecuteResult {
export interface OnDiskExecuteResult {
output_type: "execute_result";
execution_count: ExecutionCount;
data: MimeBundle;
data: OnDiskMediaBundle;
metadata: JSONObject;
}
export interface DisplayData {
export interface OnDiskDisplayData {
output_type: "display_data";
data: MimeBundle;
data: OnDiskMediaBundle;
metadata: JSONObject;

@@ -206,3 +124,3 @@ transient?: JSONObject;

export interface StreamOutput {
export interface OnDiskStreamOutput {
output_type: "stream";

@@ -213,4 +131,4 @@ name: "stdout" | "stderr";

export interface ErrorOutput {
output_type: "error" | "pyerr";
export interface OnDiskErrorOutput {
output_type: "error";
ename: string;

@@ -221,3 +139,7 @@ evalue: string;

export type Output = ExecuteResult | DisplayData | StreamOutput | ErrorOutput;
export type OnDiskOutput =
| OnDiskExecuteResult
| OnDiskDisplayData
| OnDiskStreamOutput
| OnDiskErrorOutput;

@@ -231,8 +153,8 @@ /**

*/
export const createImmutableOutput = (output: Output): ImmutableOutput => {
export function createImmutableOutput(output: OnDiskOutput): ImmutableOutput {
switch (output.output_type) {
case "execute_result":
return makeExecuteResult({
data: createFrozenMediaBundle(output.data),
execution_count: output.execution_count,
data: createImmutableMimeBundle(output.data),
metadata: immutableFromJS(output.metadata)

@@ -242,3 +164,3 @@ });

return makeDisplayData({
data: createImmutableMimeBundle(output.data),
data: createFrozenMediaBundle(output.data),
metadata: immutableFromJS(output.metadata)

@@ -253,5 +175,5 @@ });

return makeErrorOutput({
output_type: "error",
ename: output.ename,
evalue: output.evalue,
output_type: "error",
// Note: this is one of the cases where the Array of strings (for

@@ -262,4 +184,14 @@ // traceback) is part of the format, not a multiline string

default:
throw new TypeError(`Output type ${output.output_type} not recognized`);
// Since we're well typed, output is never. However we can still get new output types we don't handle
// and need to fail hard instead of making indeterminate behavior
const unknownOutput = output as any;
if (unknownOutput.output_type) {
throw new TypeError(
`Output type ${(output as any).output_type} not recognized`
);
}
throw new TypeError(
`Output structure not known: ${JSON.stringify(output)}`
);
}
};
}
/**
* @module commutable
*/
import * as Immutable from "immutable";
import uuid from "uuid/v4";
import * as Immutable from "immutable";

@@ -18,3 +18,5 @@ export type ExecutionCount = number | null;

export type CellId = string;
export const createCellId = (): CellId => uuid();
export function createCellId(): CellId {
return uuid();
}

@@ -33,1 +35,172 @@ // On disk multi-line strings are used to accomodate line-by-line diffs in tools

export type ImmutableJSONList = Immutable.List<any>;
/**
* Media Bundles as they exist on disk from the notebook format
* See https://nbformat.readthedocs.io/en/latest/format_description.html#display-data for docs
* and https://github.com/jupyter/nbformat/blob/master/nbformat/v4/nbformat.v4.schema.json for the schema
*/
export interface OnDiskMediaBundle {
"text/plain"?: MultiLineString;
"text/html"?: MultiLineString;
"text/latex"?: MultiLineString;
"text/markdown"?: MultiLineString;
"application/javascript"?: MultiLineString;
"image/png"?: MultiLineString;
"image/jpeg"?: MultiLineString;
"image/gif"?: MultiLineString;
"image/svg+xml"?: MultiLineString;
// The JSON mimetype has some corner cases because of the protocol / format assuming the values
// in a media bundle are either:
//
// * A string; which would be deserialized
// * An array; which would have to be assumed to be a multiline string
//
"application/json"?: string | string[] | {};
"application/vdom.v1+json"?: {};
"application/vnd.dataresource+json"?: {};
"text/vnd.plotly.v1+html"?: MultiLineString | {};
"application/vnd.plotly.v1+json"?: {};
"application/geo+json"?: {};
"application/x-nteract-model-debug+json"?: {};
"application/vnd.vega.v2+json"?: {};
"application/vnd.vega.v3+json"?: {};
"application/vnd.vegalite.v1+json"?: {};
"application/vnd.vegalite.v2+json"?: {};
[key: string]: string | string[] | {} | undefined;
}
// Enumerating over all the media types we currently accept
export interface MediaBundle {
"text/plain"?: string;
"text/html"?: string;
"text/latex"?: string;
"text/markdown"?: string;
"application/javascript"?: string;
"image/png"?: string;
"image/jpeg"?: string;
"image/gif"?: string;
"image/svg+xml"?: string;
// All our JSON types can only be JSON Objects
"application/json"?: { [key: string]: any };
"application/vdom.v1+json"?: { [key: string]: any };
"application/vnd.dataresource+json"?: { [key: string]: any };
"text/vnd.plotly.v1+html"?: string | { [key: string]: any };
"application/vnd.plotly.v1+json"?: { [key: string]: any };
"application/geo+json"?: { [key: string]: any };
"application/x-nteract-model-debug+json"?: { [key: string]: any };
"application/vnd.vega.v2+json"?: { [key: string]: any };
"application/vnd.vega.v3+json"?: { [key: string]: any };
"application/vnd.vegalite.v1+json"?: { [key: string]: any };
"application/vnd.vegalite.v2+json"?: { [key: string]: any };
// Other media types can also come in that we don't recognize
[key: string]: string | string[] | {} | undefined;
}
/**
* Turn nbformat multiline strings (arrays of strings for simplifying diffs) into strings
*/
export function demultiline(s: string | string[]): string {
if (Array.isArray(s)) {
return s.join("");
}
return s;
}
/**
* Split string into a list of strings delimited by newlines; useful for on-disk git comparisons;
* and is the expectation for jupyter notebooks on disk
*/
export function remultiline(s: string | string[]): string[] {
if (Array.isArray(s)) {
// Assume
return s;
}
// Use positive lookahead regex to split on newline and retain newline char
return s.split(/(.+?(?:\r\n|\n))/g).filter(x => x !== "");
}
function isJSONKey(key: string) {
return /^application\/(.*\+)json$/.test(key);
}
// A type with all ownPropertyNames also readonly; works for all JSON types
type DeepReadonly<T> = { readonly [P in keyof T]: DeepReadonly<T[P]> };
// Taken from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze
export function deepFreeze<T>(object: T): DeepReadonly<T> {
// Retrieve the property names defined on object
const propNames = Object.getOwnPropertyNames(object);
// Freeze properties before freezing self
for (const name of propNames) {
// getOwnPropertyNames assures us we can index on name
const value = (object as any)[name];
(object as any)[name] =
value && typeof value === "object" ? deepFreeze(value) : value;
}
return (Object.freeze(object) as unknown) as DeepReadonly<T>;
}
export function createFrozenMediaBundle(
mediaBundle: OnDiskMediaBundle
): Readonly<MediaBundle> {
// 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"
// }
// Since we have to convert from one type to another that has conflicting types; we need to hand convert it in a way that
// flow is able to verify correctly. The way we do that is create a new object that we declare with the type we want;
// set the keys and values we need; then seal the object with Object.freeze
const bundle: MediaBundle = {};
for (const key in mediaBundle) {
if (
!isJSONKey(key) &&
(typeof mediaBundle[key] === "string" || Array.isArray(mediaBundle[key]))
) {
// Because it's a string; we can't mutate it anyways (and don't have to Object.freeze it)
bundle[key] = demultiline(mediaBundle[key] as MultiLineString);
} else {
// we now know it's an Object of some kind
bundle[key] = deepFreeze(mediaBundle[key]!);
}
}
return Object.freeze(bundle);
}
export function createOnDiskMediaBundle(
mediaBundle: Readonly<MediaBundle>
): OnDiskMediaBundle {
// Technically we could return just the mediaBundle as is
// return mediaBundle;
// However for the sake of on-disk readability we write out remultilined versions of the array and string ones
const freshBundle: OnDiskMediaBundle = {};
for (const key in mediaBundle) {
if (
!isJSONKey(key) &&
(typeof mediaBundle[key] === "string" || Array.isArray(mediaBundle[key]))
) {
freshBundle[key] = remultiline(mediaBundle[key] as MultiLineString);
} else {
freshBundle[key] = mediaBundle[key];
}
}
return freshBundle;
}

@@ -8,7 +8,7 @@ /**

import { makeNotebookRecord, ImmutableNotebook } from "./notebook";
import { ImmutableNotebook, makeNotebookRecord } from "./notebook";
import { makeCodeCell, makeMarkdownCell, ImmutableCell } from "./cells";
import { ImmutableCell, makeCodeCell, makeMarkdownCell } from "./cells";
import { Map as ImmutableMap, List as ImmutableList } from "immutable";
import { List as ImmutableList, Map as ImmutableMap } from "immutable";

@@ -29,38 +29,40 @@ // The cell creators here are a bit duplicative

export type CellStructure = {
export interface CellStructure {
cellOrder: ImmutableList<CellId>;
cellMap: ImmutableMap<CellId, ImmutableCell>;
};
}
/**
* 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 = (
export function appendCell(
cellStructure: CellStructure,
immutableCell: ImmutableCell,
id: CellId = createCellId()
): CellStructure => ({
cellOrder: cellStructure.cellOrder.push(id),
cellMap: cellStructure.cellMap.set(id, immutableCell)
});
): CellStructure {
return {
cellOrder: cellStructure.cellOrder.push(id),
cellMap: cellStructure.cellMap.set(id, immutableCell)
};
}
/**
* 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 = (
export function appendCellToNotebook(
immnb: ImmutableNotebook,
immCell: ImmutableCell
): ImmutableNotebook =>
immnb.withMutations(nb => {
): ImmutableNotebook {
return immnb.withMutations(nb => {
const cellStructure: CellStructure = {

@@ -73,14 +75,15 @@ cellOrder: nb.get("cellOrder"),

});
}
/**
* 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 = (
/**
* 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 function insertCellAt(
notebook: ImmutableNotebook,

@@ -90,4 +93,4 @@ cell: ImmutableCell,

index: number
): ImmutableNotebook =>
notebook.withMutations(nb =>
): ImmutableNotebook {
return notebook.withMutations(nb =>
nb

@@ -97,2 +100,3 @@ .setIn(["cellMap", cellId], cell)

);
}

@@ -102,3 +106,3 @@ /**

* in the notebook.
*
*
* @param notebook The notebook the cell will be inserted into.

@@ -109,3 +113,3 @@ * @param cell The cell that will be inserted

*/
export const insertCellAfter = (
export function insertCellAfter(
notebook: ImmutableNotebook,

@@ -115,4 +119,4 @@ cell: ImmutableCell,

priorCellId: string
): ImmutableNotebook =>
insertCellAt(
): ImmutableNotebook {
return insertCellAt(
notebook,

@@ -123,18 +127,20 @@ cell,

);
}
/**
* Delete a cell with CellID at a given location. Note that this function
* is deprecated in favor of `deleteCell`.
*
* Deprecated: 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 const removeCell = (
export function removeCell(
notebook: ImmutableNotebook,
cellId: string
): ImmutableNotebook => {
): ImmutableNotebook {
console.log(

@@ -145,22 +151,23 @@ "Deprecation Warning: removeCell() is being deprecated. Please use deleteCell() instead"

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 = (
export function deleteCell(
notebook: ImmutableNotebook,
cellId: string
): ImmutableNotebook =>
notebook
): ImmutableNotebook {
return 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
* A new 'monocell' notebook with a single empty code cell. This function is useful
* if you are looking to initialize a fresh, new notebook.

@@ -167,0 +174,0 @@ */

@@ -5,8 +5,15 @@ /**

import {
Map as ImmutableMap,
fromJS as immutableFromJS,
List as ImmutableList
List as ImmutableList,
Map as ImmutableMap
} from "immutable";
import { MultiLineString, JSONObject } from "./primitives";
import {
CellId,
createFrozenMediaBundle,
demultiline,
JSONObject,
MediaBundle,
MultiLineString
} from "./primitives";

@@ -16,2 +23,3 @@ import { makeNotebookRecord } from "./notebook";

import {
ImmutableCell,
ImmutableCodeCell,

@@ -21,4 +29,4 @@ ImmutableMarkdownCell,

makeCodeCell,
makeRawCell,
makeMarkdownCell
makeMarkdownCell,
makeRawCell
} from "./cells";

@@ -28,14 +36,11 @@

ImmutableOutput,
ImmutableMimeBundle,
makeDisplayData,
makeErrorOutput,
makeExecuteResult,
makeDisplayData,
makeStreamOutput,
makeErrorOutput,
demultiline,
cleanMimeAtKey,
ErrorOutput
OnDiskErrorOutput
} from "./outputs";
import { CellStructure, appendCell } from "./structures";
import { RawCell, MarkdownCell } from "./v4";
import { appendCell, CellStructure } from "./structures";
import { MarkdownCell, RawCell } from "./v4";

@@ -64,2 +69,8 @@ const VALID_MIMETYPES = {

export interface DisplayData extends MimeOutput<"display_data"> {}
export interface ErrorOutput {
output_type: "error" | "pyerr";
ename: string;
evalue: string;
traceback: string[];
}

@@ -88,3 +99,3 @@ export interface StreamOutput {

prompt_number: number;
outputs: Array<Output>;
outputs: Output[];
}

@@ -99,3 +110,3 @@

export type Notebook = {
export interface NotebookV3 {
worksheets: Worksheet[];

@@ -105,8 +116,8 @@ metadata: object;

nbformat_minor: number;
};
}
const createImmutableMarkdownCell = (
function createImmutableMarkdownCell(
cell: MarkdownCell
): ImmutableMarkdownCell =>
makeMarkdownCell({
): ImmutableMarkdownCell {
return makeMarkdownCell({
cell_type: cell.cell_type,

@@ -116,19 +127,20 @@ source: demultiline(cell.source),

});
}
const createImmutableMimeBundle = (output: MimeOutput): ImmutableMimeBundle => {
const mimeBundle: { [key: string]: MultiLineString | undefined } = {};
/**
* Handle the old v3 version of the media
*/
function createImmutableMediaBundle(output: MimeOutput): Readonly<MediaBundle> {
const mediaBundle: { [key: string]: MultiLineString | undefined } = {};
for (const key of Object.keys(output)) {
// v3 had non-media types for rich media
if (key in VALID_MIMETYPES) {
mimeBundle[VALID_MIMETYPES[key as MimeTypeKey]] =
mediaBundle[VALID_MIMETYPES[key as MimeTypeKey]] =
output[key as keyof MimePayload];
}
}
return Object.keys(mimeBundle).reduce(
cleanMimeAtKey.bind(null, mimeBundle),
ImmutableMap()
);
};
return createFrozenMediaBundle(mediaBundle);
}
const createImmutableOutput = (output: Output): ImmutableOutput => {
function createImmutableOutput(output: Output): ImmutableOutput {
switch (output.output_type) {

@@ -139,3 +151,3 @@ case "pyout":

// Note strangeness with v4 API
data: createImmutableMimeBundle(output),
data: createImmutableMediaBundle(output),
metadata: immutableFromJS(output.metadata)

@@ -145,3 +157,3 @@ });

return makeDisplayData({
data: createImmutableMimeBundle(output),
data: createImmutableMediaBundle(output),
metadata: immutableFromJS(output.metadata)

@@ -166,6 +178,6 @@ });

}
};
}
const createImmutableCodeCell = (cell: CodeCell): ImmutableCodeCell =>
makeCodeCell({
function createImmutableCodeCell(cell: CodeCell): ImmutableCodeCell {
return makeCodeCell({
cell_type: cell.cell_type,

@@ -177,5 +189,6 @@ source: demultiline(cell.input),

});
}
const createImmutableRawCell = (cell: RawCell): ImmutableRawCell =>
makeRawCell({
function createImmutableRawCell(cell: RawCell): ImmutableRawCell {
return makeRawCell({
cell_type: cell.cell_type,

@@ -185,6 +198,7 @@ source: demultiline(cell.source),

});
}
const createImmutableHeadingCell = (cell: HeadingCell): ImmutableMarkdownCell =>
function createImmutableHeadingCell(cell: HeadingCell): ImmutableMarkdownCell {
// v3 heading cells are just markdown cells in v4+
makeMarkdownCell({
return makeMarkdownCell({
cell_type: "markdown",

@@ -203,4 +217,5 @@ source: Array.isArray(cell.source)

});
}
const createImmutableCell = (cell: Cell) => {
function createImmutableCell(cell: Cell) {
switch (cell.cell_type) {

@@ -218,6 +233,7 @@ case "markdown":

}
};
}
export const fromJS = (notebook: Notebook) => {
if (notebook.nbformat !== 3 || notebook.nbformat_minor < 0) {
export function fromJS(notebook: NotebookV3) {
if (!isNotebookV3(notebook)) {
notebook = notebook as any;
throw new TypeError(

@@ -229,5 +245,5 @@ `Notebook is not a valid v3 notebook. v3 notebooks must be of form 3.x

const starterCellStructure = {
cellOrder: ImmutableList().asMutable(),
cellMap: ImmutableMap().asMutable()
const starterCellStructure: CellStructure = {
cellOrder: ImmutableList<CellId>().asMutable(),
cellMap: ImmutableMap<CellId, ImmutableCell>().asMutable()
};

@@ -240,3 +256,3 @@

(cellStruct, cell) => appendCell(cellStruct, createImmutableCell(cell)),
starterCellStructure as CellStructure
starterCellStructure
)

@@ -253,2 +269,11 @@ )

});
};
}
export function isNotebookV3(value: any): value is NotebookV3 {
return (
value &&
typeof value === "object" &&
value.nbformat === 3 &&
value.nbformat_minor >= 0
);
}

@@ -0,1 +1,3 @@

// Due to the on-disk format needing to be written out in an explicit order, we disable ordering for this file
// tslint:disable:object-literal-sort-keys
/**

@@ -18,41 +20,36 @@ * @module commutable

import {
Map as ImmutableMap,
fromJS as immutableFromJS,
List as ImmutableList,
Map as ImmutableMap,
Set as ImmutableSet
} from "immutable";
import { ImmutableNotebook, makeNotebookRecord } from "./notebook";
import {
makeNotebookRecord,
ImmutableNotebook,
NotebookRecordParams
} from "./notebook";
CellId,
createFrozenMediaBundle,
createOnDiskMediaBundle,
demultiline,
ExecutionCount,
JSONObject,
MultiLineString,
OnDiskMediaBundle,
remultiline
} from "./primitives";
import { JSONObject, MultiLineString, ExecutionCount } from "./primitives";
import {
ImmutableCell,
ImmutableCodeCell,
ImmutableMarkdownCell,
ImmutableRawCell,
ImmutableCell,
makeCodeCell,
makeRawCell,
makeMarkdownCell
makeMarkdownCell,
makeRawCell
} from "./cells";
import {
createImmutableMimeBundle,
createImmutableOutput,
ImmutableOutput,
makeExecuteResult,
makeDisplayData,
makeStreamOutput,
makeErrorOutput,
demultiline,
remultiline,
isJSONKey,
MimeBundle,
ImmutableMimeBundle,
Output,
StreamOutput,
createImmutableOutput
OnDiskOutput
} from "./outputs";

@@ -62,3 +59,3 @@

/** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Cell Types

@@ -72,3 +69,3 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

source: MultiLineString;
outputs: Output[];
outputs: OnDiskOutput[];
}

@@ -90,8 +87,8 @@

export type Notebook = {
cells: Array<Cell>;
metadata: Object;
export interface NotebookV4 {
cells: Cell[];
metadata: JSONObject;
nbformat: 4;
nbformat_minor: number;
};
}

@@ -105,4 +102,4 @@ /**

*/
const createImmutableMetadata = (metadata: JSONObject) =>
ImmutableMap(metadata).map((v, k: string) => {
function createImmutableMetadata(metadata: JSONObject) {
return ImmutableMap(metadata).map((v, k: string) => {
if (k !== "tags") {

@@ -119,5 +116,6 @@ return v;

});
}
const createImmutableRawCell = (cell: RawCell): ImmutableRawCell =>
makeRawCell({
function createImmutableRawCell(cell: RawCell): ImmutableRawCell {
return makeRawCell({
cell_type: cell.cell_type,

@@ -127,7 +125,8 @@ source: demultiline(cell.source),

});
}
const createImmutableMarkdownCell = (
function createImmutableMarkdownCell(
cell: MarkdownCell
): ImmutableMarkdownCell =>
makeMarkdownCell({
): ImmutableMarkdownCell {
return makeMarkdownCell({
cell_type: cell.cell_type,

@@ -137,5 +136,6 @@ source: demultiline(cell.source),

});
}
const createImmutableCodeCell = (cell: CodeCell): ImmutableCodeCell =>
makeCodeCell({
function createImmutableCodeCell(cell: CodeCell): ImmutableCodeCell {
return makeCodeCell({
cell_type: cell.cell_type,

@@ -147,2 +147,3 @@ source: demultiline(cell.source),

});
}

@@ -157,3 +158,3 @@ /**

*/
const createImmutableCell = (cell: Cell): ImmutableCell => {
function createImmutableCell(cell: Cell): ImmutableCell {
switch (cell.cell_type) {

@@ -169,6 +170,7 @@ case "markdown":

}
};
}
export const fromJS = (notebook: Notebook) => {
if (notebook.nbformat !== 4 || notebook.nbformat_minor < 0) {
export function fromJS(notebook: NotebookV4) {
if (!isNotebookV4(notebook)) {
notebook = notebook as any;
throw new TypeError(

@@ -182,5 +184,5 @@ `Notebook is not a valid v4 notebook. v4 notebooks must be of form 4.x

// switch back after.
const starterCellStructure = {
cellOrder: ImmutableList().asMutable(),
cellMap: ImmutableMap().asMutable()
const starterCellStructure: CellStructure = {
cellOrder: ImmutableList<CellId>().asMutable(),
cellMap: ImmutableMap<CellId, ImmutableCell>().asMutable()
};

@@ -190,3 +192,3 @@

(cellStruct, cell) => appendCell(cellStruct, createImmutableCell(cell)),
starterCellStructure as CellStructure
starterCellStructure
);

@@ -201,33 +203,9 @@

});
};
}
const metadataToJS = (immMetadata: ImmutableMap<string, any>) =>
immMetadata.toJS() as JSONObject;
function metadataToJS(immMetadata: ImmutableMap<string, any>) {
return immMetadata.toJS() as JSONObject;
}
const mimeBundleToJS = (immMimeBundle: ImmutableMimeBundle): MimeBundle => {
const bundle = immMimeBundle.toObject();
Object.keys(bundle).map(key => {
if (isJSONKey(key)) {
if (ImmutableMap.isMap(bundle[key])) {
bundle[key] = bundle[key].toJS();
}
return bundle;
}
const data = bundle[key];
if (typeof data === "string" || Array.isArray(data)) {
bundle[key] = remultiline(data);
return bundle;
}
throw new TypeError(
`Data for ${key} is expected to be a string or an Array of strings`
);
});
return bundle;
};
const outputToJS = (output: ImmutableOutput): Output => {
function outputToJS(output: ImmutableOutput): OnDiskOutput {
switch (output.output_type) {

@@ -238,3 +216,3 @@ case "execute_result":

execution_count: output.execution_count,
data: mimeBundleToJS(output.data),
data: createOnDiskMediaBundle(output.data),
metadata: output.metadata.toJS()

@@ -245,3 +223,3 @@ };

output_type: output.output_type,
data: mimeBundleToJS(output.data),
data: createOnDiskMediaBundle(output.data),
metadata: output.metadata.toJS()

@@ -265,9 +243,11 @@ };

}
};
}
const markdownCellToJS = (immCell: ImmutableMarkdownCell): MarkdownCell => ({
cell_type: "markdown",
source: remultiline(immCell.source),
metadata: metadataToJS(immCell.metadata)
});
function markdownCellToJS(immCell: ImmutableMarkdownCell): MarkdownCell {
return {
cell_type: "markdown",
source: remultiline(immCell.source),
metadata: metadataToJS(immCell.metadata)
};
}

@@ -281,3 +261,3 @@ /**

*/
const codeCellToJS = (immCell: ImmutableCodeCell): CodeCell => {
function codeCellToJS(immCell: ImmutableCodeCell): CodeCell {
return {

@@ -290,3 +270,3 @@ cell_type: "code",

};
};
}

@@ -300,3 +280,3 @@ /**

*/
const rawCellToJS = (immCell: ImmutableRawCell): RawCell => {
function rawCellToJS(immCell: ImmutableRawCell): RawCell {
return {

@@ -307,3 +287,3 @@ cell_type: "raw",

};
};
}

@@ -317,3 +297,3 @@ /**

*/
const cellToJS = (immCell: ImmutableCell): Cell => {
function cellToJS(immCell: ImmutableCell): Cell {
switch (immCell.cell_type) {

@@ -327,5 +307,5 @@ case "markdown":

default:
throw new TypeError(`Cell type unknown at runtime`);
throw new TypeError("Cell type unknown at runtime");
}
};
}

@@ -339,4 +319,4 @@ /**

*/
export const toJS = (immnb: ImmutableNotebook): Notebook => {
const plainNotebook = immnb.toObject() as NotebookRecordParams;
export function toJS(immnb: ImmutableNotebook): NotebookV4 {
const plainNotebook = immnb.toObject();
const plainCellOrder: string[] = plainNotebook.cellOrder.toArray();

@@ -353,6 +333,15 @@ const plainCellMap: {

cells,
metadata: plainNotebook.metadata.toJS(),
metadata: plainNotebook.metadata.toJS() as JSONObject,
nbformat: 4,
nbformat_minor: plainNotebook.nbformat_minor
};
};
}
export function isNotebookV4(value: any): value is NotebookV4 {
return (
value &&
typeof value === "object" &&
value.nbformat === 4 &&
value.nbformat_minor >= 0
);
}
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc