@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.
- package/dist/bin/build.js +7 -0
- package/dist/bin/download.js +15 -19
- package/dist/package.json +31 -2
- package/dist/src/databases/duckdb.d.ts +2 -0
- package/dist/src/databases/duckdb.js +72 -0
- package/dist/src/databases/index.d.ts +46 -0
- package/dist/src/databases/index.js +54 -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/databases/sqlite.d.ts +2 -0
- package/dist/src/databases/sqlite.js +64 -0
- package/dist/src/javascript/imports/npm.js +2 -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/hash.d.ts +2 -0
- package/dist/src/lib/hash.js +20 -0
- package/dist/src/lib/hash.test.d.ts +1 -0
- package/dist/src/lib/hash.test.js +28 -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/lib/sluggify.d.ts +6 -0
- package/dist/src/lib/sluggify.js +22 -0
- package/dist/src/lib/sluggify.test.d.ts +1 -0
- package/dist/src/lib/sluggify.test.js +51 -0
- 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 +45 -0
- package/dist/src/runtime/stdlib/databaseClient.js +77 -0
- package/dist/src/runtime/stdlib/duckdb.js +7 -4
- 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 +5 -2
- package/dist/src/vite/observable.d.ts +25 -6
- package/dist/src/vite/observable.js +42 -15
- package/package.json +31 -2
- package/dist/src/runtime/stdlib/sql.d.ts +0 -5
- package/dist/src/runtime/stdlib/sql.js +0 -5
package/dist/bin/build.js
CHANGED
|
@@ -23,6 +23,10 @@ export default async function run(args) {
|
|
|
23
23
|
type: "string",
|
|
24
24
|
default: "./"
|
|
25
25
|
},
|
|
26
|
+
minify: {
|
|
27
|
+
type: "boolean",
|
|
28
|
+
default: true
|
|
29
|
+
},
|
|
26
30
|
template: {
|
|
27
31
|
type: "string"
|
|
28
32
|
},
|
|
@@ -42,6 +46,7 @@ export default async function run(args) {
|
|
|
42
46
|
--template <path> path to the HTML template
|
|
43
47
|
-o, --out <dir> path to the output directory (relative to root)
|
|
44
48
|
--base <path> serving base path; defaults to ./
|
|
49
|
+
--no-minify disable JS/CSS minification
|
|
45
50
|
--empty whether to empty the output directory before building
|
|
46
51
|
-h, --help show this message
|
|
47
52
|
`);
|
|
@@ -53,6 +58,8 @@ export default async function run(args) {
|
|
|
53
58
|
root: values.root,
|
|
54
59
|
base: values.base,
|
|
55
60
|
build: {
|
|
61
|
+
minify: values.minify ? "esbuild" : false,
|
|
62
|
+
cssMinify: values.minify ? "esbuild" : false,
|
|
56
63
|
outDir: values.out,
|
|
57
64
|
emptyOutDir: values.empty,
|
|
58
65
|
rollupOptions: {
|
package/dist/bin/download.js
CHANGED
|
@@ -3,6 +3,8 @@ import { parseArgs } from "node:util";
|
|
|
3
3
|
import { JSDOM } from "jsdom";
|
|
4
4
|
import { toNotebook } from "../src/lib/notebook.js";
|
|
5
5
|
import { serialize } from "../src/lib/serialize.js";
|
|
6
|
+
const UI_ORIGIN = "https://observablehq.com";
|
|
7
|
+
const API_ORIGIN = "https://api.observablehq.com";
|
|
6
8
|
if (process.argv[1] === import.meta.filename)
|
|
7
9
|
run();
|
|
8
10
|
export default async function run(args) {
|
|
@@ -25,25 +27,19 @@ export default async function run(args) {
|
|
|
25
27
|
`);
|
|
26
28
|
return;
|
|
27
29
|
}
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
if (url.origin === "https://observablehq.com") {
|
|
34
|
-
url = new URL(`/document${url.pathname.replace(/^\/d\//, "/")}`, "https://api.observablehq.com");
|
|
35
|
-
}
|
|
36
|
-
const response = await fetch(url);
|
|
37
|
-
if (!response.ok)
|
|
38
|
-
throw new Error(`unable to fetch: ${url}`);
|
|
39
|
-
const { title, nodes } = await response.json();
|
|
40
|
-
for (const node of nodes)
|
|
41
|
-
if (node.mode === "js")
|
|
42
|
-
node.mode = "ojs";
|
|
43
|
-
process.stdout.write(serialize(toNotebook({ title, cells: nodes })));
|
|
30
|
+
const { window } = new JSDOM();
|
|
31
|
+
for (const positional of positionals) {
|
|
32
|
+
let url = new URL(positional, UI_ORIGIN);
|
|
33
|
+
if (url.origin === UI_ORIGIN) {
|
|
34
|
+
url = new URL(`/document${url.pathname.replace(/^\/d\//, "/")}`, API_ORIGIN);
|
|
44
35
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
36
|
+
const response = await fetch(url);
|
|
37
|
+
if (!response.ok)
|
|
38
|
+
throw new Error(`unable to fetch: ${url}`);
|
|
39
|
+
const { title, nodes } = await response.json();
|
|
40
|
+
for (const node of nodes)
|
|
41
|
+
if (node.mode === "js")
|
|
42
|
+
node.mode = "ojs";
|
|
43
|
+
process.stdout.write(serialize(toNotebook({ title, cells: nodes }), { document: window.document }));
|
|
48
44
|
}
|
|
49
45
|
}
|
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.0.
|
|
8
|
+
"version": "1.1.0-rc.10",
|
|
9
9
|
"type": "module",
|
|
10
10
|
"scripts": {
|
|
11
11
|
"test": "vitest",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"lint": "tsc --noEmit && eslint bin src types",
|
|
14
14
|
"notebooks": "tsx bin/notebooks.ts",
|
|
15
15
|
"download": "tsx bin/notebooks.ts download",
|
|
16
|
-
"docs:preview": "tsx --watch bin/notebooks.ts preview --root docs --template docs/observable.tmpl",
|
|
16
|
+
"docs:preview": "tsx --watch bin/notebooks.ts preview --base /notebook-kit/ --root docs --template docs/observable.tmpl",
|
|
17
17
|
"docs:build": "tsx bin/notebooks.ts build --root docs --template docs/observable.tmpl -- $(find docs -path 'docs/.observable' -prune -o -name '*.html' -print)"
|
|
18
18
|
},
|
|
19
19
|
"bin": {
|
|
@@ -27,6 +27,10 @@
|
|
|
27
27
|
"types": "./dist/src/index.d.ts",
|
|
28
28
|
"import": "./dist/src/index.js"
|
|
29
29
|
},
|
|
30
|
+
"./databases": {
|
|
31
|
+
"types": "./dist/src/databases/index.d.ts",
|
|
32
|
+
"import": "./dist/src/databases/index.js"
|
|
33
|
+
},
|
|
30
34
|
"./runtime": {
|
|
31
35
|
"types": "./dist/src/runtime/index.d.ts",
|
|
32
36
|
"import": "./dist/src/runtime/index.js"
|
|
@@ -59,15 +63,40 @@
|
|
|
59
63
|
"vite": "^7.0.0"
|
|
60
64
|
},
|
|
61
65
|
"devDependencies": {
|
|
66
|
+
"@duckdb/node-api": "^1.3.2-alpha.26",
|
|
62
67
|
"@eslint/js": "^9.29.0",
|
|
68
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
63
69
|
"@types/jsdom": "^21.1.7",
|
|
64
70
|
"@types/markdown-it": "^14.1.2",
|
|
71
|
+
"better-sqlite3": "^12.2.0",
|
|
65
72
|
"eslint": "^9.29.0",
|
|
66
73
|
"globals": "^16.2.0",
|
|
67
74
|
"htl": "^0.3.1",
|
|
75
|
+
"postgres": "^3.4.7",
|
|
76
|
+
"snowflake-sdk": "^2.1.3",
|
|
68
77
|
"tsx": "^4.20.3",
|
|
69
78
|
"typescript": "^5.8.3",
|
|
70
79
|
"typescript-eslint": "^8.35.0",
|
|
71
80
|
"vitest": "^3.2.4"
|
|
81
|
+
},
|
|
82
|
+
"peerDependencies": {
|
|
83
|
+
"@duckdb/node-api": "^1.3.2-alpha.26",
|
|
84
|
+
"better-sqlite3": "^12.2.0",
|
|
85
|
+
"postgres": "^3.4.7",
|
|
86
|
+
"snowflake-sdk": "^2.1.3"
|
|
87
|
+
},
|
|
88
|
+
"peerDependenciesMeta": {
|
|
89
|
+
"@duckdb/node-api": {
|
|
90
|
+
"optional": true
|
|
91
|
+
},
|
|
92
|
+
"better-sqlite3": {
|
|
93
|
+
"optional": true
|
|
94
|
+
},
|
|
95
|
+
"postgres": {
|
|
96
|
+
"optional": true
|
|
97
|
+
},
|
|
98
|
+
"snowflake-sdk": {
|
|
99
|
+
"optional": true
|
|
100
|
+
}
|
|
72
101
|
}
|
|
73
102
|
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { DuckDBConnection, DuckDBInstance } from "@duckdb/node-api";
|
|
2
|
+
import { BIGINT, BIT, BLOB, BOOLEAN, DATE, DOUBLE, FLOAT, HUGEINT, INTEGER, INTERVAL, SMALLINT, TIME, TIMESTAMP, TIMESTAMP_MS, TIMESTAMP_NS, TIMESTAMP_S, TIMESTAMPTZ, TINYINT, UBIGINT, UHUGEINT, UINTEGER, USMALLINT, UTINYINT, UUID, VARCHAR, VARINT } from "@duckdb/node-api"; // prettier-ignore
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
export default function duckdb({ path, options }, context) {
|
|
5
|
+
if (path !== undefined)
|
|
6
|
+
path = join(context.cwd, path);
|
|
7
|
+
return async (strings, ...params) => {
|
|
8
|
+
const instance = await DuckDBInstance.create(path, options);
|
|
9
|
+
const connection = await DuckDBConnection.create(instance);
|
|
10
|
+
await connection.run(`SET file_search_path=$0`, [context.cwd]);
|
|
11
|
+
const date = new Date();
|
|
12
|
+
let result;
|
|
13
|
+
let rows;
|
|
14
|
+
try {
|
|
15
|
+
result = await connection.run(strings.reduce((p, c, i) => `${p}$${i - 1}${c}`), params);
|
|
16
|
+
rows = await result.getRowObjectsJson();
|
|
17
|
+
}
|
|
18
|
+
finally {
|
|
19
|
+
connection.disconnectSync();
|
|
20
|
+
instance.closeSync();
|
|
21
|
+
}
|
|
22
|
+
return {
|
|
23
|
+
rows,
|
|
24
|
+
schema: getResultSchema(result),
|
|
25
|
+
duration: Date.now() - +date,
|
|
26
|
+
date
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function getResultSchema(result) {
|
|
31
|
+
return result.columnNames().map((name, i) => ({ name, type: getColumnType(result.columnType(i)) }));
|
|
32
|
+
}
|
|
33
|
+
function getColumnType(type) {
|
|
34
|
+
switch (type) {
|
|
35
|
+
case BOOLEAN:
|
|
36
|
+
return "boolean";
|
|
37
|
+
case BIT:
|
|
38
|
+
case TINYINT:
|
|
39
|
+
case SMALLINT:
|
|
40
|
+
case INTEGER:
|
|
41
|
+
case UTINYINT:
|
|
42
|
+
case USMALLINT:
|
|
43
|
+
case UINTEGER:
|
|
44
|
+
case VARINT:
|
|
45
|
+
return "integer";
|
|
46
|
+
case BIGINT:
|
|
47
|
+
case UBIGINT:
|
|
48
|
+
case HUGEINT:
|
|
49
|
+
case UHUGEINT:
|
|
50
|
+
return "bigint";
|
|
51
|
+
case FLOAT:
|
|
52
|
+
case DOUBLE:
|
|
53
|
+
return "number";
|
|
54
|
+
case TIMESTAMP:
|
|
55
|
+
case TIMESTAMP_S:
|
|
56
|
+
case TIMESTAMP_MS:
|
|
57
|
+
case TIMESTAMP_NS:
|
|
58
|
+
case TIMESTAMPTZ:
|
|
59
|
+
case DATE:
|
|
60
|
+
return "date";
|
|
61
|
+
case TIME:
|
|
62
|
+
case VARCHAR:
|
|
63
|
+
case UUID:
|
|
64
|
+
return "string";
|
|
65
|
+
case BLOB:
|
|
66
|
+
return "buffer";
|
|
67
|
+
case INTERVAL:
|
|
68
|
+
return "array";
|
|
69
|
+
default:
|
|
70
|
+
return "other";
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { ColumnSchema, QueryParam } from "../runtime/index.js";
|
|
2
|
+
export { hash as getQueryHash, nameHash as getNameHash } from "../lib/hash.js";
|
|
3
|
+
export type DatabaseConfig = DuckDBConfig | SQLiteConfig | SnowflakeConfig | PostgresConfig;
|
|
4
|
+
export type DuckDBConfig = {
|
|
5
|
+
type: "duckdb";
|
|
6
|
+
path?: string;
|
|
7
|
+
options?: {
|
|
8
|
+
[key: string]: string;
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
export type SQLiteConfig = {
|
|
12
|
+
type: "sqlite";
|
|
13
|
+
path?: string;
|
|
14
|
+
};
|
|
15
|
+
export type SnowflakeConfig = {
|
|
16
|
+
type: "snowflake";
|
|
17
|
+
account: string;
|
|
18
|
+
database?: string;
|
|
19
|
+
role?: string;
|
|
20
|
+
schema?: string;
|
|
21
|
+
username?: string;
|
|
22
|
+
warehouse?: string;
|
|
23
|
+
password?: string;
|
|
24
|
+
};
|
|
25
|
+
export type PostgresConfig = {
|
|
26
|
+
type: "postgres";
|
|
27
|
+
host?: string;
|
|
28
|
+
port?: string | number;
|
|
29
|
+
username?: string;
|
|
30
|
+
password?: string;
|
|
31
|
+
database?: string;
|
|
32
|
+
ssl?: boolean;
|
|
33
|
+
};
|
|
34
|
+
export type DatabaseContext = {
|
|
35
|
+
cwd: string;
|
|
36
|
+
};
|
|
37
|
+
export type QueryTemplateFunction = (strings: readonly string[], ...params: QueryParam[]) => Promise<SerializableQueryResult>;
|
|
38
|
+
export type SerializableQueryResult = {
|
|
39
|
+
rows: Record<string, unknown>[];
|
|
40
|
+
schema: ColumnSchema[];
|
|
41
|
+
duration: number;
|
|
42
|
+
date: Date;
|
|
43
|
+
};
|
|
44
|
+
export declare function getDatabaseConfig(sourcePath: string, databaseName: string): Promise<DatabaseConfig>;
|
|
45
|
+
export declare function getDatabase(config: DatabaseConfig, context: DatabaseContext): Promise<QueryTemplateFunction>;
|
|
46
|
+
export declare function getQueryCachePath(sourcePath: string, databaseName: string, strings: readonly string[], ...params: unknown[]): Promise<string>;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { createReadStream } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { json } from "node:stream/consumers";
|
|
4
|
+
import { isEnoent } from "../lib/error.js";
|
|
5
|
+
import { hash as getQueryHash, nameHash as getNameHash } from "../lib/hash.js";
|
|
6
|
+
export { hash as getQueryHash, nameHash as getNameHash } from "../lib/hash.js";
|
|
7
|
+
export async function getDatabaseConfig(sourcePath, databaseName) {
|
|
8
|
+
const sourceDir = dirname(sourcePath);
|
|
9
|
+
const configPath = join(sourceDir, ".observable", "databases.json");
|
|
10
|
+
let config;
|
|
11
|
+
try {
|
|
12
|
+
const configStream = createReadStream(configPath, "utf-8");
|
|
13
|
+
const configs = (await json(configStream));
|
|
14
|
+
config = configs[databaseName];
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
if (!isEnoent(error))
|
|
18
|
+
throw error;
|
|
19
|
+
}
|
|
20
|
+
if (config === undefined) {
|
|
21
|
+
if (databaseName === "postgres")
|
|
22
|
+
config = { type: "postgres" };
|
|
23
|
+
else if (databaseName === "duckdb")
|
|
24
|
+
config = { type: "duckdb" };
|
|
25
|
+
else if (databaseName === "sqlite")
|
|
26
|
+
config = { type: "sqlite" };
|
|
27
|
+
else if (/\.duckdb$/i.test(databaseName))
|
|
28
|
+
config = { type: "duckdb", path: databaseName };
|
|
29
|
+
else if (/\.db$/i.test(databaseName))
|
|
30
|
+
config = { type: "sqlite", path: databaseName }; // TODO disambiguate
|
|
31
|
+
else
|
|
32
|
+
throw new Error(`database not found: ${databaseName}`);
|
|
33
|
+
}
|
|
34
|
+
return config;
|
|
35
|
+
}
|
|
36
|
+
export async function getDatabase(config, context) {
|
|
37
|
+
switch (config.type) {
|
|
38
|
+
case "duckdb":
|
|
39
|
+
return (await import("./duckdb.js")).default(config, context);
|
|
40
|
+
case "sqlite":
|
|
41
|
+
return (await import("./sqlite.js")).default(config, context);
|
|
42
|
+
case "snowflake":
|
|
43
|
+
return (await import("./snowflake.js")).default(config);
|
|
44
|
+
case "postgres":
|
|
45
|
+
return (await import("./postgres.js")).default(config);
|
|
46
|
+
default:
|
|
47
|
+
throw new Error(`unsupported database type: ${config["type"]}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
export async function getQueryCachePath(sourcePath, databaseName, strings, ...params) {
|
|
51
|
+
const sourceDir = dirname(sourcePath);
|
|
52
|
+
const cacheName = `${await getNameHash(databaseName)}-${await getQueryHash(strings, ...params)}.json`;
|
|
53
|
+
return join(sourceDir, ".observable", "cache", cacheName);
|
|
54
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import Postgres from "postgres";
|
|
2
|
+
import { optionalBoolean, optionalNumber, optionalString } from "./options.js";
|
|
3
|
+
export default function postgres(options) {
|
|
4
|
+
return async (strings, ...params) => {
|
|
5
|
+
const sql = Postgres({
|
|
6
|
+
host: optionalString(options.host),
|
|
7
|
+
port: optionalNumber(options.port),
|
|
8
|
+
username: optionalString(options.username),
|
|
9
|
+
password: optionalString(options.password),
|
|
10
|
+
database: optionalString(options.database),
|
|
11
|
+
ssl: optionalBoolean(options.ssl) ? { rejectUnauthorized: false } : false
|
|
12
|
+
});
|
|
13
|
+
const date = new Date();
|
|
14
|
+
let rows;
|
|
15
|
+
try {
|
|
16
|
+
rows = await sql.unsafe(strings.reduce((p, c, i) => `${p}$${i}${c}`), params);
|
|
17
|
+
}
|
|
18
|
+
finally {
|
|
19
|
+
await sql.end();
|
|
20
|
+
}
|
|
21
|
+
return {
|
|
22
|
+
rows,
|
|
23
|
+
schema: rows.columns.map(getColumnSchema),
|
|
24
|
+
duration: Date.now() - +date,
|
|
25
|
+
date
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function getColumnSchema(column) {
|
|
30
|
+
return { name: column.name, type: getColumnType(column.type) };
|
|
31
|
+
}
|
|
32
|
+
// https://github.com/brianc/node-pg-types/blob/master/lib/textParsers.js#L166
|
|
33
|
+
function getColumnType(oid) {
|
|
34
|
+
switch (oid) {
|
|
35
|
+
case 20: // int8
|
|
36
|
+
return "bigint";
|
|
37
|
+
case 21: // int2
|
|
38
|
+
case 23: // int4
|
|
39
|
+
case 26: // oid
|
|
40
|
+
return "integer";
|
|
41
|
+
case 700: // float4/real
|
|
42
|
+
case 701: // float8/double
|
|
43
|
+
return "number";
|
|
44
|
+
case 16: // bool
|
|
45
|
+
return "boolean";
|
|
46
|
+
case 1082: // date
|
|
47
|
+
case 1114: // timestamp without timezone
|
|
48
|
+
case 1184: // timestamp
|
|
49
|
+
return "date";
|
|
50
|
+
case 651: // cidr[]
|
|
51
|
+
case 1000: // bool[]
|
|
52
|
+
case 1001: // byte[]
|
|
53
|
+
case 1002: // string[]
|
|
54
|
+
case 1005: // int2[]
|
|
55
|
+
case 1007: // int4[]
|
|
56
|
+
case 1028: // oid[]
|
|
57
|
+
case 1016: // int8[]
|
|
58
|
+
case 1017: // point[]
|
|
59
|
+
case 1021: // float4[]
|
|
60
|
+
case 1022: // float8[]
|
|
61
|
+
case 1231: // numeric[]
|
|
62
|
+
case 1014: // char[]
|
|
63
|
+
case 1015: // varchar[]
|
|
64
|
+
case 1008: // string[]
|
|
65
|
+
case 1009: // string[]
|
|
66
|
+
case 1040: // macaddr[]
|
|
67
|
+
case 1041: // inet[]
|
|
68
|
+
case 1115: // timestamp without time zone[]
|
|
69
|
+
case 1182: // date[]
|
|
70
|
+
case 1185: // timestamp with time zone[]
|
|
71
|
+
case 1187: // interval[]
|
|
72
|
+
case 199: // json[]
|
|
73
|
+
case 3807: // jsonb[]
|
|
74
|
+
case 3907: // numrange[]
|
|
75
|
+
case 2951: // uuid[]
|
|
76
|
+
case 791: // money[]
|
|
77
|
+
case 1183: // time[]
|
|
78
|
+
case 1270: // timetz[]
|
|
79
|
+
return "array";
|
|
80
|
+
case 1186: // interval
|
|
81
|
+
case 114: // json
|
|
82
|
+
case 3802: // jsonb
|
|
83
|
+
case 600: // point
|
|
84
|
+
case 718: // circle
|
|
85
|
+
return "object";
|
|
86
|
+
case 17: // bytea
|
|
87
|
+
return "buffer";
|
|
88
|
+
case 18: // char
|
|
89
|
+
case 1700: // numeric
|
|
90
|
+
case 25: // text
|
|
91
|
+
case 24: // regproc
|
|
92
|
+
default:
|
|
93
|
+
return "string";
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import Snowflake from "snowflake-sdk";
|
|
2
|
+
import { optionalString } from "./options.js";
|
|
3
|
+
Snowflake.configure({ logLevel: "OFF" });
|
|
4
|
+
export default function snowflake(options) {
|
|
5
|
+
return async (strings, ...params) => {
|
|
6
|
+
const connection = await connect({
|
|
7
|
+
account: String(options.account),
|
|
8
|
+
username: optionalString(options.username),
|
|
9
|
+
password: optionalString(options.password),
|
|
10
|
+
database: optionalString(options.database),
|
|
11
|
+
schema: optionalString(options.schema),
|
|
12
|
+
warehouse: optionalString(options.warehouse),
|
|
13
|
+
role: optionalString(options.role)
|
|
14
|
+
});
|
|
15
|
+
let result;
|
|
16
|
+
try {
|
|
17
|
+
result = await execute(connection, strings.join("?"), params);
|
|
18
|
+
}
|
|
19
|
+
finally {
|
|
20
|
+
await destroy(connection);
|
|
21
|
+
}
|
|
22
|
+
return result;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
async function connect(options) {
|
|
26
|
+
const connection = Snowflake.createConnection(options);
|
|
27
|
+
await new Promise((resolve, reject) => {
|
|
28
|
+
connection.connect((error) => {
|
|
29
|
+
if (error)
|
|
30
|
+
return reject(error);
|
|
31
|
+
resolve();
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
return connection;
|
|
35
|
+
}
|
|
36
|
+
async function destroy(connection) {
|
|
37
|
+
await new Promise((resolve, reject) => {
|
|
38
|
+
connection.destroy((error) => {
|
|
39
|
+
if (error)
|
|
40
|
+
return reject(error);
|
|
41
|
+
resolve();
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
async function execute(connection, sql, params) {
|
|
46
|
+
return new Promise((resolve, reject) => {
|
|
47
|
+
const date = new Date();
|
|
48
|
+
connection.execute({
|
|
49
|
+
sqlText: sql,
|
|
50
|
+
binds: params,
|
|
51
|
+
complete(error, statement, rows) {
|
|
52
|
+
if (error)
|
|
53
|
+
return reject(error);
|
|
54
|
+
resolve({
|
|
55
|
+
rows: rows,
|
|
56
|
+
schema: getStatementSchema(statement),
|
|
57
|
+
duration: Date.now() - +date,
|
|
58
|
+
date
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
function getStatementSchema(statement) {
|
|
65
|
+
return statement.getColumns().map(getColumnSchema);
|
|
66
|
+
}
|
|
67
|
+
function getColumnSchema(column) {
|
|
68
|
+
return { name: column.getName(), type: getColumnType(column), nullable: column.isNullable() };
|
|
69
|
+
}
|
|
70
|
+
function getColumnType(column) {
|
|
71
|
+
const type = column.getType();
|
|
72
|
+
switch (type.toLowerCase()) {
|
|
73
|
+
case "date":
|
|
74
|
+
case "datetime":
|
|
75
|
+
case "timestamp":
|
|
76
|
+
case "timestamp_ltz":
|
|
77
|
+
case "timestamp_ntz":
|
|
78
|
+
case "timestamp_tz":
|
|
79
|
+
return "date";
|
|
80
|
+
case "time":
|
|
81
|
+
case "text":
|
|
82
|
+
return "string";
|
|
83
|
+
case "fixed":
|
|
84
|
+
return column.getScale() === 0 ? "integer" : "number";
|
|
85
|
+
case "float":
|
|
86
|
+
case "number":
|
|
87
|
+
case "real":
|
|
88
|
+
return "number";
|
|
89
|
+
case "binary":
|
|
90
|
+
return "buffer";
|
|
91
|
+
case "array":
|
|
92
|
+
return "array";
|
|
93
|
+
case "boolean":
|
|
94
|
+
return "boolean";
|
|
95
|
+
case "object":
|
|
96
|
+
case "variant":
|
|
97
|
+
return "object";
|
|
98
|
+
default:
|
|
99
|
+
console.warn(`unknown type: ${type}`);
|
|
100
|
+
return "other";
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import Database from "better-sqlite3";
|
|
3
|
+
export default function sqlite({ path = ":memory:" }, context) {
|
|
4
|
+
if (path !== undefined)
|
|
5
|
+
path = join(context.cwd, path);
|
|
6
|
+
return async (strings, ...params) => {
|
|
7
|
+
const date = new Date();
|
|
8
|
+
const database = new Database(path);
|
|
9
|
+
try {
|
|
10
|
+
const statement = database.prepare(strings.join("?"));
|
|
11
|
+
const rows = statement.all(...params);
|
|
12
|
+
return {
|
|
13
|
+
rows,
|
|
14
|
+
schema: getStatementSchema(statement),
|
|
15
|
+
duration: Date.now() - +date,
|
|
16
|
+
date
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
finally {
|
|
20
|
+
database.close();
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function getStatementSchema(statement) {
|
|
25
|
+
return statement
|
|
26
|
+
.columns()
|
|
27
|
+
.map((column) => ({ name: column.name, type: getColumnType(column.type) }));
|
|
28
|
+
}
|
|
29
|
+
function getColumnType(type) {
|
|
30
|
+
switch (type) {
|
|
31
|
+
case "INT":
|
|
32
|
+
case "INTEGER":
|
|
33
|
+
case "TINYINT":
|
|
34
|
+
case "SMALLINT":
|
|
35
|
+
case "MEDIUMINT":
|
|
36
|
+
case "BIGINT":
|
|
37
|
+
case "UNSIGNED BIG INT":
|
|
38
|
+
case "INT2":
|
|
39
|
+
case "INT8":
|
|
40
|
+
return "integer";
|
|
41
|
+
case "TEXT":
|
|
42
|
+
case "CLOB":
|
|
43
|
+
return "string";
|
|
44
|
+
case "REAL":
|
|
45
|
+
case "DOUBLE":
|
|
46
|
+
case "DOUBLE PRECISION":
|
|
47
|
+
case "FLOAT":
|
|
48
|
+
case "NUMERIC":
|
|
49
|
+
return "number";
|
|
50
|
+
case "BLOB":
|
|
51
|
+
return "buffer";
|
|
52
|
+
case "DATE":
|
|
53
|
+
case "DATETIME":
|
|
54
|
+
return "string"; // TODO convert strings to Date instances in sql.js
|
|
55
|
+
case null:
|
|
56
|
+
return "other";
|
|
57
|
+
default:
|
|
58
|
+
return /^(?:(?:(?:VARYING|NATIVE) )?CHARACTER|(?:N|VAR|NVAR)CHAR)\(/.test(type)
|
|
59
|
+
? "string"
|
|
60
|
+
: /^(?:DECIMAL|NUMERIC)\(/.test(type)
|
|
61
|
+
? "number"
|
|
62
|
+
: "other";
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -25,6 +25,8 @@ function getDefaultRange(name) {
|
|
|
25
25
|
switch (name) {
|
|
26
26
|
case "@duckdb/duckdb-wasm":
|
|
27
27
|
return "@1.29.0"; // https://github.com/duckdb/duckdb-wasm/issues/1994
|
|
28
|
+
case "apache-arrow":
|
|
29
|
+
return "@17.0.0"; // to match @duckdb/duckdb-wasm 1.29.0
|
|
28
30
|
default:
|
|
29
31
|
return "";
|
|
30
32
|
}
|