@observablehq/notebook-kit 1.0.1 → 1.1.0-rc.10

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.
Files changed (60) hide show
  1. package/dist/bin/build.js +7 -0
  2. package/dist/bin/download.js +15 -19
  3. package/dist/package.json +31 -2
  4. package/dist/src/databases/duckdb.d.ts +2 -0
  5. package/dist/src/databases/duckdb.js +72 -0
  6. package/dist/src/databases/index.d.ts +46 -0
  7. package/dist/src/databases/index.js +54 -0
  8. package/dist/src/databases/options.d.ts +3 -0
  9. package/dist/src/databases/options.js +6 -0
  10. package/dist/src/databases/postgres.d.ts +2 -0
  11. package/dist/src/databases/postgres.js +95 -0
  12. package/dist/src/databases/snowflake.d.ts +2 -0
  13. package/dist/src/databases/snowflake.js +102 -0
  14. package/dist/src/databases/sqlite.d.ts +2 -0
  15. package/dist/src/databases/sqlite.js +64 -0
  16. package/dist/src/javascript/imports/npm.js +2 -0
  17. package/dist/src/javascript/observable.js +24 -3
  18. package/dist/src/javascript/template.d.ts +3 -0
  19. package/dist/src/javascript/template.js +17 -1
  20. package/dist/src/javascript/transpile.d.ts +4 -2
  21. package/dist/src/javascript/transpile.js +30 -11
  22. package/dist/src/javascript/transpile.test.js +16 -0
  23. package/dist/src/lib/error.d.ts +6 -0
  24. package/dist/src/lib/error.js +6 -0
  25. package/dist/src/lib/hash.d.ts +2 -0
  26. package/dist/src/lib/hash.js +20 -0
  27. package/dist/src/lib/hash.test.d.ts +1 -0
  28. package/dist/src/lib/hash.test.js +28 -0
  29. package/dist/src/lib/notebook.d.ts +11 -1
  30. package/dist/src/lib/notebook.js +10 -3
  31. package/dist/src/lib/notebook.test.js +10 -2
  32. package/dist/src/lib/serialize.d.ts +3 -1
  33. package/dist/src/lib/serialize.js +13 -4
  34. package/dist/src/lib/serialize.test.js +10 -3
  35. package/dist/src/lib/sluggify.d.ts +6 -0
  36. package/dist/src/lib/sluggify.js +22 -0
  37. package/dist/src/lib/sluggify.test.d.ts +1 -0
  38. package/dist/src/lib/sluggify.test.js +51 -0
  39. package/dist/src/runtime/define.d.ts +2 -2
  40. package/dist/src/runtime/define.js +2 -2
  41. package/dist/src/runtime/display.d.ts +2 -0
  42. package/dist/src/runtime/display.js +5 -1
  43. package/dist/src/runtime/index.d.ts +100 -1
  44. package/dist/src/runtime/index.js +29 -3
  45. package/dist/src/runtime/stdlib/databaseClient.d.ts +45 -0
  46. package/dist/src/runtime/stdlib/databaseClient.js +77 -0
  47. package/dist/src/runtime/stdlib/duckdb.js +7 -4
  48. package/dist/src/runtime/stdlib/fileAttachment.d.ts +38 -11
  49. package/dist/src/runtime/stdlib/fileAttachment.js +21 -10
  50. package/dist/src/runtime/stdlib/generators/input.d.ts +1 -1
  51. package/dist/src/runtime/stdlib/index.d.ts +43 -6
  52. package/dist/src/runtime/stdlib/index.js +5 -5
  53. package/dist/src/styles/global.css +1 -1
  54. package/dist/src/templates/default.html +18 -18
  55. package/dist/src/vite/config.js +5 -2
  56. package/dist/src/vite/observable.d.ts +25 -6
  57. package/dist/src/vite/observable.js +42 -15
  58. package/package.json +31 -2
  59. package/dist/src/runtime/stdlib/sql.d.ts +0 -5
  60. package/dist/src/runtime/stdlib/sql.js +0 -5
