/
fileAttachment.js
109 lines (102 loc) · 4.09 KB
/
fileAttachment.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
const files = new Map();
export function registerFile(name, file) {
const href = new URL(name, location).href;
if (file == null) files.delete(href);
else files.set(href, file);
}
export function FileAttachment(name, base = location) {
if (new.target !== undefined) throw new TypeError("FileAttachment is not a constructor");
const href = new URL(name, base).href;
const file = files.get(href);
if (!file) throw new Error(`File not found: ${name}`);
const {path, mimeType, lastModified} = file;
return new FileAttachmentImpl(new URL(path, location).href, name.split("/").pop(), mimeType, lastModified);
}
async function remote_fetch(file) {
const response = await fetch(await file.url());
if (!response.ok) throw new Error(`Unable to load file: ${file.name}`);
return response;
}
async function dsv(file, delimiter, {array = false, typed = false} = {}) {
const [text, d3] = await Promise.all([file.text(), import("npm:d3-dsv")]);
const parse = delimiter === "\t" ? (array ? d3.tsvParseRows : d3.tsvParse) : array ? d3.csvParseRows : d3.csvParse;
return parse(text, typed && d3.autoType);
}
export class AbstractFile {
constructor(name, mimeType = "application/octet-stream", lastModified) {
Object.defineProperty(this, "name", {value: `${name}`, enumerable: true});
Object.defineProperty(this, "mimeType", {value: `${mimeType}`, enumerable: true});
if (lastModified !== undefined) Object.defineProperty(this, "lastModified", {value: Number(lastModified), enumerable: true}); // prettier-ignore
}
async blob() {
return (await remote_fetch(this)).blob();
}
async arrayBuffer() {
return (await remote_fetch(this)).arrayBuffer();
}
async text(encoding) {
return encoding === undefined
? (await remote_fetch(this)).text()
: new TextDecoder(encoding).decode(await this.arrayBuffer());
}
async json() {
return (await remote_fetch(this)).json();
}
async stream() {
return (await remote_fetch(this)).body;
}
async csv(options) {
return dsv(this, ",", options);
}
async tsv(options) {
return dsv(this, "\t", options);
}
async image(props) {
const url = await this.url();
return new Promise((resolve, reject) => {
const i = new Image();
if (new URL(url, document.baseURI).origin !== new URL(location).origin) i.crossOrigin = "anonymous";
Object.assign(i, props);
i.onload = () => resolve(i);
i.onerror = () => reject(new Error(`Unable to load file: ${this.name}`));
i.src = url;
});
}
async arrow() {
const [Arrow, response] = await Promise.all([import("npm:apache-arrow"), remote_fetch(this)]);
return Arrow.tableFromIPC(response);
}
async parquet() {
const [Arrow, Parquet, buffer] = await Promise.all([import("npm:apache-arrow"), import("npm:parquet-wasm/esm/arrow1.js").then(async (Parquet) => (await Parquet.default(), Parquet)), this.arrayBuffer()]); // prettier-ignore
return Arrow.tableFromIPC(Parquet.readParquet(new Uint8Array(buffer)).intoIPCStream());
}
async sqlite() {
const [{SQLiteDatabaseClient}, response] = await Promise.all([import("observablehq:stdlib/sqlite"), this.arrayBuffer()]); // prettier-ignore
return SQLiteDatabaseClient.open(response);
}
async zip() {
const [{ZipArchive}, buffer] = await Promise.all([import("observablehq:stdlib/zip"), this.arrayBuffer()]);
return ZipArchive.from(buffer);
}
async xml(mimeType = "application/xml") {
return new DOMParser().parseFromString(await this.text(), mimeType);
}
async html() {
return this.xml("text/html");
}
async xlsx() {
const [{Workbook}, buffer] = await Promise.all([import("observablehq:stdlib/xlsx"), this.arrayBuffer()]);
return Workbook.load(buffer);
}
}
class FileAttachmentImpl extends AbstractFile {
constructor(href, name, mimeType, lastModified) {
super(name, mimeType, lastModified);
Object.defineProperty(this, "href", {value: href});
}
async url() {
return this.href;
}
}
Object.defineProperty(FileAttachmentImpl, "name", {value: "FileAttachment"}); // prevent mangling
FileAttachment.prototype = FileAttachmentImpl.prototype; // instanceof