@hypequery/serve

Runtime for validated analytics APIs, OpenAPI, auth, and tenancy.

Latest: 0.25.0 · November 9, 2026 · npm · CHANGELOG.md

RSS
0.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
  • Streamed responses are now observable to the end. onRequestEnd and the query log's completed event fire when a stream finishes (or the client abandons it) rather than when it opens, with the full durationMs and a new stream field (outcome, bytes, openDurationMs). A failure after the response began — an NDJSON stream that ended with its error frame, an Arrow stream that broke off, or a stream the server timed out — now reaches onError and an error log event with the original error; the client still sees only the sanitized frame. Custom handlers can report an in-band failure with streamBody(stream, contentType, { failure }).

    The Node adapter bounds streams with streamIdleTimeout (no bytes sent for this long; defaults to requestTimeout) and streamTimeout (total, default unlimited); a stream that exceeds either is cancelled, stopping its query, and the connection closed. Accept q-values now decide between a JSON page and a stream (application/json;q=1, application/x-ndjson;q=0.1 gets a page). Streaming-capable dataset endpoints send Vary: Accept, combined with any CORS Vary: Origin. The fetch adapter no longer sends two content types when custom headers set Content-Type.

    9c08fb8

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

0.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
0.24.3Dependency update: @hypequery/datasets@0.29.0
0.24.2Dependency update: @hypequery/datasets@0.28.0
0.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
0.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

0.23.1Dependency update: @hypequery/datasets@0.26.0
0.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

0.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

0.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

0.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

0.19.3Patch

Patch changes

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

    40ac480
  • Every error response is now sent with Cache-Control: no-store, so a shared cache can never replay one caller's error to another. Previously only 401 and 403 responses set it.

    The Node adapter no longer returns a thrown error's message in its fallback 500 response. It now answers An unexpected error occurred, because an error that escapes the pipeline can carry a stack, a file path, SQL, or a driver detail. The adapter's own 413, 500, and 504 responses now also carry x-request-id and Cache-Control: no-store.

    The error envelope is pinned by the language-neutral specs/serve-http fixtures, which the Python serve also runs.

    fcdbc9f

Updated dependencies: @hypequery/datasets@0.21.0

0.19.2Patch

Patch changes

  • Add an execution timezone to dataset and metric queries, with a client default and UTC as the fallback. Buckets, local time-key filters, windows, and period comparisons use the selected IANA timezone. Cache entries are separated by execution timezone, and Serve and MCP accept query overrides.

    Remove the metadata-only timezone field from semantic definitions. Set it on createDatasetClient or the query instead.

    b3a2966

Updated dependencies: @hypequery/datasets@0.20.0

0.19.1Patch

Patch changes

  • Expose dataset segments on every query surface. Semantic input schemas, and therefore OpenAPI and the MCP tools, accept segments as an enum of the dataset's declared names. Serve dataset and metric endpoints, and the MCP query_dataset/query_metric tools, forward them to the query. Typed dataset and metric inputs infer declared segment names, including through Serve and React; MCP argument types can opt into a dataset-specific type. Datasets without segments keep their existing schemas.

    b085b08

Updated dependencies: @hypequery/datasets@0.19.0

0.19.0Minor

Minor changes

  • Add publishToCloud({ datasets, access }) to declare a validated, dataset-only Cloud deployment without a Serve API. Dataset init now creates analytics/cloud.ts. Named metrics are rejected until Cloud supports their publication explicitly. An authenticated access policy is required, with optional per-dataset overrides, so a migration cannot silently discard Serve role and scope requirements.

    The CLI deploy and deployment:build commands now consume that explicit Cloud publication. They no longer compile a Serve API module or bundle Serve runtime handlers. The unused Serve-to-Cloud contract compiler and its compatibility diagnostics are removed. Local Serve and MCP development remain. Existing prebuilt bundles can still be verified and submitted with deployment:validate and deployment:submit.

    cf9e556

Updated dependencies: @hypequery/datasets@0.18.0

0.18.0Minor