@@ -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 * from "./define.js";
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
+ revive: ({ rows, schema, date, ...meta }: import("../databases/index.js").SerializableQueryResult) => import("./stdlib/databaseClient.js").QueryResult;
62
+ prototype: {
63
+ readonly name: string;
64
+ readonly options: import("./stdlib/databaseClient.js").QueryOptions;
65
+ sql(strings: readonly string[], ...params: import("./stdlib/databaseClient.js").QueryParam[]): Promise<import("./stdlib/databaseClient.js").QueryResult>;
66
+ cachePath(strings: readonly string[], ...params: import("./stdlib/databaseClient.js").QueryParam[]): Promise<string>;
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 const runtime = Object.assign(new Runtime({ ...library, __ojs_runtime: () => runtime }), { fileAttachments });
9
- export const main = runtime.main = runtime.module();
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,45 @@
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: readonly string[], ...params: QueryParam[]): Promise<QueryResult>;
31
+ }
32
+ export declare const DatabaseClient: {
33
+ (name: string, options?: QueryOptionsSpec): DatabaseClient;
34
+ revive: typeof revive;
35
+ prototype: DatabaseClientImpl;
36
+ };
37
+ declare class DatabaseClientImpl implements DatabaseClient {
38
+ readonly name: string;
39
+ readonly options: QueryOptions;
40
+ constructor(name: string, options: QueryOptions);
41
+ sql(strings: readonly string[], ...params: QueryParam[]): Promise<QueryResult>;
42
+ cachePath(strings: readonly string[], ...params: QueryParam[]): Promise<string>;
43
+ }
44
+ declare function revive({ rows, schema, date, ...meta }: SerializableQueryResult): QueryResult;
45
+ export {};
@@ -0,0 +1,77 @@
1
+ import { hash, nameHash } from "../../lib/hash.js";
2
+ export const DatabaseClient = (name, options) => {
3
+ return new DatabaseClientImpl(name, normalizeOptions(options));
4
+ };
5
+ function normalizeOptions({ id, since } = {}) {
6
+ const options = {};
7
+ if (id !== undefined)
8
+ options.id = id;
9
+ if (since !== undefined)
10
+ options.since = new Date(since);
11
+ return options;
12
+ }
13
+ class DatabaseClientImpl {
14
+ constructor(name, options) {
15
+ Object.defineProperty(this, "name", {
16
+ enumerable: true,
17
+ configurable: true,
18
+ writable: true,
19
+ value: void 0
20
+ });
21
+ Object.defineProperty(this, "options", {
22
+ enumerable: true,
23
+ configurable: true,
24
+ writable: true,
25
+ value: void 0
26
+ });
27
+ Object.defineProperties(this, {
28
+ name: { value: name, enumerable: true },
29
+ options: { value: options, enumerable: true }
30
+ });
31
+ }
32
+ async sql(strings, ...params) {
33
+ const path = await this.cachePath(strings, ...params);
34
+ const response = await fetch(path);
35
+ if (!response.ok)
36
+ throw new Error(`failed to fetch: ${path}`);
37
+ return await response.json().then(revive);
38
+ }
39
+ async cachePath(strings, ...params) {
40
+ return `.observable/cache/${await nameHash(this.name)}-${await hash(strings, ...params)}.json`;
41
+ }
42
+ }
43
+ function revive({ rows, schema, date, ...meta }) {
44
+ for (const column of schema) {
45
+ switch (column.type) {
46
+ case "bigint": {
47
+ const { name } = column;
48
+ for (const row of rows) {
49
+ const value = row[name];
50
+ if (value == null)
51
+ continue;
52
+ row[name] = Number(value); // TODO BigInt?
53
+ }
54
+ break;
55
+ }
56
+ case "date": {
57
+ const { name } = column;
58
+ for (const row of rows) {
59
+ const value = row[name];
60
+ if (value == null)
61
+ continue;
62
+ row[name] = asDate(value);
63
+ }
64
+ break;
65
+ }
66
+ }
67
+ }
68
+ if (date != null)
69
+ date = new Date(date);
70
+ return Object.assign(rows, { schema, date }, meta);
71
+ }
72
+ function asDate(value) {
73
+ return new Date(/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}(?::\d{2})?$/.test(value) ? value + "Z" : value);
74
+ }
75
+ DatabaseClient.revive = revive;
76
+ DatabaseClient.prototype = DatabaseClientImpl.prototype; // instanceof
77
+ Object.defineProperty(DatabaseClientImpl, "name", { value: "DatabaseClient" }); // prevent mangling
@@ -29,12 +29,12 @@ import * as duckdb from "npm:@duckdb/duckdb-wasm";
29
29
  // POSSIBILITY OF SUCH DAMAGE.
