@observablehq/notebook-kit 1.0.1 → 1.1.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/build.js +7 -0
- package/dist/bin/download.js +15 -19
- package/dist/package.json +24 -1
- package/dist/src/databases/duckdb.d.ts +2 -0
- package/dist/src/databases/duckdb.js +67 -0
- package/dist/src/databases/index.d.ts +36 -0
- package/dist/src/databases/index.js +15 -0
- package/dist/src/databases/options.d.ts +3 -0
- package/dist/src/databases/options.js +6 -0
- package/dist/src/databases/postgres.d.ts +2 -0
- package/dist/src/databases/postgres.js +95 -0
- package/dist/src/databases/snowflake.d.ts +2 -0
- package/dist/src/databases/snowflake.js +102 -0
- package/dist/src/javascript/observable.js +24 -3
- package/dist/src/javascript/template.d.ts +3 -0
- package/dist/src/javascript/template.js +17 -1
- package/dist/src/javascript/transpile.d.ts +4 -2
- package/dist/src/javascript/transpile.js +30 -11
- package/dist/src/javascript/transpile.test.js +16 -0
- package/dist/src/lib/error.d.ts +6 -0
- package/dist/src/lib/error.js +6 -0
- package/dist/src/lib/notebook.d.ts +11 -1
- package/dist/src/lib/notebook.js +10 -3
- package/dist/src/lib/notebook.test.js +10 -2
- package/dist/src/lib/serialize.d.ts +3 -1
- package/dist/src/lib/serialize.js +13 -4
- package/dist/src/lib/serialize.test.js +10 -3
- package/dist/src/runtime/define.d.ts +2 -2
- package/dist/src/runtime/define.js +2 -2
- package/dist/src/runtime/display.d.ts +2 -0
- package/dist/src/runtime/display.js +5 -1
- package/dist/src/runtime/index.d.ts +100 -1
- package/dist/src/runtime/index.js +29 -3
- package/dist/src/runtime/stdlib/databaseClient.d.ts +46 -0
- package/dist/src/runtime/stdlib/databaseClient.js +80 -0
- package/dist/src/runtime/stdlib/fileAttachment.d.ts +38 -11
- package/dist/src/runtime/stdlib/fileAttachment.js +21 -10
- package/dist/src/runtime/stdlib/generators/input.d.ts +1 -1
- package/dist/src/runtime/stdlib/index.d.ts +43 -6
- package/dist/src/runtime/stdlib/index.js +5 -5
- package/dist/src/styles/global.css +1 -1
- package/dist/src/templates/default.html +18 -18
- package/dist/src/vite/config.js +1 -0
- package/dist/src/vite/observable.d.ts +25 -6
- package/dist/src/vite/observable.js +58 -15
- package/package.json +24 -1
- package/dist/src/runtime/stdlib/sql.d.ts +0 -5
- package/dist/src/runtime/stdlib/sql.js +0 -5
|
@@ -6,6 +6,13 @@ it("transpiles JavaScript expressions", () => {
|
|
|
6
6
|
expect(transpile("await z", "js")).toMatchSnapshot();
|
|
7
7
|
expect(transpile("display(1), display(2)", "js")).toMatchSnapshot();
|
|
8
8
|
});
|
|
9
|
+
it("transpiles empty cells", () => {
|
|
10
|
+
expect(transpile("", "js")).toMatchSnapshot();
|
|
11
|
+
expect(transpile("", "md")).toMatchSnapshot();
|
|
12
|
+
expect(transpile("", "html")).toMatchSnapshot();
|
|
13
|
+
expect(transpile("", "tex")).toMatchSnapshot();
|
|
14
|
+
expect(transpile("", "sql")).toMatchSnapshot();
|
|
15
|
+
});
|
|
9
16
|
it("transpiles JavaScript programs", () => {
|
|
10
17
|
expect(transpile("const x = 1, y = 2;", "js")).toMatchSnapshot();
|
|
11
18
|
expect(transpile("x + y;", "js")).toMatchSnapshot();
|
|
@@ -21,6 +28,15 @@ it("transpiles dynamic npm: imports", () => {
|
|
|
21
28
|
});
|
|
22
29
|
it("transpiles static observable: imports", () => {
|
|
23
30
|
expect(transpile('import {Scrubber} from "observable:@mbostock/scrubber";', "js")).toMatchSnapshot();
|
|
31
|
+
expect(transpile('import {viewof$rotation} from "observable:@rreusser/drawing-3d-objects-with-svg";', "js")).toMatchSnapshot();
|
|
32
|
+
});
|
|
33
|
+
it("transpiles static imports with {type: 'observable'}", () => {
|
|
34
|
+
expect(transpile('import {Scrubber} from "https://api.observablehq.com/@mbostock/scrubber.js?v=4" with {type: "observable"};', "js")).toMatchSnapshot();
|
|
35
|
+
expect(transpile('import {viewof$rotation} from "https://api.observablehq.com/@rreusser/drawing-3d-objects-with-svg.js?v=4" with {type: "observable"};', "js")).toMatchSnapshot();
|
|
36
|
+
});
|
|
37
|
+
it("transpiles Observable JavaScript imports", () => {
|
|
38
|
+
expect(transpile('import {figure, viewof rotation} from "@rreusser/drawing-3d-objects-with-svg"', "ojs")).toMatchSnapshot();
|
|
39
|
+
expect(transpile('import {figure, viewof rotation as rot} from "@rreusser/drawing-3d-objects-with-svg"', "ojs")).toMatchSnapshot();
|
|
24
40
|
});
|
|
25
41
|
it("transpiles import.meta.resolve", () => {
|
|
26
42
|
expect(transpile('import.meta.resolve("npm:d3")', "js")).toMatchSnapshot();
|
|
@@ -24,12 +24,22 @@ export interface CellSpec {
|
|
|
24
24
|
mode?: "js" | "ojs" | "md" | "html" | "tex" | "dot" | "sql";
|
|
25
25
|
/** if true, the editor will stay open when not focused; defaults to false */
|
|
26
26
|
pinned?: boolean;
|
|
27
|
+
/** if true, implicit display will be suppressed; defaults to false */
|
|
28
|
+
hidden?: boolean;
|
|
29
|
+
/** if present, exposes the cell’s value to the rest of the notebook */
|
|
30
|
+
output?: string;
|
|
31
|
+
/** for SQL cells, the database to query; use var:<name> to refer to a variable */
|
|
32
|
+
database?: string;
|
|
33
|
+
/** for SQL cells, the oldest allowable age of the cached query result */
|
|
34
|
+
since?: Date | string | number;
|
|
27
35
|
}
|
|
28
36
|
export interface Cell extends CellSpec {
|
|
29
37
|
value: NonNullable<CellSpec["value"]>;
|
|
30
38
|
mode: NonNullable<CellSpec["mode"]>;
|
|
31
39
|
pinned: NonNullable<CellSpec["pinned"]>;
|
|
40
|
+
hidden: NonNullable<CellSpec["hidden"]>;
|
|
41
|
+
since?: Date;
|
|
32
42
|
}
|
|
33
43
|
export declare function toNotebook({ cells, title, theme, readOnly }: NotebookSpec): Notebook;
|
|
34
|
-
export declare function toCell({ id, value, mode, pinned }: CellSpec): Cell;
|
|
44
|
+
export declare function toCell({ id, value, mode, pinned, hidden, output, database, since }: CellSpec): Cell;
|
|
35
45
|
export declare function defaultPinned(mode: Cell["mode"]): boolean;
|
package/dist/src/lib/notebook.js
CHANGED
|
@@ -6,14 +6,21 @@ export function toNotebook({ cells = [], title = "Untitled", theme = "air", read
|
|
|
6
6
|
readOnly
|
|
7
7
|
};
|
|
8
8
|
}
|
|
9
|
-
export function toCell({ id, value = "", mode = "js", pinned = defaultPinned(mode) }) {
|
|
9
|
+
export function toCell({ id, value = "", mode = "js", pinned = defaultPinned(mode), hidden = false, output, database = mode === "sql" ? "var:db" : undefined, since }) {
|
|
10
10
|
return {
|
|
11
11
|
id,
|
|
12
12
|
value,
|
|
13
13
|
mode,
|
|
14
|
-
pinned
|
|
14
|
+
pinned,
|
|
15
|
+
hidden,
|
|
16
|
+
output,
|
|
17
|
+
database: mode === "sql" ? database : undefined,
|
|
18
|
+
since: since !== undefined ? asDate(since) : undefined
|
|
15
19
|
};
|
|
16
20
|
}
|
|
21
|
+
function asDate(date) {
|
|
22
|
+
return date instanceof Date ? date : new Date(date);
|
|
23
|
+
}
|
|
17
24
|
export function defaultPinned(mode) {
|
|
18
|
-
return mode === "js" || mode === "ojs";
|
|
25
|
+
return mode === "js" || mode === "sql" || mode === "ojs";
|
|
19
26
|
}
|
|
@@ -13,7 +13,11 @@ test("converts a cell spec to a cell", () => {
|
|
|
13
13
|
id: 1,
|
|
14
14
|
value: "",
|
|
15
15
|
mode: "js",
|
|
16
|
-
pinned: true
|
|
16
|
+
pinned: true,
|
|
17
|
+
hidden: false,
|
|
18
|
+
output: undefined,
|
|
19
|
+
database: undefined,
|
|
20
|
+
since: undefined
|
|
17
21
|
});
|
|
18
22
|
});
|
|
19
23
|
test("computes an appropriate default pinned based on the cell mode", () => {
|
|
@@ -21,6 +25,10 @@ test("computes an appropriate default pinned based on the cell mode", () => {
|
|
|
21
25
|
id: 1,
|
|
22
26
|
value: "",
|
|
23
27
|
mode: "md",
|
|
24
|
-
pinned: false
|
|
28
|
+
pinned: false,
|
|
29
|
+
hidden: false,
|
|
30
|
+
output: undefined,
|
|
31
|
+
database: undefined,
|
|
32
|
+
since: undefined
|
|
25
33
|
});
|
|
26
34
|
});
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { Notebook } from "./notebook.js";
|
|
2
|
-
export declare function serialize(notebook: Notebook
|
|
2
|
+
export declare function serialize(notebook: Notebook, { document }?: {
|
|
3
|
+
document?: Document | undefined;
|
|
4
|
+
}): string;
|
|
3
5
|
export declare function deserialize(data: string, { parser }?: {
|
|
4
6
|
parser?: DOMParser | undefined;
|
|
5
7
|
}): Notebook;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { toNotebook } from "./notebook.js";
|
|
2
2
|
import { isEmpty } from "./text.js";
|
|
3
|
-
export function serialize(notebook) {
|
|
3
|
+
export function serialize(notebook, { document = globalThis.document } = {}) {
|
|
4
4
|
const _notebook = document.createElement("notebook");
|
|
5
5
|
_notebook.setAttribute("theme", notebook.theme);
|
|
6
6
|
if (notebook.readOnly)
|
|
@@ -17,6 +17,12 @@ export function serialize(notebook) {
|
|
|
17
17
|
_cell.textContent = indent(cell.value.replace(/<(?=\\*\/script(\s|>))/gi, "<\\"));
|
|
18
18
|
if (cell.pinned)
|
|
19
19
|
_cell.setAttribute("pinned", "");
|
|
20
|
+
if (cell.hidden)
|
|
21
|
+
_cell.setAttribute("hidden", "");
|
|
22
|
+
if (cell.database)
|
|
23
|
+
_cell.setAttribute("database", cell.database);
|
|
24
|
+
if (cell.output)
|
|
25
|
+
_cell.setAttribute("output", cell.output);
|
|
20
26
|
_notebook.appendChild(_cell);
|
|
21
27
|
}
|
|
22
28
|
_notebook.appendChild(document.createTextNode("\n"));
|
|
@@ -37,10 +43,13 @@ export function deserialize(data, { parser = new DOMParser() } = {}) {
|
|
|
37
43
|
else if (id > maxCellId)
|
|
38
44
|
maxCellId = id;
|
|
39
45
|
cellIds.add(id);
|
|
40
|
-
const pinned = cell.hasAttribute("pinned");
|
|
41
|
-
const value = dedent(cell.textContent?.replace(/<\\(?=\\*\/script(\s|>))/gi, "<") ?? "");
|
|
42
46
|
const mode = deserializeMode(cell.getAttribute("type"));
|
|
43
|
-
|
|
47
|
+
const value = dedent(cell.textContent?.replace(/<\\(?=\\*\/script(\s|>))/gi, "<") ?? "");
|
|
48
|
+
const pinned = cell.hasAttribute("pinned");
|
|
49
|
+
const hidden = cell.hasAttribute("hidden");
|
|
50
|
+
const output = cell.getAttribute("output") ?? undefined;
|
|
51
|
+
const database = cell.getAttribute("database") ?? undefined;
|
|
52
|
+
return { id, mode, value, pinned, hidden, output, database };
|
|
44
53
|
});
|
|
45
54
|
return toNotebook({ title, theme, readOnly, cells });
|
|
46
55
|
}
|
|
@@ -1,7 +1,14 @@
|
|
|
1
|
-
|
|
1
|
+
import { JSDOM } from "jsdom";
|
|
2
2
|
import { assert, test } from "vitest";
|
|
3
3
|
import { toNotebook } from "./notebook.js";
|
|
4
|
-
import { deserialize, serialize } from "./serialize.js";
|
|
4
|
+
import { deserialize as _deserialize, serialize as _serialize } from "./serialize.js";
|
|
5
|
+
const { window } = new JSDOM();
|
|
6
|
+
function serialize(notebook) {
|
|
7
|
+
return _serialize(notebook, { document: window.document });
|
|
8
|
+
}
|
|
9
|
+
function deserialize(data) {
|
|
10
|
+
return _deserialize(data, { parser: new window.DOMParser() });
|
|
11
|
+
}
|
|
5
12
|
test("serializes unpinned cells", () => {
|
|
6
13
|
const notebook1 = toNotebook({
|
|
7
14
|
cells: [
|
|
@@ -84,7 +91,7 @@ test("serialization escapes </script>, in various forms", () => {
|
|
|
84
91
|
{ id: 6, mode: "js", pinned: true, value: `'<\\/script>'` },
|
|
85
92
|
{ id: 7, mode: "js", pinned: true, value: `'<\\/script '` },
|
|
86
93
|
{ id: 8, mode: "js", pinned: true, value: `'<\\\\/SCRIPT '` },
|
|
87
|
-
{ id: 9, mode: "js", pinned: true, value: `'<\\\\/sCrIpT '` }
|
|
94
|
+
{ id: 9, mode: "js", pinned: true, value: `'<\\\\/sCrIpT '` }
|
|
88
95
|
]
|
|
89
96
|
});
|
|
90
97
|
const html = serialize(notebook1);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Variable, VariableDefinition } from "@observablehq/runtime";
|
|
1
|
+
import type { Module, Variable, VariableDefinition } from "@observablehq/runtime";
|
|
2
2
|
import type { DisplayState } from "./display.js";
|
|
3
3
|
import { observe } from "./display.js";
|
|
4
4
|
export type DefineState = DisplayState & {
|
|
@@ -25,4 +25,4 @@ export type Definition = {
|
|
|
25
25
|
/** an asset mapping to apply to any autodisplayed assets (e.g., images and videos) */
|
|
26
26
|
assets?: Map<string, string>;
|
|
27
27
|
};
|
|
28
|
-
export declare function define(state: DefineState, definition: Definition, observer?: typeof observe): void;
|
|
28
|
+
export declare function define(main: Module, state: DefineState, definition: Definition, observer?: typeof observe): void;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { clear, display, observe } from "./display.js";
|
|
2
|
-
import { main } from "./index.js";
|
|
3
2
|
import { input } from "./stdlib/generators/index.js";
|
|
4
3
|
import { Mutator } from "./stdlib/mutable.js";
|
|
5
|
-
export function define(state, definition, observer = observe) {
|
|
4
|
+
export function define(main, state, definition, observer = observe) {
|
|
6
5
|
const { id, body, inputs = [], outputs = [], output, autodisplay, autoview, automutable } = definition;
|
|
7
6
|
const variables = state.variables;
|
|
8
7
|
const v = main.variable(observer(state, definition), { shadow: {} });
|
|
9
8
|
const vid = output ?? (outputs.length ? `cell ${id}` : null);
|
|
9
|
+
state.autoclear = true;
|
|
10
10
|
if (inputs.includes("display") || inputs.includes("view")) {
|
|
11
11
|
let displayVersion = -1; // the variable._version of currently-displayed values
|
|
12
12
|
const vd = new v.constructor(2, v._module);
|
|
@@ -2,6 +2,8 @@ import type { Definition } from "./define.js";
|
|
|
2
2
|
export type DisplayState = {
|
|
3
3
|
/** the HTML element in which to render this cell’s display */
|
|
4
4
|
root: HTMLDivElement;
|
|
5
|
+
/** whether to clear on fulfilled */
|
|
6
|
+
autoclear?: boolean;
|
|
5
7
|
/** for inspected values, any expanded paths; see getExpanded */
|
|
6
8
|
expanded: (number[][] | undefined)[];
|
|
7
9
|
};
|
|
@@ -28,6 +28,7 @@ function isDisplayable(value, root) {
|
|
|
28
28
|
(!value.parentNode || root.contains(value)));
|
|
29
29
|
}
|
|
30
30
|
export function clear(state) {
|
|
31
|
+
state.autoclear = false;
|
|
31
32
|
state.expanded = Array.from(state.root.childNodes, getExpanded);
|
|
32
33
|
while (state.root.lastChild)
|
|
33
34
|
state.root.lastChild.remove();
|
|
@@ -44,11 +45,14 @@ export function observe(state, { autodisplay, assets }) {
|
|
|
44
45
|
},
|
|
45
46
|
fulfilled(value) {
|
|
46
47
|
if (autodisplay) {
|
|
47
|
-
clear(state);
|
|
48
48
|
if (assets && value instanceof Element)
|
|
49
49
|
mapAssets(value, assets);
|
|
50
|
+
clear(state);
|
|
50
51
|
display(state, value);
|
|
51
52
|
}
|
|
53
|
+
else if (state.autoclear) {
|
|
54
|
+
clear(state);
|
|
55
|
+
}
|
|
52
56
|
},
|
|
53
57
|
rejected(error) {
|
|
54
58
|
console.error(error);
|
|
@@ -1,11 +1,110 @@
|
|
|
1
1
|
import type { Module } from "@observablehq/runtime";
|
|
2
2
|
import { Runtime } from "@observablehq/runtime";
|
|
3
|
+
import type { DefineState, Definition } from "./define.js";
|
|
4
|
+
import type { observe } from "./display.js";
|
|
3
5
|
import { fileAttachments } from "./stdlib/fileAttachment.js";
|
|
4
|
-
export
|
|
6
|
+
export type { DefineState, Definition } from "./define.js";
|
|
5
7
|
export * from "./display.js";
|
|
6
8
|
export * from "./inspect.js";
|
|
7
9
|
export * from "./stdlib/index.js";
|
|
10
|
+
export type * from "./stdlib/databaseClient.js";
|
|
11
|
+
export { DatabaseClient } from "./stdlib/databaseClient.js";
|
|
12
|
+
export type * from "./stdlib/fileAttachment.js";
|
|
13
|
+
export { FileAttachment, registerFile } from "./stdlib/fileAttachment.js";
|
|
14
|
+
export declare class NotebookRuntime {
|
|
15
|
+
readonly runtime: Runtime & {
|
|
16
|
+
fileAttachments: typeof fileAttachments;
|
|
17
|
+
};
|
|
18
|
+
readonly main: Module;
|
|
19
|
+
constructor(builtins?: {
|
|
20
|
+
aapl: () => Promise<any>;
|
|
21
|
+
alphabet: () => Promise<any>;
|
|
22
|
+
cars: () => Promise<any>;
|
|
23
|
+
citywages: () => Promise<any>;
|
|
24
|
+
diamonds: () => Promise<any>;
|
|
25
|
+
flare: () => Promise<any>;
|
|
26
|
+
industries: () => Promise<any>;
|
|
27
|
+
miserables: () => Promise<any>;
|
|
28
|
+
olympians: () => Promise<any>;
|
|
29
|
+
penguins: () => Promise<any>;
|
|
30
|
+
pizza: () => Promise<any>;
|
|
31
|
+
weather: () => Promise<any>;
|
|
32
|
+
_: () => Promise<any>;
|
|
33
|
+
aq: () => Promise<any>;
|
|
34
|
+
Arrow: () => Promise<any>;
|
|
35
|
+
d3: () => Promise<any>;
|
|
36
|
+
dot: () => Promise<import("./stdlib/template.js").RawTemplateRenderer>;
|
|
37
|
+
duckdb: () => Promise<any>;
|
|
38
|
+
DuckDBClient: () => Promise<typeof import("./stdlib/duckdb.js").DuckDBClient>;
|
|
39
|
+
echarts: () => Promise<any>;
|
|
40
|
+
htl: () => Promise<any>;
|
|
41
|
+
html: () => Promise<any>;
|
|
42
|
+
svg: () => Promise<any>;
|
|
43
|
+
Inputs: () => Promise<typeof import("./stdlib/inputs.js")>;
|
|
44
|
+
L: () => Promise<typeof import("./stdlib/leaflet.js")>;
|
|
45
|
+
mapboxgl: () => Promise<any>;
|
|
46
|
+
md: () => Promise<import("./stdlib/template.js").TemplateRenderer>;
|
|
47
|
+
mermaid: () => Promise<import("./stdlib/template.js").AsyncRawTemplateRenderer>;
|
|
48
|
+
Plot: () => Promise<any>;
|
|
49
|
+
React: () => Promise<any>;
|
|
50
|
+
ReactDOM: () => Promise<any>;
|
|
51
|
+
tex: () => Promise<import("./stdlib/template.js").RawTemplateRenderer & {
|
|
52
|
+
options: (options?: any) => import("./stdlib/template.js").RawTemplateRenderer;
|
|
53
|
+
block: import("./stdlib/template.js").RawTemplateRenderer;
|
|
54
|
+
}>;
|
|
55
|
+
topojson: () => Promise<any>;
|
|
56
|
+
vl: () => Promise<any>;
|
|
57
|
+
now: () => AsyncGenerator<number, void, unknown>;
|
|
58
|
+
width: () => AsyncGenerator<number, void, unknown>;
|
|
59
|
+
DatabaseClient: () => {
|
|
60
|
+
(name: string, options?: import("./stdlib/databaseClient.js").QueryOptionsSpec): import("./stdlib/databaseClient.js").DatabaseClient;
|
|
61
|
+
hash: (strings: string[], ...params: unknown[]) => Promise<string>;
|
|
62
|
+
revive: ({ rows, schema, date, ...meta }: import("../databases/index.js").SerializableQueryResult) => import("./stdlib/databaseClient.js").QueryResult;
|
|
63
|
+
prototype: {
|
|
64
|
+
readonly name: string;
|
|
65
|
+
readonly options: import("./stdlib/databaseClient.js").QueryOptions;
|
|
66
|
+
sql(strings: string[], ...params: import("./stdlib/databaseClient.js").QueryParam[]): Promise<import("./stdlib/databaseClient.js").QueryResult>;
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
FileAttachment: () => {
|
|
70
|
+
(name: string, base?: string): import("./stdlib/fileAttachment.js").FileAttachment;
|
|
71
|
+
prototype: {
|
|
72
|
+
href: string;
|
|
73
|
+
name: string;
|
|
74
|
+
mimeType: string;
|
|
75
|
+
lastModified: number | undefined;
|
|
76
|
+
size: number | undefined;
|
|
77
|
+
url(): Promise<string>;
|
|
78
|
+
blob(): Promise<Blob>;
|
|
79
|
+
arrayBuffer(): Promise<ArrayBuffer>;
|
|
80
|
+
text(encoding?: string): Promise<string>;
|
|
81
|
+
json(): Promise<any>;
|
|
82
|
+
stream(): Promise<ReadableStream<Uint8Array<ArrayBufferLike>>>;
|
|
83
|
+
dsv({ delimiter, array, typed }?: {
|
|
84
|
+
delimiter?: string | undefined;
|
|
85
|
+
array?: boolean | undefined;
|
|
86
|
+
typed?: boolean | undefined;
|
|
87
|
+
}): Promise<import("./stdlib/fileAttachment.js").DsvResult>;
|
|
88
|
+
csv(options?: Omit<import("./stdlib/fileAttachment.js").DsvOptions, "delimiter">): Promise<import("./stdlib/fileAttachment.js").DsvResult>;
|
|
89
|
+
tsv(options?: Omit<import("./stdlib/fileAttachment.js").DsvOptions, "delimiter">): Promise<import("./stdlib/fileAttachment.js").DsvResult>;
|
|
90
|
+
image(props?: Partial<HTMLImageElement>): Promise<HTMLImageElement>;
|
|
91
|
+
arrow(): Promise<any>;
|
|
92
|
+
arquero(options?: any): Promise<any>;
|
|
93
|
+
parquet(): Promise<any>;
|
|
94
|
+
xml(mimeType?: DOMParserSupportedType): Promise<Document>;
|
|
95
|
+
html(): Promise<Document>;
|
|
96
|
+
};
|
|
97
|
+
};
|
|
98
|
+
Generators: () => typeof import("./stdlib/generators/index.js");
|
|
99
|
+
Mutable: () => typeof import("./stdlib/mutable.js").Mutable;
|
|
100
|
+
DOM: () => typeof import("./stdlib/dom/index.js");
|
|
101
|
+
require: () => typeof import("./stdlib/require.js").require;
|
|
102
|
+
__ojs_observer: () => () => import("./stdlib/observer.js").Observer;
|
|
103
|
+
});
|
|
104
|
+
define(state: DefineState, definition: Definition, observer?: typeof observe): void;
|
|
105
|
+
}
|
|
8
106
|
export declare const runtime: Runtime & {
|
|
9
107
|
fileAttachments: typeof fileAttachments;
|
|
10
108
|
};
|
|
11
109
|
export declare const main: Module;
|
|
110
|
+
export declare const define: (state: DefineState, definition: Definition, observer?: typeof observe) => void;
|
|
@@ -1,12 +1,38 @@
|
|
|
1
1
|
import { Runtime } from "@observablehq/runtime";
|
|
2
|
+
import { define as _define } from "./define.js";
|
|
2
3
|
import { fileAttachments } from "./stdlib/fileAttachment.js";
|
|
3
4
|
import { library } from "./stdlib/index.js";
|
|
4
|
-
export * from "./define.js";
|
|
5
5
|
export * from "./display.js";
|
|
6
6
|
export * from "./inspect.js";
|
|
7
7
|
export * from "./stdlib/index.js";
|
|
8
|
-
export
|
|
9
|
-
export
|
|
8
|
+
export { DatabaseClient } from "./stdlib/databaseClient.js";
|
|
9
|
+
export { FileAttachment, registerFile } from "./stdlib/fileAttachment.js";
|
|
10
|
+
export class NotebookRuntime {
|
|
11
|
+
constructor(builtins = library) {
|
|
12
|
+
Object.defineProperty(this, "runtime", {
|
|
13
|
+
enumerable: true,
|
|
14
|
+
configurable: true,
|
|
15
|
+
writable: true,
|
|
16
|
+
value: void 0
|
|
17
|
+
});
|
|
18
|
+
Object.defineProperty(this, "main", {
|
|
19
|
+
enumerable: true,
|
|
20
|
+
configurable: true,
|
|
21
|
+
writable: true,
|
|
22
|
+
value: void 0
|
|
23
|
+
});
|
|
24
|
+
const runtime = new Runtime({ ...builtins, __ojs_runtime: () => runtime });
|
|
25
|
+
this.runtime = Object.assign(runtime, { fileAttachments });
|
|
26
|
+
this.main = runtime.module();
|
|
27
|
+
}
|
|
28
|
+
define(state, definition, observer) {
|
|
29
|
+
_define(this.main, state, definition, observer);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const defaultNotebook = new NotebookRuntime();
|
|
33
|
+
export const runtime = defaultNotebook.runtime;
|
|
34
|
+
export const main = defaultNotebook.main;
|
|
35
|
+
export const define = defaultNotebook.define.bind(defaultNotebook);
|
|
10
36
|
main.constructor.prototype.defines = function (name) {
|
|
11
37
|
return (this._scope.has(name) ||
|
|
12
38
|
this._builtins.has(name) ||
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { SerializableQueryResult } from "../../databases/index.js";
|
|
2
|
+
/** A serializable value that can be interpolated into a query. */
|
|
3
|
+
export type QueryParam = any;
|
|
4
|
+
/** @see https://observablehq.com/@observablehq/database-client-specification#%C2%A71 */
|
|
5
|
+
export type QueryResult = Record<string, any>[] & {
|
|
6
|
+
schema: ColumnSchema[];
|
|
7
|
+
date: Date;
|
|
8
|
+
};
|
|
9
|
+
/** @see https://observablehq.com/@observablehq/database-client-specification#%C2%A72.2 */
|
|
10
|
+
export interface ColumnSchema {
|
|
11
|
+
/** The name of the column. */
|
|
12
|
+
name: string;
|
|
13
|
+
/** The type of the column. */
|
|
14
|
+
type: "string" | "number" | "integer" | "bigint" | "date" | "boolean" | "object" | "array" | "buffer" | "other";
|
|
15
|
+
/** If present, the nullability of the column is known. */
|
|
16
|
+
nullable?: boolean;
|
|
17
|
+
}
|
|
18
|
+
export interface QueryOptionsSpec {
|
|
19
|
+
/** if present, the id of the cell that owns this database client */
|
|
20
|
+
id?: number;
|
|
21
|
+
/** if present, query results are at least as fresh as the specified date */
|
|
22
|
+
since?: Date | string | number;
|
|
23
|
+
}
|
|
24
|
+
export interface QueryOptions extends QueryOptionsSpec {
|
|
25
|
+
since?: Date;
|
|
26
|
+
}
|
|
27
|
+
export interface DatabaseClient {
|
|
28
|
+
readonly name: string;
|
|
29
|
+
readonly options: QueryOptions;
|
|
30
|
+
sql(strings: string[], ...params: QueryParam[]): Promise<QueryResult>;
|
|
31
|
+
}
|
|
32
|
+
export declare const DatabaseClient: {
|
|
33
|
+
(name: string, options?: QueryOptionsSpec): DatabaseClient;
|
|
34
|
+
hash: typeof hash;
|
|
35
|
+
revive: typeof revive;
|
|
36
|
+
prototype: DatabaseClientImpl;
|
|
37
|
+
};
|
|
38
|
+
declare class DatabaseClientImpl implements DatabaseClient {
|
|
39
|
+
readonly name: string;
|
|
40
|
+
readonly options: QueryOptions;
|
|
41
|
+
constructor(name: string, options: QueryOptions);
|
|
42
|
+
sql(strings: string[], ...params: QueryParam[]): Promise<QueryResult>;
|
|
43
|
+
}
|
|
44
|
+
declare function hash(strings: string[], ...params: unknown[]): Promise<string>;
|
|
45
|
+
declare function revive({ rows, schema, date, ...meta }: SerializableQueryResult): QueryResult;
|
|
46
|
+
export {};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
export const DatabaseClient = (name, options) => {
|
|
2
|
+
if (!/^[\w-]+$/.test(name))
|
|
3
|
+
throw new Error(`invalid database: ${name}`);
|
|
4
|
+
return new DatabaseClientImpl(name, normalizeOptions(options));
|
|
5
|
+
};
|
|
6
|
+
function normalizeOptions({ id, since } = {}) {
|
|
7
|
+
const options = {};
|
|
8
|
+
if (id !== undefined)
|
|
9
|
+
options.id = id;
|
|
10
|
+
if (since !== undefined)
|
|
11
|
+
options.since = new Date(since);
|
|
12
|
+
return options;
|
|
13
|
+
}
|
|
14
|
+
class DatabaseClientImpl {
|
|
15
|
+
constructor(name, options) {
|
|
16
|
+
Object.defineProperty(this, "name", {
|
|
17
|
+
enumerable: true,
|
|
18
|
+
configurable: true,
|
|
19
|
+
writable: true,
|
|
20
|
+
value: void 0
|
|
21
|
+
});
|
|
22
|
+
Object.defineProperty(this, "options", {
|
|
23
|
+
enumerable: true,
|
|
24
|
+
configurable: true,
|
|
25
|
+
writable: true,
|
|
26
|
+
value: void 0
|
|
27
|
+
});
|
|
28
|
+
Object.defineProperties(this, {
|
|
29
|
+
name: { value: name, enumerable: true },
|
|
30
|
+
options: { value: options, enumerable: true }
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
async sql(strings, ...params) {
|
|
34
|
+
const path = `.observable/cache/${this.name}-${await hash(strings, ...params)}.json`;
|
|
35
|
+
const response = await fetch(path);
|
|
36
|
+
if (!response.ok)
|
|
37
|
+
throw new Error(`failed to fetch: ${path}`);
|
|
38
|
+
return await response.json().then(revive);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
async function hash(strings, ...params) {
|
|
42
|
+
const encoded = new TextEncoder().encode(JSON.stringify([strings, ...params]));
|
|
43
|
+
const buffer = await crypto.subtle.digest("SHA-256", encoded);
|
|
44
|
+
const int = new Uint8Array(buffer).reduce((i, byte) => (i << 8n) | BigInt(byte), 0n);
|
|
45
|
+
const length = 16;
|
|
46
|
+
return int.toString(36).padStart(length, "0").slice(0, length);
|
|
47
|
+
}
|
|
48
|
+
function revive({ rows, schema, date, ...meta }) {
|
|
49
|
+
for (const column of schema) {
|
|
50
|
+
switch (column.type) {
|
|
51
|
+
case "bigint": {
|
|
52
|
+
const { name } = column;
|
|
53
|
+
for (const row of rows) {
|
|
54
|
+
const value = row[name];
|
|
55
|
+
if (value == null)
|
|
56
|
+
continue;
|
|
57
|
+
row[name] = BigInt(value);
|
|
58
|
+
}
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
case "date": {
|
|
62
|
+
const { name } = column;
|
|
63
|
+
for (const row of rows) {
|
|
64
|
+
const value = row[name];
|
|
65
|
+
if (value == null)
|
|
66
|
+
continue;
|
|
67
|
+
row[name] = new Date(value);
|
|
68
|
+
}
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (date != null)
|
|
74
|
+
date = new Date(date);
|
|
75
|
+
return Object.assign(rows, { schema, date }, meta);
|
|
76
|
+
}
|
|
77
|
+
DatabaseClient.hash = hash;
|
|
78
|
+
DatabaseClient.revive = revive;
|
|
79
|
+
DatabaseClient.prototype = DatabaseClientImpl.prototype; // instanceof
|
|
80
|
+
Object.defineProperty(DatabaseClientImpl, "name", { value: "DatabaseClient" }); // prevent mangling
|
|
@@ -3,35 +3,62 @@ export type DsvOptions = {
|
|
|
3
3
|
array?: boolean;
|
|
4
4
|
typed?: boolean;
|
|
5
5
|
};
|
|
6
|
-
export type DsvResult = any[] & {
|
|
6
|
+
export type DsvResult = (Record<string, any>[] | any[][]) & {
|
|
7
7
|
columns: string[];
|
|
8
8
|
};
|
|
9
9
|
export interface FileAttachment {
|
|
10
|
+
/** The URL of the file. */
|
|
11
|
+
href: string;
|
|
12
|
+
/** The name of the file (not including the path), such as "test.csv". */
|
|
10
13
|
name: string;
|
|
14
|
+
/** The MIME type, such as "text/csv". */
|
|
11
15
|
mimeType: string;
|
|
12
|
-
|
|
13
|
-
lastModified
|
|
14
|
-
size
|
|
16
|
+
/** The time this file was most-recently modified, as milliseconds since epoch, if known. */
|
|
17
|
+
lastModified?: number;
|
|
18
|
+
/** The size of this file in bytes, if known. */
|
|
19
|
+
size?: number;
|
|
20
|
+
/** @deprecated use FileAttachment.href instead */
|
|
15
21
|
url(): Promise<string>;
|
|
22
|
+
/** Returns the contents of this file as a Blob. */
|
|
16
23
|
blob(): Promise<Blob>;
|
|
24
|
+
/** Returns the contents of this file as an ArrayBuffer. */
|
|
17
25
|
arrayBuffer(): Promise<ArrayBuffer>;
|
|
26
|
+
/** Returns the contents of this file as a string with the given encoding. */
|
|
18
27
|
text(encoding?: string): Promise<string>;
|
|
28
|
+
/** Returns the contents of this file as JSON. */
|
|
19
29
|
json(): Promise<any>;
|
|
30
|
+
/** Returns a byte stream to the contents of this file. */
|
|
20
31
|
stream(): Promise<ReadableStream<Uint8Array<ArrayBufferLike>>>;
|
|
32
|
+
/** Returns the contents of this file as delimiter-separated values. */
|
|
21
33
|
dsv(options?: DsvOptions): Promise<DsvResult>;
|
|
22
|
-
|
|
23
|
-
|
|
34
|
+
/** Returns the contents of this file as comma-separated values. */
|
|
35
|
+
csv(options?: Omit<DsvOptions, "delimiter">): Promise<DsvResult>;
|
|
36
|
+
/** Returns the contents of this file as tab-separated values. */
|
|
37
|
+
tsv(options?: Omit<DsvOptions, "delimiter">): Promise<DsvResult>;
|
|
38
|
+
/** Returns the contents of this file as an image. */
|
|
24
39
|
image(props?: Partial<HTMLImageElement>): Promise<HTMLImageElement>;
|
|
40
|
+
/** Returns the contents of this Arrow IPC file as an Apache Arrow table. */
|
|
25
41
|
arrow(): Promise<any>;
|
|
42
|
+
/** Returns the contents of this file as an Arquero table. */
|
|
26
43
|
arquero(options?: any): Promise<any>;
|
|
44
|
+
/** Returns the contents of this Parquet file as an Apache Arrow table. */
|
|
27
45
|
parquet(): Promise<any>;
|
|
46
|
+
/** Returns the contents of this file as an XML document. */
|
|
28
47
|
xml(mimeType?: DOMParserSupportedType): Promise<Document>;
|
|
48
|
+
/** Returns the contents of this file as an HTML document. */
|
|
29
49
|
html(): Promise<Document>;
|
|
30
50
|
}
|
|
31
|
-
export declare
|
|
32
|
-
|
|
33
|
-
|
|
51
|
+
export declare const FileAttachment: {
|
|
52
|
+
(name: string, base?: string): FileAttachment;
|
|
53
|
+
prototype: FileAttachmentImpl;
|
|
54
|
+
};
|
|
55
|
+
export interface FileInfo {
|
|
56
|
+
path: string;
|
|
57
|
+
mimeType?: string;
|
|
58
|
+
lastModified?: number;
|
|
59
|
+
size?: number;
|
|
34
60
|
}
|
|
61
|
+
export declare function registerFile(name: string, info: FileInfo, base?: string | URL): FileAttachmentImpl | undefined;
|
|
35
62
|
export declare abstract class AbstractFile implements FileAttachment {
|
|
36
63
|
name: string;
|
|
37
64
|
mimeType: string;
|
|
@@ -50,8 +77,8 @@ export declare abstract class AbstractFile implements FileAttachment {
|
|
|
50
77
|
array?: boolean | undefined;
|
|
51
78
|
typed?: boolean | undefined;
|
|
52
79
|
}): Promise<DsvResult>;
|
|
53
|
-
csv(options
|
|
54
|
-
tsv(options
|
|
80
|
+
csv(options?: Omit<DsvOptions, "delimiter">): Promise<DsvResult>;
|
|
81
|
+
tsv(options?: Omit<DsvOptions, "delimiter">): Promise<DsvResult>;
|
|
55
82
|
image(props?: Partial<HTMLImageElement>): Promise<HTMLImageElement>;
|
|
56
83
|
arrow(): Promise<any>;
|
|
57
84
|
arquero(options?: any): Promise<any>;
|