Changelog

Every release across the hypequery packages and the Python SDK, newest first.

RSS
  1. hypequery (Python)v0.1.0b4Pre-release
    • measure.approx_count_distinct(field) estimates a distinct count with ClickHouse uniq (uniqIf with measure filters), far cheaper than the exact count_distinct (uniqExact) on large columns. Catalogs and semantic contracts mark it, and derived measures that use it, approximate: true, as TypeScript does; it is duplicate-insensitive through belongs_to. Publishing it is refused until deployment contract 3, as in TypeScript.

    • Exported client contracts keep isNull and isNotNull on boolean filters that allow them (RFC 0023), instead of dropping them.

    • Documentation for the ported ClickHouse features: null checks, aggregate states, row resolution, segments, window and shift measures, in the Python datasets pages and the package README. A shared execution corpus (specs/datasets/time-measures-corpus-v1.json) now checks Python's window and shift results row for row against TypeScript.

    • measure.shift can wrap a window, a derived measure or another shift, as in TypeScript: a prior-year year-to-date, a shifted average order value, or shift(shift(x)). The wrapped measure is recalculated over the earlier interval, and shifted formulas divide shifted aggregates. Derived measures over derived measures that read windows or shifts are now planned too, instead of refused. Definitions reject dependency cycles through shifts and formulas, and catalogs report the grains both the shift and its input support. A shifted formula has no single aggregation, so its catalog and semantic contract entries omit aggregation and field, hashing the same as TypeScript. A query is refused as input-invalid when nested shifts would need more than 64 evaluation contexts or 1024 evaluated measures.

    • client.stream(target, query) and client.stream_arrow(target, query) stream a dataset query's result instead of returning it whole, so memory stays flat however many rows there are. stream yields batches of the rows execute returns; stream_arrow yields pyarrow.RecordBatches with ClickHouse's native column types (decimal128, uint64), with read_all() and to_reader() for DuckDB, polars and pandas (hypequery[arrow]). Both run the query execute would, under the same settings, tenant scope, deadline and cancellation, without the cache or pagination. Use them as context managers (async with on the async client): closing a stream early stops the query, with KILL QUERY where a control connection is configured. Every ClickHouse and chDB executor streams; a result that grows past max_result_rows or max_result_bytes fails as too-large rather than ending short. Custom executors stream by implementing the new StreamingQueryExecutor protocol.

    • Streams have ceilings of their own. DatasetLimits(max_stream_rows=...) lets a dataset allow streams more rows than the pages and agents max_result_size bounds; a stream with no limit runs under that ceiling as its LIMIT and ends there. A stream's memory stays flat, so its byte limit is stream_max_result_bytes, a new create_dataset_client option of at most 1 TiB. Without it, a stream keeps a max_result_bytes the client's settings set and runs under 1 TiB in place of the default. max_result_rows and max_execution_time still bound it, and a request context can still tighten all three.

    15 more changes in this release →
  2. @hypequery/servev0.25.0Minor

    Minor changes

    • Type dimension values in dataset and metric result rows (DatasetRow, DatasetRowFor, MetricRow, MetricRowFor, and the results of createDatasetClient(...).execute()) as what queries actually return. Numeric dimensions are now number | string | null, because the built-in ClickHouse adapter returns Int64, UInt64 and wider integers as JSON strings to keep their precision. Every dimension, and period, also includes null, because Nullable columns return SQL NULL. A new DimensionResultValue<T> type describes these values; @hypequery/serve re-exports it. InferDimensionType is 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 or period without handling null, now fails to compile. Convert with Number(value) or BigInt(value) as needed, and check for null.

      b99de97
    • Dataset endpoints can stream their results. Opt an entry in with stream: true or stream: { maxRows } (default maxLimit, at most the dataset's limits.maxStreamRows, else limits.maxResultSize); a request then asks with Accept: application/x-ndjson for row batches as NDJSON frames (start, rows…, then end with rowCount, or an error frame if the query fails after the response began) or Accept: application/vnd.apache.arrow.stream for 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 without stream, and requests that don't ask, get the usual JSON page. Custom handlers can stream any body by returning streamBody(stream, contentType); the Node and fetch adapters write it with backpressure. NDJSON_MEDIA_TYPE and ARROW_STREAM_MEDIA_TYPE are exported.

      27f2580
    • 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:client turns it into typed React hooks for a frontend in another repository. Set clientContract: { mountPath } when the API is mounted below a prefix. Each datasets and metrics key 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-Schema is checked before input validation. A stale hash gets 409 VALIDATION_ERROR with details.code: 'CLIENT_SCHEMA_CHANGED'. A matching hash runs under the strict client profile, and its response metadata is limited to requestId, timingMs, rowCount, pagination and cache.hit, as in Python. Requests without the header behave as before.
      • The default CORS allow-headers now include X-Hypequery-Client-Schema.
      ae3a5fd

    Patch changes

    • Fix extractClientConfig(api) returning paths without the API's base path and ignoring api.route(...) overrides (for example /queries/hello instead of /api/analytics/queries/hello), which sent React hooks configured from it to URLs that 404. It now returns the same routes as api.manifest(). extractClientConfig is deprecated in favour of api.manifest(), which @hypequery/react accepts directly as manifest.

      8c5e6d2
    • With a required tenant, the built-in /openapi.json, /docs and semantic /contract endpoints 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.

      c320820
    • Fix four issues found running serve against a live ClickHouse:

      • createFetchHandler (and the Vercel edge handler and Next.js mounts built on it) threw Invalid response status code 204 on CORS preflight requests. Responses with 204, 205, and 304 now have no body.
      • serve({ metrics }) and serve({ datasets }) threw "queryBuilder is required" when the builder came from context: () => ({ db }), the form the docs lead with. Semantic endpoints now use each request's ctx.db. A request whose context returns no db fails instead of using another request's builder.
      • tenant: { column } without mode silently skipped tenant filtering, because mode defaulted to 'manual', which ignores column. Setting a column now defaults mode to 'auto-inject'. Pass mode: 'manual' to keep filtering in your own queries.
      • createAnalyticsTokenIssuer could not mint scopes, so its tokens never satisfied requiredScopes. It now accepts scopes and writes them as the standard scope claim.
      89cb73f
    1 more change in this release →

    Updated dependencies: @hypequery/datasets@0.30.0, @hypequery/protocol@0.25.0

    @hypequery/protocolv0.25.0Minor

    Minor changes

    • Portable semantic queries accept having and timezone (RFC 0025), so a host running through the semantic data plane can execute what the MCP query_dataset tool and client contracts already advertise.

      • @hypequery/protocol: validateProtocolSemanticQuery accepts having (dataset queries; each condition names a selected measure, with a numeric comparison) and timezone (RFC 0018's pinned registry) under extensions 1 and 2. New ProtocolHavingCondition and ProtocolHavingComparisonOperator types. Cache preimages refuse a non-empty having or a non-UTC timezone rather than leave them out of the key; "UTC" normalizes as if absent, so no existing preimage changes.
      • @hypequery/deployment: the semantic data plane counts having conditions against maxFilters together with filters and enforced constraints, refuses a timezone on a dataset with no visible time field, and refuses having on a hidden measure exactly as it refuses selecting one.
      • @hypequery/datasets: the portable executor passes having and timezone to the dataset client.
      d8829e6
    @hypequery/clickhousev2.15.0Minor

    Minor changes

    • Aggregate result types now match what ClickHouse returns, instead of string for every aggregate:

      • sum(): number for Float* and Decimal columns, string for integer columns (they widen to a quoted Int64), plus | null for Nullable columns. Over a subquery or CTE column known only as number, it is number | string.
      • avg(), stddev(), variance(): number | null. NaN over too few rows arrives as null.
      • min(), max(), argMin(), argMax(): the column's own type.
      • quantile(): number | null for numeric columns, the column's type for dates.
      • count(), countDistinct(), approxCountDistinct(): unchanged (string).

      Code that converted results with Number(...) keeps compiling. Code that called string methods on, for example, a Float64 sum now gets a type error where it used to fail at runtime.

      Schemas can now declare AggregateFunction(...) and SimpleAggregateFunction(fn, T) columns, so a generated schema containing an AggregatingMergeTree table type-checks with createQueryBuilder<IntrospectedSchema>(). A SimpleAggregateFunction(fn, T) column has T's type; an AggregateFunction column is an opaque state that you read with mergeState().

      GROUP BY inference no longer adds a selected expression that wraps an aggregate (round(avg(x), 2)) or a window function (rank() OVER (ORDER BY sum(x))), which ClickHouse rejected with "Aggregate function ... is found in GROUP BY".

      The readonly error for the precision-safe 64-bit setting now names the fixes that keep precision: setting output_format_json_quote_64bit_integers = 1 in the user's profile, or readonly = 2. integerJsonEncoding: 'server-default' is still offered as the fallback.

      The legacy hypequery-generate-types generator types parameterized views, and types SimpleAggregateFunction columns by their value type, as @hypequery/cli now does.

      c320820
    • Stream results as Arrow. builder.streamArrow() and db.rawArrowStream(sql, params) return the result as Arrow IPC stream bytes (ClickHouse ArrowStream) with native column types, for any Arrow reader to decode or for passing on unchanged. Batches are uncompressed by default, because JavaScript Arrow readers don't include the LZ4 codec; a connection-level or per-call output_format_arrow_compression_method overrides that. db.rawStream(sql, params) streams a raw statement's rows, as builder.stream() does for built queries. If ClickHouse fails after sending part of an Arrow result, the stream errors rather than handing the exception text to the decoder: with ClickHouse's own error on ClickHouse 25.11 or later, which marks the failure, and on older servers because the bytes end without Arrow's end-of-stream marker. Custom adapters can implement the new optional DatabaseAdapter.streamArrow.

      3bb485c
    • Fix JavaScript Date filter values against DateTime sorting-key columns. A Date used to render as a quoted ISO string ('2026-03-01T00:00:00.000Z'), which ClickHouse rejects with TYPE_MISMATCH when it compares the string with a DateTime column in the sorting key. The same happened for a Date column, and for any value with milliseconds. A Date now renders as toDateTime('2026-03-01 00:00:00', 'UTC'), or as toDateTime64(..., 3, 'UTC') when it has milliseconds or falls outside the DateTime range. This keeps the exact UTC instant, still lets the primary index prune, and works in IN lists. It applies to where, between, in, rawQuery placeholders and CrossFilter. CrossFilter no longer converts Date values to ISO strings itself. Dates inside array, tuple and map parameters are still sent as ISO text, which CAST parses correctly.

      c320820

    Patch changes

    • Arrow streams work for readonly = 1 users and catch mid-stream failures on servers before ClickHouse 25.11.

      • streamArrow() and rawArrowStream() request the format with a FORMAT ArrowStream clause instead of the default_format setting, which readonly = 1 users may not change. A trailing ; or comment is dropped first.
      • The uncompressed-batches default (output_format_arrow_compression_method = 'none') is still sent, and a readonly = 1 user accepts it when its profile already sets that value. When ClickHouse rejects it, the error explains the fix. The new arrowCompression: 'server-default' adapter option leaves the setting out, so batches use the server's compression method.
      • Detecting a mid-stream failure from its tagged exception block needs ClickHouse 25.11 or later, because older servers send no X-ClickHouse-Exception-Tag. An Arrow stream from any server now also errors when its bytes end without Arrow's end-of-stream marker, so a failed or truncated stream no longer looks complete. On older servers, ClickHouse's error message is recovered from the response where possible.
      0ba4596
    • stream() no longer hangs when an adapter's byte stream splits a JSON row across two chunks, keeps a final row that has no trailing newline, and keeps characters split across chunks intact. The built-in adapter was unaffected because its client hands over whole rows; custom adapters that stream raw bytes were.

      3bb485c
    @hypequery/cliv1.29.0Minor

    Minor changes

    • Add hypequery skill install: writes hypequery's agent skills (hypequery-datasets, hypequery-query) where Claude Code (.claude/skills/), Cursor and Codex (.agents/skills/) load them. --agent, --scope project|user, --check for CI and --force; re-runs are idempotent and edited skills are never replaced without --force.

      2d4f9aa
    • Running hypequery with no command in an interactive terminal now starts hypequery init instead of printing help, so npx hypequery takes new users straight into setup. A one-line hint points to hypequery --help for the full command list. When the CLI is not attached to a terminal (scripts, CI, piped input), the bare command still prints help and exits with code 1, so nothing waits on a prompt.

      524bcf5
    • generate:datasets and init --style datasets now read each table's engine from system.tables, so scaffolded datasets no longer count rows ClickHouse hasn't merged yet. ReplacingMergeTree tables get rowResolution: { kind: 'final', engine: 'replacing' } (with deletedColumn when the engine has one), and CollapsingMergeTree / VersionedCollapsingMergeTree tables get the matching signColumn resolution. Version, sign and deleted columns are no longer summed or averaged. Pre-aggregated tables drop the row-count measure: AggregateFunction columns become measure.merge(...) measures (parametric states such as quantile(0.9) are noted and skipped), SimpleAggregateFunction(sum|min|max, …) columns use that function, and SummingMergeTree tables sum only their summing columns (without a list, numeric columns outside the sorting key). If the engines can't be read, tables are scaffolded as before.

      fbe9482
    • Add hypequery export:client-contract <api-file>. It writes the client contract of a TypeScript serve API (client-contract.json by default) for hypequery generate:client, with --output, --stdout, --check and --force, matching Python's hypequery export client-contract. Like Python, it refuses a contract with no endpoints.

      ae3a5fd

    Patch changes

    • Report ClickHouse connection failures accurately:

      • generate, generate:datasets, and dev no longer report "Connected to ClickHouse" with 0 tables when the database is unreachable or rejects the credentials. They now show the real error.
      • A refused connection now shows its message and troubleshooting hints instead of an empty error.
      • The ClickHouse client's raw stack-trace logging is off, so commands show only their own error output.
      • hypequery dev showed Docs and OpenAPI URLs without the base path for createAPI entries (for example /docs instead of /api/analytics/docs). It now shows the routes the API actually serves.
      89cb73f
    • hypequery --help now describes the CLI as "Type-safe embedded analytics on ClickHouse", matching the website and README.

      141f0b6
    2 more changes in this release →

    Updated dependencies: @hypequery/protocol@0.25.0, @hypequery/deployment@0.12.0

    @hypequery/deploymentv0.12.0Minor

    Minor changes

    • Portable semantic queries accept having and timezone (RFC 0025), so a host running through the semantic data plane can execute what the MCP query_dataset tool and client contracts already advertise.

      • @hypequery/protocol: validateProtocolSemanticQuery accepts having (dataset queries; each condition names a selected measure, with a numeric comparison) and timezone (RFC 0018's pinned registry) under extensions 1 and 2. New ProtocolHavingCondition and ProtocolHavingComparisonOperator types. Cache preimages refuse a non-empty having or a non-UTC timezone rather than leave them out of the key; "UTC" normalizes as if absent, so no existing preimage changes.
      • @hypequery/deployment: the semantic data plane counts having conditions against maxFilters together with filters and enforced constraints, refuses a timezone on a dataset with no visible time field, and refuses having on a hidden measure exactly as it refuses selecting one.
      • @hypequery/datasets: the portable executor passes having and timezone to the dataset client.
      d8829e6

    Updated dependencies: @hypequery/protocol@0.25.0

    @hypequery/mcpv0.12.0Minor

    Minor changes

    • Hosted error details on MCP tool errors: MCPToolError and its published error envelope carry optional retryAfterSeconds, relist and reason (a host's own stable code), and a new MCP_RATE_LIMITED code (category budget, retryable). mcpToolErrorFromSemanticFailure maps a semantic data-plane failure to the MCP error an agent receives, and createMCPErrorResponse is exported so hosts build tool errors exactly as the local server does.

      a649997

    Patch changes

    • Describe the package as a ClickHouse MCP server in its npm description, keywords and README, with a link to the five-minute quickstart.

      148b262
    • Add mcpName (io.github.hypequery/hypequery) to the package manifest and a server.json, so the server can be published to the official MCP Registry. The build and the release version step write the package version into server.json, since the registry verifies mcpName against the npm release it names. No runtime change.

      75648f9

    Updated dependencies: @hypequery/datasets@0.30.0, @hypequery/protocol@0.25.0

    @hypequery/datasetsv0.30.0Minor

    Minor changes

    • buildClientContract no longer advertises having predicates or a per-request timezone by default. They are off unless the host's route passes them through to the data plane: a route that builds its operation from a fixed list of fields drops them, so a generated client could send them and get unfiltered rows. Hosts that pass them through opt in with capabilities: { having: true, timezone: true }.

      Both flags are part of each endpoint's hashed schema, so upgrading moves the schemaHash of every endpoint with a time field or a numeric measure. Regenerate clients (hypequery generate:client) after upgrading; until then generate:client --check reports drift, and a host that validates requests against the new contract refuses an old client's timezone instead of ignoring it.

      872713e
    • Type dimension values in dataset and metric result rows (DatasetRow, DatasetRowFor, MetricRow, MetricRowFor, and the results of createDatasetClient(...).execute()) as what queries actually return. Numeric dimensions are now number | string | null, because the built-in ClickHouse adapter returns Int64, UInt64 and wider integers as JSON strings to keep their precision. Every dimension, and period, also includes null, because Nullable columns return SQL NULL. A new DimensionResultValue<T> type describes these values; @hypequery/serve re-exports it. InferDimensionType is 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 or period without handling null, now fails to compile. Convert with Number(value) or BigInt(value) as needed, and check for null.

      b99de97
    • Stream dataset and metric results. createDatasetClient(...).stream(target, query, context) returns { data, meta }, where data is a ReadableStream of row batches with the same row types and wire values as execute(), so memory stays flat however many rows there are. streamArrow(target, query, context) returns the result as Arrow IPC stream bytes with native column types. Both apply the same validation and tenant scoping as execute(). Their row ceiling is limits.maxResultSize, or the new limits.maxStreamRows where a dataset declares one, so exports can be allowed more rows than pages and agents; meta.resultLimit reports when the ceiling bounded a query. Streams skip the result cache and report no pagination. They need @hypequery/clickhouse 2.15.0 or later; the query builder protocol gains optional stream, streamArrow, rawStream and rawArrowStream methods.

      3bb485c
    • Add buildDatasetClientContract(endpoints, { mountPath }). It builds an RFC 0018 client contract from dataset definitions and metrics. It mirrors Python's export_client_contract, so an equivalent definition exports the same contract in both languages.

      ae3a5fd

    Patch changes

    • Client contracts built from a deployment keep isNull and isNotNull on boolean filters that allow them, as RFC 0023 specifies. They were previously dropped, so a boolean filter allowing only null checks disappeared from the contract.

      8d02067
    • A relationship join to a resolved (FINAL) target whose deleted column is named _hq_match no longer drops every target row: the join's match marker now avoids the current-row column's name.

      4c7cc13
    2 more changes in this release →

    Updated dependencies: @hypequery/protocol@0.25.0

    @hypequery/reactv0.9.0Minor

    Minor changes

    • Type dataset and metric dimension values from InferApiType APIs as what serve actually returns. Numeric dimensions are now number | string | null, because the built-in ClickHouse adapter returns Int64, UInt64 and wider integers as JSON strings to keep their precision. Every dimension, and period, also includes null, because Nullable columns return SQL NULL. Types from generated clients already followed these rules.

      This is a breaking type change. Code that read a numeric dimension as number, or any dimension without handling null, now fails to compile. Convert with Number(value) or BigInt(value) as needed, and check for null.

      a6b4478
  3. hypequery (Python)v0.1.0b3Pre-release
    • Datasets can run on embedded ClickHouse with the new chdb extra: create_chdb_executor(path) (and create_async_chdb_executor) execute the same compiled, read-only, parameter-bound SQL in-process on chDB, with no server. path is an on-disk directory or ":memory:"; database= sets where unqualified sources resolve. Results decode exactly as from a ClickHouse server.

    • hypequery init --database chdb scaffolds a project on embedded ClickHouse, as the TypeScript CLI does: the standalone or FastAPI app opens a chDB database directory (--chdb-path, default analytics.chdb) with no server or connection settings, and installs the chdb extra. An existing database is inspected like a server's; a new one gets the orders example and a seeding step. hypequery generate datasets reads the same database from --chdb-path, the path init records in pyproject.toml, or a project analytics.chdb when no ClickHouse server is configured, and says plainly when another process such as hypequery dev has the database open.

    • hypequery dev serves a chDB project with reload, its default: the supervisor that checks the app before starting the worker now releases the app's database, since chDB holds a database directory for one process at a time (run_dev(reload=True) does the same; other chDB executors in the process stay open).

    • The CLI now collects pseudonymous usage telemetry, on by default like the TypeScript CLI: one event per command with the command, flag names (never values), outcome, a duration bucket, and install context such as the Python version and installer. It never records code, SQL, names, paths or credentials, and never delays a command. Disable it with hypequery telemetry disable, --no-telemetry, DO_NOT_TRACK=1 or HYPEQUERY_TELEMETRY_DISABLED=1; set HYPEQUERY_TELEMETRY_DEBUG=1 to print the event instead of sending it. Settings and the install ID are shared with the TypeScript CLI.

    • New hypequery telemetry [status|enable|disable] command and global --no-telemetry flag.

    • create_api(client_profile=...) puts every Dataset entry of datasets= in the client contract, so the shorthand can produce typed React hooks.

    9 more changes in this release →
  4. @hypequery/servev0.24.4Patch

    Patch changes

    • Type ctx.tenantId on the query resolver context. The pipeline already set it at runtime when a tenant config extracts a tenant ID, but QueryRuntimeContext did not declare it, so manual-mode resolvers reading ctx.tenantId failed to type-check. Also corrects the createAuthSystem JSDoc example, which passed an options object to a function that takes only type parameters.

      934b6ce
    hypequery (Python)v0.1.0b2Pre-release
    • hypequery init is guided in a terminal, as the TypeScript CLI is. It asks for whatever its flags leave unset: where to create the project; how to reach ClickHouse (the connection already in your environment, or a URL and credentials typed in, with the password hidden, used only to read the schema and never written to a file); which tables, when the database has more than ten; the tenant column, when one looks like a tenant id; and which app to scaffold, defaulting to FastAPI, Django or Flask when it finds one. A failed connection offers to try again, change the details, or create the offline example. It ends with the same choices as flags. Every flag answers its question, and --no-interactive, a CI environment variable, or input that is not a terminal skip the questions and keep the previous behavior.

    • The hypequery CLI loads .env.local and .env from the directory it runs in, as the TypeScript CLI does. A variable already set wins over both files. The library and generated apps still read only the real environment; hypequery.cli.load_env_files(directory) applies the same rules in your own scripts, such as the scaffold README's seed step.

    • When init can't reach ClickHouse, the message names CLICKHOUSE_URL as well as the host settings and suggests --skip-connection, and the driver's own "Unexpected Http Driver Exception" log line no longer reaches the terminal.

    • hypequery init --framework fastapi scaffolds a FastAPI app.py: the bearer-token agents API mounted at /api/agents, and /analytics/<dataset>, a route of your own that refuses everyone until its current_principal dependency reads your login. The project depends on fastapi directly; hypequery adds no FastAPI extra.

    • Declaring filters on a dataset no longer makes its other dimensions unfilterable (RFC 0024), matching TypeScript. Callers may filter on every declared filter and every dimension not marked filterable=False. A dimension's own filter takes the operators of the filters declared on its field, so declaring plan_filter on plan with operators=("eq",) also limits plan to eq. Relationship filters reach every filterable, non-SQL dimension of a belongs_to or has_one target. An explicit empty filters={} no longer disables filtering; mark a dimension filterable=False to keep it out. Catalogs, client contracts and deployment contracts are unchanged.

    • Base dataset columns are always qualified (__hq_base), not only when a relationship join is present. ClickHouse resolves an output alias before a column of the same name, so a dimension status reading column country captured a filter or selection on the real status column (silent wrong rows), and a measure named like its column (amount=measure.sum("amount")) broke any other measure or filter on that column.

    11 more changes in this release →
  5. @hypequery/datasetsv0.29.0Minor

    Minor changes

    • Declaring filters on a dataset no longer makes the rest of its dimensions unfilterable. A dataset's filterable fields are now its declared filters plus every dimension not marked filterable: false (RFC 0024). A dimension that no declared filter is named after accepts the default operators, without null checks, unless filters declared on it list operators, in which case it takes theirs: declaring plan_filter on plan with operators: ['eq'] also limits plan to eq.

      Relationship filters reach every filterable, non-SQL dimension of a belongsTo or hasOne target. Rehydrated deployment contracts, whose filter map lists only the declared filters, follow the same rule. Validation, enforced constraints, buildDatasetInputSchema and the DatasetFilterFor types all agree.

      This widens what queries accept. To keep a dimension out of filters, mark it filterable: false; an explicit empty filters map no longer disables filtering. Published catalogs and contracts are unchanged.

      86f9207
    @hypequery/cliv1.28.0Minor

    Minor changes

    • hypequery generate:client --cloud generates typed React routes and types from the live Cloud release of the target chosen with hypequery login (or --project, --environment and --endpoint with HYPEQUERY_API_TOKEN). The file also exports clientBaseUrl, the origin the environment is served from. deploy --client <path> regenerates that file after each live publish; if regeneration fails, the deploy still succeeds and the CLI prints the command to retry.

      0f1f056

    Patch changes

    • hypequery deploy and deployment:build warn when a served dataset has a customer-like column (tenant_id, org_id, organization_id, organisation_id, account_id, customer_id, workspace_id or company_id) in a dimension, measure or filter, and neither the dataset nor its endpoint is scoped by tenant. Without that, every caller can read every customer's rows. The warning names the tenantKey line to add, or says to mark the dataset shared in Cloud. The cloud.ts scaffold now explains what empty roles and scopes allow.

      a7cf856

    Updated dependencies: @hypequery/protocol@0.24.2, @hypequery/deployment@0.11.2

    @hypequery/reactv0.8.0Minor

    Minor changes

    • Requests from a generated client that the server refuses with 409 CLIENT_SCHEMA_CHANGED now throw ClientSchemaChangedError, a subclass of HttpError, whose message says to regenerate the client.

      0f1f056
    @hypequery/mcpv0.11.2Patch

    Patch changes

    • Strict client-query rejections say where they failed. validateProtocolClientQuery now sets a JSON Pointer on every HQ_CLIENT_QUERY error (/filters/0/value, /orderBy/1/field, /grain for agents) instead of the root for most of them. Pointers name contract members and item indexes only; an unknown member is still reported at the root because its name is submitted data. The shared conformance fixtures pin the pointers for TypeScript and Python.

      The typed endpoint schemas from buildClientContractQuerySchemas carry the pointer as the zod issue path, and the MCP HTTP bridge names it in its error message (Invalid client query at /filters/0/value.).

      17ffe2b

    Updated dependencies: @hypequery/datasets@0.28.0, @hypequery/protocol@0.24.2

    @hypequery/datasetsv0.28.0Minor

    Minor changes

    • Add buildClientContract(deployment, options), which turns a deployment contract (for example publishToCloud's output) into a hashed client contract that hypequery generate:client reads. It follows the Python builder's rules: unknown nullability for local fields, tenant-key filters never advertised, operators narrowed by field type, portable time grains, and limits narrowed by each dataset. Relationship fields are not exported yet. Hosts pass their own routes, query ceilings and default timezone; view: 'authorized' with include marks a caller's view; include filters whole datasets only, so field-level narrowing stays with the host. Only datasets with an endpoint are exported, and a dataset with a field named period alongside a time field is rejected.

      4847c9c

    Patch changes

    • Strict client-query rejections say where they failed. validateProtocolClientQuery now sets a JSON Pointer on every HQ_CLIENT_QUERY error (/filters/0/value, /orderBy/1/field, /grain for agents) instead of the root for most of them. Pointers name contract members and item indexes only; an unknown member is still reported at the root because its name is submitted data. The shared conformance fixtures pin the pointers for TypeScript and Python.

      The typed endpoint schemas from buildClientContractQuerySchemas carry the pointer as the zod issue path, and the MCP HTTP bridge names it in its error message (Invalid client query at /filters/0/value.).

      17ffe2b
    • Three planner fixes that change results:

      • Base dataset columns are always qualified with the dataset source, not only when a relationship join is present. ClickHouse resolves an output alias before a column of the same name, so a dimension status reading column country captured a filter or selection on the real status column (silent wrong rows), and a measure named like its column (amount: measure.sum('amount')) broke any other measure or filter on that column with ILLEGAL_AGGREGATION.
      • Under a runtime scope of several tenants ({ in: [...] }) or all tenants ({ scope: 'all' }), relationship joins also match the tenant keys, so a key reused by another tenant no longer joins that tenant's row. checkRelationships counts (key, tenant) pairs under those scopes to match. Single-tenant SQL is unchanged.
      • Formula divide casts the dividend to Float64. Decimal division kept the dividend's scale and truncated, so round(divide(revenue, nullIfZero(orders)), 2) could come out one cent low and a Decimal share such as 0.685 read 0.68.
      2b5ff4e

    Updated dependencies: @hypequery/protocol@0.24.2

    @hypequery/protocolv0.24.2Patch

    Patch changes

    • Strict client-query rejections say where they failed. validateProtocolClientQuery now sets a JSON Pointer on every HQ_CLIENT_QUERY error (/filters/0/value, /orderBy/1/field, /grain for agents) instead of the root for most of them. Pointers name contract members and item indexes only; an unknown member is still reported at the root because its name is submitted data. The shared conformance fixtures pin the pointers for TypeScript and Python.

      The typed endpoint schemas from buildClientContractQuerySchemas carry the pointer as the zod issue path, and the MCP HTTP bridge names it in its error message (Invalid client query at /filters/0/value.).

      17ffe2b
  6. @hypequery/cliv1.27.0Minor

    Minor changes

    • Support deploy --draft <name> and deployment:submit --draft <name> for named Cloud code drafts. Draft uploads preserve Production, validate the returned draft and staging URL, and print staging details instead of Production endpoints.

      07bc05f

    Patch changes

    • generate:client names the output path it refuses, and why: the symbolic link or non-directory ancestor it found, with the same path written without the link (for example /tmp → /private/tmp on macOS). An ancestor that is a file is now reported as such, not as a bare ENOTDIR.

      6652388
    • Name the field when a client contract has one this version doesn't know. ProtocolClientContractError gains an optional field: for HQ_CLIENT_UNKNOWN_FIELD, the first unknown member at path (whose value, the parent object, is unchanged). hypequery generate:client reports where it is (for example /endpoints/0/annotations) and suggests upgrading @hypequery/cli, since the contract usually comes from a newer hypequery.

      21171cf
    • generate:datasets now maps ClickHouse column types by their exact outer type, the same mapping Cloud uses, instead of by substring. Previously Point, Array(Int32) and Tuple(Float64, Float64) became dimension.number() (with sum/avg measures), and Array(String) and Map(...) became dimension.string(), so the generated catalog advertised scalars that queries then failed on. Each scalar element of a named Tuple now becomes its own dimension through tupleElement, named <column><Element> (pickupCity). Other composite columns (Array, Map, unnamed Tuple, geo, JSON) are left out with a comment in the generated file. Only date/time-typed columns are picked as timeKey, and totalCount counts a column that is actually a dimension.

      Generated field names no longer clash. A field name is also its SQL alias, and ClickHouse resolves an alias before a column of the same name, so a field only takes a source column's name when it reads exactly that column. Otherwise it takes a numeric suffix (fareTotal2), with a comment saying why: with columns fare_total and fareTotal, the real fareTotal column keeps its name. Measure names (totalCount, totalX, avgX) follow the same rule, so a count column or a fare/Fare pair no longer produces duplicate keys.

      19c3de0

    Updated dependencies: @hypequery/protocol@0.24.1, @hypequery/deployment@0.11.1

    @hypequery/protocolv0.24.1Patch

    Patch changes

    • Name the field when a client contract has one this version doesn't know. ProtocolClientContractError gains an optional field: for HQ_CLIENT_UNKNOWN_FIELD, the first unknown member at path (whose value, the parent object, is unchanged). hypequery generate:client reports where it is (for example /endpoints/0/annotations) and suggests upgrading @hypequery/cli, since the contract usually comes from a newer hypequery.

      21171cf
    @hypequery/servev0.24.1Patch

    Patch changes

    • Node adapter (createNodeHandler) now works inside Express, NestJS and Fastify without workarounds:

      • Routes on req.originalUrl when a framework sets it, so app.use('/api/analytics', createNodeHandler(api.handler)) matches routes instead of returning 404. If you mounted under a prefix and set basePath: '' to compensate for the stripped URL, set basePath to the mount prefix instead.
      • Uses a body that an upstream parser already read (express.json(), express.text(), express.raw(), NestJS's default parser) from req.body instead of failing with a 400 VALIDATION_ERROR. String and Buffer bodies still go through bodyLimit and content-type parsing. On Fastify, set request.raw.body = request.body before calling the handler, or hijack from an onRequest hook.
      • If the body stream was consumed and no req.body is available, returns a 500 that explains how to fix the setup, instead of validating an empty body.
      eb75fc2
    • Node adapter: a JSON request body left as text by express.text() is parsed again when it was sent chunked or compressed, instead of reaching the endpoint as a raw string. When no length can be compared, a string body that is JSON object or array text is parsed and any other string is kept as a string, so a decoded "123" stays "123". As a result, a chunked or compressed express.text() body that is a bare JSON primitive (123, true, null) reaches the endpoint as a string; send such bodies with a Content-Length, or parse them with express.json(). And a JSON string decoded by express.json({ strict: false }) is held to bodyLimit.

      e7fe2bd
  7. hypequery (Python)v0.1.0b1Pre-release
    • hypequery init --framework standalone|django|flask chooses the application written beside datasets.py (decision 0023 step 6). standalone, the default, is app.py: an API from create_api, served as plain ASGI by hypequery dev with no web framework. It replaces the fastapi choice, which wrote the same app; to use FastAPI, mount app in it.

    • --framework django writes a small runnable Django project (settings.py, urls.py, manage.py) and analytics.py; --framework flask writes a Flask app.py. Both serve hypequery's endpoints natively (urls(api), blueprint(api)) twice: at /api/analytics behind the framework's own login (django_user, flask_session) with the same-origin check, and Django's CSRF middleware, for @hypequery/react; and at /api/agents behind a bearer token (HYPEQUERY_DEV_TOKEN in development), for @hypequery/mcp. Each also has a view or route of its own on the per-request client. Until wired to a login, Flask's session API and route answer 401, and tenant-scoped datasets refuse signed-in Django users. Pyproject extras are django or flask; the README covers secrets, hosts, jwt_strategy and moving the pieces into an existing Django project.

    • --framework works with --skip-connection: each framework gets the offline orders example, with datasets.py and seed.sql. The standalone example's dataset now lives in datasets.py too.

    • hypequery init accepts --tenant-column, like generate datasets, so a tenant-scoped project needs one command.

    • hypequery init records --tables / --exclude-tables in schema.json, and generate datasets reuses that selection unless given --tables, --exclude-tables or the new --all-tables. Regenerating no longer widens a project to every table in the database.

    • The scaffold's .env.example, README and app.py match how discovery connected: CLICKHOUSE_URL when it was set, otherwise the host, port and TLS setting discovery used. Credentials stay placeholders.

    152 more changes in this release →
  8. @hypequery/datasetsv0.27.0Minor

    Minor changes

    • Add isNull and isNotNull comparisons, which take no value (RFC 0023), so "has a churn date" is churned_at IS NOT NULL rather than a sentinel range. A protocol comparison using one is { kind: 'comparison', operator: 'isNotNull', left } with no right; a client query filter is { field, operator: 'isNull' } with no value. Validators reject a value on them and accept any field type. Measure fixed filters and segments can use them directly, including through the new isNull(field) and isNotNull(field) helpers (also re-exported by @hypequery/serve). Caller query filters are opt-in: a filter allows them only when its operators list names them, so default operator lists, and the identity of every existing deployment and client contract, are unchanged. The deployment data plane, the MCP dataset guide and the generated client input types support them, and the conformance corpus pins the new cases. Upgrade every consumer before publishing a contract that uses them: earlier validators reject the new operators.

      f103760

    Updated dependencies: @hypequery/protocol@0.24.0

    @hypequery/protocolv0.24.0Minor

    Minor changes

    • Client contracts can carry an optional annotations member per endpoint: the dataset group's label/description and per-dimension/measure fields text (RFC 0018 amendment). Text follows the deployment contract's rules (non-blank, no control characters, at most 4096 UTF-8 bytes) and is excluded from schemaHash, so rewording a description never trips the execution-time schema pin; contentHash still covers it. The MCP HTTP bridge's list_datasets now shows each group's own description (and label) instead of the generic text, and get_dataset_schema returns the annotations with each endpoint profile. generate:client emits them as JSDoc on each ClientApi entry and output row field. Upgrade these packages before pointing them at a backend that emits annotations: earlier validators reject the new member.

      24f82d5
    • Add isNull and isNotNull comparisons, which take no value (RFC 0023), so "has a churn date" is churned_at IS NOT NULL rather than a sentinel range. A protocol comparison using one is { kind: 'comparison', operator: 'isNotNull', left } with no right; a client query filter is { field, operator: 'isNull' } with no value. Validators reject a value on them and accept any field type. Measure fixed filters and segments can use them directly, including through the new isNull(field) and isNotNull(field) helpers (also re-exported by @hypequery/serve). Caller query filters are opt-in: a filter allows them only when its operators list names them, so default operator lists, and the identity of every existing deployment and client contract, are unchanged. The deployment data plane, the MCP dataset guide and the generated client input types support them, and the conformance corpus pins the new cases. Upgrade every consumer before publishing a contract that uses them: earlier validators reject the new operators.

      f103760
    @hypequery/servev0.24.0Minor

    Minor changes

    • Add isNull and isNotNull comparisons, which take no value (RFC 0023), so "has a churn date" is churned_at IS NOT NULL rather than a sentinel range. A protocol comparison using one is { kind: 'comparison', operator: 'isNotNull', left } with no right; a client query filter is { field, operator: 'isNull' } with no value. Validators reject a value on them and accept any field type. Measure fixed filters and segments can use them directly, including through the new isNull(field) and isNotNull(field) helpers (also re-exported by @hypequery/serve). Caller query filters are opt-in: a filter allows them only when its operators list names them, so default operator lists, and the identity of every existing deployment and client contract, are unchanged. The deployment data plane, the MCP dataset guide and the generated client input types support them, and the conformance corpus pins the new cases. Upgrade every consumer before publishing a contract that uses them: earlier validators reject the new operators.

      f103760

    Updated dependencies: @hypequery/datasets@0.27.0

    @hypequery/cliv1.26.0Minor

    Minor changes

    • Client contracts can carry an optional annotations member per endpoint: the dataset group's label/description and per-dimension/measure fields text (RFC 0018 amendment). Text follows the deployment contract's rules (non-blank, no control characters, at most 4096 UTF-8 bytes) and is excluded from schemaHash, so rewording a description never trips the execution-time schema pin; contentHash still covers it. The MCP HTTP bridge's list_datasets now shows each group's own description (and label) instead of the generic text, and get_dataset_schema returns the annotations with each endpoint profile. generate:client emits them as JSDoc on each ClientApi entry and output row field. Upgrade these packages before pointing them at a backend that emits annotations: earlier validators reject the new member.

      24f82d5
    • Add isNull and isNotNull comparisons, which take no value (RFC 0023), so "has a churn date" is churned_at IS NOT NULL rather than a sentinel range. A protocol comparison using one is { kind: 'comparison', operator: 'isNotNull', left } with no right; a client query filter is { field, operator: 'isNull' } with no value. Validators reject a value on them and accept any field type. Measure fixed filters and segments can use them directly, including through the new isNull(field) and isNotNull(field) helpers (also re-exported by @hypequery/serve). Caller query filters are opt-in: a filter allows them only when its operators list names them, so default operator lists, and the identity of every existing deployment and client contract, are unchanged. The deployment data plane, the MCP dataset guide and the generated client input types support them, and the conformance corpus pins the new cases. Upgrade every consumer before publishing a contract that uses them: earlier validators reject the new operators.

      f103760

    Updated dependencies: @hypequery/protocol@0.24.0, @hypequery/deployment@0.11.0

    @hypequery/protocol-conformancev0.17.0Minor

    Minor changes

    • Add isNull and isNotNull comparisons, which take no value (RFC 0023), so "has a churn date" is churned_at IS NOT NULL rather than a sentinel range. A protocol comparison using one is { kind: 'comparison', operator: 'isNotNull', left } with no right; a client query filter is { field, operator: 'isNull' } with no value. Validators reject a value on them and accept any field type. Measure fixed filters and segments can use them directly, including through the new isNull(field) and isNotNull(field) helpers (also re-exported by @hypequery/serve). Caller query filters are opt-in: a filter allows them only when its operators list names them, so default operator lists, and the identity of every existing deployment and client contract, are unchanged. The deployment data plane, the MCP dataset guide and the generated client input types support them, and the conformance corpus pins the new cases. Upgrade every consumer before publishing a contract that uses them: earlier validators reject the new operators.

      f103760

    Updated dependencies: @hypequery/protocol@0.24.0

    @hypequery/deploymentv0.11.0Minor

    Minor changes

    • Add isNull and isNotNull comparisons, which take no value (RFC 0023), so "has a churn date" is churned_at IS NOT NULL rather than a sentinel range. A protocol comparison using one is { kind: 'comparison', operator: 'isNotNull', left } with no right; a client query filter is { field, operator: 'isNull' } with no value. Validators reject a value on them and accept any field type. Measure fixed filters and segments can use them directly, including through the new isNull(field) and isNotNull(field) helpers (also re-exported by @hypequery/serve). Caller query filters are opt-in: a filter allows them only when its operators list names them, so default operator lists, and the identity of every existing deployment and client contract, are unchanged. The deployment data plane, the MCP dataset guide and the generated client input types support them, and the conformance corpus pins the new cases. Upgrade every consumer before publishing a contract that uses them: earlier validators reject the new operators.

      f103760

    Updated dependencies: @hypequery/protocol@0.24.0

    @hypequery/mcpv0.11.0Minor

    Minor changes

    • Client contracts can carry an optional annotations member per endpoint: the dataset group's label/description and per-dimension/measure fields text (RFC 0018 amendment). Text follows the deployment contract's rules (non-blank, no control characters, at most 4096 UTF-8 bytes) and is excluded from schemaHash, so rewording a description never trips the execution-time schema pin; contentHash still covers it. The MCP HTTP bridge's list_datasets now shows each group's own description (and label) instead of the generic text, and get_dataset_schema returns the annotations with each endpoint profile. generate:client emits them as JSDoc on each ClientApi entry and output row field. Upgrade these packages before pointing them at a backend that emits annotations: earlier validators reject the new member.

      24f82d5
    • Add isNull and isNotNull comparisons, which take no value (RFC 0023), so "has a churn date" is churned_at IS NOT NULL rather than a sentinel range. A protocol comparison using one is { kind: 'comparison', operator: 'isNotNull', left } with no right; a client query filter is { field, operator: 'isNull' } with no value. Validators reject a value on them and accept any field type. Measure fixed filters and segments can use them directly, including through the new isNull(field) and isNotNull(field) helpers (also re-exported by @hypequery/serve). Caller query filters are opt-in: a filter allows them only when its operators list names them, so default operator lists, and the identity of every existing deployment and client contract, are unchanged. The deployment data plane, the MCP dataset guide and the generated client input types support them, and the conformance corpus pins the new cases. Upgrade every consumer before publishing a contract that uses them: earlier validators reject the new operators.

      f103760

    Updated dependencies: @hypequery/protocol@0.24.0, @hypequery/datasets@0.27.0

  9. @hypequery/protocolv0.23.0Minor

    Minor changes

    • Publish measure.merge measures to the deployment contract (RFC 0022). A base measure may use aggregation: 'merge' with a stateFunction from the shared allowlist (now exported by @hypequery/protocol); it takes no filters or sql, and in contract 3 carries approximate: true exactly for estimating states. Deployments rehydrate merge measures, so hosted runtimes can serve pre-aggregated tables. A metric over a merge measure is still refused, because the expression grammar has no merge aggregate.

      5c6c96c
    @hypequery/datasetsv0.26.0Minor

    Minor changes

    • Publish measure.merge measures to the deployment contract (RFC 0022). A base measure may use aggregation: 'merge' with a stateFunction from the shared allowlist (now exported by @hypequery/protocol); it takes no filters or sql, and in contract 3 carries approximate: true exactly for estimating states. Deployments rehydrate merge measures, so hosted runtimes can serve pre-aggregated tables. A metric over a merge measure is still refused, because the expression grammar has no merge aggregate.

      5c6c96c

    Updated dependencies: @hypequery/protocol@0.23.0

    @hypequery/servev0.23.0Minor

    Minor changes

    • Datasets can declare more than one time axis. timeKey stays the default; list the others in timeKeys, then select one per query with timeKey, on dataset and metric queries alike:

      Typed queries narrow timeKey to the dataset's declared axes, so a misspelled axis is a compile error (exported as DatasetTimeKeyNames). The selected axis drives grains, window and shift measures, and their bounded-range checks. Timezone-free filters on any declared axis use the query timezone. The result cache keys an alternate axis separately; queries on the default axis keep their existing keys. Catalogs and contracts publish timeKeys only when a dataset declares alternates. buildDatasetInputSchema / buildMetricInputSchema take a new opt-in timeKey option, which Serve endpoints and generateDatasetTools enable, so other schemas and manifest hashes are unchanged. The deprecated backend path rejects alternate axes.

      18257df

    Patch changes

    • initServe(...).serve() / .define() called with only datasets and/or metrics (no queries) no longer widens InferApiType to a string index signature. The inferred API now keeps its dataset:<name> and metric keys, so createAnalyticsHooks<Api>()'s useDataset('orders', …) and useMetric(…) type-check with inferred rows instead of typing the name as never.

      aad8ad3

    Updated dependencies: @hypequery/datasets@0.25.0

    @hypequery/protocolv0.22.0Minor

    Minor changes

    • Add rowResolution (RFC 0021), the one API for reading versioned ClickHouse sources, in code and in the deployment contract, so they can be published. { kind: 'final' } reads the source with FINAL; naming the engine (replacing with deletedColumn, collapsing/versioned-collapsing with signColumn) also keeps only current rows, on base reads and relationship targets. Deployment contracts preserve the policy on validation, hashing and rehydration, so a published dataset reads exactly as it does locally.

      final: true is deprecated: it still works, is read as rowResolution: { kind: 'final' }, and logs a one-time warning. Its result-cache keys change once, because they are now built from rowResolution.

      ffc2610
    @hypequery/reactv0.7.1Patch

    Patch changes

    • useDataset and useMetric responses type meta as present when the input sets includeMeta: true, and every useInfiniteDataset / useInfiniteMetric page types meta as present, since infinite queries always request it. Responses without includeMeta keep meta optional.

      useDataset and useMetric options now type select against the narrowed response, and the hook's result type follows what select returns.

      edc2fb3
    @hypequery/datasetsv0.25.0Minor

    Minor changes

    • Add enforced constraints to createDatasetClient: context.runtime.constraints takes RFC 0019 comparisons (a declared filter or a one-hop relationship.field) resolved from trusted context. They are ANDed onto every read of the dataset's source, in every query shape: aggregate, derived, having, window, shift, metrics, derived metrics and relationship joins. A constraint the dataset can't enforce refuses the call without describing the constraint. Constraints share the dataset's maxFilters ceiling with the caller (at most 32 per call), enter the result-cache key, and are refused on the deprecated semantic-backend path rather than dropped. The runtime tenant and constraints are now applied at a single source-scoping step (RFC 0020). Queries without constraints generate identical SQL and keep their cache keys.

      3492dc9
    • Datasets can declare more than one time axis. timeKey stays the default; list the others in timeKeys, then select one per query with timeKey, on dataset and metric queries alike:

      Typed queries narrow timeKey to the dataset's declared axes, so a misspelled axis is a compile error (exported as DatasetTimeKeyNames). The selected axis drives grains, window and shift measures, and their bounded-range checks. Timezone-free filters on any declared axis use the query timezone. The result cache keys an alternate axis separately; queries on the default axis keep their existing keys. Catalogs and contracts publish timeKeys only when a dataset declares alternates. buildDatasetInputSchema / buildMetricInputSchema take a new opt-in timeKey option, which Serve endpoints and generateDatasetTools enable, so other schemas and manifest hashes are unchanged. The deprecated backend path rejects alternate axes.

      18257df
    • Add rowResolution (RFC 0021), the one API for reading versioned ClickHouse sources, in code and in the deployment contract, so they can be published. { kind: 'final' } reads the source with FINAL; naming the engine (replacing with deletedColumn, collapsing/versioned-collapsing with signColumn) also keeps only current rows, on base reads and relationship targets. Deployment contracts preserve the policy on validation, hashing and rehydration, so a published dataset reads exactly as it does locally.

      final: true is deprecated: it still works, is read as rowResolution: { kind: 'final' }, and logs a one-time warning. Its result-cache keys change once, because they are now built from rowResolution.

      ffc2610

    Patch changes

    • Fix to-date measures returning null on every row in a non-UTC query timezone when they are reached through a nested derived measure (the composite time-measure plan). The to-date start is now kept in the query timezone instead of the server's.

      db271b2

    Updated dependencies: @hypequery/protocol@0.22.0

  10. @hypequery/protocolv0.21.0Minor

    Minor changes

    • Add semantic client contract v1 validation, canonical identities and strict HTTP/MCP query validation. Compile typed endpoint/tool schemas from public descriptors and test Python/TypeScript with the required shared conformance family. Existing catalog schemas keep their defaults.

      9d97163
    • Add field visibility and enforced constraints (RFC 0019). @hypequery/protocol exports validateProtocolFieldVisibility and resolveProtocolVisibleDatasetFields. The semantic data plane accepts resolveFieldVisibility and resolveConstraints: hidden datasets and fields are refused exactly like unknown ones (before access and tenant checks), joins over hidden keys are hidden, and provider-enforced comparisons are ANDed onto every call within the dataset's filter ceiling. DeploymentSemanticContextInput names the input every context resolver receives. projectAuthorizedDeploymentContract takes a visibility option so discovery advertises only what execution accepts.

      e0f9c6e

    Patch changes

    • MCP tools refuse a limit above the endpoint's maxLimit with MCP_INVALID_ARGUMENTS instead of silently clamping it, so an agent never mistakes a shortened page for the whole answer. Tool schemas built from a client contract advertise that ceiling as the limit maximum, and validateProtocolClientQuery(..., 'mcp') enforces it. HTTP requests keep clamping, as both Serve implementations do.

      cbf8691
    • Upgrade @noble/hashes to v2. Hash outputs are unchanged.

      e32fa97
    @hypequery/servev0.22.0Minor

    Minor changes

    • Serve accepts your schemas from Zod 3 or Zod 4. Query inputs and outputs are typed against a structural ServeSchema instead of Zod 3's ZodTypeAny, so npm install zod (which now resolves Zod 4) works with the quick start, and existing Zod 3 projects keep compiling. Query-string coercion and the OpenAPI document read both majors. The optional zod peer range is now ^3.23.8 || ^4.0.0.

      Serve and MCP now depend on zod@^4.6.6 (was ^3.23.8), clearing GHSA-mrhx-c7xc-m3w5. For dataset and metric endpoints, whose schemas Serve builds, VALIDATION_ERROR issue text and codes now follow Zod 4 (for example Invalid option: expected one of …, and no received field). Endpoints with your own schemas report whatever your Zod reports, as before.

      hypequery init scaffolds zod@^4.6.6.

      afa5e81

    Patch changes

    • Dataset and metric endpoints now enforce the related dataset's own policy. Previously a query on one served dataset could read fields of a related dataset through a relationship, such as shipment.carrier on orders, even when the caller failed that dataset's own endpoint's requiredRoles, requiredScopes or auth requirement. Such a query now answers 403 FORBIDDEN before it is planned. The check covers measures whose fixed filters read a related field, directly or through a derived measure, including the measures an omitted measures list selects. A related dataset with no local auth setting inherits the API's global auth, as its own endpoint does, and policies are matched to the related dataset definition itself rather than to any dataset sharing its name. Related datasets that are not served as their own entry are unaffected, so existing APIs keep working unless they relied on reading a protected dataset through another one.

      bb910a4

    Updated dependencies: @hypequery/datasets@0.24.0

    @hypequery/mcpv0.10.0Minor

    Minor changes

    • Add an HTTP MCP executor over authorized Python client contracts, retaining endpoint-specific query permissions and schema pins. Bound discovery, tool manifests, streamed JSON results and execution time; reject redirects, stale views, private metadata and result fields outside the requested projection. Existing local and discovery-only executors remain supported. MCPToolExecutor.listTools, listPrompts and getPrompt accept an optional cancellation signal, which the protocol server now forwards from each request.

      329f16b

    Patch changes

    • Exclude internal runtime and type-test programs from published package tarballs while preserving public runtime and declaration exports.

      aa10003
    • MCP tools refuse a limit above the endpoint's maxLimit with MCP_INVALID_ARGUMENTS instead of silently clamping it, so an agent never mistakes a shortened page for the whole answer. Tool schemas built from a client contract advertise that ceiling as the limit maximum, and validateProtocolClientQuery(..., 'mcp') enforces it. HTTP requests keep clamping, as both Serve implementations do.

      cbf8691
    • Serve accepts your schemas from Zod 3 or Zod 4. Query inputs and outputs are typed against a structural ServeSchema instead of Zod 3's ZodTypeAny, so npm install zod (which now resolves Zod 4) works with the quick start, and existing Zod 3 projects keep compiling. Query-string coercion and the OpenAPI document read both majors. The optional zod peer range is now ^3.23.8 || ^4.0.0.

      Serve and MCP now depend on zod@^4.6.6 (was ^3.23.8), clearing GHSA-mrhx-c7xc-m3w5. For dataset and metric endpoints, whose schemas Serve builds, VALIDATION_ERROR issue text and codes now follow Zod 4 (for example Invalid option: expected one of …, and no received field). Endpoints with your own schemas report whatever your Zod reports, as before.

      hypequery init scaffolds zod@^4.6.6.

      afa5e81

    Updated dependencies: @hypequery/datasets@0.24.0, @hypequery/protocol@0.21.0

    @hypequery/datasetsv0.24.0Minor

    Minor changes

    • Add semantic client contract v1 validation, canonical identities and strict HTTP/MCP query validation. Compile typed endpoint/tool schemas from public descriptors and test Python/TypeScript with the required shared conformance family. Existing catalog schemas keep their defaults.

      9d97163
    • Add measure.merge(stateFunction, field) (and merge() for metric configs) to read AggregateFunction columns of AggregatingMergeTree tables with the matching -Merge combinator, for example measure.merge('uniq', 'users') renders uniqMerge(users). Trailing and to-date windows merge states across buckets, so weekly distinct counts from daily states are exact to the state function. State functions are allowlisted; merges of estimating states are catalogued as approximate. Merge measures are refused with measure filters, raw sql, relationships, cumulative windows, semantic backends and deployment-contract publishing. @hypequery/clickhouse adds mergeState(stateFunction, column, alias) to the query builder; QueryBuilderLike gains it as an optional member.

      4af1695
    • Add final: true to dataset definitions for ReplacingMergeTree and CollapsingMergeTree sources. Every query on the dataset, every join that targets it and checkRelationships read the source with ClickHouse FINAL, so unmerged row versions are no longer counted and soft-deleted keys (ReplacingMergeTree(ver, is_deleted)) disappear. Builders gain an optional final() protocol method; a final dataset is refused with a clear error on builders without it, on semantic backends, and when publishing to the deployment contract, which cannot carry the flag yet. Datasets without final generate identical SQL and keep their cache keys.

      c1f1dd8
    • Zod-backed schema builders and agent tools now have their own entry, @hypequery/datasets/schemas: generateDatasetTools, toOpenAITools, toAISDKTools, toMcpTools, buildDatasetInputSchema, buildMetricInputSchema, buildCanonicalSemanticQuerySchemas, toSemanticJsonSchema, buildDatasetInputProtocolSchema, buildMetricInputProtocolSchema, zodToProtocolSchema, ProtocolSchemaAdapterError and buildClientContractQuerySchemas. Their root-entry re-exports still work and are marked @deprecated; 1.0 removes them, and Zod becomes an optional peer dependency needed only by /schemas.

      Datasets now depends on zod@^4.6.6 (was ^3.22.4), which clears GHSA-mrhx-c7xc-m3w5 (memory exhaustion validating oversized arrays). Zod was only used to build datasets' own schemas, so this needs no change from you. The generated JSON Schemas and manifestHash are unchanged. Two visible differences: validation messages for an unknown field now list the valid options (Invalid option: expected one of … instead of Invalid enum value), and zodToProtocolSchema accepts schemas from Zod 3 or Zod 4.

      afa5e81
    • Add assertDeployable(dataset), which refuses derived measures the deployment contract cannot carry (a reference to another derived measure, or a literal anywhere other than round decimals or a coalesce fallback) with an error naming the dataset and measure, for example orders.doubled: a numeric literal cannot be an arithmetic operand in a deployable formula. buildProtocolDeploymentContract, and so hypequery deploy, runs the check before building the contract instead of failing later with only a contract path such as $.datasets[2].measures[19].expression.right.

      68b0aa8

    Patch changes

    • Add toSQLWithPlaceholders(placeholder) to the query builder: it compiles the query and writes each bound value through the callback, in SQL order, so a caller can number placeholders when embedding the statement in a larger one. @hypequery/datasets adds it as an optional QueryBuilderLike member, used only by datasets dialects that number placeholders. ClickHouse datasets queries keep using toSQLWithParams, so existing builders and generated SQL are unchanged.

      bc14e60
    14 more changes in this release →

    Updated dependencies: @hypequery/protocol@0.21.0

    @hypequery/reactv0.7.0Minor

    Minor changes

    • Support generated Python client contracts with selection-aware nullable wire results, explicit default measures, metric aliases and endpoint schema headers. Preserve schema pins through route configuration and authentication refresh, and retain declared outputs for ordinary APIs without semantic metadata.

      4736fb3

    Patch changes

    • Exclude internal runtime and type-test programs from published package tarballs while preserving public runtime and declaration exports.

      aa10003
    @hypequery/clickhousev2.14.0Minor

    Minor changes

    • Add toSQLWithPlaceholders(placeholder) to the query builder: it compiles the query and writes each bound value through the callback, in SQL order, so a caller can number placeholders when embedding the statement in a larger one. @hypequery/datasets adds it as an optional QueryBuilderLike member, used only by datasets dialects that number placeholders. ClickHouse datasets queries keep using toSQLWithParams, so existing builders and generated SQL are unchanged.

      bc14e60
    • Add measure.merge(stateFunction, field) (and merge() for metric configs) to read AggregateFunction columns of AggregatingMergeTree tables with the matching -Merge combinator, for example measure.merge('uniq', 'users') renders uniqMerge(users). Trailing and to-date windows merge states across buckets, so weekly distinct counts from daily states are exact to the state function. State functions are allowlisted; merges of estimating states are catalogued as approximate. Merge measures are refused with measure filters, raw sql, relationships, cumulative windows, semantic backends and deployment-contract publishing. @hypequery/clickhouse adds mergeState(stateFunction, column, alias) to the query builder; QueryBuilderLike gains it as an optional member.

      4af1695

    Patch changes

    • Upgrade dotenv to v18 and load env files quietly, so dotenv's "injected env" log lines no longer appear in CLI output.

      1570a03
    @hypequery/cliv1.25.0Minor

    Minor changes

    • Add MCP stdio bridging and discovery-only self-tests for authorized Python HTTP client contracts with --client-contract-url. Read Bearer credentials from HYPEQUERY_API_KEY, keep tenant resolution in Python, and abort discovery on shutdown.

      3d1c757
    • Add offline generate:client generation of self-contained API types, installed POST route mappings and schema hashes from validated Python client contracts. Preserve typed filters, nullable wire values, metric aliases and default measures, with drift checks and explicit replacement of existing generated files.

      b0d168e

    Patch changes

    • @hypequery/serve is now an optional peer dependency. Installing @hypequery/cli in a frontend project, for example to run generate:client for a Python backend, no longer makes npm install the server packages too. hypequery dev says to install @hypequery/serve when it is missing.

      4fda9e7
    • Upgrade dotenv to v18 and load env files quietly, so dotenv's "injected env" log lines no longer appear in CLI output.

      1570a03
    • Serve accepts your schemas from Zod 3 or Zod 4. Query inputs and outputs are typed against a structural ServeSchema instead of Zod 3's ZodTypeAny, so npm install zod (which now resolves Zod 4) works with the quick start, and existing Zod 3 projects keep compiling. Query-string coercion and the OpenAPI document read both majors. The optional zod peer range is now ^3.23.8 || ^4.0.0.

      Serve and MCP now depend on zod@^4.6.6 (was ^3.23.8), clearing GHSA-mrhx-c7xc-m3w5. For dataset and metric endpoints, whose schemas Serve builds, VALIDATION_ERROR issue text and codes now follow Zod 4 (for example Invalid option: expected one of …, and no received field). Endpoints with your own schemas report whatever your Zod reports, as before.

      hypequery init scaffolds zod@^4.6.6.

      afa5e81

    Updated dependencies: @hypequery/protocol@0.21.0, @hypequery/deployment@0.10.0

    @hypequery/protocol-conformancev0.16.0Minor

    Minor changes

    • Add semantic client contract v1 validation, canonical identities and strict HTTP/MCP query validation. Compile typed endpoint/tool schemas from public descriptors and test Python/TypeScript with the required shared conformance family. Existing catalog schemas keep their defaults.

      9d97163

    Updated dependencies: @hypequery/protocol@0.21.0

    @hypequery/deploymentv0.10.0Minor

    Minor changes

    • Add field visibility and enforced constraints (RFC 0019). @hypequery/protocol exports validateProtocolFieldVisibility and resolveProtocolVisibleDatasetFields. The semantic data plane accepts resolveFieldVisibility and resolveConstraints: hidden datasets and fields are refused exactly like unknown ones (before access and tenant checks), joins over hidden keys are hidden, and provider-enforced comparisons are ANDed onto every call within the dataset's filter ceiling. DeploymentSemanticContextInput names the input every context resolver receives. projectAuthorizedDeploymentContract takes a visibility option so discovery advertises only what execution accepts.

      e0f9c6e

    Updated dependencies: @hypequery/protocol@0.21.0

  11. @hypequery/protocolv0.20.0Minor

    Minor changes

    • Add checkProtocolDeploymentContract, checkProtocolDeploymentContractV3 and checkProtocolDatasetContract, which report every failure in a contract in one pass (bounded at 100) instead of throwing the first. They accept exactly what the matching validate* functions accept and skip checks that depend on a part that already failed, so a mistake is not reported twice.

      A malformed SQL expression embedded in a deployment contract still fails with its HQ_QUERY_IMPLEMENTATION_* code, but its path now locates it in the contract (for example $.datasets[0].dimensions[1].source.sql) instead of the expression root ($.sql).

      d1e4194
  12. @hypequery/cliv1.24.0Minor

    Minor changes

    • Enable CLI telemetry by default. Unless opted out, the CLI now sends pseudonymous usage events to Hypequery's first-party endpoint (https://hypequery.com/api/telemetry) without needing HYPEQUERY_TELEMETRY_URL, which now only overrides the endpoint for staging or self-hosting. Events never include code, SQL, names, paths or credentials; see https://hypequery.com/docs/telemetry for every field. A one-time notice appears on the first interactive run. Opt out with hypequery telemetry disable, --no-telemetry, DO_NOT_TRACK=1 or HYPEQUERY_TELEMETRY_DISABLED=1.

      879180e
  13. @hypequery/mcpv0.9.0Minor

    Minor changes

    • Expose dataset having conditions (HQ-329) on every consumption surface:

      • Serve dataset endpoints accept and forward having. The input schema and OpenAPI enumerate each dataset's measures, and the endpoint description documents the field.
      • MCP query_dataset advertises and applies having. Local servers and hosted discovery gateways list identical schemas (CORE-03), so hosted execution must support having once it is wired in.
      • React useDataset, useInfiniteDataset and useQuery('dataset:…') accept having through the Serve API types, constrained to the dataset's measures. Metric hooks do not accept it.
      064661f

    Updated dependencies: @hypequery/datasets@0.23.0

    @hypequery/protocolv0.19.0Minor

    Minor changes

    • Support composite equality keys in dataset relationships using a non-empty keys array while preserving existing from/to definitions. Compile all pairs as a single-match AND join through the key-pair form of leftAnyJoin (@hypequery/clickhouse >= 2.13.0), preserve tenant scoping and related-measure inputs, and check uniqueness of full non-NULL target tuples. Retain all key pairs in catalogs and portable contract round-trips. Declare @hypequery/clickhouse >= 2.13.0 as an optional peer dependency of datasets. Existing { from, to } relationships are unchanged and are not subject to the composite identifier rules. Portable contracts only carry keys for two or more pairs, and keys[0] must match from/to.

      489a658
    @hypequery/cliv1.23.0Minor

    Minor changes

    • Add staged generation metrics: actual filtered table and column count buckets, known fallback type families, dataset check/diff modes and drift results, warning-code buckets, and manifest/registry count buckets. Exclude schema names, type expressions, warning text, paths, and endpoint contents. Default collection remains inactive.

      b5674d4
    • Add staged dev and MCP session telemetry with one start/end pair, bucketed reload and load-failure counts, and aggregate MCP tool-kind and error counts. Preserve MCP stdout for JSON-RPC and exclude paths, client identity, names, tenants, and tool arguments. Default collection remains inactive.

      842c4a5
    • Add persistent telemetry preferences, hypequery telemetry status|enable|disable, and a global --no-telemetry flag. Honor DO_NOT_TRACK=1, HYPEQUERY_TELEMETRY_DISABLED=1, and test environments. Default collection remains inactive; maintainers can activate the staged core with HYPEQUERY_TELEMETRY_DEBUG=1 or HYPEQUERY_TELEMETRY_URL.

      Define the v1 event catalog (command completion and crash events) with strict property validation, bucketed measurements, and a generated public event reference that lists only data the CLI sends. Unknown fields and content-bearing values are rejected before a payload can be constructed.

      Add pseudonymous identity, local environment context, a one-time stderr disclosure, dependency-free bounded batch delivery, debug inspection, a per-version kill switch, and centralized command exit handling. Preserve command output and exit codes when telemetry is disabled or fails. No telemetry runtime dependency is added.

      Instrument the init onboarding funnel in staged mode with progress, failure stages, scaffold choices, bucketed counts, and configuration-file outcomes. Preserve a single completion event across retries and classify early setup declines as cancellations. Honor Commander's --no-example option when scaffolding.

      ca0de07

    Patch changes

    • Deliver telemetry from a detached sender process instead of a 40 ms in-process request. The command exits without waiting on the network, while the sender gets up to five seconds, so batches survive TLS handshakes and serverless cold starts. The per-version kill switch (HTTP 410) is now applied by the sender.

      9894390
    • hypequery mcp <file> and hypequery dev <file> now work from any working directory. TypeScript entries are bundled into .hypequery/tmp beside the entry's nearest package.json instead of the current directory, so imports like @hypequery/serve resolve from the entry's project. The entry project's .env* files are loaded before the entry runs, so a generated client finds its ClickHouse settings; variables already set in the environment or the launch directory's env files keep precedence. hypequery mcp also loads @hypequery/mcp from that project, so desktop MCP clients can launch npx -y @hypequery/cli mcp /absolute/path/to/analytics/api.ts without changing directory first.

      fbd4613

    Updated dependencies: @hypequery/protocol@0.19.0, @hypequery/deployment@0.9.5

    @hypequery/datasetsv0.23.0Minor

    Minor changes

    • Support composite equality keys in dataset relationships using a non-empty keys array while preserving existing from/to definitions. Compile all pairs as a single-match AND join through the key-pair form of leftAnyJoin (@hypequery/clickhouse >= 2.13.0), preserve tenant scoping and related-measure inputs, and check uniqueness of full non-NULL target tuples. Retain all key pairs in catalogs and portable contract round-trips. Declare @hypequery/clickhouse >= 2.13.0 as an optional peer dependency of datasets. Existing { from, to } relationships are unchanged and are not subject to the composite identifier rules. Portable contracts only carry keys for two or more pairs, and keys[0] must match from/to.

      489a658
    • Dataset queries accept having conditions on aggregated measure values (HQ-329):

      Each condition must reference a selected measure, including derived measures. Operators are eq, neq, gt, gte, lt, lte, between, in and notIn. Values are finite numbers bound as parameters. Conditions are applied after grouping and before ordering and limits. Queries with having bypass the result cache until the shared cache-key preimage covers conditions, as in the Python SDK. A dataset's limits.maxFilters bounds filters and having conditions together. Queries that select window or shift measures, and the deprecated semantic backend path, reject having. Generated input schemas accept having only when built with { having: true }, so existing schemas and manifest hashes are unchanged.

      064661f
    • Catalogs and semantic contracts now include each derived measure's inputs (uses) and expression, so changing a formula changes the snapshot hash. Datasets that already declare derived measures report a new semantic contract contentHash after upgrading, including the one Serve's contract endpoint publishes; consumers that compare it to detect definition changes will see one change. Definitions without derived measures are unchanged, as are result-cache keys and deployment contracts. Shared fixtures now pin Python derived-measure authoring to the same deployment and semantic contract output.

      0d88c0e

    Patch changes

    • dataset() now validates filter definitions before any query can use them. A measure filter's field must be a declared dimension or filter, or a safe column identifier (optionally <relationship>.<field>), and a declared filter's field must be a declared dimension or a safe column identifier. Unsupported operators are rejected in both places. Previously these values were rendered into SQL unchecked. Definitions that put SQL text in a filter field now fail at construction: declare a dimension with sql and filter on that dimension.

      aada8f3
    • Fix calendar shifts (month, quarter, year) on a partial week bucket. When a query filter cut a week short, its lower bound was shifted on its own while its upper bound stayed anchored to the shifted week. A week starting mid-way could get an empty range and read null even when the prior period had rows, and a week cut on both sides could count rows from days it never covered. Both bounds are now placed inside the shifted week: a whole week still maps to its start minus the interval plus seven days, and a partial week maps to the matching slice. Shifted formulas (derived, windowed and cumulative measures under a shift) are fixed the same way.

      03ba7f2

    Updated dependencies: @hypequery/protocol@0.19.0

    @hypequery/servev0.21.0Minor

    Minor changes

    • Expose dataset having conditions (HQ-329) on every consumption surface:

      • Serve dataset endpoints accept and forward having. The input schema and OpenAPI enumerate each dataset's measures, and the endpoint description documents the field.
      • MCP query_dataset advertises and applies having. Local servers and hosted discovery gateways list identical schemas (CORE-03), so hosted execution must support having once it is wired in.
      • React useDataset, useInfiniteDataset and useQuery('dataset:…') accept having through the Serve API types, constrained to the dataset's measures. Metric hooks do not accept it.
      064661f

    Updated dependencies: @hypequery/datasets@0.23.0

  14. @hypequery/reactv0.6.4Patch

    Patch changes

    • Support one-hop relationship base measures in dataset queries through the query-builder client. Allow duplicate-insensitive aggregates through belongsTo and all base aggregates through declared hasOne relationships, preserve tenant and fixed-filter scoping, and exclude unmatched target rows from aggregate inputs. Advertise safe names and approximate metadata in runtime catalogs and input schemas, with typed projections through Serve and React.

      9b5b523
    • Preserve dataset filter allowlist names and field-specific operators through dataset and metric queries, Serve API inputs, and React hooks. Generated filters respect filterable: false, and one-hop relationship filters follow the target allowlist. Filter helpers retain operator literals.

      Prevent normal dataset execution from falling through to the legacy dynamic-query overload; explicit result-row generic calls remain supported. Fix Serve in-process execution input inference so its optional schema storage property no longer widens typed inputs to unknown.

      f30add1
    @hypequery/datasetsv0.22.0Minor

    Minor changes

    • Add compileDataset to preview the same dataset compilation path used by execution, including effective row limits, pagination overfetch, measure dependencies and time-axis preflight statements. Structural diagnostics and JSON serialization omit SQL, parameter values and tenant identifiers.

      Add createPortableSemanticRuntime for provider-side compilation and execution over the same activated catalog, and expose a tested capability matrix that separates protocol representation from TS, Python and Cloud implementation support. Existing logical SQL and portable executor APIs remain available.

      0841d1c
    • Support one-hop relationship base measures in dataset queries through the query-builder client. Allow duplicate-insensitive aggregates through belongsTo and all base aggregates through declared hasOne relationships, preserve tenant and fixed-filter scoping, and exclude unmatched target rows from aggregate inputs. Advertise safe names and approximate metadata in runtime catalogs and input schemas, with typed projections through Serve and React.

      9b5b523
    • Preserve dataset filter allowlist names and field-specific operators through dataset and metric queries, Serve API inputs, and React hooks. Generated filters respect filterable: false, and one-hop relationship filters follow the target allowlist. Filter helpers retain operator literals.

      Prevent normal dataset execution from falling through to the legacy dynamic-query overload; explicit result-row generic calls remain supported. Fix Serve in-process execution input inference so its optional schema storage property no longer widens typed inputs to unknown.

      f30add1

    Patch changes

    • Snapshot dataset and metric query inputs and tenant runtime before cache lookup, so caller mutations during an asynchronous store read cannot cache another tenant's result under the original tenant's key.

      339deef
    • Add an optional datasets SQL rendering hook to query builder factories, with a ClickHouse default that preserves existing SQL. Resolve the hook from runtime builder overrides and route existing quoted identifiers through it as the foundation for subsequent dialect extraction.

      1effbb2
    • Reject unsupported semantic filter operators and invalid sort directions at runtime, including direct SDK inputs, before compilation or cached execution. Render sort directions through a closed mapping instead of interpolating input.

      339deef
    @hypequery/servev0.20.0Minor

    Minor changes

    • Add authenticated logical /discovery with configurable endpoint policy. The public /contract retains its version and definition identity while omitting physical and tenant-policy fields. Use local serializeSemanticContract for trusted physical definitions.

      0f36d1f
    • Support one-hop relationship base measures in dataset queries through the query-builder client. Allow duplicate-insensitive aggregates through belongsTo and all base aggregates through declared hasOne relationships, preserve tenant and fixed-filter scoping, and exclude unmatched target rows from aggregate inputs. Advertise safe names and approximate metadata in runtime catalogs and input schemas, with typed projections through Serve and React.

      9b5b523

    Patch changes

    • Filter unexpected fields from nested pagination, cache, and result-limit metadata in dataset and metric responses. Preserve the existing trustedDiagnostics option and response format.

      e92973f
    • Omit SQL and internal tenant IDs from caller-requested dataset and metric metadata by default. Public metadata remains available through includeMeta or x-include-meta. Trusted endpoints can explicitly retain diagnostics with the server-side trustedDiagnostics option; protect those endpoints with auth and roles.

      339deef
    • Preserve dataset filter allowlist names and field-specific operators through dataset and metric queries, Serve API inputs, and React hooks. Generated filters respect filterable: false, and one-hop relationship filters follow the target allowlist. Filter helpers retain operator literals.

      Prevent normal dataset execution from falling through to the legacy dynamic-query overload; explicit result-row generic calls remain supported. Fix Serve in-process execution input inference so its optional schema storage property no longer widens typed inputs to unknown.

      f30add1

    Updated dependencies: @hypequery/datasets@0.22.0

    @hypequery/mcpv0.8.6Patch

    Patch changes

    • Support one-hop relationship base measures in dataset queries through the query-builder client. Allow duplicate-insensitive aggregates through belongsTo and all base aggregates through declared hasOne relationships, preserve tenant and fixed-filter scoping, and exclude unmatched target rows from aggregate inputs. Advertise safe names and approximate metadata in runtime catalogs and input schemas, with typed projections through Serve and React.

      9b5b523

    Updated dependencies: @hypequery/datasets@0.22.0

    @hypequery/clickhousev2.13.0Minor

    Minor changes

    • Support non-empty arrays of typed column pairs in innerJoin, leftJoin, leftAnyJoin, rightJoin, and fullJoin for composite-key equality joins to schema tables and typed CTEs. Preserve existing single-pair calls, alias handling, and literal ON filters.

      ccb2577

    Patch changes

    • Fix the CLI declaration re-export to use an explicit .js extension, so importing the package root or CLI entry point passes NodeNext and Node16 type checking with skipLibCheck: false and preserves schema-generator types.

      0d593a3
  15. @hypequery/protocolv0.18.0Minor

    Minor changes

    • Accept RFC 0011 (query event 1 and query diagnostics 1), and align the reference validators with the accepted text.

      @hypequery/protocol:

      • validateProtocolQueryEvent and validateProtocolQueryDiagnostics check kind and version before the field set. A newer record that adds fields now reports INVALID_VERSION, not UNKNOWN_FIELD, so a consumer can skip it.
      • occurredAt must be a real calendar instant with zero or three fractional digits. 2026-02-30, 24:00, and .5 are rejected.
      • Free-text fields reject unpaired surrogates. debugQuery may contain tab, line feed, and carriage return, because the compiler's debug form spans lines.

      @hypequery/protocol-conformance:

      • query-events-v1 and query-diagnostics-v1 gain cases for validation order, versions with added fields, calendar and precision limits, exact byte boundaries, attempts, runtime identity format, and unpaired surrogates.
      a8e502e
    @hypequery/protocol-conformancev0.15.0Minor

    Minor changes

    • Accept RFC 0011 (query event 1 and query diagnostics 1), and align the reference validators with the accepted text.

      @hypequery/protocol:

      • validateProtocolQueryEvent and validateProtocolQueryDiagnostics check kind and version before the field set. A newer record that adds fields now reports INVALID_VERSION, not UNKNOWN_FIELD, so a consumer can skip it.
      • occurredAt must be a real calendar instant with zero or three fractional digits. 2026-02-30, 24:00, and .5 are rejected.
      • Free-text fields reject unpaired surrogates. debugQuery may contain tab, line feed, and carriage return, because the compiler's debug form spans lines.

      @hypequery/protocol-conformance:

      • query-events-v1 and query-diagnostics-v1 gain cases for validation order, versions with added fields, calendar and precision limits, exact byte boundaries, attempts, runtime identity format, and unpaired surrogates.
      a8e502e

    Updated dependencies: @hypequery/protocol@0.18.0

    @hypequery/cliv1.22.0Minor

    Minor changes

    • hypequery deploy now prints where the deployment is served: the REST base URL, each dataset's URL, and the MCP URL, as reported by Cloud. --mcp-config also prints MCP client configuration that reads the key from ${HYPEQUERY_API_KEY} and never contains a credential. hypequery mcp --self-test --url <mcp-url> checks a hosted MCP endpoint without calling a tool, reading the key from HYPEQUERY_API_KEY. hypequery mcp --self-test now fails when @hypequery/mcp isn't installed; before, it passed and the real command then failed.

      fa2d7a2
    • Add hypequery deployment:status to show the selected Cloud target's live release and hosted REST/MCP URLs without redeploying. The command can print MCP client configuration with an environment-variable placeholder and never includes the stored deployment credential.

      ff36939

    Patch changes

    • Refresh production dependencies. Keep the ClickHouse adapter compatible with the client packages' bundled setting types.

      40ac480

    Updated dependencies: @hypequery/protocol@0.18.0, @hypequery/deployment@0.9.4

    @hypequery/clickhousev2.12.2Patch

    Patch changes

    • Expose a HypeQuery-owned ClickHouse settings union that accepts settings from the Node, web, and legacy common clients without widening accepted setting values.

      5ce2ac8
    • Refresh production dependencies. Keep the ClickHouse adapter compatible with the client packages' bundled setting types.

      40ac480

Looking for an older release?

This timeline shows the latest 60 releases. Each package page has its full history, including dependency-only updates.

Get early access to hypequery cloud.

Cloud hosts the same model you run in your backend, with a managed API and MCP server, versions, and rollback. We're letting teams in a few at a time. Join the waitlist and we'll reach out when your project can go live.

How do you want to build your analytics layer? (Optional)
What features do you most need? (Optional)

Pick as many as you like.

Only your email is required. All other questions are optional.