@observablehq/notebook-kit 1.1.1 → 1.2.0-rc.2

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/query.js CHANGED
@@ -3,6 +3,7 @@ import { mkdir, writeFile } from "node:fs/promises";
3
3
  import { dirname, join } from "node:path";
4
4
  import { parseArgs } from "node:util";
5
5
  import { getDatabase, getDatabaseConfig, getQueryCachePath } from "../src/databases/index.js";
6
+ import { getReplacer } from "../src/databases/index.js";
6
7
  if (process.argv[1] === import.meta.filename)
7
8
  run();
8
9
  export default async function run(args) {
@@ -49,16 +50,19 @@ export default async function run(args) {
49
50
  const database = await getDatabase(config);
50
51
  const results = await database.call(null, strings, ...params);
51
52
  await mkdir(dirname(cachePath), { recursive: true });
52
- await writeFile(cachePath, JSON.stringify(results, replace));
53
+ await writeFile(cachePath, JSON.stringify(results, await getReplacer(config)));
53
54
  console.log(join(values.root, cachePath));
54
55
  }
55
56
  catch (error) {
56
- console.error(String(error));
57
+ console.error(getErrorMessage(error));
57
58
  process.exit(1);
58
59
  }
59
60
  }
60
- // Force dates to be serialized as ISO 8601 UTC, undoing this:
61
- // https://github.com/snowflakedb/snowflake-connector-nodejs/blob/a9174fb7/lib/connection/result/sf_timestamp.js#L177-L179
62
- function replace(key, value) {
63
- return this[key] instanceof Date ? Date.prototype.toJSON.call(this[key]) : value;
61
+ function getErrorMessage(error) {
62
+ return error instanceof Error &&
63
+ "errors" in error && // AggregateError
64
+ Array.isArray(error.errors) &&
65
+ error.errors.length > 0
66
+ ? String(error.errors[0])
67
+ : String(error);
64
68
  }
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.1.1",
8
+ "version": "1.2.0-rc.2",
9
9
  "type": "module",
10
10
  "scripts": {
11
11
  "test": "vitest",
@@ -63,8 +63,10 @@
63
63
  "vite": "^7.0.0"
64
64
  },
65
65
  "devDependencies": {
66
+ "@databricks/sql": "^1.11.0",
66
67
  "@duckdb/node-api": "^1.3.2-alpha.26",
67
68
  "@eslint/js": "^9.29.0",
69
+ "@google-cloud/bigquery": "^8.1.1",
68
70
  "@types/jsdom": "^21.1.7",
69
71
  "@types/markdown-it": "^14.1.2",
70
72
  "bun-types": "^1.2.20",
@@ -79,14 +81,22 @@
79
81
  "vitest": "^3.2.4"
80
82
  },
81
83
  "peerDependencies": {
84
+ "@databricks/sql": "^1.11.0",
82
85
  "@duckdb/node-api": "^1.3.2-alpha.26",
86
+ "@google-cloud/bigquery": "^8.1.1",
83
87
  "postgres": "^3.4.7",
84
88
  "snowflake-sdk": "^2.1.3"
85
89
  },
86
90
  "peerDependenciesMeta": {
91
+ "@databricks/sql": {
92
+ "optional": true
93
+ },
87
94
  "@duckdb/node-api": {
88
95
  "optional": true
89
96
  },
97
+ "@google-cloud/bigquery": {
98
+ "optional": true
99
+ },
90
100
  "postgres": {
91
101
  "optional": true
92
102
  },
@@ -0,0 +1,5 @@
1
+ import type { BigQueryConfig, QueryTemplateFunction } from "./index.js";
2
+ export default function bigquery({ type, ...options }: BigQueryConfig): QueryTemplateFunction;
3
+ export declare function replacer(this: {
4
+ [key: string]: unknown;
5
+ }, key: string, value: unknown): unknown;
@@ -0,0 +1,59 @@
1
+ import { BigQuery } from "@google-cloud/bigquery";
2
+ import { BigQueryDate, BigQueryDatetime, BigQueryTimestamp } from "@google-cloud/bigquery";
3
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
4
+ export default function bigquery({ type, ...options }) {
5
+ return async (strings, ...params) => {
6
+ const bigquery = new BigQuery(options);
7
+ const date = new Date();
8
+ const [job] = await bigquery.createQueryJob({ query: strings.join("?"), params });
9
+ const [rows, , response] = await job.getQueryResults();
10
+ return { rows, schema: getTableSchema(response.schema), duration: Date.now() - +date, date };
11
+ };
12
+ }
13
+ function getTableSchema({ fields }) {
14
+ return fields.map(getColumnSchema);
15
+ }
16
+ function getColumnSchema(field) {
17
+ return { name: field.name, type: getColumnType(field) };
18
+ }
19
+ function getColumnType({ type, mode }) {
20
+ switch (mode) {
21
+ case "REPEATED":
22
+ return "array";
23
+ }
24
+ switch (type) {
25
+ case "DATE":
26
+ case "DATETIME":
27
+ case "TIMESTAMP":
28
+ return "date";
29
+ case "BOOL":
30
+ case "BOOLEAN":
31
+ return "boolean";
32
+ case "STRUCT":
33
+ case "RECORD":
34
+ case "GEOGRAPHY":
35
+ return "object";
36
+ case "BYTES":
37
+ return "buffer";
38
+ case "FLOAT":
39
+ case "FLOAT64":
40
+ return "number";
41
+ case "INT64":
42
+ case "INTEGER":
43
+ return "number";
44
+ case "NUMERIC":
45
+ return "number";
46
+ case "STRING":
47
+ case "TIME":
48
+ default:
49
+ return "string";
50
+ }
51
+ }
52
+ export function replacer(key, value) {
53
+ const v = this[key];
54
+ return v instanceof BigQueryDate ||
55
+ v instanceof BigQueryDatetime ||
56
+ v instanceof BigQueryTimestamp
57
+ ? new Date(v.value).toJSON()
58
+ : value;
59
+ }
@@ -0,0 +1,2 @@
1
+ import type { DatabricksConfig, QueryTemplateFunction } from "./index.js";
2
+ export default function databricks({ type, ...options }: DatabricksConfig): QueryTemplateFunction;
@@ -0,0 +1,65 @@
1
+ import { DBSQLClient, DBSQLLogger, LogLevel, thrift } from "@databricks/sql";
2
+ const TTypeId = thrift.TCLIService_types.TTypeId;
3
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
4
+ export default function databricks({ type, ...options }) {
5
+ return async (strings, ...params) => {
6
+ const logger = new DBSQLLogger({ level: LogLevel.error });
7
+ const client = new DBSQLClient({ logger });
8
+ await client.connect(options);
9
+ try {
10
+ const session = await client.openSession();
11
+ try {
12
+ const date = new Date();
13
+ const operation = await session.executeStatement(strings.join("?"), {
14
+ runAsync: true,
15
+ ordinalParameters: params,
16
+ maxRows: 10000
17
+ });
18
+ try {
19
+ const rows = (await operation.fetchAll());
20
+ const schema = await operation.getSchema();
21
+ return { rows, schema: getTableSchema(schema), duration: Date.now() - +date, date };
22
+ }
23
+ finally {
24
+ await operation.close();
25
+ }
26
+ }
27
+ finally {
28
+ await session.close();
29
+ }
30
+ }
31
+ finally {
32
+ await client.close();
33
+ }
34
+ };
35
+ }
36
+ function getTableSchema({ columns }) {
37
+ return columns.map(getColumnSchema);
38
+ }
39
+ function getColumnSchema(column) {
40
+ return { name: column.columnName, type: getColumnType(column.typeDesc) };
41
+ }
42
+ function getColumnType({ types: [type] }) {
43
+ switch (type.primitiveEntry?.type) {
44
+ case TTypeId.BINARY_TYPE:
45
+ return "buffer";
46
+ case TTypeId.BOOLEAN_TYPE:
47
+ return "boolean";
48
+ case TTypeId.BIGINT_TYPE:
49
+ case TTypeId.TINYINT_TYPE:
50
+ case TTypeId.SMALLINT_TYPE:
51
+ case TTypeId.INT_TYPE:
52
+ case TTypeId.DECIMAL_TYPE:
53
+ return "integer";
54
+ case TTypeId.DOUBLE_TYPE:
55
+ case TTypeId.FLOAT_TYPE:
56
+ return "number";
57
+ case TTypeId.DATE_TYPE:
58
+ case TTypeId.TIMESTAMP_TYPE:
59
+ case TTypeId.INTERVAL_DAY_TIME_TYPE:
60
+ case TTypeId.INTERVAL_YEAR_MONTH_TYPE:
61
+ return "date";
62
+ default:
63
+ return "string";
64
+ }
65
+ }
@@ -1,5 +1,24 @@
1
1
  import type { ColumnSchema, QueryParam } from "../runtime/index.js";
2
- export type DatabaseConfig = DuckDBConfig | SQLiteConfig | SnowflakeConfig | PostgresConfig;
2
+ export type DatabaseConfig = BigQueryConfig | DatabricksConfig | DuckDBConfig | SQLiteConfig | SnowflakeConfig | PostgresConfig;
3
+ export type BigQueryConfig = {
4
+ type: "bigquery";
5
+ apiKey?: string;
6
+ keyFilename?: string;
7
+ keyFile?: string;
8
+ projectId?: string;
9
+ };
10
+ export type DatabricksConfig = {
11
+ type: "databricks";
12
+ host: string;
13
+ path: string;
14
+ } & ({
15
+ authType?: "access-token";
16
+ token: string;
17
+ } | {
18
+ authType: "databricks-oauth";
19
+ oauthClientId?: string;
20
+ oauthClientSecret?: string;
21
+ });
3
22
  export type DuckDBConfig = {
4
23
  type: "duckdb";
5
24
  path?: string;
@@ -39,4 +58,8 @@ export type SerializableQueryResult = {
39
58
  };
40
59
  export declare function getDatabaseConfig(sourcePath: string, databaseName: string): Promise<DatabaseConfig>;
41
60
  export declare function getDatabase(config: DatabaseConfig): Promise<QueryTemplateFunction>;
61
+ export type Replacer = (this: {
62
+ [key: string]: unknown;
63
+ }, key: string, value: unknown) => unknown;
64
+ export declare function getReplacer(config: DatabaseConfig): Promise<Replacer | undefined>;
42
65
  export declare function getQueryCachePath(sourcePath: string, databaseName: string, strings: readonly string[], ...params: unknown[]): Promise<string>;
@@ -26,7 +26,7 @@ export async function getDatabaseConfig(sourcePath, databaseName) {
26
26
  else if (/\.duckdb$/i.test(databaseName))
27
27
  config = { type: "duckdb", path: databaseName };
28
28
  else if (/\.db$/i.test(databaseName))
29
- config = { type: "sqlite", path: databaseName }; // TODO disambiguate
29
+ config = { type: "sqlite", path: databaseName };
30
30
  else
31
31
  throw new Error(`database not found: ${databaseName}`);
32
32
  }
@@ -34,10 +34,14 @@ export async function getDatabaseConfig(sourcePath, databaseName) {
34
34
  }
35
35
  export async function getDatabase(config) {
36
36
  switch (config.type) {
37
+ case "bigquery":
38
+ return (await import("./bigquery.js")).default(config);
39
+ case "databricks":
40
+ return (await import("./databricks.js")).default(config);
37
41
  case "duckdb":
38
42
  return (await import("./duckdb.js")).default(config);
39
43
  case "sqlite":
40
- return (await import(process.versions.bun ? "./sqlite-bun.js" : "./sqlite-node.js")).default(config);
44
+ return (await import(process.versions.bun ? "./sqlite-bun.js" : "./sqlite-node.js")).default(config); // prettier-ignore
41
45
  case "snowflake":
42
46
  return (await import("./snowflake.js")).default(config);
43
47
  case "postgres":
@@ -46,6 +50,14 @@ export async function getDatabase(config) {
46
50
  throw new Error(`unsupported database type: ${config["type"]}`);
47
51
  }
48
52
  }
53
+ export async function getReplacer(config) {
54
+ switch (config.type) {
55
+ case "bigquery":
56
+ return (await import("./bigquery.js")).replacer;
57
+ case "snowflake":
58
+ return (await import("./snowflake.js")).replacer;
59
+ }
60
+ }
49
61
  export async function getQueryCachePath(sourcePath, databaseName, strings, ...params) {
50
62
  const sourceDir = dirname(sourcePath);
51
63
  const cacheName = `${await getNameHash(databaseName)}-${await getQueryHash(strings, ...params)}.json`;
@@ -1,2 +1,5 @@
1
1
  import { QueryTemplateFunction, SnowflakeConfig } from "./index.js";
2
2
  export default function snowflake(options: SnowflakeConfig): QueryTemplateFunction;
3
+ export declare function replacer(this: {
4
+ [key: string]: unknown;
5
+ }, key: string, value: unknown): unknown;
@@ -100,3 +100,9 @@ function getColumnType(column) {
100
100
  return "other";
101
101
  }
102
102
  }
103
+ // Force dates to be serialized as ISO 8601 UTC, undoing this:
104
+ // https://github.com/snowflakedb/snowflake-connector-nodejs/blob/a9174fb7/lib/connection/result/sf_timestamp.js#L177-L179
105
+ export function replacer(key, value) {
106
+ const v = this[key];
107
+ return v instanceof Date ? Date.prototype.toJSON.call(v) : value;
108
+ }
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.1.1",
8
+ "version": "1.2.0-rc.2",
9
9
  "type": "module",
10
10
  "scripts": {
11
11
  "test": "vitest",
@@ -63,8 +63,10 @@
63
63
  "vite": "^7.0.0"
64
64
  },
65
65
  "devDependencies": {
66
+ "@databricks/sql": "^1.11.0",
66
67
  "@duckdb/node-api": "^1.3.2-alpha.26",
67
68
  "@eslint/js": "^9.29.0",
69
+ "@google-cloud/bigquery": "^8.1.1",
68
70
  "@types/jsdom": "^21.1.7",
69
71
  "@types/markdown-it": "^14.1.2",
70
72
  "bun-types": "^1.2.20",
@@ -79,14 +81,22 @@
79
81
  "vitest": "^3.2.4"
80
82
  },
81
83
  "peerDependencies": {
84
+ "@databricks/sql": "^1.11.0",
82
85
  "@duckdb/node-api": "^1.3.2-alpha.26",
86
+ "@google-cloud/bigquery": "^8.1.1",
83
87
  "postgres": "^3.4.7",
84
88
  "snowflake-sdk": "^2.1.3"
85
89
  },
86
90
  "peerDependenciesMeta": {
91
+ "@databricks/sql": {
92
+ "optional": true
93
+ },
87
94
  "@duckdb/node-api": {
88
95
  "optional": true
89
96
  },
97
+ "@google-cloud/bigquery": {
98
+ "optional": true
99
+ },
90
100
  "postgres": {
91
101
  "optional": true
92
102
  },