Minor changes

  • Build deployment bundles from the canonical deployment contract. hypequery deployment:build writes dataset definitions and derived measures without query entrypoints or runtime artifacts. Named queries and standalone metrics remain local. api.deploymentContract() is the sole contract source, and bundle verification accepts that contract directly. Remove obsolete build flags, JSON output mode, prebuilt deploy --release mode, and customer-handler runtime machinery. Bundle verification requires canonical deployment JSON, and the filesystem store preserves declared source snapshots when persisting a bundle.

    f4b30e1

Updated dependencies: @hypequery/datasets@0.17.0, @hypequery/protocol@0.14.0

0.17.2Patch

Patch changes

  • Emit semantic input schemas as ProtocolSchema, so a hosted registry stops rebuilding them.

    buildDatasetInputProtocolSchema and buildMetricInputProtocolSchema return the same shape buildDatasetInputSchema and buildMetricInputSchema already produce, in the protocol's own schema format.

    A hosted gateway advertises one endpoint per dataset and per metric, in ProtocolSchema. Without this it has to rebuild that shape by hand — which is a second generator of the knowledge CORE-03 exists to keep in one place, and it drifts silently rather than loudly: an advertised field the validator rejects, or a row ceiling the data plane does not apply. Deriving both from one catalog makes that class of bug impossible instead of merely unlikely.

    zodToProtocolSchema and ProtocolSchemaAdapterError move from @hypequery/serve to @hypequery/datasets, beside the builders they convert. @hypequery/serve re-exports both, so nothing importing them changes.

    The adapter also learns shape-preserving Zod refinements. A protocol schema cannot express a cross-field rule such as "at least one dimension or measure", but the rule remains enforced by the Zod validator the data plane runs. Transforms and preprocessors remain unsupported because they can change the output or accepted input shape and would make the advertised contract inaccurate.

    8bfd22f

Updated dependencies: @hypequery/datasets@0.16.0

0.17.1Dependency update: @hypequery/protocol@0.13.0, @hypequery/datasets@0.15.0
0.17.0Minor

Minor changes

  • Add a typed dataset publishing builder for attaching and aliasing named metrics without object spreading, and re-export it from Serve's dataset API.

    9289c14
  • Fail a deployment build when the Serve configuration would behave differently under managed execution. Unenforceable tenant isolation and dropped middleware block the build; dropped hooks, dropped context factories, and endpoints that are authenticated without declared roles or scopes are reported as warnings. Pass --allow-unsupported-config to deploy anyway.

    f71910d
  • Add hypequery mcp, which serves a project's datasets to an MCP client over stdio from the same entrypoint hypequery dev uses, instead of a second MCP config that can drift from it.

    --tenant supplies the trusted tenant; the command fails closed when a tenant-scoped dataset is registered without one, since MCP has no request to resolve a tenant from. --self-test checks the entrypoint and exits without speaking the protocol. Application logging is routed to stderr before the entrypoint is imported, because MCP owns stdout.

    @hypequery/serve attaches the registered datasets and shared semantic client to the built API under a registered symbol, readable with readServeMcpSource().

    6b4f7b4
  • Add a canonical catalog-derived semantic query schema compiler with shared Zod validators, JSON Schemas, exact field and operator constraints, bounded closed objects, and deterministic manifest hashing. Migrate Serve and MCP query schemas to the shared compiler.

    d347f89

Updated dependencies: @hypequery/datasets@0.14.0, @hypequery/protocol@0.12.0

0.16.2Patch

Patch changes

  • Normalize non-null dataset measure and metric results to strings across query-builder and in-memory execution, preserve SQL nulls, and update public result types to string | null.

    6c1bcb6

Updated dependencies: @hypequery/datasets@0.13.6

0.16.1Patch

