@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 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.6.2",
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.6.2",
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",