@gscdump/engine-duckdb-wasm 0.25.13 → 0.25.14
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/index.mjs +51 -28
- package/package.json +7 -7
package/dist/index.mjs
CHANGED
|
@@ -166,7 +166,8 @@ function compileArchetypeSql(query) {
|
|
|
166
166
|
}
|
|
167
167
|
const col = DIM_COLUMN[query.dimension];
|
|
168
168
|
const facet = facetPredicate(query);
|
|
169
|
-
|
|
169
|
+
const totalCol = query.includeTotal ? ", COUNT(*) OVER() AS __total" : "";
|
|
170
|
+
let sql = `SELECT ${col} AS ${query.dimension}, ${metricSelectList(query.metrics)}${totalCol} FROM ${table} WHERE ${where.sql}${facet.sql} GROUP BY ${col} ORDER BY ${query.orderBy.metric} ${dir} LIMIT ?`;
|
|
170
171
|
const params = [
|
|
171
172
|
...where.params,
|
|
172
173
|
...facet.params,
|
|
@@ -489,12 +490,27 @@ function isOpfsAccessHandleConflict(err) {
|
|
|
489
490
|
const msg = err instanceof Error ? err.message : String(err);
|
|
490
491
|
return /createSyncAccessHandle|Access Handle/i.test(msg);
|
|
491
492
|
}
|
|
492
|
-
function opfsFileName(table,
|
|
493
|
-
|
|
494
|
-
return hashSlug ? `${base}_${hashSlug}.parquet` : `${base}.parquet`;
|
|
493
|
+
function opfsFileName(table, hashSlug, index) {
|
|
494
|
+
return hashSlug ? `${OPFS_PREFIX}${table}_${hashSlug}.parquet` : `${OPFS_PREFIX}${table}_${index}.parquet`;
|
|
495
495
|
}
|
|
496
|
-
function
|
|
497
|
-
return
|
|
496
|
+
function escapeRegExp(s) {
|
|
497
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
498
|
+
}
|
|
499
|
+
function tableEntryMatcher(table) {
|
|
500
|
+
return new RegExp(`^${escapeRegExp(`${OPFS_PREFIX}${table}_`)}(?:[0-9a-f]{16}|\\d+(?:_[0-9a-f]{16})?)\\.parquet$`);
|
|
501
|
+
}
|
|
502
|
+
async function sweepStaleEntries(root, registry, expectedByTable) {
|
|
503
|
+
const dir = root;
|
|
504
|
+
if (!dir.keys) return;
|
|
505
|
+
const matchers = [...expectedByTable].map(([table, expected]) => ({
|
|
506
|
+
expected,
|
|
507
|
+
re: tableEntryMatcher(table)
|
|
508
|
+
}));
|
|
509
|
+
for await (const name of dir.keys()) {
|
|
510
|
+
const m = matchers.find((m) => m.re.test(name));
|
|
511
|
+
if (!m || m.expected.has(name) || registry.refs(name) > 0) continue;
|
|
512
|
+
await root.removeEntry(name).catch(() => {});
|
|
513
|
+
}
|
|
498
514
|
}
|
|
499
515
|
async function requestPersistentStorage() {
|
|
500
516
|
const storage = globalThis.navigator?.storage;
|
|
@@ -523,7 +539,7 @@ async function getOpfsRoot() {
|
|
|
523
539
|
if (!dir) throw new Error("[engine-duckdb-wasm/opfs] OPFS unavailable: navigator.storage.getDirectory missing");
|
|
524
540
|
return dir.call(globalThis.navigator.storage);
|
|
525
541
|
}
|
|
526
|
-
async function materialiseFile(root, name,
|
|
542
|
+
async function materialiseFile(root, name, file, fetchImpl, fetchInit, signal) {
|
|
527
543
|
signal?.throwIfAborted();
|
|
528
544
|
let handle;
|
|
529
545
|
try {
|
|
@@ -533,12 +549,6 @@ async function materialiseFile(root, name, staleSweepPrefix, file, fetchImpl, fe
|
|
|
533
549
|
outcome: "cache-hit"
|
|
534
550
|
};
|
|
535
551
|
} catch {}
|
|
536
|
-
const dir = root;
|
|
537
|
-
if (dir.keys) for await (const existing of dir.keys()) {
|
|
538
|
-
if (existing === name || !existing.startsWith(staleSweepPrefix) || !existing.endsWith(".parquet")) continue;
|
|
539
|
-
const next = existing.charAt(staleSweepPrefix.length);
|
|
540
|
-
if (next === "." || next === "_") await root.removeEntry(existing).catch(() => {});
|
|
541
|
-
}
|
|
542
552
|
signal?.throwIfAborted();
|
|
543
553
|
const resp = await fetchImpl(file.url, {
|
|
544
554
|
...fetchInit,
|
|
@@ -596,24 +606,39 @@ async function attachOpfsParquetTables(options) {
|
|
|
596
606
|
await requestPersistentStorage();
|
|
597
607
|
const root = await getOpfsRoot();
|
|
598
608
|
const flat = [];
|
|
609
|
+
const expectedByTable = /* @__PURE__ */ new Map();
|
|
599
610
|
for (const t of tables) {
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
file
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
+
const expected = /* @__PURE__ */ new Set();
|
|
612
|
+
for (let i = 0; i < t.files.length; i++) {
|
|
613
|
+
const file = t.files[i];
|
|
614
|
+
const slug = file.contentHash ? await contentHashSlug(file.contentHash) : void 0;
|
|
615
|
+
const name = opfsFileName(t.table, slug, i);
|
|
616
|
+
flat.push({
|
|
617
|
+
table: t.table,
|
|
618
|
+
file,
|
|
619
|
+
name
|
|
620
|
+
});
|
|
621
|
+
expected.add(name);
|
|
622
|
+
}
|
|
623
|
+
if (t.overlay) {
|
|
624
|
+
const slug = t.overlay.contentHash ? await contentHashSlug(t.overlay.contentHash) : void 0;
|
|
625
|
+
const name = opfsFileName(t.table, slug, t.files.length);
|
|
626
|
+
flat.push({
|
|
627
|
+
table: t.table,
|
|
628
|
+
file: t.overlay,
|
|
629
|
+
name,
|
|
630
|
+
overlay: true
|
|
631
|
+
});
|
|
632
|
+
expected.add(name);
|
|
633
|
+
}
|
|
634
|
+
expectedByTable.set(t.table, expected);
|
|
611
635
|
}
|
|
612
636
|
const total = flat.length;
|
|
613
637
|
const overlayNames = /* @__PURE__ */ new Map();
|
|
614
638
|
const expectedCount = new Map(tables.map((t) => [t.table, t.files.length + (t.overlay ? 1 : 0)]));
|
|
615
639
|
const { DuckDBDataProtocol } = await import("@duckdb/duckdb-wasm");
|
|
616
640
|
const registry = getOpfsRegistry(db, DuckDBDataProtocol.BROWSER_FSACCESS);
|
|
641
|
+
await sweepStaleEntries(root, registry, expectedByTable).catch(() => {});
|
|
617
642
|
const tableFiles = /* @__PURE__ */ new Map();
|
|
618
643
|
const degraded = /* @__PURE__ */ new Set();
|
|
619
644
|
const acquiredNames = /* @__PURE__ */ new Set();
|
|
@@ -626,12 +651,10 @@ async function attachOpfsParquetTables(options) {
|
|
|
626
651
|
const runDownloads = () => runWithConcurrency$1(flat, Math.max(1, fetchConcurrency), async (item, index) => {
|
|
627
652
|
if (degraded.has(item.table)) return;
|
|
628
653
|
signal?.throwIfAborted();
|
|
629
|
-
const
|
|
630
|
-
const name = opfsFileName(item.table, item.fileIndex, hashSlug);
|
|
631
|
-
const sweepPrefix = opfsFileNamePrefix(item.table, item.fileIndex);
|
|
654
|
+
const name = item.name;
|
|
632
655
|
let result;
|
|
633
656
|
try {
|
|
634
|
-
result = await materialiseFile(root, name,
|
|
657
|
+
result = await materialiseFile(root, name, item.file, fetchImpl, fetchInit, signal);
|
|
635
658
|
} catch (err) {
|
|
636
659
|
if (isAbortError$1(err)) throw err;
|
|
637
660
|
if (isQuotaError(err)) {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gscdump/engine-duckdb-wasm",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.25.
|
|
4
|
+
"version": "0.25.14",
|
|
5
5
|
"description": "DuckDB-WASM engine adapter for @gscdump/analysis — typed browser analytics against parquet via R2.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Harlan Wilton",
|
|
@@ -45,16 +45,16 @@
|
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"drizzle-orm": "1.0.0-rc.3",
|
|
48
|
-
"@gscdump/engine": "0.25.
|
|
49
|
-
"gscdump": "0.25.
|
|
50
|
-
"
|
|
48
|
+
"@gscdump/engine": "0.25.14",
|
|
49
|
+
"@gscdump/sdk": "0.25.14",
|
|
50
|
+
"gscdump": "0.25.14"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
53
|
"@duckdb/duckdb-wasm": "^1.32.0",
|
|
54
|
-
"@vitest/browser": "^4.1.
|
|
55
|
-
"@vitest/browser-playwright": "^4.1.
|
|
54
|
+
"@vitest/browser": "^4.1.8",
|
|
55
|
+
"@vitest/browser-playwright": "^4.1.8",
|
|
56
56
|
"playwright": "^1.60.0",
|
|
57
|
-
"vitest": "^4.1.
|
|
57
|
+
"vitest": "^4.1.8"
|
|
58
58
|
},
|
|
59
59
|
"scripts": {
|
|
60
60
|
"build": "obuild",
|