feat(wren-core-wasm): cubeQuery + listCubes browser API#2278
Conversation
WrenEngine now keeps the AnalyzedWrenMDL produced by loadMDL so the
cube API can read the manifest after load. New JS-facing methods:
* cubeQuery(query) — translate a structured CubeQuery to SQL via
wren-core and execute it through the same path
as query(). Returns rows as JSON.
* listCubes() — return cubes from the loaded MDL (name,
baseObject, measures, dimensions, time
dimensions, hierarchies).
TypeScript wrapper adds CubeQueryInput / TimeDimensionInput /
CubeFilterInput / CubeInfo (plus Granularity and FilterOperator string
unions) so agents get end-to-end typing. The hand-maintained
wren_core_wasm.d.ts mirror is updated to match.
Both Rust methods route through `wren_core::mdl::{cube_query_to_sql,
CubeQuery}` — the curated re-export — since `pub(crate) mod cube`
(PR #2276) makes the inner module unreachable from outside the crate.
AGENT_GUIDE gains a Cube Query API section and the unpkg CDN URL is
bumped from 0.1.0 to 0.3.0 to match the current npm release.
Adds 5 SDK integration tests covering the happy path, an unknown cube,
calling either method before loadMDL, and listCubes shape.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
core/wren-core-wasm/sdk/tests/index.test.mjs (2)
377-390: 💤 Low valueConsider asserting dimensions array length before accessing elements.
Line 388 accesses
cubes[0].dimensions[0].namewithout first verifyingcubes[0].dimensions.length. While the test data guarantees one dimension, adding an explicit length check improves test clarity and prevents confusing failures if the fixture changes.✨ Optional assertion to add
assert.equal(cubes[0].measures.length, 2); assert.equal(cubes[0].measures[0].name, "total"); +assert.equal(cubes[0].dimensions.length, 1); assert.equal(cubes[0].dimensions[0].name, "status");🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@core/wren-core-wasm/sdk/tests/index.test.mjs` around lines 377 - 390, Add an explicit assertion on the dimensions array length before accessing its first element: after calling engine.listCubes() and validating measures, assert cubes[0].dimensions.length === 1 (or the expected count) before referencing cubes[0].dimensions[0].name; update the test in index.test.mjs around the listCubes assertions to include this dimensions length check so future fixture changes won’t cause unclear indexing errors.
376-445: ⚖️ Poor tradeoffConsider adding test coverage for filters, time dimensions, and empty results.
The current test suite covers core cube functionality well, but the PR introduces
CubeFilterInput,TimeDimensionInput,Granularity, andFilterOperatortypes that aren't exercised. Consider adding tests for:
- Filters: Test
cubeQuerywith filter conditions using various operators- Time dimensions: Test queries with time granularity (day/week/month) and date ranges
- Empty results: Test
cubeQuerywhen no rows match the criteria- Hierarchies: Exercise the hierarchies field if it's part of the public API
These would increase confidence that the full API surface works end-to-end.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@core/wren-core-wasm/sdk/tests/index.test.mjs` around lines 376 - 445, Add unit tests exercising the new CubeFilterInput, TimeDimensionInput, Granularity, and FilterOperator logic by extending the existing suite around cubeQuery/listCubes: create a test that applies CubeFilterInput with multiple FilterOperator types (equals, gt, in, isNull) against the "order_metrics" cube and assert filtered aggregates; add tests that pass TimeDimensionInput with different Granularity values (day, week, month) and date ranges to verify grouping and boundaries; add a test that issues a cubeQuery whose filters yield no matching rows and assert an empty result set; and, if the public cube metadata exposes hierarchies, add a listCubes or cubeQuery case that validates the hierarchies field is populated. Use existing helpers like WrenEngine.init, registerJson, loadMDL, and cubeMDL() to set up data and MDL in each test.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@core/wren-core-wasm/AGENT_GUIDE.md`:
- Around line 91-123: Add an explicit precondition note that engine.listCubes()
and engine.cubeQuery() require loadMDL() to have been called first: update the
AGENT_GUIDE.md section around the cube API examples to state that both
engine.listCubes() and engine.cubeQuery() will throw or fail if loadMDL() has
not been run, and show a tiny example or single-line reminder to call await
engine.loadMDL() before using those methods (reference the symbols loadMDL,
engine.listCubes, and engine.cubeQuery in the note).
---
Nitpick comments:
In `@core/wren-core-wasm/sdk/tests/index.test.mjs`:
- Around line 377-390: Add an explicit assertion on the dimensions array length
before accessing its first element: after calling engine.listCubes() and
validating measures, assert cubes[0].dimensions.length === 1 (or the expected
count) before referencing cubes[0].dimensions[0].name; update the test in
index.test.mjs around the listCubes assertions to include this dimensions length
check so future fixture changes won’t cause unclear indexing errors.
- Around line 376-445: Add unit tests exercising the new CubeFilterInput,
TimeDimensionInput, Granularity, and FilterOperator logic by extending the
existing suite around cubeQuery/listCubes: create a test that applies
CubeFilterInput with multiple FilterOperator types (equals, gt, in, isNull)
against the "order_metrics" cube and assert filtered aggregates; add tests that
pass TimeDimensionInput with different Granularity values (day, week, month) and
date ranges to verify grouping and boundaries; add a test that issues a
cubeQuery whose filters yield no matching rows and assert an empty result set;
and, if the public cube metadata exposes hierarchies, add a listCubes or
cubeQuery case that validates the hierarchies field is populated. Use existing
helpers like WrenEngine.init, registerJson, loadMDL, and cubeMDL() to set up
data and MDL in each test.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 36a3355d-a489-43c9-b72f-00ffa7973bd2
📒 Files selected for processing (5)
core/wren-core-wasm/AGENT_GUIDE.mdcore/wren-core-wasm/sdk/src/index.tscore/wren-core-wasm/sdk/src/wren_core_wasm.d.tscore/wren-core-wasm/sdk/tests/index.test.mjscore/wren-core-wasm/src/lib.rs
* AGENT_GUIDE Cube Query section calls out that listCubes() and
cubeQuery() require a prior loadMDL() — common integration footgun.
* listCubes test now asserts dimensions length, timeDimensions, and
the hierarchies dict so future fixture changes show up here, not
via a confusing IndexError.
* Adds two cubeQuery cases that exercise the new SDK types:
- dimension filter via `in` (CubeFilterInput / FilterOperator)
- month time-dimension with dateRange window (TimeDimensionInput /
Granularity), verifying both the bucket column and the date-range
WHERE filter.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
🧹 Nitpick comments (1)
core/wren-core-wasm/sdk/tests/index.test.mjs (1)
480-513: ⚡ Quick winConsider verifying actual bucket values for more precise validation.
The test validates bucketing behavior and dateRange filtering well, but lines 509-510 only check that two totals (7 and 35) exist without verifying which month corresponds to which total. If the implementation were to return incorrect bucket labels (e.g., both labeled "2024-01"), this wouldn't be caught.
📊 More precise assertion example
Replace lines 507-510 with:
- const totals = Object.fromEntries(rows.map((r) => [r[bucketCol], r.total])); - // Two distinct months — Jan totals 35, Feb totals 7. - const values = Object.values(totals).sort((a, b) => a - b); - assert.deepEqual(values, [7, 35]); + const totals = Object.fromEntries(rows.map((r) => [r[bucketCol], r.total])); + // Verify bucket labels and totals + assert.ok("2024-01" in totals || "2024-01-01" in totals, "Expected January bucket"); + assert.ok("2024-02" in totals || "2024-02-01" in totals, "Expected February bucket"); + const janTotal = totals["2024-01"] || totals["2024-01-01"]; + const febTotal = totals["2024-02"] || totals["2024-02-01"]; + assert.equal(janTotal, 35); + assert.equal(febTotal, 7);Note: Adjust the expected bucket format based on the actual implementation's date formatting.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@core/wren-core-wasm/sdk/tests/index.test.mjs` around lines 480 - 513, The test currently only checks sorted totals; update it to assert the actual bucket labels and their corresponding totals using the bucket column created_at__month and the totals mapping (const totals = Object.fromEntries(rows.map(r => [r[bucketCol], r.total]))); specifically verify that the expected month bucket keys (e.g., "2024-01-01" or the project's month-label format) exist and map to total 35 and "2024-02-01" maps to 7 so mislabelled buckets will fail the test.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@core/wren-core-wasm/sdk/tests/index.test.mjs`:
- Around line 480-513: The test currently only checks sorted totals; update it
to assert the actual bucket labels and their corresponding totals using the
bucket column created_at__month and the totals mapping (const totals =
Object.fromEntries(rows.map(r => [r[bucketCol], r.total]))); specifically verify
that the expected month bucket keys (e.g., "2024-01-01" or the project's
month-label format) exist and map to total 35 and "2024-02-01" maps to 7 so
mislabelled buckets will fail the test.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 31de4491-f9f3-4800-8a78-e382864bdf6d
📒 Files selected for processing (2)
core/wren-core-wasm/AGENT_GUIDE.mdcore/wren-core-wasm/sdk/tests/index.test.mjs
✅ Files skipped from review due to trivial changes (1)
- core/wren-core-wasm/AGENT_GUIDE.md
Two new example pages alongside inline.html / url-mode.html:
* cube-quickstart.html — minimal cubeQuery() demo. Three preset
queries (group-by, filter, time bucket) over an order_metrics cube
with 7 rows of embedded data. Read the source for the smallest
end-to-end cube example.
* cube-explorer.html — interactive form-driven builder. Checkboxes
for measures/dimensions, dropdown granularity + date-range box for
time dimensions, and a repeatable filter row that exposes all 12
FilterOperator values. The generated CubeQuery JSON re-renders
live next to the result table; demo data is spread across regions
/ customers / months so groupings produce non-trivial numbers.
Both pages import directly from pkg/wren_core_wasm.js so they always
reflect the local build — re-run `just build-wasm-dev` after Rust
changes and refresh.
serve.mjs prints the new URLs on startup. README gains an Examples
section pointing at all five demos and explaining when to use
quickstart vs explorer.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
🧹 Nitpick comments (2)
core/wren-core-wasm/examples/cube-explorer.html (2)
292-298: ⚡ Quick winEscape HTML in table cell values.
Line 296 interpolates
r[k]directly into<td>tags without escaping. If query results contain HTML characters, they will be rendered as markup rather than text. UsetextContentor escape HTML entities to prevent potential XSS.🛡️ Proposed fix using textContent
Replace the string concatenation approach with DOM construction:
function renderTable(rows) { if (!rows.length) return '<p>No rows.</p>'; const keys = Object.keys(rows[0]); - return '<table><tr>' + keys.map(k => `<th>${k}</th>`).join('') - + '</tr>' + rows.map(r => '<tr>' + keys.map(k => `<td>${r[k] ?? ''}</td>`).join('') + '</tr>').join('') - + '</table>'; + const table = document.createElement('table'); + const headerRow = document.createElement('tr'); + keys.forEach(k => { + const th = document.createElement('th'); + th.textContent = k; + headerRow.appendChild(th); + }); + table.appendChild(headerRow); + rows.forEach(r => { + const row = document.createElement('tr'); + keys.forEach(k => { + const td = document.createElement('td'); + td.textContent = r[k] ?? ''; + row.appendChild(td); + }); + table.appendChild(row); + }); + return table.outerHTML;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@core/wren-core-wasm/examples/cube-explorer.html` around lines 292 - 298, The renderTable function currently injects cell values directly (r[k]) into HTML strings, enabling markup/XSS; modify renderTable to escape HTML entities or build the table via DOM APIs and set each cell's textContent instead of string interpolation. Specifically, update the function that produces the <td> content (referencing renderTable, rows, keys, and r[k]) to either run r[k] through an HTML-escape utility (escape &, <, >, ", ') before concatenation or construct elements with document.createElement('td') and assign cell.textContent = String(r[k] ?? '') so values render as text not markup.
195-229: ⚡ Quick winUse textContent or sanitize HTML to prevent XSS.
Lines 201 and 216-227 use
innerHTMLwith unsanitized data (item.name, dimension names, filter values). While the demo data is controlled, this demonstrates an insecure pattern that readers might copy to production code where data could come from untrusted sources.Consider using
textContentfor text content,createElement/setAttributefor dynamic elements, or a sanitization library like DOMPurify for any HTML content.🛡️ Example fix for line 201
const label = document.createElement('label'); -label.innerHTML = `<input type="checkbox" id="${id}" value="${item.name}"> ${item.name} <span style="color:`#999`;font-size:0.85em;">${item.type || ''}</span>`; +const checkbox = document.createElement('input'); +checkbox.type = 'checkbox'; +checkbox.id = id; +checkbox.value = item.name; +label.appendChild(checkbox); +label.appendChild(document.createTextNode(` ${item.name} `)); +const typeSpan = document.createElement('span'); +typeSpan.style.cssText = 'color:`#999`;font-size:0.85em;'; +typeSpan.textContent = item.type || ''; +label.appendChild(typeSpan);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@core/wren-core-wasm/examples/cube-explorer.html` around lines 195 - 229, The code uses innerHTML with unsanitized values in checkboxGroup (label.innerHTML) and renderFilters (row.innerHTML for select/option/input/button) which risks XSS; replace these innerHTML assignments by building elements programmatically: create input/label/span via document.createElement and set text via textContent and attributes via setAttribute for checkboxGroup (use id/namePrefix and item.name safely), and for renderFilters create option elements with option.value and option.textContent, create input and set placeholder/textContent safely (or run values through a sanitizer like DOMPurify) so no unescaped user data is injected into HTML strings.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@core/wren-core-wasm/examples/cube-explorer.html`:
- Around line 292-298: The renderTable function currently injects cell values
directly (r[k]) into HTML strings, enabling markup/XSS; modify renderTable to
escape HTML entities or build the table via DOM APIs and set each cell's
textContent instead of string interpolation. Specifically, update the function
that produces the <td> content (referencing renderTable, rows, keys, and r[k])
to either run r[k] through an HTML-escape utility (escape &, <, >, ", ') before
concatenation or construct elements with document.createElement('td') and assign
cell.textContent = String(r[k] ?? '') so values render as text not markup.
- Around line 195-229: The code uses innerHTML with unsanitized values in
checkboxGroup (label.innerHTML) and renderFilters (row.innerHTML for
select/option/input/button) which risks XSS; replace these innerHTML assignments
by building elements programmatically: create input/label/span via
document.createElement and set text via textContent and attributes via
setAttribute for checkboxGroup (use id/namePrefix and item.name safely), and for
renderFilters create option elements with option.value and option.textContent,
create input and set placeholder/textContent safely (or run values through a
sanitizer like DOMPurify) so no unescaped user data is injected into HTML
strings.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: c000b1e9-9e5a-4d3b-a689-f6bb3e4879bc
📒 Files selected for processing (4)
core/wren-core-wasm/README.mdcore/wren-core-wasm/examples/cube-explorer.htmlcore/wren-core-wasm/examples/cube-quickstart.htmlcore/wren-core-wasm/examples/serve.mjs
✅ Files skipped from review due to trivial changes (1)
- core/wren-core-wasm/README.md
Summary
Phase F of the cube rollout (impl-cube-wasm.md): expose the cube translator to the browser through
@wrenai/wren-core-wasm.F0 — store the analyzed MDL
WrenEnginenow holdsanalyzed_mdl: Option<Arc<AnalyzedWrenMDL>>.loadMDLArc::clones the analyzed manifest into the struct afterapply_wren_on_ctx, so the cube API can read the manifest without re-analyzing.F1 — Rust wasm-bindgen API
cubeQuery(json) -> Promise<string>— deserializeCubeQueryvia serde, translate throughwren_core::mdl::cube_query_to_sql, execute via the existingquery()path. Curated re-export, notwren_core::mdl::cube::*(which ispub(crate)since PR refactor(wren-core): cube Phase C cleanup nits #2276).listCubes() -> string— emit a JSON array of{ name, baseObject, measures, dimensions, timeDimensions, hierarchies }.loadMDL.F2 + F5 — TypeScript SDK
CubeQueryInput,TimeDimensionInput,CubeFilterInput,CubeInfo, plusGranularityandFilterOperatorstring unions.cubeQuery(query)returnsPromise<Record<string, unknown>[]>;listCubes()returnsCubeInfo[].wren_core_wasm.d.tsmirror updated to match.F3 — Integration tests
sdk/tests/index.test.mjs:listCubesshape,cubeQueryaggregation, unknown cube rejection, and "missing MDL" errors for both methods.F4 — AGENT_GUIDE
@0.1.0to@0.3.0so the doc matches the current npm release.Test plan
just build-wasm-dev— WASM dev build compilesjust build-dist+just typecheck— TypeScript SDK builds and type-checks cleanjust test— 19 SDK integration tests pass (5 new + 14 prior)cargo clippy --target wasm32-unknown-unknown -- -D warnings— cleancargo fmt --all -- --check— note: there is a pre-existing fmt diff inextract_bare_table_nametest onfeat/wasm-cubeHEAD that this PR doesn't introduce or touch; left alone to keep the diff minimal.Phase G (skills wiring + npm release) is the last step before merging
feat/wasm-cubeintomain.🤖 Generated with Claude Code
Summary by CodeRabbit
Release Notes
New Features
cubeQuery()andlistCubes()methods for structured data querying, supporting measures, dimensions, filters, time bucketing, and granularity options.Documentation