Skip to content

feat: infer query/command argument types from the handler signature#2

Merged
SomaticIT merged 1 commit into
mainfrom
claude/svelte-remote-functions-lib-nhgm2b
Jul 14, 2026
Merged

feat: infer query/command argument types from the handler signature#2
SomaticIT merged 1 commit into
mainfrom
claude/svelte-remote-functions-lib-nhgm2b

Conversation

@SomaticIT

Copy link
Copy Markdown
Contributor

What

Allows declaring query, query.batch, query.live and command with just a handler — the client-side callable gets the exact same signature as the handler's parameter, type-checked by TypeScript, with no runtime validation:

const my_query = query(({ filter, sort }: { filter?: string; sort?: string }) => ...);
my_query({ filter: 'name eq Claude' }); // type-checked against the handler's parameter

SvelteKit can't offer this because remote-function arguments cross a network trust boundary; locally the handler and caller live in the same bundle, so the handler's signature is authoritative.

Semantics

  • One-argument rule kept: exactly one argument, still used as the stable-JSON cache key (same dedup rules).
  • Runtime behavior is passthrough — equivalent to 'unchecked'. Schema and 'unchecked' declarations are unchanged (kit parity preserved).
  • form is excluded: form data comes from the DOM as strings and needs coercion + validation, so forms still require a schema or 'unchecked'.
  • Bare-handler arity is detected via fn.length: zero-parameter handlers stay argument-less (the dev-time guard against accidental arguments is kept). Documented caveat: handlers with only default/rest parameters ((arg = {}) => ...) have length 0 and are treated as argument-less.

Implementation

  • parse_declaration() in src/lib/validation.ts is the single choke point all factories flow through: a bare handler with fn.length >= 1 now maps to 'unchecked'. This activates the feature for query, command and query.live with no factory changes.
  • query.batch additionally accepts the bare-handler form (create_query_batch now uses parse_declaration; a batch handler always takes the args array, so a bare declaration simply means unvalidated).
  • New TypeScript overloads on query / query.batch / query.live (in src/lib/query/index.ts) and command (in src/lib/command.svelte.ts), placed after the existing no-argument overloads so zero-param handlers keep resolving to LocalQueryFunction<void, Output>.

Docs & tests

  • New DIFFERENCES.md section "Validation is optional for query and command" with rationale and the arity caveat; README examples updated; playground switched to the inferred form for getLikes/addLike.
  • 8 new tests (103 total): inferred args for all four factories, passthrough (no coercion), cache-key dedup, dev-guard regression for default-parameter handlers, and @ts-expect-error compile-time assertions.

Verification

  • svelte-check: 0 errors / 0 warnings · vitest: 103/103 · svelte-package + publint: clean
  • Playground driven by Playwright/Chromium: 12/12 checks with the inferred-form declarations

🤖 Generated with Claude Code

https://claude.ai/code/session_01EJj9ab3v9HjudgX8L1x821


Generated by Claude Code

Allow declaring query, query.batch, query.live and command with a bare handler
whose parameter type becomes the client-side argument type — no schema, no
'unchecked'. The value passes through unvalidated (there is no trust boundary
locally); the one-argument rule and stable-JSON cache keys are unchanged. form
still requires a schema or 'unchecked' since FormData needs coercion/validation.

Documented as a new difference in DIFFERENCES.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EJj9ab3v9HjudgX8L1x821
@SomaticIT
SomaticIT merged commit f56d177 into main Jul 14, 2026
1 check passed
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.

2 participants