30
30
  const bundles = {
31
31
  mvp: {
32
- mainModule: "https://cdn.jsdelivr.net/npm/@duckdb/duckdb-wasm/dist/duckdb-mvp.wasm",
33
- mainWorker: "https://cdn.jsdelivr.net/npm/@duckdb/duckdb-wasm/dist/duckdb-browser-mvp.worker.js"
32
+ mainModule: "https://cdn.jsdelivr.net/npm/@duckdb/duckdb-wasm@1.29.0/dist/duckdb-mvp.wasm",
33
+ mainWorker: "https://cdn.jsdelivr.net/npm/@duckdb/duckdb-wasm@1.29.0/dist/duckdb-browser-mvp.worker.js"
34
34
  },
35
35
  eh: {
36
- mainModule: "https://cdn.jsdelivr.net/npm/@duckdb/duckdb-wasm/dist/duckdb-eh.wasm",
37
- mainWorker: "https://cdn.jsdelivr.net/npm/@duckdb/duckdb-wasm/dist/duckdb-browser-eh.worker.js"
36
+ mainModule: "https://cdn.jsdelivr.net/npm/@duckdb/duckdb-wasm@1.29.0/dist/duckdb-eh.wasm",
37
+ mainWorker: "https://cdn.jsdelivr.net/npm/@duckdb/duckdb-wasm@1.29.0/dist/duckdb-browser-eh.worker.js"
38
38
  }
39
39
  };
40
40
  const bundle = duckdb.selectBundle(bundles);
@@ -150,6 +150,9 @@ export class DuckDBClient {
150
150
  }