Patch changes

  • Coerce GET query-string values against the endpoint's input schema before validating.

    Query strings carry no types, so every value arrives as a string. A field declared z.number() therefore always failed:

    GET /api/analytics/queries/busiestRoutes?minTrips=500&limit=8
    → 400 { "code": "invalid_type", "expected": "number",
            "received": "string", "path": ["limit"] }
    

    That made GET endpoints unusable for any input that was not itself a string — including through @hypequery/react, whose client serialises GET input as flat query params.

    Values are now converted toward what the schema declares: numbers, booleans, bigints, and dates. Strings and enums are untouched. A repeated key becomes an array, and a single occurrence of a key typed as z.array(...) is wrapped into one.

    Coercion is conservative and never invents a value. Anything it cannot convert passes through unchanged so validation still reports the genuine error — ?limit=abc continues to fail with expected number, received string, and ?limit=5000 is coerced and then correctly rejected by .max(100).

    Explicit z.preprocess(...) callbacks receive the raw query value and own coercion for the schema subtree they wrap.

    POST bodies are unaffected; JSON already carries types, so a body value of "5" stays a string.

    801d4fd
0.16.0Minor

Minor changes

  • Type query resolvers with zod's output type instead of its input type.

    SchemaInput<T> (T["_input"]) was used for the resolver, its middlewares, and the endpoint handler. All three run after validation — pipeline.ts assigns context.input = validationResult.data before composing middlewares — so they receive zod's parsed output.

    The visible symptom was .default():

    .transform(), .coerce, .catch(), and .pipe() were wrong in the same way — the resolver saw the pre-parse type for all of them.

    Caller-facing types are unchanged and still use SchemaInput: InferApiType, InferQueryInput, api.execute(), and StandaloneQueryDefinition.execute(). Over the wire a defaulted field really is optional, so @hypequery/react and anything driving api.execute() keep the types they had.

    Resolvers are now typed more precisely, so code written against the previous (incorrect) type may surface new errors — most often a now-redundant ?? fallback, which is safe to delete. Code that already handled the value correctly keeps compiling, and no runtime behaviour changes.

    c2a0ff9
0.15.5Patch

Patch changes

  • api.manifest() now reports routes registered with api.route() instead of the auto-generated convention route.

    route() registers the endpoint with the router but leaves queryEntries holding the auto-registered /queries/<key> entry, and manifest() read from queryEntries. So an endpoint registered as:

    appeared in the manifest — and therefore in hypequery generate:manifest output — as GET /queries/busiestRoutes. @hypequery/react follows the manifest, so useQuery('busiestRoutes', { limit: 8 }) issued a GET the server rejected with a 400 rather than calling the POST route the author declared.

    Both routes remain live; the manifest can only name one, and it now names the explicit registration. When an endpoint is routed more than once the first registration wins, so regenerating the manifest is deterministic.

    bd1548b
0.15.4Patch

Patch changes

  • Declare zod as an optional peer dependency so package managers warn when a zod 4 install is hoisted over the zod 3 these packages build against.

    Both packages depend on zod@^3 but declared no peer range, so npm install @hypequery/serve zod silently resolved zod 4 at the top level with no warning. The first query({ input: z.object(...) }) then failed to compile with Type 'ZodObject<...>' is missing the following properties from type 'ZodType<any, any, any>': _type, _parse, _getType, _getOrReturnCtx, and 7 more, which gives no hint that a version mismatch is the cause.

    The peer is marked optional, so nothing breaks for consumers who never install zod directly. Quick Start now pins zod@^3 in its install commands.

    8d08644

Updated dependencies: @hypequery/datasets@0.13.5

0.15.3Dependency update: @hypequery/protocol@0.11.0, @hypequery/datasets@0.13.4
0.15.2Patch

Patch changes

  • Refresh every npm package page with a concise README and complete HypeQuery homepage and repository metadata.

    e370da0

Updated dependencies: @hypequery/datasets@0.13.3, @hypequery/protocol@0.10.2

0.15.1Patch

Patch changes

  • Reject credentialed CORS configurations without an explicit origin allowlist.

    43c4f7a
0.15.0Minor

