Skip to content

Split TaggedRequestFor into Query/Command factories and enforce helper surfaces by request kind#676

Merged
patroza merged 10 commits intomainfrom
copilot/change-taggedrequestfor-object
Apr 21, 2026
Merged

Split TaggedRequestFor into Query/Command factories and enforce helper surfaces by request kind#676
patroza merged 10 commits intomainfrom
copilot/change-taggedrequestfor-object

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 21, 2026

TaggedRequestFor previously produced a single request factory, so requests were not explicitly typed as query vs command and Vue client helpers exposed mixed query/mutation APIs on all operations. This change introduces request-kind tagging at definition time and narrows helper exposure to match that kind.

  • Request factory model

    • TaggedRequestFor(moduleName) now returns an object with:
      • Query
      • Command
      • moduleName
    • Generated request classes are stamped with type: "query" | "command" in addition to id and moduleName.
  • Core request typing

    • Req now includes a required type discriminator ("query" | "command"), enabling downstream type-level gating.
  • Vue client helper gating

    • Query requests expose only query-side helpers (query, suspense, request).
    • Command requests expose only mutation-side helpers (mutate, request).
    • Query operations no longer expose command-related helpers (fn, wrap).
    • Command-oriented generated helpers (helpers.*Request) are now restricted to type: "command" requests only.
    • Runtime mapping and mapped helper types were updated together to keep API surface and type system aligned.
  • Call-site and test updates

    • Request declarations were migrated from TaggedRequestFor("X")<...>()(...) to explicit TaggedRequestFor("X").Query / .Command.
    • Updated affected tests/stubs in effect-app, infra, and vue.
    • Added type-level assertions in Vue tests to confirm query requests do not expose command helpers.
    • Renamed Vue request helper usage from .fetch to .request in types/tests/docs.
    • Added a changeset for effect-app and @effect-app/vue.
const Req = TaggedRequestFor("Something")

class GetSomething extends Req.Query<GetSomething>()("GetSomething", { id: S.String }, { success: S.String }) {}
class DoSomething extends Req.Command<DoSomething>()("DoSomething", { id: S.String }, { success: S.Void }) {}

// Vue client surface is kind-aware:
// client.GetSomething.query / .suspense / .request
// client.DoSomething.mutate / .request
// client.GetSomething.fn / .wrap -> not available

@patroza
Copy link
Copy Markdown
Member

patroza commented Apr 21, 2026

@copilot remove fn/wrap and other command related helpers from queries, and enforce Command helpers to work with type: “command” only

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 21, 2026

@copilot remove fn/wrap and other command related helpers from queries, and enforce Command helpers to work with type: “command” only

Done in a6e39b0.

  • Removed query-side command helpers (fn / wrap) from client.<QueryOp>.
  • Kept command helpers only on command operations.
  • Restricted helpers.*Request generation to command requests (type: "command") only.
  • Updated type tests to assert query requests no longer expose command helpers.

@patroza
Copy link
Copy Markdown
Member

patroza commented Apr 21, 2026

@copilot rename .fetch to .request

Copilot AI changed the title Split TaggedRequestFor into Query/Command factories and enforce helper surface by request kind Split TaggedRequestFor into Query/Command factories and enforce helper surfaces by request kind Apr 21, 2026
Copilot AI requested a review from patroza April 21, 2026 18:06
@patroza
Copy link
Copy Markdown
Member

patroza commented Apr 21, 2026

@copilot update makeClient.test.ts, to add a check that .wrap is available on commands.
and change the

  // @ts-expect-error query requests no longer expose command helpers
  const e0 = client.GetSomething2WithDependencies.wrap().handle // not available as we require dependencies not provided by the runtime
  // @ts-expect-error query requests no longer match Command.wrap mutation signature
  const e000 = Command.wrap(client.GetSomething2WithDependencies)().handle // not available as we require dependencies not provided by the runtime
  // @ts-expect-error query requests no longer expose command helpers
  const e00 = client.GetSomething2WithDependencies.wrap((_) => _ as Effect.Effect<number, never, never>).handle(
    null as any
  )

tests to actually test on Commands (with and without dependencies), as Queries don't have these helpers anymore anyway.

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 21, 2026

@copilot rename .fetch to .request

Done in fd01992.

Renamed the Vue request helper from .fetch to .request across the client surface, mapped types, test usage, and changelog docs.

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 21, 2026

@copilot update makeClient.test.ts, to add a check that .wrap is available on commands.
and change the

...

Done in c059b81.

I updated makeClient.test.ts to add a command-side .wrap check (client.DoSomething.wrap(...)) and replaced the query-side .wrap assertions with direct @ts-expect-error checks that query requests don’t expose .wrap and don’t satisfy Command.wrap(...).

No UI changes in this PR (type-test only), so screenshot is not applicable.

@patroza
Copy link
Copy Markdown
Member

patroza commented Apr 21, 2026

@copilot add jsdoc on all:

  • Command / Query - explain that Commands mutate state, but don't return (complex) values, and Queries read state but don't modify it
  • .query/.suspense
  • .request - it just sends a request to the endpoint and receives a response\
  • .mutate - same like .request (link) but also invalidates query caches. explain how the namespaced cache invalidation works and that it can be configured in the client options.

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 21, 2026

@copilot add jsdoc on all:

  • Command / Query - explain that Commands mutate state, but don't return (complex) values, and Queries read state but don't modify it
  • .query/.suspense
    ...

Done in 312cdf1.

I added JSDoc for TaggedRequestFor(...).Query and .Command, plus the Vue client helpers .query, .suspense, .request, and .mutate in makeClient.ts.

.mutate docs now also describe default namespaced cache invalidation behavior and that it is configurable via queryInvalidation in client options.

No UI changes in this PR, so screenshot is not applicable.

@patroza
Copy link
Copy Markdown
Member

patroza commented Apr 21, 2026

@copilot MutationExtWithInput still seems to have a stray function definition that should no longer be there:

  /**
   * Send the request to the endpoint and return the raw Effect response.
   * Also invalidates query caches using the request namespace by default.
   * Namespace invalidation targets parent namespace keys
   * (for example `$project/$configuration.get` invalidates `$project`).
   * Override invalidation in client options via `queryInvalidation`.
   */
  (i: I): Effect.Effect<A, E, R>

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 21, 2026

@copilot MutationExtWithInput still seems to have a stray function definition that should no longer be there:

  /**

...

Addressed in c303476.

I reshaped MutationExtWithInput from an interface call-signature form to a type-alias intersection while preserving the existing .mutate(...) behavior and helper typing.

No UI changes in this PR, so screenshot is not applicable.

Copilot stopped work on behalf of patroza due to an error April 21, 2026 18:26
@patroza
Copy link
Copy Markdown
Member

patroza commented Apr 21, 2026

@copilot actually I was wrong, please undo it.

@patroza patroza marked this pull request as ready for review April 21, 2026 18:27
Copilot stopped work on behalf of patroza due to an error April 21, 2026 18:28
@patroza patroza merged commit 57db551 into main Apr 21, 2026
2 of 3 checks passed
@patroza patroza deleted the copilot/change-taggedrequestfor-object branch April 21, 2026 18:30
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