@observablehq/notebook-kit 1.6.2 → 1.7.0
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/package.json +3 -1
- package/dist/src/runtime/index.d.ts +2 -0
- package/dist/src/runtime/stdlib/fileAttachment.d.ts +2 -0
- package/dist/src/runtime/stdlib/fileAttachment.js +8 -0
- package/dist/src/runtime/stdlib/index.d.ts +2 -0
- package/dist/src/runtime/stdlib/xlsx.d.ts +14 -0
- package/dist/src/runtime/stdlib/xlsx.js +119 -0
- package/dist/src/runtime/stdlib/zip.d.ts +21 -0
- package/dist/src/runtime/stdlib/zip.js +49 -0
- package/package.json +3 -1
package/dist/package.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/observablehq/notebook-kit.git"
|
|
7
7
|
},
|
|
8
|
-
"version": "1.
|
|
8
|
+
"version": "1.7.0",
|
|
9
9
|
"type": "module",
|
|
10
10
|
"scripts": {
|
|
11
11
|
"test": "vitest",
|
|
@@ -73,8 +73,10 @@
|
|
|
73
73
|
"@types/markdown-it": "^14.1.2",
|
|
74
74
|
"bun-types": "^1.2.20",
|
|
75
75
|
"eslint": "^9.29.0",
|
|
76
|
+
"exceljs": "^4.4.0",
|
|
76
77
|
"globals": "^16.2.0",
|
|
77
78
|
"htl": "^0.3.1",
|
|
79
|
+
"jszip": "^3.10.1",
|
|
78
80
|
"postgres": "^3.4.9",
|
|
79
81
|
"snowflake-sdk": "^2.4.0",
|
|
80
82
|
"tsx": "^4.20.3",
|
|
@@ -94,8 +94,10 @@ export declare class NotebookRuntime {
|
|
|
94
94
|
arrow(): Promise<any>;
|
|
95
95
|
arquero(options?: any): Promise<any>;
|
|
96
96
|
parquet(): Promise<any>;
|
|
97
|
+
zip(): Promise<import("./stdlib/zip.js").ZipArchive>;
|
|
97
98
|
xml(mimeType?: DOMParserSupportedType): Promise<Document>;
|
|
98
99
|
html(): Promise<Document>;
|
|
100
|
+
xlsx(): Promise<import("./stdlib/xlsx.js").Workbook>;
|
|
99
101
|
};
|
|
100
102
|
};
|
|
101
103
|
Generators: () => typeof import("./stdlib/generators/index.js");
|
|
@@ -84,8 +84,10 @@ export declare abstract class AbstractFile implements FileAttachment {
|
|
|
84
84
|
arrow(): Promise<any>;
|
|
85
85
|
arquero(options?: any): Promise<any>;
|
|
86
86
|
parquet(): Promise<any>;
|
|
87
|
+
zip(): Promise<import("./zip.js").ZipArchive>;
|
|
87
88
|
xml(mimeType?: DOMParserSupportedType): Promise<Document>;
|
|
88
89
|
html(): Promise<Document>;
|
|
90
|
+
xlsx(): Promise<import("./xlsx.js").Workbook>;
|
|
89
91
|
}
|
|
90
92
|
declare class FileAttachmentImpl extends AbstractFile {
|
|
91
93
|
href: string;
|
|
@@ -155,12 +155,20 @@ export class AbstractFile {
|
|
|
155
155
|
const [Arrow, Parquet, buffer] = await Promise.all([import("https://cdn.jsdelivr.net/npm/apache-arrow@17.0.0/+esm"), import("https://cdn.jsdelivr.net/npm/parquet-wasm/+esm").then(async (Parquet) => (await Parquet.default("https://cdn.jsdelivr.net/npm/parquet-wasm/esm/parquet_wasm_bg.wasm"), Parquet)), this.arrayBuffer()]); // prettier-ignore
|
|
156
156
|
return Arrow.tableFromIPC(Parquet.readParquet(new Uint8Array(buffer)).intoIPCStream());
|
|
157
157
|
}
|
|
158
|
+
async zip() {
|
|
159
|
+
const [{ ZipArchive }, buffer] = await Promise.all([import("./zip.js"), this.arrayBuffer()]);
|
|
160
|
+
return ZipArchive.from(buffer);
|
|
161
|
+
}
|
|
158
162
|
async xml(mimeType = "application/xml") {
|
|
159
163
|
return new DOMParser().parseFromString(await this.text(), mimeType);
|
|
160
164
|
}
|
|
161
165
|
async html() {
|
|
162
166
|
return this.xml("text/html");
|
|
163
167
|
}
|
|
168
|
+
async xlsx() {
|
|
169
|
+
const [{ Workbook }, buffer] = await Promise.all([import("./xlsx.js"), this.arrayBuffer()]);
|
|
170
|
+
return Workbook.load(buffer);
|
|
171
|
+
}
|
|
164
172
|
}
|
|
165
173
|
// TODO Replace this with static analysis of files.
|
|
166
174
|
function guessMimeType(name) {
|
|
@@ -84,8 +84,10 @@ export declare const library: {
|
|
|
84
84
|
arrow(): Promise<any>;
|
|
85
85
|
arquero(options?: any): Promise<any>;
|
|
86
86
|
parquet(): Promise<any>;
|
|
87
|
+
zip(): Promise<import("./zip.js").ZipArchive>;
|
|
87
88
|
xml(mimeType?: DOMParserSupportedType): Promise<Document>;
|
|
88
89
|
html(): Promise<Document>;
|
|
90
|
+
xlsx(): Promise<import("./xlsx.js").Workbook>;
|
|
89
91
|
};
|
|
90
92
|
};
|
|
91
93
|
Generators: () => typeof Generators;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import Excel from "https://cdn.jsdelivr.net/npm/exceljs/+esm";
|
|
2
|
+
export declare class Workbook {
|
|
3
|
+
private readonly _;
|
|
4
|
+
readonly sheetNames: string[];
|
|
5
|
+
constructor(workbook: Excel.Workbook);
|
|
6
|
+
static load(buffer: ArrayBuffer): Promise<Workbook>;
|
|
7
|
+
sheet(name: string | number, options?: ExtractOptions): any[] & {
|
|
8
|
+
columns: any[];
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export interface ExtractOptions {
|
|
12
|
+
range?: string;
|
|
13
|
+
headers?: boolean;
|
|
14
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import Excel from "https://cdn.jsdelivr.net/npm/exceljs/+esm";
|
|
2
|
+
export class Workbook {
|
|
3
|
+
constructor(workbook) {
|
|
4
|
+
Object.defineProperties(this, {
|
|
5
|
+
_: { value: workbook },
|
|
6
|
+
sheetNames: { value: workbook.worksheets.map((s) => s.name), enumerable: true }
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
static async load(buffer) {
|
|
10
|
+
const workbook = new Excel.Workbook();
|
|
11
|
+
await workbook.xlsx.load(buffer);
|
|
12
|
+
return new Workbook(workbook);
|
|
13
|
+
}
|
|
14
|
+
sheet(name, options) {
|
|
15
|
+
const sname = typeof name === "number"
|
|
16
|
+
? this.sheetNames[name]
|
|
17
|
+
: this.sheetNames.includes((name = `${name}`))
|
|
18
|
+
? name
|
|
19
|
+
: null;
|
|
20
|
+
if (sname == null)
|
|
21
|
+
throw new Error(`Sheet not found: ${name}`);
|
|
22
|
+
const sheet = this._.getWorksheet(sname);
|
|
23
|
+
if (!sheet)
|
|
24
|
+
throw new Error(`Sheet not found: ${name}`);
|
|
25
|
+
return extract(sheet, options);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function extract(sheet, { range, headers } = {}) {
|
|
29
|
+
let [[c0, r0], [c1, r1]] = parseRange(range, sheet); // eslint-disable-line prefer-const
|
|
30
|
+
const headerRow = headers ? sheet.getRow(++r0) : null;
|
|
31
|
+
const nameset = new Set(["#"]);
|
|
32
|
+
for (let n = c0; n <= c1; n++) {
|
|
33
|
+
const value = headerRow ? valueOf(headerRow.findCell(n + 1)) : null;
|
|
34
|
+
let name = (value && value + "") || toColumn(n);
|
|
35
|
+
while (nameset.has(name))
|
|
36
|
+
name += "_";
|
|
37
|
+
nameset.add(name);
|
|
38
|
+
}
|
|
39
|
+
const names = new Array(c0).concat(Array.from(nameset));
|
|
40
|
+
const columns = names.filter(() => true); // Filter sparse columns
|
|
41
|
+
const output = Object.assign(new Array(r1 - r0 + 1), { columns });
|
|
42
|
+
for (let r = r0; r <= r1; r++) {
|
|
43
|
+
const row = (output[r - r0] = Object.create(null, { "#": { value: r + 1 } }));
|
|
44
|
+
const _row = sheet.getRow(r + 1);
|
|
45
|
+
if (_row.hasValues)
|
|
46
|
+
for (let c = c0; c <= c1; c++) {
|
|
47
|
+
const value = valueOf(_row.findCell(c + 1));
|
|
48
|
+
if (value != null)
|
|
49
|
+
row[names[c + 1]] = value;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return output;
|
|
53
|
+
}
|
|
54
|
+
function isPrimitive(value) {
|
|
55
|
+
return !value || typeof value !== "object" || value instanceof Date;
|
|
56
|
+
}
|
|
57
|
+
function isFormula(value) {
|
|
58
|
+
return "formula" in value || "sharedFormula" in value;
|
|
59
|
+
}
|
|
60
|
+
function isRichText(value) {
|
|
61
|
+
return "richText" in value;
|
|
62
|
+
}
|
|
63
|
+
function isHyperlink(value) {
|
|
64
|
+
return "hyperlink" in value;
|
|
65
|
+
}
|
|
66
|
+
function valueOf(cell) {
|
|
67
|
+
if (!cell)
|
|
68
|
+
return;
|
|
69
|
+
const { value } = cell;
|
|
70
|
+
if (isPrimitive(value))
|
|
71
|
+
return value;
|
|
72
|
+
if (isFormula(value))
|
|
73
|
+
return isPrimitive(value.result) ? value.result : NaN; // result is error
|
|
74
|
+
if (isRichText(value))
|
|
75
|
+
return richText(value);
|
|
76
|
+
if (isHyperlink(value))
|
|
77
|
+
return hyperlink(value);
|
|
78
|
+
return undefined; // value is error
|
|
79
|
+
}
|
|
80
|
+
function richText(value) {
|
|
81
|
+
return value.richText.map((d) => d.text).join("");
|
|
82
|
+
}
|
|
83
|
+
function hyperlink({ hyperlink, text }) {
|
|
84
|
+
return hyperlink && hyperlink !== text ? `${hyperlink} ${text}` : text;
|
|
85
|
+
}
|
|
86
|
+
function parseRange(specifier = ":", { columnCount, rowCount }) {
|
|
87
|
+
specifier = `${specifier}`;
|
|
88
|
+
if (!specifier.match(/^[A-Z]*\d*:[A-Z]*\d*$/))
|
|
89
|
+
throw new Error("Malformed range specifier");
|
|
90
|
+
const [[c0 = 0, r0 = 0], [c1 = columnCount - 1, r1 = rowCount - 1]] = specifier
|
|
91
|
+
.split(":")
|
|
92
|
+
.map(fromCellReference);
|
|
93
|
+
return [
|
|
94
|
+
[c0, r0],
|
|
95
|
+
[c1, r1]
|
|
96
|
+
];
|
|
97
|
+
}
|
|
98
|
+
// Returns the default column name for a zero-based column index.
|
|
99
|
+
// For example: 0 -> "A", 1 -> "B", 25 -> "Z", 26 -> "AA", 27 -> "AB".
|
|
100
|
+
function toColumn(c) {
|
|
101
|
+
let sc = "";
|
|
102
|
+
c++;
|
|
103
|
+
do
|
|
104
|
+
sc = String.fromCharCode(64 + (c % 26 || 26)) + sc;
|
|
105
|
+
while ((c = Math.floor((c - 1) / 26)));
|
|
106
|
+
return sc;
|
|
107
|
+
}
|
|
108
|
+
// Returns the zero-based indexes from a cell reference.
|
|
109
|
+
// For example: "A1" -> [0, 0], "B2" -> [1, 1], "AA10" -> [26, 9].
|
|
110
|
+
function fromCellReference(specifier) {
|
|
111
|
+
const [, sc, sr] = specifier.match(/^([A-Z]*)(\d*)$/);
|
|
112
|
+
let c = 0;
|
|
113
|
+
if (sc) {
|
|
114
|
+
for (let i = 0; i < sc.length; i++) {
|
|
115
|
+
c += Math.pow(26, sc.length - i - 1) * (sc.charCodeAt(i) - 64);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return [c ? c - 1 : undefined, sr ? +sr - 1 : undefined];
|
|
119
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import JSZip from "https://cdn.jsdelivr.net/npm/jszip/+esm";
|
|
2
|
+
import { AbstractFile } from "./fileAttachment.js";
|
|
3
|
+
export declare class ZipArchive {
|
|
4
|
+
private readonly _;
|
|
5
|
+
readonly filenames: string[];
|
|
6
|
+
constructor(archive: JSZip);
|
|
7
|
+
static from(buffer: ArrayBuffer): Promise<ZipArchive>;
|
|
8
|
+
file(path: string): ZipArchiveEntry;
|
|
9
|
+
}
|
|
10
|
+
declare class ZipArchiveEntry extends AbstractFile {
|
|
11
|
+
href: string;
|
|
12
|
+
private readonly _;
|
|
13
|
+
private _url;
|
|
14
|
+
constructor(object: JSZip.JSZipObject);
|
|
15
|
+
url(): Promise<string>;
|
|
16
|
+
blob(): Promise<Blob>;
|
|
17
|
+
arrayBuffer(): Promise<ArrayBuffer>;
|
|
18
|
+
text(): Promise<string>;
|
|
19
|
+
json(): Promise<any>;
|
|
20
|
+
}
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import JSZip from "https://cdn.jsdelivr.net/npm/jszip/+esm";
|
|
2
|
+
import { AbstractFile } from "./fileAttachment.js";
|
|
3
|
+
export class ZipArchive {
|
|
4
|
+
constructor(archive) {
|
|
5
|
+
Object.defineProperties(this, {
|
|
6
|
+
_: { value: archive },
|
|
7
|
+
filenames: { value: Object.keys(archive.files).filter((name) => !archive.files[name].dir) }
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
static async from(buffer) {
|
|
11
|
+
return new ZipArchive(await JSZip.loadAsync(buffer));
|
|
12
|
+
}
|
|
13
|
+
file(path) {
|
|
14
|
+
const object = this._.file((path = `${path}`));
|
|
15
|
+
if (!object || object.dir)
|
|
16
|
+
throw new Error(`file not found: ${path}`);
|
|
17
|
+
return new ZipArchiveEntry(object);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
class ZipArchiveEntry extends AbstractFile {
|
|
21
|
+
constructor(object) {
|
|
22
|
+
super(undefined, object.name);
|
|
23
|
+
Object.defineProperty(this, "href", {
|
|
24
|
+
enumerable: true,
|
|
25
|
+
configurable: true,
|
|
26
|
+
writable: true,
|
|
27
|
+
value: void 0
|
|
28
|
+
}); // async
|
|
29
|
+
Object.defineProperties(this, {
|
|
30
|
+
_: { value: object },
|
|
31
|
+
_url: { writable: true }
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
async url() {
|
|
35
|
+
return this._url || (this._url = this.blob().then(URL.createObjectURL));
|
|
36
|
+
}
|
|
37
|
+
async blob() {
|
|
38
|
+
return this._.async("blob");
|
|
39
|
+
}
|
|
40
|
+
async arrayBuffer() {
|
|
41
|
+
return this._.async("arraybuffer");
|
|
42
|
+
}
|
|
43
|
+
async text() {
|
|
44
|
+
return this._.async("text");
|
|
45
|
+
}
|
|
46
|
+
async json() {
|
|
47
|
+
return JSON.parse(await this.text());
|
|
48
|
+
}
|
|
49
|
+
}
|
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/observablehq/notebook-kit.git"
|
|
7
7
|
},
|
|
8
|
-
"version": "1.
|
|
8
|
+
"version": "1.7.0",
|
|
9
9
|
"type": "module",
|
|
10
10
|
"scripts": {
|
|
11
11
|
"test": "vitest",
|
|
@@ -73,8 +73,10 @@
|
|
|
73
73
|
"@types/markdown-it": "^14.1.2",
|
|
74
74
|
"bun-types": "^1.2.20",
|
|
75
75
|
"eslint": "^9.29.0",
|
|
76
|
+
"exceljs": "^4.4.0",
|
|
76
77
|
"globals": "^16.2.0",
|
|
77
78
|
"htl": "^0.3.1",
|
|
79
|
+
"jszip": "^3.10.1",
|
|
78
80
|
"postgres": "^3.4.9",
|
|
79
81
|
"snowflake-sdk": "^2.4.0",
|
|
80
82
|
"tsx": "^4.20.3",
|