Minor changes

  • Allow trusted in-process hosts to provide an already-authenticated principal to Serve execution, and forward that principal through deployment runtime artifacts while retaining role, scope, and tenant enforcement.

    api.execute() (and client() / run()) now accept a trustedAuth option. Supplying it skips credential parsing only; required roles and scopes, tenant extraction, the context factory, validation, middleware, hooks, and cache-control: no-store all still apply. It is unreachable from the HTTP handler, so a network caller cannot set it. Pass null or omit it to fall through to the configured auth strategies.

    Because the principal is what authorization ran against, the pipeline now owns ctx.auth and ctx.tenantId. Two behavior changes follow:

    • A caller-supplied context containing auth or tenantId is rejected with a VALIDATION_ERROR instead of being merged over the authenticated principal.
    • A context factory returning auth no longer replaces the authenticated principal on ctx.auth.

    Deployment runtime artifacts refuse a trustedAuth argument when the bundled module exposes no Serve execute() pipeline, rather than running the handler with no enforcement.

    225cba9
0.14.1Dependency update: @hypequery/protocol@0.10.0, @hypequery/datasets@0.13.2
0.14.0Minor

Minor changes

  • Harden request-ID handling: authoritative id is now server-generated (R0-04).

    The x-request-id on responses and in logs is always a server-generated authoritative identifier and is never derived from client input. A caller-supplied x-request-id or x-trace-id is validated (control characters rejected, bounded to 200 UTF-8 bytes) and surfaced only as a separate, non-authoritative x-correlation-id response header. This closes a path where a client could inject control characters into logs/headers or spoof cross-request correlation via the authoritative id.

    Additive and non-breaking to the API surface: x-request-id is still present on every response; only its value changes from an echoed client header to a trusted server id, with the validated client value preserved under x-correlation-id.

    02706c1
0.13.7Dependency update: @hypequery/datasets@0.13.0
0.13.6Dependency update: @hypequery/protocol@0.9.0, @hypequery/datasets@0.12.7
0.13.5Dependency update: @hypequery/protocol@0.8.0, @hypequery/datasets@0.12.6
0.13.4Dependency update: @hypequery/protocol@0.7.0, @hypequery/datasets@0.12.5
0.13.3Dependency update: @hypequery/protocol@0.6.0, @hypequery/datasets@0.12.4
0.13.2Dependency update: @hypequery/protocol@0.5.0, @hypequery/datasets@0.12.3
0.13.1Dependency update: @hypequery/protocol@0.4.0, @hypequery/datasets@0.12.2
0.13.0Minor

Minor changes

  • Normalize auth overrides across queries, datasets, and metrics. Semantic entries with auth: null now continue to inherit global auth; use requiresAuth: false to make a dataset or metric endpoint explicitly public.

    Compatibility note: semantic endpoints that previously used auth: null as a public override must migrate to requiresAuth: false. This breaking behavior change ships as a minor deliberately because @hypequery/serve is 0.x, where breaking changes use the minor version slot.

    303d402

Patch changes

  • Build deterministic Node runtime artifacts automatically when deployment metadata references Serve handlers.

    fdec655
0.12.0Minor

Minor changes

  • Add canonical deployment contract encoding and domain-separated identities, expose deployment generation on Serve APIs, and add CLI build and validation commands for deployment artifacts.

    05d2a4d

Updated dependencies: @hypequery/protocol@0.3.0, @hypequery/datasets@0.12.1

0.11.0Minor

Minor changes

  • Add the portable Dataset deployment contract, strict protocol validation, and Dataset/Serve adapters for producing deployment artifacts from existing definitions.

    28e998f

Updated dependencies: @hypequery/protocol@0.2.0, @hypequery/datasets@0.12.0

0.10.0Minor

