Skip to content

fix(engine): emit query parameters that admit QueryValue - #196

Merged
Upd4ting merged 14 commits into
mainfrom
fix/query-parameters-admit-query-value
Nov 12, 2026
Merged

Upd4ting merged 14 commits into
mainfrom
fix/query-parameters-admit-query-value

Conversation

@Upd4ting

@Upd4ting Upd4ting commented Nov 12, 2026 •

Copy link
Copy Markdown
Member

Why

The Builder has to write period-bound and comparison queries that typecheck on the current @antelopejs/interface-api. Since 0.3.1, @Parameter(name, "query") refuses a handler argument whose type does not admit QueryValue (string | true | undefined). @antelopejs/dms-builder@0.10.2 emits @Parameter("from", "query") from: string. The engine typechecks every page it writes, so in a project on 0.3.2 the Builder refuses to save the query with TS1239. That is the known issue in AntelopeJS/template-dms-connect-demo#21, where the assistant could not write the period-bound queries (step 7).

This repository did not notice because its lockfile still resolved interface-api 0.3.0.

Changes

  • Emitter (engine/query-emit.ts). A query-string parameter is declared string | true | undefined and narrowed in the call: new Date(String(from)) for a date, String(status) for text. Number(x) and x === "true" already accept the union, so they are unchanged. A path parameter keeps string. The type is written out rather than imported as QueryValue, because interface-api 0.3.0 does not export that name and a project may still resolve it.
  • Read-back (engine/query-structure.ts). The Builder reads String(x) and new Date(String(x)) back as the parameter x. Routes written by 0.10.2 still read back.
  • Ranges. The module, the modules it scaffolds (engine/project-scaffold.ts) and both playgrounds now require interface-api >=0.3.2 <1.0.0. The lockfiles resolve 0.3.2, and the README table is regenerated.
  • The module's own routes (routes/builder.ts, routes/database.ts). Their 16 query parameters had the same bug, which the 0.3.2 floor exposes. They now take QueryValue and keep only real text through routes/query-text.ts. A ?ref with no value becomes "", which every engine operation answers as not found. Before, it reached them as true.
  • Docs. 10.a-chart-without-code.md shows the new route shape.
  • Checks race (root package.json). antelopejs-checks runs typecheck and lint at the same time. typecheck ran rimraf dist && tsc on the interface while type-aware lint read its declarations. When lint loaded first, every interface type became an error type: 2,514 lint errors on this PR's first four CI runs, and the failures main has had on several pushes. A new checks:prepare script builds both dist folders once before the steps start. typecheck still runs a full tsc over both packages and still emits both dist folders, but no longer deletes them first. With no interface dist, oxlint reproduces exactly those 2,514 errors locally.
  • Verify skill. A new queries journey drives a period-bound series and a comparison card through the Builder, rebuilds, and calls the generated routes.
flowchart LR
  A[AddQuery with $param from/to] --> B[emit route]
  B -->|0.10.2| C["from: string"] --> D{engine typecheck on interface-api 0.3.2}
  D -->|TS1239| E[typecheck_failed, nothing written]
  B -->|this PR| F["from: string | true | undefined<br/>new Date(String(from))"] --> G{engine typecheck}
  G -->|ok| H[page written, route answers]
Loading

Proof

The failing test lands before the fix: 58424f5 adds it on top of the 0.3.2 floor (099fc69), and e0c84c9 fixes the emitter.

At 58424f5, mocha dist/test/query-response.spec.js dist/test/query-route-read.spec.js:

  11 passing (7s)
  6 failing
  1) reading whether a route compares
     AssertionError: expected undefined to deeply equal [ { kind: 'param', …(2) }, …(1) ]
  2) how a query's answer is arranged
     Error: AddQuery failed: {"code":"typecheck_failed","diagnostics":[{"file":".../board/page.ts","line":61,"message":"Unable to resolve signature of parameter decorator when called as an expression."},{"file":".../board/page.ts","line":62,"message":"Unable to resolve signature of parameter decorator when called as an expression."}]}
  3) how a query's answer is arranged
     Error: AddQuery failed: {"code":"typecheck_failed","diagnostics":[... lines 61 to 64, same message ...]}
  4) how a query's answer is arranged
     AssertionError: expected undefined to equal true
  5) how a query's answer is arranged
     Error: ConfigureQuery failed: {"code":"not_found","ref":"/shop/board@revenueTrend"}
  6) how a query's answer is arranged
     Error: ConfigureQuery failed: {"code":"not_found","ref":"/shop/board@revenueTrend"}

At head, the same two files plus project-without-tsconfig.spec.js:

  24 passing (10s)

The full engine suite (pnpm --filter @antelopejs/dms-builder test:unit) shows 527 passing (4m), and pnpm run checks shows Checks: 12 passed, 0 failed.

The generated route shape, compiled with tsc --strict --experimentalDecorators --emitDecoratorMetadata against each interface-api version:

interface-api 0.3.0, old shape: typechecks
interface-api 0.3.0, new shape: typechecks
interface-api 0.3.1, old shape: error TS1239: Unable to resolve signature of parameter decorator when called as an expression.
interface-api 0.3.1, new shape: typechecks
interface-api 0.3.2, old shape: error TS1239: Unable to resolve signature of parameter decorator when called as an expression.
interface-api 0.3.2, new shape: typechecks

Builder end to end

The verify-dms-builder journey queries/a-period-bound-series-and-a-comparison-card-typecheck-and-answer-over-http follows the step 7 scenario of template-dms-connect-demo#21 on a DMS with interface-api 0.3.2.

With the emitter from 58424f5, the journey fails:

FAIL queries/a-period-bound-series-and-a-comparison-card-typecheck-and-answer-over-http
    06-the-engine-typechecks-both-pages-of-generated-code: the series is added ({"ok":false,"error":{"code":"typecheck_failed","diagnostics":[{"file":".../playground/src/lab.ts","line":23,"message":"Unable to resolve signature of parameter decorator when called as an expression."}, ...]}})
    06-the-engine-typechecks-both-pages-of-generated-code: the comparison card is added ({"ok":false,"error":{"code":"typecheck_failed", ...}})
    critical step 06-the-engine-typechecks-both-pages-of-generated-code failed

At head, it passes 16 of 16 checks:

  • Both AddQuery calls succeed, and the page imports no QueryValue.
  • The project rebuilds.
  • The owner's GETs answer the seeded orders: paid=2,refunded=1 for 50 to 20 days ago and paid=2,pending=1 for the last 20 days.
  • The comparison card answers value 705 against previousValue 530, a delta of 33.02 percent.

The whole skill run (smoke and all 17 features) passes 59 of 59 journeys. Its verdict is PASS+NOTES.

Attention

  • A missing or valueless query value now becomes text: String(undefined) is "undefined" and a bare ?x is "true". For date bounds this is still an Invalid Date, as before. For a text filter it was already wrong under the string type, since undefined or true reached the model. The preview refuses a missing value, while the route does not. Matching it would mean answering 400, which changes the route's return type and how the Builder reads the route back. That belongs in its own change.
  • Pages written by 0.10.2 keep from: string until their query is reconfigured, so they still fail typecheck on interface-api 0.3.1 and later. This PR does not migrate them.
  • @antelopejs/dms-builder needs a release for projects to get the fix. The interface package changes only in its docs.

@Upd4ting
Upd4ting merged commit a02aa04 into main Nov 12, 2026
14 checks passed
@Upd4ting
Upd4ting deleted the fix/query-parameters-admit-query-value branch November 12, 2026 13:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant