Minor changes
- b99de97
Type dimension values in dataset and metric result rows (
DatasetRow,DatasetRowFor,MetricRow,MetricRowFor, and the results ofcreateDatasetClient(...).execute()) as what queries actually return. Numeric dimensions are nownumber | string | null, because the built-in ClickHouse adapter returnsInt64,UInt64and wider integers as JSON strings to keep their precision. Every dimension, andperiod, also includesnull, becauseNullablecolumns return SQL NULL. A newDimensionResultValue<T>type describes these values;@hypequery/servere-exports it.InferDimensionTypeis unchanged and still describes a dimension's declared type.This is a breaking type change. Code that read a numeric dimension as
number, or any dimension orperiodwithout handlingnull, now fails to compile. Convert withNumber(value)orBigInt(value)as needed, and check fornull. - 27f2580
Dataset endpoints can stream their results. Opt an entry in with
stream: trueorstream: { maxRows }(defaultmaxLimit, at most the dataset'slimits.maxStreamRows, elselimits.maxResultSize); a request then asks withAccept: application/x-ndjsonfor row batches as NDJSON frames (start,rows…, thenendwithrowCount, or anerrorframe if the query fails after the response began) orAccept: application/vnd.apache.arrow.streamfor an Arrow IPC stream. Streams keep the endpoint's auth, tenant isolation and validation, are never cached, and stop the query when the client disconnects. Endpoints withoutstream, and requests that don't ask, get the usual JSON page. Custom handlers can stream any body by returningstreamBody(stream, contentType); the Node and fetch adapters write it with backpressure.NDJSON_MEDIA_TYPEandARROW_STREAM_MEDIA_TYPEare exported. - ae3a5fd
TypeScript serve APIs can now export a client contract and enforce its schema pin.
api.clientContract()returns the build view of every dataset endpoint and measure-backed metric endpoint, with full paths and schema hashes.hypequery generate:clientturns it into typed React hooks for a frontend in another repository. SetclientContract: { mountPath }when the API is mounted below a prefix. Eachdatasetsandmetricskey must be the dataset's or metric's own name. Metrics with a fixed grain (metric.by(grain)) aren't advertised.- A request that sends
X-Hypequery-Client-Schemais checked before input validation. A stale hash gets409 VALIDATION_ERRORwithdetails.code: 'CLIENT_SCHEMA_CHANGED'. A matching hash runs under the strict client profile, and its response metadata is limited torequestId,timingMs,rowCount,paginationandcache.hit, as in Python. Requests without the header behave as before. - The default CORS allow-headers now include
X-Hypequery-Client-Schema.
Patch changes
- 8c5e6d2
Fix
extractClientConfig(api)returning paths without the API's base path and ignoringapi.route(...)overrides (for example/queries/helloinstead of/api/analytics/queries/hello), which sent React hooks configured from it to URLs that 404. It now returns the same routes asapi.manifest().extractClientConfigis deprecated in favour ofapi.manifest(), which@hypequery/reactaccepts directly asmanifest. - c320820
With a required tenant, the built-in
/openapi.json,/docsand semantic/contractendpoints returned 403 for any request without a tenant, including signed-in users without one. These endpoints only describe the API and read no tenant data, so they now skip tenant resolution. Every query, dataset and discovery endpoint still requires the tenant. - 89cb73f
Fix four issues found running serve against a live ClickHouse:
createFetchHandler(and the Vercel edge handler and Next.js mounts built on it) threwInvalid response status code 204on CORS preflight requests. Responses with 204, 205, and 304 now have no body.serve({ metrics })andserve({ datasets })threw "queryBuilderis required" when the builder came fromcontext: () => ({ db }), the form the docs lead with. Semantic endpoints now use each request'sctx.db. A request whose context returns nodbfails instead of using another request's builder.tenant: { column }withoutmodesilently skipped tenant filtering, becausemodedefaulted to'manual', which ignorescolumn. Setting acolumnnow defaultsmodeto'auto-inject'. Passmode: 'manual'to keep filtering in your own queries.createAnalyticsTokenIssuercould not mint scopes, so its tokens never satisfiedrequiredScopes. It now acceptsscopesand writes them as the standardscopeclaim.
- 9c08fb8
Streamed responses are now observable to the end.
onRequestEndand the query log'scompletedevent fire when a stream finishes (or the client abandons it) rather than when it opens, with the fulldurationMsand a newstreamfield (outcome,bytes,openDurationMs). A failure after the response began — an NDJSON stream that ended with itserrorframe, an Arrow stream that broke off, or a stream the server timed out — now reachesonErrorand anerrorlog event with the original error; the client still sees only the sanitized frame. Custom handlers can report an in-band failure withstreamBody(stream, contentType, { failure }).The Node adapter bounds streams with
streamIdleTimeout(no bytes sent for this long; defaults torequestTimeout) andstreamTimeout(total, default unlimited); a stream that exceeds either is cancelled, stopping its query, and the connection closed.Acceptq-values now decide between a JSON page and a stream (application/json;q=1, application/x-ndjson;q=0.1gets a page). Streaming-capable dataset endpoints sendVary: Accept, combined with any CORSVary: Origin. The fetch adapter no longer sends two content types when custom headers setContent-Type.