Minor changes

  • Add cacheObservability to createAPI()/defineServe() results and to DevIntegrationApi: per-layer stats and clear for the semantic query cache and the query-builder cache (serve itself holds no cache). The builder layer is detected structurally — query builders created by createQueryBuilder() expose their CacheController as .cache and are picked up automatically; bare factories simply report no builder layer. getStats() returns an empty layer list until a semantic endpoint or cache-capable builder is registered. Consumers advertising clear affordances (e.g. the playground gateway's cache:clear capability) should check each layer's clearSupported.

    Compatibility note: cacheObservability is a required member of the exported HypeQueryAPI/ServeBuilder/DevIntegrationApi interfaces (0.x minor, same policy as @hypequery/datasets 0.11): hand-rolled implementations must add it — createCacheObservability({}) provides an empty aggregator.

    42041fc
0.9.2Dependency update: @hypequery/datasets@0.11.0
0.9.1Dependency update: @hypequery/datasets@0.10.0
0.9.0Minor

Minor changes

  • Relationship-aware semantics: to-one relationships are now queryable end to end, and every metadata surface advertises them.

    Querying (@hypequery/datasets, @hypequery/clickhouse): dataset and metric queries can select, filter, and order by to-one related fields one hop deep (dimensions: ['customer.country']). Traversal executes as a ClickHouse LEFT ANY JOIN (new leftAnyJoin query-builder method), so base rows survive, duplicate target join keys can never fan out aggregates, and production matches the in-memory backend's first-match semantics. When runtime tenancy is active, joined targets with a tenantKey are scoped inside the join condition. hasMany remains metadata-only, and result row types include qualified fields (row['customer.country'] is typed).

    Metadata: the catalog and semantic contract expose queryable and fields per relationship (replacing execution: 'metadata_only'; SEMANTIC_CONTRACT_VERSION is now 2). Generated agent tools, Serve's Zod/OpenAPI input schemas, and MCP get_dataset_schema all advertise the same qualified field names the validators accept — including for config-shaped datasets in MCP. Serve no longer advertises dimensions as filterable when a dataset explicitly declares filters: {}.

    Validation: dataset() now rejects relationship names that match the dataset's source table (the join alias would shadow the base table) or contain dots — both configurations previously failed confusingly at query time.

    Deprecations (no removals, no behavior changes): the plan/backend execution path is frozen in favor of createDatasetClient({ queryBuilder }). createBackend, the backend client option, createInMemoryBackend, and the PlanNode/SemanticBackend protocol exports are @deprecated and receive bug fixes only.

    b6b5111

Updated dependencies: @hypequery/datasets@0.9.0

0.8.1Patch

Patch changes

  • Harden logging and diagnostics without changing public APIs: never mark authenticated or tenant-aware responses as publicly cacheable, log the parameterized SQL template instead of a value-substituted string, and redact connection URLs in CLI output and error messages.

    688a9e2
0.8.0Minor

Minor changes

  • Type semantic query measure and metric values as string instead of number.

    ClickHouse serializes aggregate results (UInt64, Decimal, ...) as strings over JSON, and the query builder already types aggregation outputs as string. The dataset/metric result row types (DatasetRow, DatasetRowFor, MetricRow, MetricRowFor, and the @hypequery/react hook rows inferred through @hypequery/serve) previously typed those same values as number, so a typed row claimed revenue: number while the runtime handed back "1234.56". The types now match runtime.

    createInMemoryBackend now serializes measure/metric values as strings too, so it stays a faithful double of the ClickHouse backend (values are still computed and ordered numerically — only the emitted columns are stringified).

    Breaking (types only): code that assigned a measure or metric value straight into a number — e.g. const revenue: number = row.revenue — will no longer compile. Parse at the edge instead: Number(row.revenue) (or parseFloat). Dimension values are unchanged; only aggregated measure/metric columns are affected.

    53f6149

Updated dependencies: @hypequery/datasets@0.8.0

0.7.0Minor

Minor changes

  • Semantic query result caching keyed by the query signature.

    createDatasetClient accepts cache: { ttlMs, staleWhileRevalidateMs, maxEntries, store, scope }; results are keyed by the canonical query signature (target, dimensions, measures, filters, ordering, pagination, grain, tenant scope, and cache scope), so different queries never share entries and tenant-scoped datasets are partitioned per tenant. Per-call controls via ExecutionContext.cache ({ ttlMs } to opt in, false to bypass, mode: 'refresh' to force a fresh execution and re-store). Errors are never cached, concurrent identical queries share one execution — including against async stores — and hits carry meta.cache = { hit, ageMs, stale? }.

    Custom stores (e.g. Redis) degrade gracefully: a failed read is treated as a miss and a failed write is dropped, so a store outage means "no caching", never failed queries. cache.scope partitions entries when the same query can run against different data sources — client-level for clients sharing one store, per-call when overriding the query builder at runtime (unscoped builder overrides skip the cache entirely).

    Serve metric and dataset entries with a cache value now cache results server-side with that TTL (previously the value only emitted Cache-Control headers, which POST semantic endpoints cannot use). Dataset endpoints now execute through the shared DatasetClient, so metric and dataset entries share one result cache per API.

    4fed0a7

Updated dependencies: @hypequery/datasets@0.7.0

0.6.1Patch

Patch changes

  • Accept schema-typed query builders at semantic entry points without casts.

    createQueryBuilder<Schema> results narrow column parameters to literal unions, type execute() rows concretely, and overload where, so they could not structurally satisfy QueryBuilderFactoryLike — passing the documented createDatasetClient({ queryBuilder: db }) / createAPI({ queryBuilder: db }) pattern failed to compile for typed-schema users.

    Public acceptance points (CreateDatasetClientOptions.queryBuilder, SemanticExecutionRuntime.builderFactory, serve's ServeConfig.queryBuilder) now take the new QueryBuilderFactoryInput, which admits both protocol-shaped and schema-typed builders. The strict QueryBuilderFactoryLike remains the internal call contract; the exported toQueryBuilderFactory adapter converts between them.

    98ad4b6

Updated dependencies: @hypequery/datasets@0.6.0

0.6.0Minor

Minor changes

  • Tighten semantic API type inference, add projection-aware dataset and metric result types, preserve projected rows through React analytics hooks, and add static manifest generation for Next.js clients.

    BREAKING (types only, no runtime change): dataset and metric result rows are now projection-typed. DatasetQueryResultFor / MetricResultFor rows — including the output types produced by InferApiType / InferAPIType and the result of createDatasetClient().execute() — no longer expose dimension keys or period unless the query selects them via dimensions / by. Code that read dimension fields off default (non-projected) result types must now pass the projection in the query it executes.

    d7259f0

Patch changes

  • Make context optional in initServe, matching the runtime (which already defaults a missing context to {}) and the documented auth-only usage. When omitted, query context is typed as Record<string, unknown>.

    5cec806

Updated dependencies: @hypequery/datasets@0.5.0

0.5.0Minor

Minor changes

  • Add a stable, hashable semantic contract export.

    @hypequery/datasets:

    • Add serializeSemanticContract, contractToStableJson, hashContract, and SEMANTIC_CONTRACT_VERSION. The contract is a deterministic, sorted projection of the dataset catalog (dimensions, measures, metrics, filters, relationships, tenant/time policy, limits) with a version marker and SHA-256 content hash, so logically equal models produce identical JSON and hashes. This is the shared source for snapshots, diffs, CI validation, docs, and codegen.
    • serializeSemanticContract accepts { includeSql } (default true) to omit raw SQL escape hatches for untrusted consumers.
    • Export the DatasetCatalogSource type.
    • Adds a dependency on @noble/hashes for the contract content hash, keeping the package isomorphic (no node:crypto).

    @hypequery/serve:

    • Expose the contract via a GET /contract endpoint (configurable through semanticPaths.contract) that serializes the registered datasets with their named metrics grouped onto each dataset. Raw SQL is redacted on this public endpoint by default.
    12ee5e6

Updated dependencies: @hypequery/datasets@0.4.0

0.4.0Minor

Minor changes

  • Add catalog-backed semantic tool generation and harden SQL exposure across MCP and generated tools.

    @hypequery/datasets:

    • Add generateDatasetTools with catalog, per-dataset, and per-metric modes, plus toOpenAITools, toAISDKTools, and toMcpTools adapters. Generated tools validate agent inputs against catalog metadata and redact SQL from results by default.
    • Expand the dataset catalog with default filter operators, filter value types, supported time grains, tenant requirement, orderable fields, max result limit, and measure filter counts.
    • Export SEMANTIC_FILTER_OPERATORS and SUPPORTED_TIME_GRAINS and use the shared operator list across packages.

    @hypequery/serve:

    • Build semantic input schemas and endpoint descriptions from catalog metadata, including supported grains, filters, relationships, and tenant scoping.

    @hypequery/mcp:

    • Distinguish measures from named metrics in dataset listing and introspection (metricCount no longer falls back to the measure count).
    • Add an includeSql server option (default false) so get_dataset_schema, query_dataset, and query_metric no longer expose generated SQL to agents unless explicitly enabled for trusted debugging.
    236ce16

Updated dependencies: @hypequery/datasets@0.3.0

0.3.0Minor

Minor changes

  • Add a route manifest to bridge serve and react for metric/dataset endpoints.

    @hypequery/serve now exposes api.manifest() (and ServeBuilder.manifest()), a serializable map of every query/metric/dataset key to its { method, path } (full path, including the base path; datasets keyed as dataset:<name>).

    @hypequery/react's createHooks/createAnalyticsHooks accept a manifest option to resolve client routes without importing server code into the bundle. This fixes metric/dataset hooks (POST routes whose paths differ from their map keys) silently defaulting to GET {baseUrl}/{key}. Hooks now also derive routes from a runtime api object via api.manifest(), and throw a clear error when a semantic (dataset:) key has no resolved route instead of calling the wrong URL.

    e64d6f4
  • Generate per-dataset/per-metric request schemas with enumerated fields.

    Metric and dataset endpoints previously typed their request body as dimensions: string[] / filters[].field: string, so the OpenAPI spec (and hypequery dev docs) advertised "array of arbitrary strings" and clients could not be code-generated with valid field names.

    Endpoints now build their Zod input schema from the dataset/metric contract: dimensions, measures, filters[].field, and orderBy[].field are emitted as enums of the valid field names, and array sizes are bounded by the dataset's declared limits. The enums are a superset-safe mirror of the runtime validators — they never reject a field the validator would accept — so behavior is unchanged while docs and codegen become precise.

    2c1425f
  • Add offset pagination with reliable hasMore and infinite-query hooks.

    Metric and dataset queries that specify a limit now return meta.pagination = { limit, offset, hasMore }. hasMore is exact: the executor over-fetches one row (LIMIT n + 1) and trims it, so no separate count query is needed. The extra field is included in the serve endpoints' OpenAPI response schema (surfaced when meta is requested via x-include-meta).

    @hypequery/react adds useInfiniteQuery (and useInfiniteMetric / useInfiniteDataset on createAnalyticsHooks) built on TanStack Query's infinite query. They advance the offset using meta.pagination, automatically requesting meta, so paginating a dataset is just fetchNextPage() until hasNextPage is false.

    Metric endpoints now treat the page-size limit like dataset endpoints: a configurable maxLimit on the metric entry (defaulting to the dataset's limits.maxResultSize, else 1000), with over-limit requests clamped rather than rejected, and a default cap applied so a metric query is never unbounded.

    69b67c0
  • Add first-class semantic APIs and production authentication primitives to the Serve runtime.

    • createAPI and the builder can register datasets and metrics backed by the unified dataset client, execute them programmatically, expose typed HTTP endpoints, and carry semantic metadata through caching and lifecycle hooks.
    • Add standalone Node and Fetch adapters plus reusable API builder methods for composing and describing an API.
    • Add context authentication, remote JWKS verification, analytics token issuance, and configurable auth paths for separating browser authentication from analytics endpoints.
    • Wire configured CORS behavior through createAPI, including preflight and response headers.
    75349dd
  • Thread dataset field types through the generated API type for typed React hooks.

    @hypequery/datasets now exports typed query/result helpers (DatasetQueryFor, DatasetRow, DatasetQueryResultFor, and the DatasetDimensionNames/DatasetMeasureNames/DatasetOrderableNames name helpers, plus the metric equivalents).

    @hypequery/serve's SemanticDatasetEndpointMap now specializes each dataset endpoint to its concrete instance, so InferAPIType carries field-level types. With @hypequery/react, useDataset(name, input) gets autocomplete and type-checking for dimensions/measures/orderBy, and result rows are typed by the dataset's dimensions and measures.

    Metric endpoints remain on the loose MetricQuery/MetricResult types for now: MetricRef does not preserve its dataset's concrete dimension keys, so field-level metric typing requires threading the dataset generics through MetricRef — tracked as a follow-up.

    2f3c293
  • Carry a metric's dataset type through MetricRef for field-level metric hooks.

    MetricRef / GrainedMetricRef / MetricHandle (and BaseMetricRef / DerivedMetricRef) gain an optional TDataset type parameter that defaults to the previous wide instance, so existing usages are unchanged. DatasetInstance.metric() now returns a ref carrying its dataset's concrete dimension/measure types.

    @hypequery/serve's SemanticMetricEndpointMap uses this to specialize each metric endpoint, so via @hypequery/react useMetric(name, input) gets autocomplete and type-checking for dimensions/orderBy, and result rows are typed by the dataset's dimensions plus the metric's value column. This completes the typed-hooks work started for datasets; metric endpoints degrade gracefully to loose string fields when a ref has been widened.

    278924e

Updated dependencies: @hypequery/datasets@0.2.0

0.2.1Patch

Patch changes

  • Security update: upgrade openapi-typescript dependency from 7.4.2 to 7.13.0 to resolve high-severity fast-uri vulnerabilities (CVE path traversal and host confusion issues).

    de7f4b4
0.2.0Minor

Minor changes

  • Expand the current object-style query({ ... }) API so runtime auth and tenant metadata work the same way as the older builder-first flow.

    • support auth, requiresAuth, tenant, requiredRoles, requiredScopes, and custom directly on object-style query definitions
    • preserve that metadata on standalone queries created via query({ ... }) so it survives when reused through serve({ queries })
    • enforce object-style auth requirements and public routes through the serve runtime
    • include object-style auth metadata in endpoint descriptions and runtime inspection output
    • apply object-style tenant overrides through the serve runtime

    This brings the object-style API closer to feature parity with the builder-first serve path and makes it the clearer default for new integrations.

    66a6ca4
0.1.1Patch

Patch changes

  • Add getHeader and apiKeyAuth helpers for header-based auth, plus structured auth errors for missing/invalid credentials.

    5c60f20
0.1.0Minor

Minor changes

  • Add per-request header resolvers to React hooks and improve serve multi-tenant ergonomics, ESM-safe startup, and docs alignment.

    e15ce16
  • Implement auth guard enhancements with type-safe authorization. Add createAuthSystem for compile-time role/scope safety, shared authorization validators (checkRoleAuthorization, checkScopeAuthorization), comprehensive integration tests, and OpenAPI documentation for auth requirements. Mark middleware functions (requireAuthMiddleware, requireRoleMiddleware, requireScopeMiddleware) as deprecated in favor of the declarative guard API.

    3a2aaea

Patch changes

  • Ensure the release workflow builds every package before running the Changesets publish step so the CI release ships with fresh dist artifacts.

    ed06077
0.0.9Patch

Patch changes

  • Republish so CI builds dist output before publishing and the package ships compiled files.

0.0.7Patch

Patch changes

  • Enable query execution stats logging in dev server. Removed "Coming soon!" placeholder as the feature is already implemented via serveDev.

    4bbab53
0.0.6Patch

Patch changes

  • Fix validation for queries with void input schema.

    The buildContextInput function now correctly returns undefined instead of an empty object {} for requests with no body or query parameters. This fixes a bug where queries using z.void() input validation would fail with "Expected void, received object" errors.

    Changes:

    • Fixed buildContextInput in pipeline.ts to return undefined for empty requests
    • Added test to prevent future regressions
    • Updated vite-starter example to display validation errors in UI
    5acbaf3
0.0.4Patch

Patch changes

  • Pre-release improvements:

    • CLI loading spinners, serve runtime fixes, and React integration updates
    f99e80e

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.