queria 0.0.0-stage → 0.1.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.
Files changed (85) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +58 -2
  3. package/dist/boot.d.ts +24 -0
  4. package/dist/boot.js +137 -0
  5. package/dist/boot.js.map +1 -0
  6. package/dist/client.d.ts +56 -0
  7. package/dist/client.js +194 -0
  8. package/dist/client.js.map +1 -0
  9. package/dist/elements/define.d.ts +1 -0
  10. package/dist/elements/define.js +4 -0
  11. package/dist/elements/define.js.map +1 -0
  12. package/dist/elements/index.d.ts +40 -0
  13. package/dist/elements/index.js +144 -0
  14. package/dist/elements/index.js.map +1 -0
  15. package/dist/index.d.ts +3 -0
  16. package/dist/index.js +4 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/react/Chart.d.ts +12 -0
  19. package/dist/react/Chart.js +42 -0
  20. package/dist/react/Chart.js.map +1 -0
  21. package/dist/react/DataMap.d.ts +12 -0
  22. package/dist/react/DataMap.js +44 -0
  23. package/dist/react/DataMap.js.map +1 -0
  24. package/dist/react/MapView.d.ts +13 -0
  25. package/dist/react/MapView.js +31 -0
  26. package/dist/react/MapView.js.map +1 -0
  27. package/dist/react/PoweredBy.d.ts +2 -0
  28. package/dist/react/PoweredBy.js +5 -0
  29. package/dist/react/PoweredBy.js.map +1 -0
  30. package/dist/react/index.d.ts +5 -0
  31. package/dist/react/index.js +6 -0
  32. package/dist/react/index.js.map +1 -0
  33. package/dist/react/query.d.ts +20 -0
  34. package/dist/react/query.js +50 -0
  35. package/dist/react/query.js.map +1 -0
  36. package/dist/render/attribution.d.ts +6 -0
  37. package/dist/render/attribution.js +17 -0
  38. package/dist/render/attribution.js.map +1 -0
  39. package/dist/render/chart.d.ts +27 -0
  40. package/dist/render/chart.js +50 -0
  41. package/dist/render/chart.js.map +1 -0
  42. package/dist/render/data-map.d.ts +61 -0
  43. package/dist/render/data-map.js +200 -0
  44. package/dist/render/data-map.js.map +1 -0
  45. package/dist/render/escape.d.ts +2 -0
  46. package/dist/render/escape.js +4 -0
  47. package/dist/render/escape.js.map +1 -0
  48. package/dist/render/index.d.ts +5 -0
  49. package/dist/render/index.js +6 -0
  50. package/dist/render/index.js.map +1 -0
  51. package/dist/render/map.d.ts +7 -0
  52. package/dist/render/map.js +9 -0
  53. package/dist/render/map.js.map +1 -0
  54. package/dist/render/poweredBy.d.ts +2 -0
  55. package/dist/render/poweredBy.js +5 -0
  56. package/dist/render/poweredBy.js.map +1 -0
  57. package/dist/sql.d.ts +10 -0
  58. package/dist/sql.js +13 -0
  59. package/dist/sql.js.map +1 -0
  60. package/package.json +91 -4
  61. package/src/boot.test.ts +65 -0
  62. package/src/boot.ts +141 -0
  63. package/src/client.test.ts +77 -0
  64. package/src/client.ts +247 -0
  65. package/src/elements/define.ts +4 -0
  66. package/src/elements/index.ts +151 -0
  67. package/src/index.ts +12 -0
  68. package/src/react/Chart.tsx +49 -0
  69. package/src/react/DataMap.tsx +52 -0
  70. package/src/react/MapView.tsx +45 -0
  71. package/src/react/PoweredBy.tsx +4 -0
  72. package/src/react/index.ts +5 -0
  73. package/src/react/query.tsx +64 -0
  74. package/src/render/attribution.test.ts +22 -0
  75. package/src/render/attribution.ts +21 -0
  76. package/src/render/chart.test.ts +23 -0
  77. package/src/render/chart.ts +80 -0
  78. package/src/render/data-map.test.ts +40 -0
  79. package/src/render/data-map.ts +230 -0
  80. package/src/render/escape.ts +4 -0
  81. package/src/render/index.ts +12 -0
  82. package/src/render/map.ts +18 -0
  83. package/src/render/poweredBy.ts +6 -0
  84. package/src/sql.test.ts +21 -0
  85. package/src/sql.ts +19 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 flo8s
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,59 @@
1
- # Temporary Holding Version
1
+ # queria
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Query and visualize [Queria](https://queria.io/) open data in the browser. SQL runs in DuckDB WASM, charts are drawn with Vega-Lite, and the sources and licenses of the datasets are shown under each chart.
4
+
5
+ This package is in 0.x. Breaking changes can happen in any release until 1.0.
6
+
7
+ ## Datasets
8
+
9
+ Declare every dataset the SQL reads by its fully qualified name (`handle/name`). In SQL, refer to it by the last part of the name, as in `e_stat.<schema>.<table>`. To read two datasets with the same name, give them aliases: `{ a: "alice/e_stat", b: "bob/e_stat" }`.
10
+
11
+ ## HTML
12
+
13
+ ```html
14
+ <script type="module">import "queria/elements/define";</script>
15
+
16
+ <queria-chart
17
+ datasets="queria/e_stat"
18
+ spec='{"mark": "line", "encoding": {"x": {"field": "year"}, "y": {"field": "population", "type": "quantitative"}}}'
19
+ sql="SELECT year, any_value(value) AS population FROM e_stat.ssds.a_municipal_population
20
+ WHERE area = '13224' AND cat01 = 'A1101' GROUP BY year ORDER BY year"></queria-chart>
21
+ ```
22
+
23
+ ## React
24
+
25
+ ```tsx
26
+ import { Chart, QueriaProvider } from "queria/react";
27
+
28
+ <QueriaProvider datasets={["queria/e_stat"]}>
29
+ <Chart sql={sql} spec={spec} />
30
+ </QueriaProvider>;
31
+ ```
32
+
33
+ ## JavaScript
34
+
35
+ ```ts
36
+ import { connect, renderChart } from "queria";
37
+
38
+ const q = connect({ datasets: ["queria/e_stat"] });
39
+ await renderChart(el, spec, await q.sql(sql));
40
+ ```
41
+
42
+ SQL that calls `ST_*` functions loads the DuckDB spatial extension on first use.
43
+
44
+ To read datasets that are not public, pass a data token (`qd_...`) or a function that returns one: `connect({ datasets, token })`. The token is sent to the Queria API and to the hosts of the declared datasets. DuckDB is shared by the page, so use one token per page.
45
+
46
+ ## Maps
47
+
48
+ ```html
49
+ <queria-map datasets="queria/e_stat" sql="SELECT area AS lg_code, value AS population FROM ..."
50
+ layer='{"area": "city", "code": "lg_code", "color": "population", "label": "lg_code"}'></queria-map>
51
+ ```
52
+
53
+ `layer` puts rows on the map by area code (`area` is `prefecture`, `city` or `small_area`, `code` is the column) or by position (`lng` and `lat` columns). `color` colors by a column: numbers from light to dark, other values one color each. `label` names a row on hover. In React, use `<DataMap sql={sql} layer={layer} />`; in JavaScript, `renderMap(el, layer, rows)`. Include maplibre-gl's CSS in the page. The map height is the CSS variable `--queria-map-height` (400px by default).
54
+
55
+ Charts need `vega`, `vega-lite`, `vega-embed` and `vega-interpreter`; maps need `maplibre-gl`. Install them only when you use them.
56
+
57
+ ## License
58
+
59
+ MIT
package/dist/boot.d.ts ADDED
@@ -0,0 +1,24 @@
1
+ import type { AsyncDuckDB, DuckDBBundles } from "@duckdb/duckdb-wasm";
2
+ /**
3
+ * Source put before the DuckDB worker script. It adds a Bearer token to the worker's XHRs, and only to
4
+ * the origins it was given. DuckDB WASM sends no headers of its own (neither CREATE SECRET nor
5
+ * EXTRA_HTTP_HEADERS reach the request in the browser), and keeping the token in a JS variable of the
6
+ * worker keeps it out of reach of SQL. It takes only { queriaAuth: { token, origins } } messages and
7
+ * does not pass them on to DuckDB.
8
+ */
9
+ export declare const authPatch = "(() => {\n let token = null;\n let origins = [];\n self.addEventListener(\"message\", (event) => {\n const auth = event.data && typeof event.data === \"object\" ? event.data.queriaAuth : undefined;\n if (auth === undefined) return;\n event.stopImmediatePropagation();\n token = auth && typeof auth.token === \"string\" ? auth.token : null;\n origins = auth && Array.isArray(auth.origins) ? auth.origins.map((o) => String(o) + \"/\") : [];\n });\n const open = XMLHttpRequest.prototype.open;\n XMLHttpRequest.prototype.open = function (method, url, ...rest) {\n open.call(this, method, url, ...rest);\n if (token !== null && origins.some((o) => String(url).startsWith(o))) {\n this.setRequestHeader(\"Authorization\", \"Bearer \" + token);\n }\n };\n})();\n";
10
+ export declare const bundlesAt: (base: string) => DuckDBBundles;
11
+ /**
12
+ * Send the token to these origins from now on, as well as to the origins given before: queries run
13
+ * concurrently, and one that is still reading must keep its origins. A null token sends none.
14
+ */
15
+ export declare const setAuth: (db: AsyncDuckDB, token: string | null, origins: readonly string[]) => void;
16
+ /** Start DuckDB from the given WASM URL, and from jsDelivr when that fails. */
17
+ export declare const boot: (wasm: string) => Promise<AsyncDuckDB>;
18
+ /** Whether the SQL calls an ST_* function outside string literals and comments. */
19
+ export declare const needsSpatial: (sql: string) => boolean;
20
+ /** Whether the error comes from the spatial extension not being loaded. */
21
+ export declare const isSpatialMissing: (message: string) => boolean;
22
+ export declare const loadSpatial: (db: AsyncDuckDB) => Promise<void>;
23
+ /** Run fn, loading spatial first when the SQL needs it, and once more after a missing-spatial error. */
24
+ export declare const withSpatial: <T>(db: AsyncDuckDB, sql: string, fn: () => Promise<T>) => Promise<T>;
package/dist/boot.js ADDED
@@ -0,0 +1,137 @@
1
+ import { stripLiterals } from "./sql.js";
2
+ /**
3
+ * Source put before the DuckDB worker script. It adds a Bearer token to the worker's XHRs, and only to
4
+ * the origins it was given. DuckDB WASM sends no headers of its own (neither CREATE SECRET nor
5
+ * EXTRA_HTTP_HEADERS reach the request in the browser), and keeping the token in a JS variable of the
6
+ * worker keeps it out of reach of SQL. It takes only { queriaAuth: { token, origins } } messages and
7
+ * does not pass them on to DuckDB.
8
+ */
9
+ export const authPatch = `(() => {
10
+ let token = null;
11
+ let origins = [];
12
+ self.addEventListener("message", (event) => {
13
+ const auth = event.data && typeof event.data === "object" ? event.data.queriaAuth : undefined;
14
+ if (auth === undefined) return;
15
+ event.stopImmediatePropagation();
16
+ token = auth && typeof auth.token === "string" ? auth.token : null;
17
+ origins = auth && Array.isArray(auth.origins) ? auth.origins.map((o) => String(o) + "/") : [];
18
+ });
19
+ const open = XMLHttpRequest.prototype.open;
20
+ XMLHttpRequest.prototype.open = function (method, url, ...rest) {
21
+ open.call(this, method, url, ...rest);
22
+ if (token !== null && origins.some((o) => String(url).startsWith(o))) {
23
+ this.setRequestHeader("Authorization", "Bearer " + token);
24
+ }
25
+ };
26
+ })();
27
+ `;
28
+ export const bundlesAt = (base) => ({
29
+ mvp: { mainModule: `${base}/duckdb-mvp.wasm`, mainWorker: `${base}/duckdb-browser-mvp.worker.js` },
30
+ eh: { mainModule: `${base}/duckdb-eh.wasm`, mainWorker: `${base}/duckdb-browser-eh.worker.js` },
31
+ });
32
+ const workers = new WeakMap();
33
+ const authOrigins = new WeakMap();
34
+ /**
35
+ * Send the token to these origins from now on, as well as to the origins given before: queries run
36
+ * concurrently, and one that is still reading must keep its origins. A null token sends none.
37
+ */
38
+ export const setAuth = (db, token, origins) => {
39
+ const all = token === null ? new Set() : new Set([...(authOrigins.get(db) ?? []), ...origins]);
40
+ authOrigins.set(db, all);
41
+ workers.get(db)?.postMessage({ queriaAuth: { token, origins: [...all] } });
42
+ };
43
+ const instantiate = async (bundles) => {
44
+ const duckdb = await import("@duckdb/duckdb-wasm");
45
+ const bundle = await duckdb.selectBundle(bundles);
46
+ if (!bundle.mainWorker)
47
+ throw new Error("The DuckDB bundle has no worker script");
48
+ // The worker lives on another origin, and a page with a strict-dynamic CSP blocks importScripts,
49
+ // so fetch the script and start it from a blob URL.
50
+ const res = await fetch(bundle.mainWorker);
51
+ if (!res.ok)
52
+ throw new Error(`${bundle.mainWorker}: ${res.status}`);
53
+ const url = URL.createObjectURL(new Blob([authPatch, await res.text()], { type: "text/javascript" }));
54
+ try {
55
+ // A CSP without blob: in worker-src throws here, so this is inside the try that revokes the URL.
56
+ const worker = new Worker(url);
57
+ const db = new duckdb.AsyncDuckDB(new duckdb.VoidLogger(), worker);
58
+ try {
59
+ // Pages without cross-origin isolation cannot start threads.
60
+ await (bundle.pthreadWorker
61
+ ? db.instantiate(bundle.mainModule, bundle.pthreadWorker).catch(() => db.instantiate(bundle.mainModule))
62
+ : db.instantiate(bundle.mainModule));
63
+ }
64
+ catch (e) {
65
+ await db.terminate().catch(() => undefined);
66
+ throw e;
67
+ }
68
+ workers.set(db, worker);
69
+ return db;
70
+ }
71
+ finally {
72
+ URL.revokeObjectURL(url);
73
+ }
74
+ };
75
+ /** Start DuckDB from the given WASM URL, and from jsDelivr when that fails. */
76
+ export const boot = async (wasm) => {
77
+ const db = await instantiate(bundlesAt(wasm)).catch(async (e) => {
78
+ const { getJsDelivrBundles } = await import("@duckdb/duckdb-wasm");
79
+ return instantiate(getJsDelivrBundles()).catch((e2) => {
80
+ throw new AggregateError([e, e2], "DuckDB WASM could not be started");
81
+ });
82
+ });
83
+ try {
84
+ // Read Parquet by range instead of downloading whole files. Arrow returns DECIMAL as an unscaled
85
+ // integer, so let DuckDB convert it to DOUBLE.
86
+ await db.open({ path: ":memory:", filesystem: { forceFullHTTPReads: false }, query: { castDecimalToDouble: true } });
87
+ const conn = await db.connect();
88
+ await conn.query("INSTALL ducklake; LOAD ducklake").finally(() => conn.close());
89
+ }
90
+ catch (e) {
91
+ await db.terminate().catch(() => undefined);
92
+ throw e;
93
+ }
94
+ return db;
95
+ };
96
+ // The spatial extension is large, so it is loaded only for SQL that needs it, and on the error
97
+ // DuckDB gives for a missing spatial function.
98
+ /** Whether the SQL calls an ST_* function outside string literals and comments. */
99
+ export const needsSpatial = (sql) => /\bST_\w+\s*\(/i.test(stripLiterals(sql));
100
+ /** Whether the error comes from the spatial extension not being loaded. */
101
+ export const isSpatialMissing = (message) => /exists in the spatial extension/i.test(message) || /\b(function|type) with name (st_\w+|geometry)\b.*does not exist/i.test(message);
102
+ const spatial = new WeakMap();
103
+ const spatialLoaded = new WeakSet();
104
+ export const loadSpatial = (db) => {
105
+ const found = spatial.get(db);
106
+ if (found)
107
+ return found;
108
+ const p = db
109
+ .connect()
110
+ .then((conn) => conn.query("INSTALL spatial; LOAD spatial").finally(() => conn.close()))
111
+ .then(() => {
112
+ spatialLoaded.add(db);
113
+ })
114
+ .catch((e) => {
115
+ spatial.delete(db);
116
+ throw e;
117
+ });
118
+ spatial.set(db, p);
119
+ return p;
120
+ };
121
+ /** Run fn, loading spatial first when the SQL needs it, and once more after a missing-spatial error. */
122
+ export const withSpatial = async (db, sql, fn) => {
123
+ if (needsSpatial(sql))
124
+ await loadSpatial(db);
125
+ // Once spatial is loaded, running the SQL again gives the same error.
126
+ const loaded = spatialLoaded.has(db);
127
+ try {
128
+ return await fn();
129
+ }
130
+ catch (e) {
131
+ if (loaded || !isSpatialMissing(String(e)))
132
+ throw e;
133
+ await loadSpatial(db);
134
+ return fn();
135
+ }
136
+ };
137
+ //# sourceMappingURL=boot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"boot.js","sourceRoot":"","sources":["../src/boot.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ /** A license of a dataset, as returned by the Queria API. */
2
+ export type License = {
3
+ id?: string;
4
+ title?: string;
5
+ url?: string;
6
+ };
7
+ /** The part of a dataset row from the Queria API that the package uses. */
8
+ export type Dataset = {
9
+ qualified_name: string;
10
+ title?: string;
11
+ page_url?: string;
12
+ licenses?: License[];
13
+ catalog_url: string;
14
+ versioned_catalog_url?: string;
15
+ };
16
+ /** Datasets to read, by fully qualified name. An array uses each name's last part as the SQL alias. */
17
+ export type Declared = readonly string[] | Readonly<Record<string, string>>;
18
+ export type ConnectOptions = {
19
+ datasets: Declared;
20
+ /** Base URL of the Queria API. */
21
+ apiUrl?: string;
22
+ /** Base URL of the DuckDB WASM bundles. jsDelivr is used when they cannot be loaded. */
23
+ wasmUrl?: string;
24
+ /**
25
+ * A data token (qd_...) for datasets that are not public, or a function that returns one. A function is
26
+ * called before each request, so it can return a fresh token. The token is sent to the API and to the
27
+ * hosts of the declared datasets. DuckDB is shared by the page, so use one token per page.
28
+ */
29
+ token?: string | (() => string | Promise<string>);
30
+ };
31
+ export type Connection = {
32
+ /** Run SQL and return rows as plain objects. Datasets the SQL references are attached on first use. */
33
+ sql: <T = Record<string, unknown>>(sql: string) => Promise<T[]>;
34
+ /**
35
+ * The declared datasets the SQL references, for showing their sources and licenses.
36
+ * Without SQL (rows computed elsewhere), every declared dataset.
37
+ */
38
+ sources: (sql?: string) => Promise<Dataset[]>;
39
+ };
40
+ /** Map each SQL alias to a fully qualified name, rejecting names the package cannot use. */
41
+ export declare const aliases: (declared: Declared) => Map<string, string>;
42
+ /** Look up a dataset row. The API returns the catalog URL; the package never builds it. */
43
+ export declare const lookup: (api: string, qualified: string, token?: string | null) => Promise<Dataset>;
44
+ /** Parquet files sit next to the catalog: .../ducklake.duckdb → .../ducklake.duckdb.files/ */
45
+ export declare const dataPath: (catalogUrl: string) => string;
46
+ /** Convert Arrow values to plain JS: lists to arrays, structs to objects, BIGINT and HUGEINT to number. */
47
+ export declare const plain: (v: unknown) => unknown;
48
+ /** Stop every DuckDB the package started. A later query starts it again. */
49
+ export declare const closeAll: () => Promise<void>;
50
+ /**
51
+ * Connect to Queria datasets. Declare every dataset the SQL reads by its fully qualified name;
52
+ * the SQL refers to it by alias, such as e_stat.census.population. Short names are never resolved.
53
+ */
54
+ export declare const connect: ({ datasets, apiUrl, wasmUrl, token }: ConnectOptions) => Connection;
55
+ /** Read a list of datasets from an HTML attribute: "queria/e_stat queria/jma" or "a=alice/x b=bob/x". */
56
+ export declare const parseDatasets: (attr: string) => Declared;
package/dist/client.js ADDED
@@ -0,0 +1,194 @@
1
+ import { boot, setAuth, withSpatial } from "./boot.js";
2
+ import { datasetsIn, lit } from "./sql.js";
3
+ const API = "https://api.queria.io";
4
+ // Must match the @duckdb/duckdb-wasm version in package.json "dependencies".
5
+ const WASM = "https://assets.queria.io/duckdb-wasm/1.33.1-dev57.0";
6
+ const ALIAS = /^[a-z_][a-z0-9_]*$/;
7
+ const QUALIFIED = /^[a-z0-9_-]+\/[a-z_][a-z0-9_]*$/i;
8
+ /** Map each SQL alias to a fully qualified name, rejecting names the package cannot use. */
9
+ export const aliases = (declared) => {
10
+ const pairs = Array.isArray(declared)
11
+ ? declared.map((q) => [q.split("/")[1] ?? "", q])
12
+ : Object.entries(declared);
13
+ pairs.forEach(([alias, q]) => {
14
+ if (!QUALIFIED.test(q))
15
+ throw new Error(`"${q}" is not a fully qualified dataset name such as "queria/e_stat"`);
16
+ if (!ALIAS.test(alias))
17
+ throw new Error(`"${alias}" cannot be used as an SQL alias`);
18
+ });
19
+ const map = new Map(pairs);
20
+ if (map.size !== pairs.length)
21
+ throw new Error("Two datasets share an alias. Declare them as { alias: name }");
22
+ return map;
23
+ };
24
+ /** Look up a dataset row. The API returns the catalog URL; the package never builds it. */
25
+ export const lookup = async (api, qualified, token = null) => {
26
+ const res = await fetch(`${api}/v1/datasets/${qualified}`, bearer(token));
27
+ if (res.status === 404)
28
+ throw new Error(`Dataset ${qualified} was not found`);
29
+ if (!res.ok)
30
+ throw new Error(`${api}/v1/datasets/${qualified}: ${res.status}`);
31
+ const d = (await res.json());
32
+ if (typeof d?.catalog_url !== "string" || typeof d.qualified_name !== "string")
33
+ throw new Error(`${api}/v1/datasets/${qualified}: the response has no catalog_url or qualified_name`);
34
+ return d;
35
+ };
36
+ const bearer = (token) => token === null ? undefined : { headers: { Authorization: `Bearer ${token}` } };
37
+ /** Parquet files sit next to the catalog: .../ducklake.duckdb → .../ducklake.duckdb.files/ */
38
+ export const dataPath = (catalogUrl) => `${catalogUrl.split("?")[0]}.files/`;
39
+ /**
40
+ * Attach one dataset. The catalog is fetched in a single GET: letting DuckDB read it by range
41
+ * costs hundreds of 16 KiB round trips for large catalogs. Data files are still read by range.
42
+ */
43
+ const attach = async (db, alias, d, token) => {
44
+ const url = d.versioned_catalog_url ?? d.catalog_url;
45
+ const res = await fetch(url, bearer(token));
46
+ if (!res.ok)
47
+ throw new Error(`${url}: ${res.status}`);
48
+ // One file per alias: an alias is attached once per DuckDB, while one dataset can be attached
49
+ // under two aliases, and DuckDB refuses to attach one path twice.
50
+ const file = `${alias}.duckdb`;
51
+ await db.registerFileBuffer(file, new Uint8Array(await res.arrayBuffer()));
52
+ const conn = await db.connect();
53
+ try {
54
+ await conn.query(`ATTACH ${lit(`ducklake:${file}`)} AS ${alias} (READ_ONLY, DATA_PATH ${lit(dataPath(d.catalog_url))}, OVERRIDE_DATA_PATH true)`);
55
+ }
56
+ finally {
57
+ await conn.close();
58
+ }
59
+ };
60
+ /** Convert Arrow values to plain JS: lists to arrays, structs to objects, BIGINT and HUGEINT to number. */
61
+ export const plain = (v) => {
62
+ if (typeof v === "bigint")
63
+ return Number(v);
64
+ if (v === null || typeof v !== "object")
65
+ return v;
66
+ if (Array.isArray(v))
67
+ return v.map(plain);
68
+ // 128-bit integers come as Arrow big numbers (typed arrays); BLOBs stay Uint8Array.
69
+ if (ArrayBuffer.isView(v))
70
+ return Symbol.for("isArrowBigNum") in v ? Number(String(v)) : v;
71
+ if ("toJSON" in v && typeof v.toJSON === "function")
72
+ return plain(v.toJSON());
73
+ return Object.fromEntries(Object.entries(v).map(([k, x]) => [k, plain(x)]));
74
+ };
75
+ const engines = new Map();
76
+ const engine = (wasm) => {
77
+ const found = engines.get(wasm);
78
+ if (found)
79
+ return found;
80
+ let db = null;
81
+ const e = {
82
+ db: async () => {
83
+ const p = (db ??= boot(wasm).catch((err) => {
84
+ if (db === p) {
85
+ db = null;
86
+ e.attached.clear();
87
+ }
88
+ throw err;
89
+ }));
90
+ const d = await p;
91
+ // stop() ran while DuckDB was starting; it terminates this one.
92
+ if (db !== p)
93
+ throw new Error("DuckDB was stopped");
94
+ return d;
95
+ },
96
+ stop: async () => {
97
+ const started = db;
98
+ db = null;
99
+ e.attached.clear();
100
+ // A failed start was already reported to the query that started it.
101
+ await started?.then((d) => d.terminate(), () => undefined);
102
+ },
103
+ attached: new Map(),
104
+ };
105
+ engines.set(wasm, e);
106
+ return e;
107
+ };
108
+ /** Stop every DuckDB the package started. A later query starts it again. */
109
+ export const closeAll = async () => {
110
+ const all = [...engines.values()];
111
+ engines.clear();
112
+ await Promise.all(all.map((e) => e.stop()));
113
+ };
114
+ /**
115
+ * Connect to Queria datasets. Declare every dataset the SQL reads by its fully qualified name;
116
+ * the SQL refers to it by alias, such as e_stat.census.population. Short names are never resolved.
117
+ */
118
+ export const connect = ({ datasets, apiUrl = API, wasmUrl = WASM, token }) => {
119
+ const declared = aliases(datasets);
120
+ const auth = async () => (token === undefined ? null : typeof token === "string" ? token : await token());
121
+ const rows = new Map();
122
+ /** The dataset row, looked up once. Pass the token when the caller has already asked for it. */
123
+ const row = (qualified, token) => {
124
+ const p = rows.get(qualified) ??
125
+ (token ?? auth())
126
+ .then((t) => lookup(apiUrl, qualified, t))
127
+ .catch((e) => {
128
+ rows.delete(qualified);
129
+ throw e;
130
+ });
131
+ rows.set(qualified, p);
132
+ return p;
133
+ };
134
+ /** Declared aliases the SQL references. Other three-part names are left to DuckDB (alias.column.field). */
135
+ const used = (sql) => datasetsIn(sql).filter((a) => declared.has(a));
136
+ const ensure = async (e, db, alias, t) => {
137
+ const qualified = declared.get(alias) ?? "";
138
+ const prev = e.attached.get(alias);
139
+ if (prev && prev.qualified !== qualified)
140
+ throw new Error(`Alias ${alias} is already used for ${prev.qualified} on this page. Declare ${qualified} with another alias`);
141
+ const done = prev?.done ??
142
+ row(qualified, Promise.resolve(t))
143
+ .then((d) => attach(db, alias, d, t))
144
+ .catch((err) => {
145
+ e.attached.delete(alias);
146
+ throw err;
147
+ });
148
+ e.attached.set(alias, { qualified, done });
149
+ await done;
150
+ };
151
+ const sql = async (query) => {
152
+ const eng = engine(wasmUrl);
153
+ const aliasesUsed = used(query);
154
+ // Look up the datasets while DuckDB starts. The token function is called once per query.
155
+ const token = auth();
156
+ const [db, t, ds] = await Promise.all([eng.db(), token, Promise.all(aliasesUsed.map((a) => row(declared.get(a) ?? "", token)))]);
157
+ // Data files are read by the DuckDB worker, so hand it the token and the hosts it may go to.
158
+ if (t !== null)
159
+ setAuth(db, t, [...new Set(ds.map((d) => new URL(d.catalog_url).origin))]);
160
+ await Promise.all(aliasesUsed.map((a) => ensure(eng, db, a, t)));
161
+ const run = async () => {
162
+ const conn = await db.connect();
163
+ try {
164
+ return (await conn.query(query)).toArray().map((r) => plain(r));
165
+ }
166
+ finally {
167
+ await conn.close();
168
+ }
169
+ };
170
+ try {
171
+ return await withSpatial(db, query, run);
172
+ }
173
+ catch (e) {
174
+ const undeclared = datasetsIn(query).filter((a) => !declared.has(a));
175
+ if (undeclared.length === 0)
176
+ throw e;
177
+ throw new Error(`${String(e)}\nIf ${undeclared.join(", ")} is a dataset, declare it in connect({ datasets })`, { cause: e });
178
+ }
179
+ };
180
+ const sources = (query) => Promise.all((query === undefined ? [...declared.keys()] : used(query)).map((a) => row(declared.get(a) ?? "")));
181
+ return { sql, sources };
182
+ };
183
+ /** Read a list of datasets from an HTML attribute: "queria/e_stat queria/jma" or "a=alice/x b=bob/x". */
184
+ export const parseDatasets = (attr) => {
185
+ const pairs = attr
186
+ .split(/[\s,]+/)
187
+ .filter(Boolean)
188
+ .map((w) => (w.includes("=") ? w.split("=", 2) : [w.split("/")[1] ?? "", w]));
189
+ // Object.fromEntries would keep only the last of two datasets with one alias.
190
+ if (new Set(pairs.map(([alias]) => alias)).size !== pairs.length)
191
+ throw new Error("Two datasets share an alias. Declare them as alias=name");
192
+ return Object.fromEntries(pairs);
193
+ };
194
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,4 @@
1
+ // Importing this module registers the custom elements.
2
+ import { defineElements } from "./index.js";
3
+ defineElements();
4
+ //# sourceMappingURL=define.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define.js","sourceRoot":"","sources":["../../src/elements/define.ts"],"names":[],"mappings":"AAAA,uDAAuD;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE5C,cAAc,EAAE,CAAC"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * <queria-chart datasets="queria/e_stat" sql="..." spec='{"mark": "line", ...}'>
3
+ * Runs the SQL and draws the rows with the Vega-Lite spec, followed by the sources of the datasets.
4
+ * Instead of sql, `rows` takes the rows as JSON, for pages that run the SQL at build time. Declare the datasets the
5
+ * rows came from in `datasets` so their sources are shown.
6
+ * `api-url` replaces the Queria API, for example with a staging host.
7
+ */
8
+ export declare class QueriaChart extends HTMLElement {
9
+ #private;
10
+ static observedAttributes: string[];
11
+ connectedCallback(): void;
12
+ attributeChangedCallback(): void;
13
+ disconnectedCallback(): void;
14
+ }
15
+ /**
16
+ * <queria-map datasets="queria/e_stat" sql="..." layer='{"area": "city", "code": "lg_code", "color": "population"}'>
17
+ * Runs the SQL and puts the rows on a map by area code or by lng/lat, followed by the sources of the datasets.
18
+ * `rows`, `api-url` work as on <queria-chart>. `theme` is light or dark; `map-style` takes another MapLibre style URL.
19
+ * Include maplibre-gl's CSS in the page. The map height is the CSS variable --queria-map-height (400px by default).
20
+ */
21
+ export declare class QueriaMap extends HTMLElement {
22
+ #private;
23
+ static observedAttributes: string[];
24
+ connectedCallback(): void;
25
+ attributeChangedCallback(): void;
26
+ disconnectedCallback(): void;
27
+ }
28
+ /** <queria-powered-by> */
29
+ export declare class QueriaPoweredBy extends HTMLElement {
30
+ connectedCallback(): void;
31
+ }
32
+ /** Register the custom elements. Safe to call more than once. */
33
+ export declare const defineElements: () => void;
34
+ declare global {
35
+ interface HTMLElementTagNameMap {
36
+ "queria-chart": QueriaChart;
37
+ "queria-map": QueriaMap;
38
+ "queria-powered-by": QueriaPoweredBy;
39
+ }
40
+ }