151
151
  static async of(sources = {}, config = {}) {
152
152
  const db = await createDuckDB();
153
+ if (config.query?.castDecimalToDouble === undefined) {
154
+ config = { ...config, query: { ...config.query, castDecimalToDouble: true } };
155
+ }
153
156
  if (config.query?.castTimestampToDate === undefined) {
154
157
  config = { ...config, query: { ...config.query, castTimestampToDate: true } };
155
158
  }
@@ -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
- href: string;
13
- lastModified: number | undefined;
14
- size: number | undefined;
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
- csv(options?: Exclude<DsvOptions, "delimiter">): Promise<DsvResult>;
23
- tsv(options?: Exclude<DsvOptions, "delimiter">): Promise<DsvResult>;
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 function FileAttachment(name: string, base?: string): FileAttachment;
32
- export declare namespace FileAttachment {
33
- var prototype: FileAttachmentImpl;
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: Exclude<DsvOptions, "delimiter">): Promise<DsvResult>;
54
- tsv(options: Exclude<DsvOptions, "delimiter">): Promise<DsvResult>;
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>;
@@ -1,8 +1,7 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
2
  const files = new Map();
3
- export function FileAttachment(name, base = document.baseURI) {
4
- if (new.target !== undefined)
5
- throw new TypeError("FileAttachment is not a constructor");
3
+ // TODO Enforce that files have been registered; throw error if not found.
4
+ export const FileAttachment = (name, base = document.baseURI) => {
6
5
  const href = new URL(name, base).href;
7
6
  let file = files.get(href);
8
7
  if (!file) {
@@ -10,8 +9,20 @@ export function FileAttachment(name, base = document.baseURI) {
10
9
  files.set(href, file);
11
10
  }
12
11
  return file;
12
+ };
13
+ export function registerFile(name, info, base = location.href) {
14
+ const href = new URL(name, base).href;
15
+ if (info == null) {
16
+ files.delete(href);
17
+ }
18
+ else {
19
+ const { path, mimeType, lastModified, size } = info;
20
+ const file = new FileAttachmentImpl(new URL(path, base).href, name.split("/").pop(), mimeType, lastModified, size);
21
+ files.set(href, file);
22
+ return file;
23
+ }
13
24
  }
14
- async function remote_fetch(file) {
25
+ async function fetchFile(file) {
15
26
  const response = await fetch(file.href);
16
27
  if (!response.ok)
17
28
  throw new Error(`Unable to load file: ${file.name}`);
@@ -54,21 +65,21 @@ export class AbstractFile {
54
65
  return this.href;
55
66
  }
56
67
  async blob() {
57
- return (await remote_fetch(this)).blob();
68
+ return (await fetchFile(this)).blob();
58
69
  }
59
70
  async arrayBuffer() {
60
- return (await remote_fetch(this)).arrayBuffer();
71
+ return (await fetchFile(this)).arrayBuffer();
61
72
  }
62
73
  async text(encoding) {
63
74
  return encoding === undefined
64
- ? (await remote_fetch(this)).text()
75
+ ? (await fetchFile(this)).text()
65
76
  : new TextDecoder(encoding).decode(await this.arrayBuffer());
66
77
  }
67
78
  async json() {
68
- return (await remote_fetch(this)).json();
79
+ return (await fetchFile(this)).json();
69
80
  }
70
81
  async stream() {
71
- return (await remote_fetch(this)).body;
82
+ return (await fetchFile(this)).body;
72
83
  }
73
84
  async dsv({ delimiter = ",", array = false, typed = false } = {}) {
74
85
  const [text, d3] = await Promise.all([this.text(), import("npm:d3-dsv")]);
@@ -95,7 +106,7 @@ export class AbstractFile {
95
106
  });
96
107
  }
97
108
  async arrow() {
98
- const [Arrow, response] = await Promise.all([import("npm:apache-arrow"), remote_fetch(this)]);
109
+ const [Arrow, response] = await Promise.all([import("npm:apache-arrow"), fetchFile(this)]);
99
110
  return Arrow.tableFromIPC(response);
100
111
  }
101
112
  async arquero(options) {
@@ -1 +1 @@
1
- export declare function input(element: Element): AsyncGenerator<string | number | boolean | string[] | Date | File | FileList | null, void, unknown>;
1
+ export declare function input(element: Element): AsyncGenerator<string | number | boolean | string[] | Date | FileList | File | null, void, unknown>;
@@ -1,10 +1,10 @@
1
- import { Mutable } from "./mutable.js";
2
- import * as Generators from "./generators/index.js";
3
- import { FileAttachment } from "./fileAttachment.js";
1
+ import { DatabaseClient } from "./databaseClient.js";
4
2
  import * as DOM from "./dom/index.js";
3
+ import { FileAttachment } from "./fileAttachment.js";
4
+ import * as Generators from "./generators/index.js";
5
+ import { Mutable } from "./mutable.js";
5
6
  import { Observer } from "./observer.js";
6
7
  import { require } from "./require.js";
7
- import { __sql } from "./sql.js";
8
8
  export declare const root: HTMLElement;
9
9
  export declare const library: {
10
10
  aapl: () => Promise<any>;
@@ -46,11 +46,48 @@ export declare const library: {
46
46
  vl: () => Promise<any>;
47
47
  now: () => AsyncGenerator<number, void, unknown>;
48
48
  width: () => AsyncGenerator<number, void, unknown>;
49
- FileAttachment: () => typeof FileAttachment;
49
+ DatabaseClient: () => {
50
+ (name: string, options?: import("./databaseClient.js").QueryOptionsSpec): DatabaseClient;
51
+ revive: ({ rows, schema, date, ...meta }: import("../../databases/index.js").SerializableQueryResult) => import("./databaseClient.js").QueryResult;
52
+ prototype: {
53
+ readonly name: string;
54
+ readonly options: import("./databaseClient.js").QueryOptions;
55
+ sql(strings: readonly string[], ...params: import("./databaseClient.js").QueryParam[]): Promise<import("./databaseClient.js").QueryResult>;
56
+ cachePath(strings: readonly string[], ...params: import("./databaseClient.js").QueryParam[]): Promise<string>;
57
+ };
58
+ };
59
+ FileAttachment: () => {
60
+ (name: string, base?: string): FileAttachment;
61
+ prototype: {
62
+ href: string;
63
+ name: string;
64
+ mimeType: string;
65
+ lastModified: number | undefined;
66
+ size: number | undefined;
67
+ url(): Promise<string>;
68
+ blob(): Promise<Blob>;
69
+ arrayBuffer(): Promise<ArrayBuffer>;
70
+ text(encoding?: string): Promise<string>;
71
+ json(): Promise<any>;
72
+ stream(): Promise<ReadableStream<Uint8Array<ArrayBufferLike>>>;
73
+ dsv({ delimiter, array, typed }?: {
74
+ delimiter?: string | undefined;
75
+ array?: boolean | undefined;
76
+ typed?: boolean | undefined;
77
+ }): Promise<import("./fileAttachment.js").DsvResult>;
78
+ csv(options?: Omit<import("./fileAttachment.js").DsvOptions, "delimiter">): Promise<import("./fileAttachment.js").DsvResult>;
79
+ tsv(options?: Omit<import("./fileAttachment.js").DsvOptions, "delimiter">): Promise<import("./fileAttachment.js").DsvResult>;
80
+ image(props?: Partial<HTMLImageElement>): Promise<HTMLImageElement>;
81
+ arrow(): Promise<any>;
82
+ arquero(options?: any): Promise<any>;
83
+ parquet(): Promise<any>;
84
+ xml(mimeType?: DOMParserSupportedType): Promise<Document>;
85
+ html(): Promise<Document>;
86
+ };
87
+ };
50
88
  Generators: () => typeof Generators;
51
89
  Mutable: () => typeof Mutable;
52
90
  DOM: () => typeof DOM;
53
91
  require: () => typeof require;
54
- __sql: () => typeof __sql;
55
92
  __ojs_observer: () => () => Observer;
56
93
  };
@@ -1,22 +1,22 @@
1
- import { Mutable } from "./mutable.js";
2
- import * as Generators from "./generators/index.js";
3
- import { FileAttachment } from "./fileAttachment.js";
1
+ import { DatabaseClient } from "./databaseClient.js";
4
2
  import * as DOM from "./dom/index.js";
3
+ import { FileAttachment } from "./fileAttachment.js";
4
+ import * as Generators from "./generators/index.js";
5
+ import { Mutable } from "./mutable.js";
5
6
  import { Observer } from "./observer.js";
6
7
  import * as recommendedLibraries from "./recommendedLibraries.js";
7
8
  import { require } from "./require.js";
8
9
  import * as sampleDatasets from "./sampleDatasets.js";
9
- import { __sql } from "./sql.js";
10
10
  export const root = document.querySelector("main") ?? document.body;
11
11
  export const library = {
12
12
  now: () => Generators.now(),
13
13
  width: () => Generators.width(root),
14
+ DatabaseClient: () => DatabaseClient,
14
15
  FileAttachment: () => FileAttachment,
15
16
  Generators: () => Generators,
16
17
  Mutable: () => Mutable,
17
18
  DOM: () => DOM, // deprecated!
18
19
  require: () => require, // deprecated!
19
- __sql: () => __sql,
20
20
  __ojs_observer: () => () => new Observer(),
21
21
  ...recommendedLibraries,
22
22
  ...sampleDatasets
@@ -17,7 +17,7 @@ html {
17
17
  -moz-osx-font-smoothing: grayscale;
18
18
  background: var(--theme-background);
19
19
  color: var(--theme-foreground);
20
- margin-top: 16px;
20
+ margin: 16px 0;
21
21
  padding: 16px 24px;
22
22
  }
23
23
 
@@ -5,24 +5,24 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
6
  <style type="text/css">
7
7
  @import url("observable:styles/index.css");
8
- @import "@fontsource/spline-sans-mono/400.css";
9
- @import "@fontsource/spline-sans-mono/600.css";
10
- @import "@fontsource/spline-sans-mono/700.css";
11
- @import "@fontsource/spline-sans-mono/400-italic.css";
12
- @import "@fontsource/spline-sans-mono/600-italic.css";
13
- @import "@fontsource/spline-sans-mono/700-italic.css";
14
- @import "@fontsource/source-serif-4/400.css";
15
- @import "@fontsource/source-serif-4/700.css";
16
- @import "@fontsource/source-serif-4/400-italic.css";
17
- @import "@fontsource/source-serif-4/700-italic.css";
18
- @import "@fontsource/inter/400.css";
19
- @import "@fontsource/inter/500.css";
20
- @import "@fontsource/inter/600.css";
21
- @import "@fontsource/inter/700.css";
22
- @import "@fontsource/inter/400-italic.css";
23
- @import "@fontsource/inter/500-italic.css";
24
- @import "@fontsource/inter/600-italic.css";
25
- @import "@fontsource/inter/700-italic.css";
8
+ @import url("@fontsource/spline-sans-mono/400.css");
9
+ @import url("@fontsource/spline-sans-mono/600.css");
10
+ @import url("@fontsource/spline-sans-mono/700.css");
11
+ @import url("@fontsource/spline-sans-mono/400-italic.css");
12
+ @import url("@fontsource/spline-sans-mono/600-italic.css");
13
+ @import url("@fontsource/spline-sans-mono/700-italic.css");
14
+ @import url("@fontsource/source-serif-4/400.css");
15
+ @import url("@fontsource/source-serif-4/700.css");
16
+ @import url("@fontsource/source-serif-4/400-italic.css");
17
+ @import url("@fontsource/source-serif-4/700-italic.css");
18
+ @import url("@fontsource/inter/400.css");
19
+ @import url("@fontsource/inter/500.css");
20
+ @import url("@fontsource/inter/600.css");
21
+ @import url("@fontsource/inter/700.css");
22
+ @import url("@fontsource/inter/400-italic.css");
23
+ @import url("@fontsource/inter/500-italic.css");
24
+ @import url("@fontsource/inter/600-italic.css");
25
+ @import url("@fontsource/inter/700-italic.css");
26
26
  </style>
27
27
  </head>
28
28
  <body>
@@ -1,10 +1,12 @@
1
1
  import { dirname, resolve } from "node:path";
2
2
  import { fileURLToPath } from "node:url";
3
+ import { resolveNpmImport } from "../javascript/imports/npm.js";
3
4
  const __filename = fileURLToPath(import.meta.url);
4
5
  const __dirname = dirname(__filename);
5
6
  export function config() {
6
7
  return {
7
8
  base: "./",
9
+ appType: "mpa", // return 404 for missing pages
8
10
  esbuild: {
9
11
  supported: {
10
12
  "top-level-await": true
@@ -13,8 +15,9 @@ export function config() {
13
15
  resolve: {
14
16
  alias: [
15
17
  {
16
- find: /^npm:(.*)$/,
17
- replacement: "https://cdn.jsdelivr.net/npm/$1/+esm"
18
+ find: /^(npm:.*)$/,
19
+ replacement: "$1",
20
+ customResolver: (source) => ({ id: resolveNpmImport(source), external: true })
18
21
  },
19
22
  {
20
23
  find: /^jsr:(.*)$/,