Skip to content

Commit a152007

Browse files
committed
Update dependencies
1 parent 5069508 commit a152007

File tree

12 files changed

+70
-73
lines changed

12 files changed

+70
-73
lines changed

.claude/commands/update-deps.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
Update all npm dependencies to their latest versions.
2+
3+
## Steps
4+
5+
1. Run `npm outdated` to see what's behind.
6+
2. Update `package.json` with the latest versions, **pinned** (no `^` or `~`).
7+
3. Run `npm install` to update the lockfile.
8+
4. Run `npx tsc`, `npm run lint`, and tests `npm test`.
9+
5. If anything fails:
10+
- For **major version bumps**, research the changelog on GitHub (check the repo's CHANGELOG.md, releases page, or migration guide) to understand breaking changes.
11+
- Apply the necessary code fixes based on what you find.
12+
- Re-run tsc, lint, and tests until green.
13+
6. Summarize what was updated and any breaking changes you resolved.

bin/chat.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ async function sendToServer(chatInput) {
121121
async function sendMessages(messages) {
122122
/** @type {ChatInput} */
123123
const chatInput = {
124-
model: 'gpt-5',
124+
model: 'gpt-5.4',
125125
instructions,
126126
messages,
127127
reasoning: {
@@ -161,8 +161,6 @@ async function sendMessages(messages) {
161161
const { toolCall, tool } = toolResult
162162
const { call_id } = toolCall
163163
try {
164-
const output = await toolResult.result
165-
166164
// Construct function call message
167165
const args = JSON.parse(toolCall.arguments)
168166
const entries = Object.entries(args)
@@ -175,6 +173,8 @@ async function sendMessages(messages) {
175173
func += `(${pairs.join(', ')})`
176174
}
177175
write(colors.tool, `${tool.emoji} ${func}`, colors.normal, '\n')
176+
177+
const output = await toolResult.result
178178
incoming.push({ type: 'function_call_output', output, call_id })
179179
} catch (error) {
180180
const message = error instanceof Error ? error.message : String(error)

bin/tools/parquetSql.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { asyncBufferFromFile, asyncBufferFromUrl, parquetMetadataAsync } from 'hyparquet'
22
import { compressors } from 'hyparquet-compressors'
33
import { collect, executeSql } from 'squirreling'
4-
import { parquetDataSource } from '../../src/lib/parquet/parquetDataSource.js'
4+
import { parquetDataSource } from 'hyperparam'
55
import { markdownTable } from './markdownTable.js'
66

77
const maxRows = 100

package.json

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -55,36 +55,36 @@
5555
"watch:url": "NODE_ENV=development nodemon bin/cli.js https://hyperparam.blob.core.windows.net/hyperparam/starcoderdata-js-00000-of-00065.parquet"
5656
},
5757
"dependencies": {
58-
"hightable": "0.26.3",
58+
"hightable": "0.26.4",
5959
"hyparquet": "1.25.1",
6060
"hyparquet-compressors": "1.1.1",
6161
"icebird": "0.3.1",
62-
"squirreling": "0.10.2"
62+
"squirreling": "0.10.3"
6363
},
6464
"devDependencies": {
65-
"@storybook/react-vite": "10.2.13",
65+
"@storybook/react-vite": "10.2.19",
6666
"@testing-library/react": "16.3.2",
67-
"@types/node": "25.3.3",
67+
"@types/node": "25.5.0",
6868
"@types/react": "19.2.14",
6969
"@types/react-dom": "19.2.3",
7070
"@vitejs/plugin-react": "5.1.4",
71-
"@vitest/coverage-v8": "4.0.18",
71+
"@vitest/coverage-v8": "4.1.0",
7272
"eslint": "9.39.2",
7373
"eslint-plugin-react": "7.37.5",
7474
"eslint-plugin-react-hooks": "7.0.1",
7575
"eslint-plugin-react-refresh": "0.5.2",
76-
"eslint-plugin-storybook": "10.2.13",
76+
"eslint-plugin-storybook": "10.2.19",
7777
"globals": "17.4.0",
78-
"jsdom": "28.1.0",
78+
"jsdom": "29.0.0",
7979
"nodemon": "3.1.14",
8080
"npm-run-all": "4.1.5",
8181
"react": "19.2.4",
8282
"react-dom": "19.2.4",
83-
"storybook": "10.2.13",
83+
"storybook": "10.2.19",
8484
"typescript": "5.9.3",
85-
"typescript-eslint": "8.56.1",
85+
"typescript-eslint": "8.57.0",
8686
"vite": "7.3.1",
87-
"vitest": "4.0.18"
87+
"vitest": "4.1.0"
8888
},
8989
"peerDependencies": {
9090
"react": "^18.3.1 || ^19",

src/components/Folder/Folder.test.tsx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { render, waitFor } from '@testing-library/react'
22
import { userEvent } from '@testing-library/user-event'
33
import { strict as assert } from 'assert'
44
import { act } from 'react'
5-
import { beforeEach, describe, expect, it, test, vi } from 'vitest'
5+
import { describe, expect, it, test, vi } from 'vitest'
66
import { Config, ConfigProvider } from '../../hooks/useConfig.js'
77
import { DirSource, FileMetadata, HyperparamFileMetadata, getHyperparamSource } from '../../lib/sources/index.js'
88
import Folder from './Folder.js'
@@ -20,12 +20,9 @@ const config: Config = {
2020
}
2121

2222
globalThis.fetch = vi.fn()
23+
globalThis.console.error = vi.fn()
2324

2425
describe('Folder Component', () => {
25-
beforeEach(() => {
26-
vi.clearAllMocks()
27-
})
28-
2926
test.for([
3027
'',
3128
'subfolder/',
@@ -85,6 +82,7 @@ describe('Folder Component', () => {
8582
await findByText('Error: ' + errorMessage)
8683
expect(queryByText('file1.txt')).toBeNull()
8784
expect(queryByText('folder1/')).toBeNull()
85+
expect(console.error).toHaveBeenCalledWith(new Error(errorMessage))
8886
})
8987

9088
it('filters files based on search query', async () => {

src/components/JsonView/JsonView.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ describe('JsonView Component', () => {
3434
} as Response)
3535

3636
const { findAllByRole, findByText } = render(
37-
<JsonView source={source} setError={console.error} />
37+
<JsonView source={source} setError={vi.fn()} />
3838
)
3939

4040
expect(fetch).toHaveBeenCalledWith('testKey0', undefined)

src/components/Layout/Layout.test.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import { render } from '@testing-library/react'
2-
import { describe, expect, it } from 'vitest'
2+
import { describe, expect, it, vi } from 'vitest'
33
import Layout from './Layout.js'
44

5+
globalThis.console.error = vi.fn()
6+
57
describe('Layout Component', () => {
68
it('renders children', () => {
79
const { getByText } = render(<Layout>Test Content</Layout>)
@@ -23,5 +25,6 @@ describe('Layout Component', () => {
2325
const testError = new Error('Test Error')
2426
const { getByText } = render(<Layout error={testError}>Test Content</Layout>)
2527
getByText('Error: Test Error')
28+
expect(console.error).toHaveBeenCalledWith(testError)
2629
})
2730
})

src/components/MarkdownView/MarkdownView.test.tsx

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,12 @@
11
import { render } from '@testing-library/react'
22
import { strict as assert } from 'assert'
3-
import { beforeEach, describe, expect, it, vi } from 'vitest'
3+
import { describe, expect, it, vi } from 'vitest'
44
import { getHyperparamSource } from '../../lib/sources/index.js'
55
import MarkdownView from './MarkdownView.js'
66

77
globalThis.fetch = vi.fn()
88

99
describe('MarkdownView Component', () => {
10-
beforeEach(() => {
11-
vi.clearAllMocks()
12-
// unnecessary for now because it has only one test, but safer for future tests
13-
})
14-
1510
it('renders markdown correctly', async () => {
1611
const text = '# Markdown\n\nThis is a test of the markdown viewer.'
1712
vi.mocked(fetch).mockResolvedValueOnce({
@@ -23,7 +18,7 @@ describe('MarkdownView Component', () => {
2318
assert(source?.kind === 'file')
2419

2520
const { findByText } = render(
26-
<MarkdownView source={source} setError={console.error} />
21+
<MarkdownView source={source} setError={vi.fn()} />
2722
)
2823

2924
expect(fetch).toHaveBeenCalled()

src/lib/parquet/parquetDataSource.ts

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
import { parquetReadObjects, parquetSchema } from 'hyparquet'
22
import type { AsyncBuffer, Compressors, FileMetaData } from 'hyparquet'
3-
import type { AsyncDataSource, AsyncRow, ScanOptions, SqlPrimitive } from 'squirreling'
3+
import { AsyncDataSource, ScanOptions, asyncRow } from 'squirreling'
44
import { whereToParquetFilter } from './parquetFilter.js'
5+
import { extractSpatialFilter, rowGroupOverlaps } from './parquetSpatial.js'
56

67
/**
78
* Creates a parquet data source for use with squirreling SQL engine.
89
*/
910
export function parquetDataSource(file: AsyncBuffer, metadata: FileMetaData, compressors: Compressors): AsyncDataSource {
1011
const schema = parquetSchema(metadata)
1112
return {
13+
numRows: Number(metadata.num_rows),
1214
columns: schema.children.map(child => child.element.name),
1315
scan({ columns, where, limit, offset, signal }: ScanOptions) {
1416
// Convert WHERE AST to hyparquet filter format
@@ -17,6 +19,9 @@ export function parquetDataSource(file: AsyncBuffer, metadata: FileMetaData, com
1719
const appliedWhere = Boolean(filter && whereFilter)
1820
const appliedLimitOffset = !where || appliedWhere
1921

22+
// Extract spatial filter for row group pruning
23+
const spatialFilter = extractSpatialFilter(where)
24+
2025
// Ensure columns exist in metadata if provided
2126
if (columns) {
2227
for (const col of columns) {
@@ -35,6 +40,12 @@ export function parquetDataSource(file: AsyncBuffer, metadata: FileMetaData, com
3540
if (signal?.aborted) break
3641
const rowCount = Number(rowGroup.num_rows)
3742

43+
// Skip row groups using geospatial statistics
44+
if (spatialFilter && !rowGroupOverlaps(rowGroup, spatialFilter)) {
45+
groupStart += rowCount
46+
continue
47+
}
48+
3849
// Skip row groups by offset if where is fully applied
3950
let safeOffset = 0
4051
let safeLimit = rowCount
@@ -65,7 +76,7 @@ export function parquetDataSource(file: AsyncBuffer, metadata: FileMetaData, com
6576

6677
// Yield each row
6778
for (const row of data) {
68-
yield asyncRow(row)
79+
yield asyncRow(row, Object.keys(row))
6980
}
7081

7182
remainingLimit -= data.length
@@ -78,11 +89,3 @@ export function parquetDataSource(file: AsyncBuffer, metadata: FileMetaData, com
7889
},
7990
}
8091
}
81-
82-
function asyncRow(obj: Record<string, SqlPrimitive>): AsyncRow {
83-
const cells: Record<string, () => Promise<SqlPrimitive>> = {}
84-
for (const [key, value] of Object.entries(obj)) {
85-
cells[key] = () => Promise.resolve(value)
86-
}
87-
return { columns: Object.keys(obj), cells }
88-
}

src/lib/parquet/parquetSpatial.ts

Lines changed: 18 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
import type { RowGroup } from 'hyparquet'
2-
import { bbox } from 'squirreling/src/spatial/bbox.js'
3-
import { parseWkt } from 'squirreling/src/spatial/wkt.js'
42
import type { ExprNode } from 'squirreling/src/ast.js'
5-
import type { BBox, Geometry, SimpleGeometry } from 'squirreling/src/spatial/geometry.js'
3+
import { bbox, decompose, parseWkt } from 'squirreling/src/spatial/index.js'
4+
import type { BoundingBox, Geometry } from 'squirreling/src/spatial/geometry.js'
65

76
export interface SpatialFilter {
87
column: string
9-
queryBbox: BBox
8+
queryBbox: BoundingBox
109
}
1110

1211
/**
@@ -40,35 +39,30 @@ function evaluateConstantGeom(node: ExprNode): Geometry | undefined {
4039
return parseWkt(arg.value) ?? undefined
4140
}
4241
if (node.funcName === 'ST_MAKEENVELOPE') {
43-
if (node.args.length < 4) return
44-
const nums = node.args.slice(0, 4).map(a => a.type === 'literal' ? Number(a.value) : NaN)
45-
if (nums.some(n => isNaN(n))) return
46-
const [xmin = 0, ymin = 0, xmax = 0, ymax = 0] = nums
42+
if (node.args.length !== 4) return
43+
const nums = node.args.map(evaluateConstantNumber) as [number, number, number, number]
44+
if (nums.some(isNaN)) return
45+
const [xmin, ymin, xmax, ymax] = nums
4746
return {
48-
type: 'Polygon' as const,
47+
type: 'Polygon',
4948
coordinates: [[[xmin, ymin], [xmax, ymin], [xmax, ymax], [xmin, ymax], [xmin, ymin]]],
5049
}
5150
}
5251
}
5352

53+
function evaluateConstantNumber(node: ExprNode): number {
54+
if (node.type !== 'literal') return NaN
55+
if (node.value === null) return NaN // Number(null) => 0
56+
return Number(node.value)
57+
}
58+
5459
/**
5560
* Compute the bounding box of any geometry type.
5661
*/
57-
function geomBbox(geom: Geometry): BBox | undefined {
58-
if (geom.type === 'Point' || geom.type === 'LineString' || geom.type === 'Polygon') {
59-
return bbox(geom)
60-
}
61-
let parts: SimpleGeometry[]
62-
if (geom.type === 'MultiPoint') {
63-
parts = geom.coordinates.map(c => ({ type: 'Point' as const, coordinates: c }))
64-
} else if (geom.type === 'MultiLineString') {
65-
parts = geom.coordinates.map(c => ({ type: 'LineString' as const, coordinates: c }))
66-
} else if (geom.type === 'MultiPolygon') {
67-
parts = geom.coordinates.map(c => ({ type: 'Polygon' as const, coordinates: c }))
68-
} else {
69-
return // GeometryCollection - not worth the complexity
70-
}
71-
if (!parts.length) return
62+
function geomBbox(geom: Geometry): BoundingBox | undefined {
63+
const parts = decompose(geom)
64+
if (parts.length === 0) return
65+
7266
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity
7367
for (const part of parts) {
7468
const { minX: bMinX, minY: bMinY, maxX: bMaxX, maxY: bMaxY } = bbox(part)

0 commit comments

Comments
 (0)